Skip to main content

Cross-chain swap (bridge aggregation)

GET 

/v1/actions/swap/x-chain

Quote a cross-chain swap across all supported bridge aggregators (Across, LI.FI, Squid, Stargate, Symbiosis, DZap, Circle CCTP, …) and build the winning transactions. Omit account for quote-only. Include account to build full transaction calldata. Each build quote carries approvalTarget/approvalRequired, and actions.permissions holds one ERC-20 approve per unique spender (composed routes share the 1delta composer; plain bridges use their own deposit contract) with a spender field — execute only the permission whose spender equals your chosen quote's approvalTarget. When fromChainId equals toChainId the request falls back to the spot meta-aggregator (same response shape with aggregator instead of bridge per quote, marked fallback: 'spot'). Uncurated tokens: a leg missing from its chain's curated token list is resolved on-chain (per chain — the two legs live on different chains), so uncurated EVM tokens still quote; such a leg has no USD price. Non-EVM legs (Solana) are not resolved this way. An address that is not an ERC-20 answers Unknown Token: <address> on <chainId>. CCTP (CCTP = Standard finality, free; CCTP Fast = soft finality, 0–13 bps) is Circle's burn-and-mint for its native USDC with the Forwarding Service submitting the destination mint — one transaction, output is exact (amount − maxFee, Circle keeps the whole ceiling). The output token must be the destination's Circle USDC; any input is swapped into USDC on the source. Poll /v1/data/bridge/status for the mint (toHash). Solana: CCTP serves Solana both ways. Out of Solana the burn is our own instruction, co-signed by a throwaway event-account key, and locks ~0.003 SOL of rent (eventRentLamports) that /v1/actions/bridge/cctp/reclaim returns after 5 days. Into Solana the mint goes to the receiver's USDC token account; when that account does not exist Circle creates it first, at a higher forwarding fee (recipientSetup: true).

Plain-text reference — GET /v1/actions/swap/x-chain

Parameters

ParameterInTypeRequiredDescription
fromChainIdquerystringyesSource chain ID
toChainIdquerystringyesDestination chain ID
tokenInquerystringyesInput token address on the source chain (zero address for native)
tokenOutquerystringyesOutput token address on the destination chain (zero address for native)
amountquerystringyesInput amount in wei
slippagequerynumberyesSlippage tolerance (basis points)
accountquerystringnoAccount address on the source chain. Include to build transactions, omit for quote-only.
receiverquerystringnoReceiver address on the destination chain (defaults to account)
orderqueryCHEAPEST, FASTESTnoRoute preference
bridgesquerystringnoComma-separated bridge filter (e.g. Across,LI.FI). Defaults to all supported bridges.
destinationGasquerystringnoNative gas to deliver on the DESTINATION with the transfer — a decimal in the destination's native units (0.005 = 0.005 SOL, 0.0003 = 0.0003 ETH) or auto (Solana only: 0.005 SOL). Each quote row then carries destinationGas: { amount, decimals, mode, paidFrom, cost? } (base units). mode: 'native-drop': the bridge drops it inside the route's own transaction — Mayan and Relay (EVM destinations; Relay's topupGas) out of the bridged amount (paidFrom: 'output', Relay's cost.usd), LayerZero OFT and Stargate Fast (taxi) via an executor drop priced into msg.value (paidFrom: 'value', cost = the extra value; every LayerZero executor caps a drop per destination, e.g. 1.01 PLUME into Plume, and a larger request drops those routes). mode: 'batch': every other route carries a gas LEG beside it, bought with the source native — a NEAR Intents EXACT_OUTPUT deposit (orderId = its status key for /v1/data/bridge/status?bridge=NEAR Intents), or, where NEAR Intents serves neither chain (Plume), a Gas.zip deposit sized to at least that amount (bridge: 'GasZip', status by its tx hash) — paidFrom: 'gas-leg', cost incl. usd, whose transaction is destinationGas.transaction — send it with the route's transaction in one EIP-5792 wallet_sendCalls, or right after it. From Solana the leg's SOL transfer is appended to the route's own transaction instead (bundled: true, no destinationGas.transaction) — one signature; where that cannot be built (a second signer, over the size limit) the leg rides beside it as above. On EVM a route that runs through the 1delta composer carries a NEAR Intents leg inside its own transaction too (bundled: true — a native sweep to the deposit address prepended to the composer program, its value added to tx.value); routes that call their own router (Squid's own, Relay, LI.FI …) are never wrapped, since the router would see the composer as the sender and a depositor-refund would land there. A drop bridge that does not commit to the amount, and every other route when the gas leg cannot be quoted, is dropped rather than ranked. With gas requested, rows are ranked by tradeOutputNetOfGas: tradeOutput minus what the gas costs ON TOP of it (a gas leg, a msg.value drop), converted at current prices — an output drop is already inside tradeOutput. Without a price the order is by tradeOutput. Independently of gas, every EVM row with a transaction reports nativeFee: { amount, decimals, usd? } (source gas coin base units) when its tx.value exceeds the native input — Axelar execution gas on Squid, LayerZero messaging fees, Fly's fee — which tradeInput does not include; the ranking deducts it too (tradeOutputNetOfGas), and a client sending a native Max must leave it in the wallet. On Solana the minimum is 0.00089088 SOL, the rent-exempt balance a new wallet must receive. The gas goes to receiver (the end user).
permitqueryoff, auto, requirednoPermit mode: 'off' (default) approve-only; 'auto' additionally offers ONE composer-scoped permit signature covering every composed route (router-spender bridges are named in permitSkipped and keep their approve); 'required' fails with PERMIT_UNAVAILABLE when no composed route can take one.

Response 200

FieldTypeDescription
successTrue
dataobjectInformational data (quotes, simulation results, etc.)
data.currencyInobjectInput currency info (source chain)
data.currencyOutobjectOutput currency info (destination chain)
data.quotesobject[]Candidate routes, best output first. Execute exactly one.
data.quotes[].bridgestring
data.quotes[].tradeInputnumber
data.quotes[].tradeOutputnumber
data.quotes[].estimatedDurationnumberEstimated bridging duration in seconds
data.currencyInobjectInput currency info (source chain)
data.currencyOutobjectOutput currency info (destination chain)
data.quotesobject[]Candidate routes, best output first. Execute exactly one.
data.quotes[].bridgestring
data.quotes[].tradeInputnumber
data.quotes[].tradeOutputnumber
data.quotes[].estimatedDurationnumberEstimated bridging duration in seconds
data.quotes[].approvalTargetstringThis bridge's deposit contract — the ERC-20 approve spender
data.quotes[].approvalRequiredbooleanFalse when the existing on-chain allowance already covers the input amount — or when a submitted permit is embedded in this route's calldata
data.quotes[].permitAppliedbooleanTrue when the submitted permit rides inside this route's calldata (verified against the assembled bytes): no approve needed for this route
data.quotes[].txobjectAn EVM transaction ready to sign and broadcast. Send to, data and value as-is; do not re-encode them.
data.quotes[].tx.chainTypeevmWhich VM executes this step. ABSENT means evm, which is the only value any endpoint returns today — every EVM response is unchanged. A non-EVM chain would return a different shape (a serialized, PERISHABLE transaction rather than to/data/value) carrying its own chainType, so a client that wants to stay forward-compatible should branch on this field rather than assume to is present.
data.quotes[].tx.tostringTarget contract address
data.quotes[].tx.datastringEncoded calldata
data.quotes[].tx.valuestringETH value to send with the transaction
data.quotes[].tx.descriptionstringHuman-readable label. For alternatives, this is the aggregator name (e.g. "Paraswap"). For transactions, describes the setup action (e.g. "Switch e-mode to 1").
data.permissionTxnsobject[]ERC-20 approves per bridge deposit contract; each is labeled with the bridge name — execute only the one matching the chosen quote
data.permissionTxns[].tostringTarget contract address
data.permissionTxns[].datastringEncoded calldata
data.permissionTxns[].valuestringETH value
data.permissionTxns[].descriptionstringHuman-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20")
data.permissionTxns[].spenderstringERC-20 approve spender (x-chain permissions). Match it against the selected quote's approvalTarget — several bridges can share one spender, so do not match by description.
data.permissionTxns[].typeERC20, LenderWhat kind of grant this is. A signatures[] offer names the type it replaces via its replaces field.
actionsobjectTransaction calldata and approvals. Null for quote-only responses (no account provided).
actions.transactionsobject[]Pre-trade setup transactions (e.g. e-mode switch, collateral enable). Execute these before the main swap. Empty when no setup is needed.
actions.transactions[].chainTypeevmWhich VM executes this step. ABSENT means evm, which is the only value any endpoint returns today — every EVM response is unchanged. A non-EVM chain would return a different shape (a serialized, PERISHABLE transaction rather than to/data/value) carrying its own chainType, so a client that wants to stay forward-compatible should branch on this field rather than assume to is present.
actions.transactions[].tostringTarget contract address
actions.transactions[].datastringEncoded calldata
actions.transactions[].valuestringETH value to send with the transaction
actions.transactions[].descriptionstringHuman-readable label. For alternatives, this is the aggregator name (e.g. "Paraswap"). For transactions, describes the setup action (e.g. "Switch e-mode to 1").
actions.alternativesobject[]DEX aggregator swap transactions sorted by best output (descending). Each entry's description is the aggregator name. The client should pick one to execute. Present on loop action endpoints.

Example response

{
"success": true,
"data": {
"currencyIn": {},
"currencyOut": {},
"quotes": [
{
"bridge": "string",
"tradeInput": 1.0,
"tradeOutput": 1.0,
"estimatedDuration": 1.0
}
]
},
"actions": {
"transactions": [
{
"chainType": "evm",
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"alternatives": [
{
"chainType": "evm",
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"permissions": [
{
"to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"type": "ERC20"
}
],
"signatures": [
{
"permitId": "string",
"kind": "erc2612",
"typedData": {},
"replaces": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"description": "string",
"unscoped": true,
"deadline": "string"
}
],
"signs": [
{
"purpose": "authorize-action",
"format": "eip712",
"typedData": {},
"message": "string",
"signer": "string",
"deadline": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"description": "string",
"submit": {
"method": "GET",
"endpoint": "string",
"params": {},
"body": {},
"signatureField": "string"
}
}
],
"permitSkipped": [
{
"replaces": "string",
"reason": "string"
}
]
}
}

Request​

Responses​

Cross-chain swap quote or full build