Deposit into any protocol on any EVM chain, starting from wherever your assets are. LI.FI handles the bridge, swaps, and final deposit in one flow.
Composer supports cross-chain deposits across EVM chains. A user on Ethereum can deposit into a Morpho vault on Base, or deposit USDC from Arbitrum into any supported protocol on Base. LI.FI handles bridge selection, intermediate swaps, and the final deposit automatically.From a developer’s perspective, the integration is identical to same-chain: just set fromChain and toChain to different values.
Cross-chain Composer works across EVM chains today. Non-EVM chains (Solana,
etc.) are not yet supported.
Deposit ETH from Ethereum into a Spark-curated Morpho USDC vault on Base. LI.FI bridges ETH, swaps to USDC on Base, and deposits into the vault.
curl -X GET 'https://li.quest/v1/quote?\fromChain=1&\toChain=8453&\fromToken=0x0000000000000000000000000000000000000000&\toToken=0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A&\fromAddress=0xYOUR_WALLET_ADDRESS&\toAddress=0xYOUR_WALLET_ADDRESS&\fromAmount=100000000000000000&\slippage=0.01'
import axios from "axios";const { data: quote } = await axios.get("https://li.quest/v1/quote", { params: { fromChain: 1, // Ethereum toChain: 8453, // Base fromToken: "0x0000000000000000000000000000000000000000", // ETH (native) toToken: "0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A", // Morpho vault on Base fromAddress: "0xYOUR_WALLET_ADDRESS", toAddress: "0xYOUR_WALLET_ADDRESS", fromAmount: "100000000000000000", // 0.1 ETH slippage: 0.01, // 1% for cross-chain },});// No approval needed for native ETH; submit directlyconst tx = await signer.sendTransaction(quote.transactionRequest);await tx.wait();console.log("Source chain tx confirmed:", tx.hash);// Poll status until the cross-chain transfer completeslet status;do { const { data } = await axios.get("https://li.quest/v1/status", { params: { txHash: tx.hash, fromChain: quote.action.fromChainId, toChain: quote.action.toChainId, }, }); status = data; console.log(`Status: ${status.status} ${status.substatus || ""}`); if (status.status !== "DONE" && status.status !== "FAILED") { await new Promise((r) => setTimeout(r, 5000)); }} while (status.status !== "DONE" && status.status !== "FAILED");console.log("Final status:", status.status);// User now holds Morpho vault tokens on Base
Bridge USDC from Arbitrum and deposit into the Spark-curated Morpho vault on Base in a single flow.
curl -X GET 'https://li.quest/v1/quote?fromChain=42161&toChain=8453&fromToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&toToken=0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A&fromAddress=0xYOUR_WALLET_ADDRESS&toAddress=0xYOUR_WALLET_ADDRESS&fromAmount=10000000'
const { data: quote } = await axios.get("https://li.quest/v1/quote", { params: { fromChain: 42161, // Arbitrum toChain: 8453, // Base fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum toToken: "0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A", // Morpho vault on Base fromAddress: "0xYOUR_WALLET_ADDRESS", toAddress: "0xYOUR_WALLET_ADDRESS", fromAmount: "1000000000", // 1000 USDC (6 decimals) slippage: 0.01, },});// Approve USDC, send tx, and poll status (same pattern as above)
What may happen under the hood:
Bridge USDC from Arbitrum to Base
Deposit USDC into Morpho vault on Base
User receives vault tokens
The exact steps depend on the route returned by the API. The routing engine may choose a different bridge or insert intermediate swaps if that produces a better outcome.
User has a different token on a different chain.Example: ETH on Ethereum → Aave USDC lending on Optimism
curl -X GET 'https://li.quest/v1/quote?fromChain=1&toChain=10&fromToken=0x0000000000000000000000000000000000000000&toToken=AAVE_AUSDC_TOKEN_ADDRESS_ON_OPTIMISM&fromAddress=0xYOUR_WALLET_ADDRESS&toAddress=0xYOUR_WALLET_ADDRESS&fromAmount=100000000000000000'
Cross-chain transfers require status polling. After the source chain transaction confirms, poll GET /v1/status until the transfer reaches DONE or FAILED.
TypeScript
const pollStatus = async ( txHash: string, fromChain: number, toChain: number,) => { let status; do { const { data } = await axios.get("https://li.quest/v1/status", { params: { txHash, fromChain, toChain }, }); status = data; console.log(`Status: ${status.status} (${status.substatus || ""})`); if (status.status !== "DONE" && status.status !== "FAILED") { await new Promise((r) => setTimeout(r, 5000)); } } while (status.status !== "DONE" && status.status !== "FAILED"); return status;};