exchange.IO("bridge.*", ...)
Cross-chain bridge (Across, Stargate, CCTP and others aggregated by LI.FI): lists the supported chains and tokens, quotes and simulates, moves tokens from this exchange object's chain to another EVM chain or Solana, and tracks delivery.
exchange.IO(k)
exchange.IO(k, chain)
exchange.IO(k, params)
exchange.IO(k, params, stateOverride)
exchange.IO(k, txHash, options)
exchange.IO(k, key)Examples
-
Quote and simulate bridging 0.01 ETH from this chain to Arbitrum (read-only, costs nothing).
javascriptfunction main() { var params = {toChain: "arb", fromToken: "ETH", toToken: "ETH", amount: 0.01} var q = exchange.IO("bridge.quote", params) if (!q) { Log("Quote failed:", GetLastError()) return } Log("Via", q.toolName, "to", q.toChainName, "expected", q.toAmount, "at least", q.toAmountMin, "in about", q.executionDuration, "s") q.feeCosts.forEach(function (f) { Log("Fee:", f.name, f.amount, f.token, f.included ? "included in the quote" : "paid on top") }) // when the wallet balance is short, give it a temporary 100 ETH for the simulation var ov = {} ov[exchange.IO("address")] = {balance: "0x56bc75e2d63100000"} var s = exchange.IO("bridge.simulate", q, ov) Log(s ? "Simulation passed, calling " + s.to : "Simulation failed: " + GetLastError()) } -
The complete flow: quote → simulate → send → poll the progress every 20 seconds until delivered.
javascriptfunction main() { // bridge 20 USDC from this chain to this wallet on Arbitrum var params = {toChain: "arb", fromToken: "USDC", toToken: "USDC", amount: 20} var q = exchange.IO("bridge.quote", params) if (!q || !exchange.IO("bridge.simulate", q)) { Log("Quote or simulation failed:", GetLastError()) return } var r = exchange.IO("bridge.send", q) if (!r) { Log("Send failed:", GetLastError()) return } Log("Source transaction", r.txHash, "via", r.tool) while (true) { Sleep(20000) var st = exchange.IO("bridge.status", r.txHash, {toChain: "arb", bridge: r.tool}) if (!st) { continue } Log(st.status, st.substatus || "") if (st.status == "DONE") { Log("Received", st.receiving.amount, st.receiving.token.symbol, st.receiving.txLink) break } if (st.status == "FAILED" || st.status == "INVALID") { Log("Bridge failed:", st.substatusMessage) break } } } -
List the supported chains and quote a transfer to Solana.
javascriptfunction main() { var chains = exchange.IO("bridge.chains") Log(chains.length, "chains supported, such as", chains[0].name, chains[0].key) // a Solana destination needs toAddress (a Solana address) var q = exchange.IO("bridge.quote", {toChain: "Solana", fromToken: "USDC", toToken: "USDC", amount: 50, toAddress: "YOUR_SOLANA_ADDRESS", order: "CHEAPEST"}) Log(q ? [q.toolName, q.toAmount, q.toAmountMin] : GetLastError()) }
Returns
| Type | Description |
object / array / bool / null value | Depends on the command: |
Arguments
| Name | Type | Required | Description |
k | string | Yes | The command: |
chain | string / number | No |
|
params | object | No |
|
stateOverride | object | No |
|
txHash | string | No |
|
options | object | No |
|
key | string | No |
|
See Also
Uniswap / PancakeSwap Aerodrome / Velodrome KyberSwap 1inch exchange.IO exchange.IO("receipt") exchange.IO("approve") exchange.IO("slippage") exchange.IO("gasMultiplier")
Remarks
The commands:
| Command | Arguments | Description and result |
|---|---|---|
bridge.chains | none | Chains LI.FI supports (EVM chains and Solana), cached for an hour: an array of {id, key, name, chainType, nativeToken}, chainType being EVM or SVM |
bridge.tokens | [chain] | Tokens LI.FI knows on a chain (this chain by default), cached for an hour: an array of {symbol, address, decimals, name, priceUSD} |
bridge.quote | params | A bridge quote (read-only); fields below |
bridge.simulate | params or a quote [, stateOverride] | Re-quotes and runs LI.FI's transaction with eth_call, without signing or broadcasting |
bridge.send | params or a quote | Re-quotes, approves, simulates, then signs and broadcasts; waits for the source-chain broadcast only, not for delivery |
bridge.status | txHash[, {toChain, bridge}] | Bridge progress |
bridge.apiKey | [key] | Sets the LI.FI API key (for this exchange object only, never echoed); "" clears it and no argument queries; returns whether a key is set |
Keys of params (unknown keys are errors):
| Key | Description |
|---|---|
toChain | Destination chain: a chain ID (such as 42161), a LI.FI chain key (such as arb) or a chain name (such as Arbitrum or Solana) |
fromToken | Token on this chain: a name in the token table (such as USDC or ETH) or a contract address |
toToken | Token on the destination chain: a name or address, resolved by LI.FI to its canonical token (USDC on Arbitrum is native USDC, not USDC.e) |
amount | Amount of fromToken in token units; digits beyond the token's decimals are dropped |
toAddress | Recipient; this wallet by default for an EVM destination, required (a Solana address) for Solana |
slippage | Slippage; the exchange.IO("slippage") value by default |
order | CHEAPEST or FASTEST |
allowBridges, denyBridges | Bridges to use only or to avoid (LI.FI bridge names, an array or comma-separated) |
A quote is {tool, toolName, fromChain, toChain, toChainName, fromToken, toToken, fromAmount, toAmount, toAmountMin, fromAmountRaw, toAmountRaw, toAmountMinRaw, fromAmountUSD, toAmountUSD, slippage, toAddress, feeCosts, gasCosts, executionDuration, approvalAddress, steps, transactionId, request}: tool is the chosen bridge; amounts are in token units (*Raw in the smallest unit); toAmountMin is the amount guaranteed after slippage; feeCosts is [{name, amount, token, amountUSD, percentage, included}] (fees with included false are paid on top); gasCosts estimates source-chain gas; executionDuration is the expected time in seconds; approvalAddress is the contract to approve (null for the native coin); steps lists the steps (cross bridges, swap swaps); request echoes the parameters. A bridge.simulate result also has simulated (true), to, value and walletBalance, plus allowanceOverride when an allowance was supplied.
bridge.simulate: a short token allowance is supplied as unlimited through a state override (as in exchange.IO("simulate", ...)); balances are not, so when bridging the native coin give the wallet a balance in stateOverride. A failure caused by a short balance reports the wallet balance.
bridge.send steps: re-quote → check the balance → approve LI.FI's approvalAddress when the token allowance is short (following exchange.IO("approve") exact/max, resetting USDT-like tokens to zero first, waiting for the approval to be mined and re-quoting after it) → simulate with eth_call → sign and broadcast. The gas limit is the larger of the estimate × gasMultiplier and LI.FI's suggestion. It returns {txHash, tool, fromChain, toChain, toChainName, fromToken, toToken, fromAmount, toAmount, toAmountMin, toAddress, value, transactionId}, txHash being the source-chain transaction, whose receipt exchange.IO("receipt", ...) returns. Given a quote it re-quotes from its request; the new quote applies and toAmountMin is enforced on chain.
bridge.status returns {status, substatus, substatusMessage, tool, sending, receiving, fromAddress, toAddress, explorer, bridgeExplorer}: status is NOT_FOUND, INVALID, PENDING, DONE or FAILED; when DONE, substatus is COMPLETED (delivered as quoted), PARTIAL (a different token arrived) or REFUNDED (returned to the source chain); sending and receiving are {txHash, txLink, chainId, amount, amountRaw, token, timestamp}, receiving being what actually arrived. Right after broadcasting LI.FI may not have indexed it yet and returns NOT_FOUND; keep polling (every 10 to 30 seconds); toChain and bridge (the tool of the send result) in options speed up the lookup.
Bridging is asynchronous: delivery takes seconds to tens of minutes (see executionDuration) and must be confirmed by polling bridge.status; cross-VM transfers (EVM↔Solana) require toAddress, and funds sent to a wrong address cannot be recovered; the destination wallet needs gas to use what arrives. Without an API key LI.FI limits by outgoing IP (/quote 75 times per 2 hours, used once each by bridge.quote, bridge.simulate and bridge.send), shared by the strategies on a docker; with a key the limit is 100 per minute. Test with a small amount first.