This guide explains how to bridge tokens across chains using the LI.FI API with Crossmint wallets. LI.FI aggregates bridges and DEXs to find optimal cross-chain routes.
High level steps to bridging tokens with LI.FI and Crossmint:
Request a bridge quote from the LI.FI API
Approve the LI.FI router to spend the source token
Execute the bridge transaction using the quote calldata
Poll the LI.FI status endpoint until the bridge completes
Cross-chain bridges operate on mainnet only. The wallet must hold sufficient tokens on the source chain and may need native tokens for bridge protocol fees — see Gas Sponsorship and Bridge Fees.
React
Node.js
React Native
Swift
Kotlin
REST
import { useWallet, EVMWallet } from "@crossmint/client-sdk-react-ui";import { encodeFunctionData, erc20Abi, parseUnits } from "viem";const LIFI_API = "https://li.quest/v1";const FROM_CHAIN = "8453"; // Baseconst TO_CHAIN = "137"; // Polygonconst FROM_TOKEN = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // USDC on Baseconst TO_TOKEN = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"; // USDC on Polygonconst USDC_DECIMALS = 6;export function BridgeComponent() { const { wallet } = useWallet(); async function bridge(amount: string) { if(!wallet) return; const evmWallet = EVMWallet.from(wallet); // 1. Get a bridge quote from the LI.FI API const quoteRes = await fetch( `${LIFI_API}/quote?` + new URLSearchParams({ fromChain: FROM_CHAIN, toChain: TO_CHAIN, fromToken: FROM_TOKEN, toToken: TO_TOKEN, fromAmount: parseUnits(amount, USDC_DECIMALS).toString(), fromAddress: wallet.address, integrator: "crossmint", }) ); if (!quoteRes.ok) { throw new Error(`Quote request failed: ${quoteRes.status}`); } const quote = await quoteRes.json(); // 2. Approve the LI.FI router to spend USDC if (quote.estimate.approvalAddress) { await evmWallet.sendTransaction({ to: FROM_TOKEN, data: encodeFunctionData({ abi: erc20Abi, functionName: "approve", args: [ quote.estimate.approvalAddress, parseUnits(amount, USDC_DECIMALS), ], }), }); } // 3. Execute the bridge transaction const tx = await evmWallet.sendTransaction({ to: quote.transactionRequest.to, data: quote.transactionRequest.data, value: quote.transactionRequest.value ? BigInt(quote.transactionRequest.value) : 0n, }); // 4. Poll the LI.FI status endpoint until completion let status = "PENDING"; while (status === "PENDING" || status === "NOT_FOUND") { await new Promise((r) => setTimeout(r, 5000)); const statusRes = await fetch( `${LIFI_API}/status?` + new URLSearchParams({ txHash: tx.hash, fromChain: FROM_CHAIN, toChain: TO_CHAIN, }) ); if (!statusRes.ok) { throw new Error(`Status request failed: ${statusRes.status}`); } const s = await statusRes.json(); status = s.status; } } return ( <button onClick={() => bridge("5")}>Bridge Tokens</button> );}
Transactions must be approved by one of the wallet’s signers.
The SDK handles this automatically, but with the REST API you must approve the transaction to complete it.
1
Get a bridge quote from the LI.FI API
Call the LI.FI API/quote endpoint with the source and destination chain, token addresses, and amount.
curl --request GET \ --url 'https://li.quest/v1/quote?fromChain=8453&toChain=137&fromToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&toToken=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&fromAmount=5000000&fromAddress=YOUR_WALLET_ADDRESS&integrator=crossmint' \ --header 'Content-Type: application/json'
The response includes estimate.approvalAddress and transactionRequest with the bridge calldata.
2
Send the approve and bridge transactions
Use the quote response to send two transactions via the Crossmint REST API:
Approve — call the USDC contract with ERC-20 approve calldata targeting estimate.approvalAddress
Bridge — send transactionRequest.to, transactionRequest.data, and transactionRequest.value
Follow the steps in the Send a transaction guide to submit each transaction via the Crossmint REST API.
3
Poll for bridge status
Call the LI.FI /status endpoint every five seconds until the bridge completes.
curl --request GET \ --url 'https://li.quest/v1/status?txHash=YOUR_TRANSACTION_HASH&fromChain=8453&toChain=137' \ --header 'Content-Type: application/json'
The bridge is complete when status is DONE. If FAILED, check the transaction on the block explorer for details.
Gas sponsorship covers execution gas only — not native-token bridge fees. Some bridge protocols (e.g., LayerZero, Stargate) charge a protocol fee in the source chain’s native token, passed as transactionRequest.value in the quote response. This fee must be paid from the wallet’s own balance — the paymaster does not cover it. Routes through Relay or CCTP typically set transactionRequest.value = 0 and work with wallets that hold no native tokens.
If your wallets use gas sponsorship and hold no native tokens, add allowBridges=across,cctp,hop,relay to the LI.FI quote request to restrict to bridges that do not require native-token fees. Alternatively, validate BigInt(quote.transactionRequest.value) === 0n before executing.
You can bridge any token pair that LI.FI supports by changing the chain and token constants. You can also perform cross-chain swaps — changing both the asset and the chain in a single operation:
Verify the wallet holds enough USDC on Base. If gas sponsorship is not enabled, the wallet also needs ETH for gas fees.
Use the Check Balances guide to confirm token balances before bridging.
Transaction reverts with gas sponsorship enabled
If the wallet holds 0 ETH and the bridge transaction reverts, the LI.FI route likely requires a native-token bridge fee (transactionRequest.value > 0). Gas sponsorship covers execution gas only, not bridge protocol fees. See Gas Sponsorship and Bridge Fees for recommended solutions.
ERC-20 approval transaction is rejected
Ensure the approval amount is greater than or equal to the bridge amount.
If a previous approval exists for a smaller amount, send a new approval transaction with the correct value.
Verify the API key has the wallets:transactions.create scope.
Bridge status stays PENDING for a long time
Cross-chain bridges can take several minutes depending on the route and chain congestion.
The LI.FI /status endpoint returns PENDING until both the source and destination chain transactions confirm.
If the status does not change after 10 minutes, check the source transaction on a block explorer to verify it was included.