Skip to content
Multi token swap

LCX Liberty. American DeFi. Your keys. Your assets. Your control.

API reference

Multi token swap

POST /v1/multi/quote and /v1/multi/build trade several tokens for several tokens in one transaction, on one chain or across two.

One transaction, several tokens

A bundle sells up to eight tokens into one hub token — the chain's USDC, USDT or WETH, or the native coin — and spends that hub on up to eight others. Sells with no buys sweeps dust into one token; one sell that is the hub, with buys, spreads one token across several. Either way it is a single transaction the user signs once.

It is priced in two calls, like a single swap: /v1/multi/quote prices every leg and pins them, /v1/multi/build re-checks them and returns the calldata. Every leg is routed by the same engine as /v1/quote, so a bundle is never worse than sending the swaps one by one — it is the same routes, paid for once.

The endpoints answer multi_swap_disabled or not_found on a deployment whose router does not carry the multi swap module yet.

Quote

POST/v1/multi/quote
chainId*number

The chain everything settles on.

hub*string

The settlement token: the chain's USDC, USDT or WETH, or the zero address for the native coin (wrapped inside the router, paid out unwrapped).

sells*array

1 to 8 legs of { token, amount, decimals }. amount is in human units, as on /v1/quote.

buysarray

0 to 8 legs of { token, bps }. bps is that leg's share of the hub and the shares sum to exactly 10000. Omit to consolidate.

slippageBpsnumber

Default 50, at most 2000. Every leg is priced at it.

* required

json
{
  "chainId": 8453,
  "hub": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "sells": [
    { "token": "0x0000000000000000000000000000000000000000", "amount": "0.5", "decimals": 18 },
    { "token": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", "amount": "0.01", "decimals": 8 }
  ],
  "buys": [
    { "token": "0x940181a94A35A4569E4529A3CDfB74e38FD98631", "bps": 6000 },
    { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "bps": 4000 }
  ]
}

The answer carries an mq_ id valid for 120 seconds, each leg with its own quote and floor, and two hub figures: hubExpected, what the sells should deliver, and hubFloor, what they guarantee. Buys are sized from the floor, never the expectation, because a buy sized for hub the sells do not produce reverts on chain.

A leg the engine cannot route carries error and the others still price. Read those before building: the bundle is one transaction, so a leg nobody looked at is a surprise at signing time.

Build

POST/v1/multi/build
multiQuoteId*string

An mq_ id. A single-swap q_ id returns bad_quote_id, and an mq_ id sent to /v1/build does the same.

recipient*string

Receives the hub or the buys, and sends the transaction unless taker is set.

takerstring

The wallet that signs, when it differs from the recipient. Its allowances are the ones read.

pull*"allowance" | "permit2"

How the sells reach the router: an ERC-20 allowance per token, or one Permit2 batch signature covering all of them.

deadlineSecsnumber

Default 300, at most 3600, as on /v1/build.

* required

Every sell re-runs the guards a single build runs. A sell that fails is dropped and named in skipped; a buy that fails refuses the build, because the buy hop is all or nothing on chain and a bundle missing one of its buys is not the bundle that was quoted. tokens reports what each sell still needs — an approval, a signature, or nothing — so a wallet can ask for only what is missing.

On the Permit2 path the calldata comes back with an empty signature: re-encode it from args once the wallet has signed. Check the calldata against what the user saw before signing — each sell's amount exactly, each floor at or above what was quoted.

Across chains

POST/v1/multi/xquote
POST/v1/multi/xbuild

The same bundle with a bridge in the middle, quoted in two halves because the amount that crosses is not known until the first half has run. Consolidate into the corridor asset with an ordinary bundle (no buys), then price the far side with xquote, passing the amount the wallet actually holds. The destination buys run inside the bridge's arrival message, so the user signs nothing on the far chain, and whatever the message cannot spend is paid to them in the corridor asset.

xbuild returns the bridge deposit. Pass sourceQuoteId — the mq_ id of the source bundle — and, on a network whose router carries the entry, the bundle and the deposit come back as ONE transaction aimed at the router. That path is all or nothing: a sell that fails reverts everything and leaves the tokens in the wallet, while the two-transaction path keeps a partial fill.

Track the arrival with GET /v1/bridge/transfers, exactly as for a single cross-chain swap.