TAIFOONOne API, every route
POST /v1/compare with a source chain, destination chain, token addresses and an amount. You get every protocol that can serve the pair, ranked best-first, with fees, gas and a provenance label saying how each number was obtained. No key is required to quote.
The gateway is read-only. It quotes routes and builds deposit calldata for your user to sign — it never holds funds and never signs anything on their behalf.
Quoting a route
Token fields take contract addresses, not symbols, because the same symbol means different contracts on different chains. Amounts are strings in the token's own base units — 1000 USDC (6 decimals) is "1000000000".
curl -X POST https://arc.taifoon.dev/v1/compare \
-H 'Content-Type: application/json' \
-d '{
"src_chain_id": 8453,
"dst_chain_id": 42161,
"src_token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"dst_token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"input_amount": "1000000000",
"output_amount": "0"
}'The response carries the winning slug in best and one entry per protocol in rows, already sorted:
{
"best": "across",
"protocol_count": 25,
"value_usd": 1000.0,
"rows": [
{
"protocol_slug": "across",
"protocol_name": "Across Protocol V3",
"total_cost_usd": 0.955239,
"protocol_fee_usd": 0.106841,
"gas_cost_usd": 0.008398,
"amount_out_usd": 999.053159,
"fee_bps": 28,
"source": "vendor",
"priced": true,
"executable": true,
"best": true
}
]
}Reading a quote honestly
Two fields decide how much weight a number deserves. Read them before you read the price.
| Field | Meaning |
|---|---|
source: "vendor" | The fee came from the protocol's own live API. This is a real price. |
source: "synthetic" | The fee came from a constant in our catalogue. It is an estimate, and it can be wrong. |
priced: false | Nobody has priced this protocol yet. A 0 fee here means unknown, not free. |
executable: true | A fill path exists for this protocol. Non-executable rows are omitted from compare. |
Ranking is by provenance first, then cost. A route with a real vendor quote outranks a cheaper unpriced one. This is deliberate: ranking on cost alone put unpriced placeholders claiming 0 fees above the one protocol publishing real numbers, which made the cheapest-looking route the least trustworthy. Unpriced routes stay in the list — nothing is hidden — but they cannot win.
The rest of the surface
| Endpoint | What it gives you |
|---|---|
POST /v1/compare | Every protocol for a pair, ranked, with full cost breakdown. |
POST /v1/route | The same set ranked by coverage tier rather than cost. |
POST /v1/quote | A single protocol's quote when you already know which you want. |
GET /v1/chains | Supported chains with per-chain protocol coverage counts. |
GET /v1/catalogue | All 32 protocols, their families, fee models and chains. |
GET /v1/mode | Whether the instance is live or simulating, and its network. |
GET /v1/health | Liveness. |
GET /v1/cctp/fees/:src/:dst | Circle CCTP v2 fee tiers for a pair, live from Circle. Base → Arc: standard 0 bps, fast 0.325 bps. |
GET /v1/cctp/attestation/:src/:tx | Attestation status for a burn — pending_confirmations, then complete with the message and attestation for the destination mint. |
POST /v1/bridge/plan | The bridge as a service: your exact approve + burn transactions, Circle's live fee tier, and the service-fee split for the partner in X-Arc-Partner. |
GET /v1/bridge/status/:src/:tx | Attestation progress for a burn made through the plan. |
GET /v1/bridge/relay-plan/:src/:tx | Once complete: the exact receiveMessage transaction for any funded relayer on the destination. |
GET /v1/bridge/events/:chain?sender=&partner=&blocks= | The router's Bridged ledger, decoded — a reseller's volume and share, or one wallet's bridges, read from chain by us. |
GET /v1/bridge/queue | Every bridge we service that is not resolved yet — state, age, what happens next, and the resolution when past SLA. |
GET /v1/bridge/tx/:chain/:hash | One bridge in the standardized shape: burned → attested → minted (Circle) or sent → done (LI.FI), stuck after 45 min unattested / 15 min unforwarded, with its transition timeline. |
POST /v1/bridge/track | Register a send for the state machine to follow ({ chain_id, tx_hash }; classified from the receipt). Router burns are found on their own. |
GET /v1/bridge/ledger | All records + summary: volume, fee split, measured p50/p90 burn→mint by tier; ?sender= / ?partner=t3rn. |
GET /v1/bridge/proof/:chain/:tx?log_index= | The receipt-inclusion proof (ReceiptInclusion) plus the Base verifier's verdict by eth_call. Human view at www.taifoon.io/proof/tx/:chain/:tx. |
POST /v1/lifi/quote | Jumper (LI.FI) for a pair: the tool it resolved to, the amount out, and a ready-to-sign transaction. Base → Arc is listed. |
From a browser, call these through /api/arc/* on this origin rather than arc.taifoon.dev directly — the same-origin proxy avoids a CORS preflight and shares one rate-limit budget instead of one per tab.
Bridging to Arc
Arc (chain 5042) is served by Circle CCTP v2, the same rail Arc’s own App Kit uses. USDC is burned on the source chain and minted on Arc as native USDC (ERC-20 facade 0x3600000000000000000000000000000000000000). Standard transfers cost 0 bps; the route appears in /v1/compare with source: "vendor" because the fee is read from Circle live.
The flow is approve → depositForBurn (your wallet, on the source chain) → attestation (Circle) →receiveMessage (any relayer, on Arc). One thing to know before your first transfer: gas on Arc is USDC, so whoever calls receiveMessage there needs a little native USDC. In practice Arc mints are picked up by third-party relayers — we measured a Base burn minted on Arc 9 seconds later, with roughly 90 mints landing every five minutes — and /v1/bridge/relay-planis the fallback if nobody does. A recipient with no USDC on Arc has two other doors: Arc's own fiat onramp (App Kit), or via: "lifi"in the plan, where Jumper's tool delivers on Arc with no destination step.
Bridge as a service: Taifoon operates, partners resell
Use it directly at taifoon.io/bridge; the full call-by-call walkthrough with real numbers is in Bridge API, step by step.
POST /v1/bridge/plan turns the flow above into one call for an integrator. Send { src_chain_id, dst_chain_id, recipient, amount, speed? } (amount in USDC base units) and, if you are reselling, an X-Arc-Partner: <your-name> header. The answer is a list of steps your user signs in order — approve, then one burn — plus Circle's live fee tier and a full breakdown of what is burned and what is kept.
On Base the burn goes through CctpFeeRouter at 0xedF0C3B2BA863179780F7c595B9038b4075F08ed: one transaction pulls the amount, keeps the service fee (10 bps, hard-capped at 1% in the contract), pays the reseller's share (50%) to its registered vault, and hands the rest to Circle's TokenMessengerV2. The partner name is hashed (keccak256) into the Bridged event, so a reseller's volume and earnings are readable from the chain — and provable cross-chain with the same receipt proof the V5 verifier checks. An unregistered partner still bridges; its share simply goes to the operator until a vault is on file. On chains without a router the plan falls back to depositForBurn directly, with no service fee.
The service signs nothing. After the burn, poll /v1/bridge/status; when it reports complete, /v1/bridge/relay-plan returns the receiveMessage transaction any funded address may send on the destination. quote(amount, partner) on the router answers the same split without a transaction.
Rate limits
Quoting is unauthenticated and limited per IP: 30 requests per second with a burst of 60. Exceeding it returns 429 with an x-ratelimit-after header giving the seconds to wait. Reference data (/v1/chains, /v1/catalogue) is edge-cached, so polling it costs you nothing against that budget.
Executing a bridge
The gateway builds the deposit calldata; your user's wallet signs and sends it. Call /v1/donut/calldata/depositwith the route you chose and submit the returned transaction from the user's own address.
Fills are not part of the public gateway. /v1/fill spends solver funds and is reachable only over the authenticated solver path — never the browser proxy. If you are integrating a solver rather than a front end, that is a separate credentialed surface; ask us for it directly.
Bringing your own token
Attaching a token means registering it with the solver's market-making layer: declare the chains it lives on and a risk tier, and it inherits the same quoting, cross-chain inventory rebalancing and edge capture as every other asset. A token nobody has tuned starts on the most conservative tier — wide spreads, a small position cap, and a minimum edge floor set against measured bridge costs rather than assumed ones.