Skip to main content
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.

How It Works

Cross-chain Composer combines two LI.FI capabilities:
  1. Bridge routing: LI.FI selects the optimal bridge to move assets from the source chain to the destination chain.
  2. Composer execution: On the destination chain, Composer deposits into the target protocol atomically.
From the user’s perspective, this is a single flow with one signature on the source chain.

Cross-Chain vs. Same-Chain

Same-chainCross-chain
Transactions12 (source chain + destination chain)
AtomicityFully atomicPer-chain atomic; eventually consistent overall
Status trackingNot neededPoll /status until complete
IntegrationfromChain === toChainfromChain !== toChain

Example: ETH (Ethereum) → Morpho Vault (Base)

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'

Example: USDC (Arbitrum) → Morpho Vault (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'
What may happen under the hood:
  1. Bridge USDC from Arbitrum to Base
  2. Deposit USDC into Morpho vault on Base
  3. 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.

Swap + Bridge + Deposit

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'
What may happen under the hood:
  1. Swap ETH → USDC on Ethereum, then bridge USDC to Optimism, or bridge ETH directly and swap on the destination chain
  2. Deposit USDC into Aave on Optimism
  3. User receives aUSDC tokens
The routing engine decides the optimal sequence. You will see the chosen path in the route response.

Bridge + Swap + Stake

User wants to stake on a different chain. Example: USDC on Arbitrum → wstETH (Lido) on Ethereum
const quote = await getQuote({
  fromChain: 42161,                                             // Arbitrum
  toChain: 1,                                                   // Ethereum
  fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',     // USDC on Arbitrum
  toToken: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0',       // wstETH on Ethereum
  fromAmount: '1000000000',                                     // 1000 USDC
  fromAddress: '0xYOUR_WALLET_ADDRESS',
});
What may happen under the hood:
  1. Bridge USDC from Arbitrum to Ethereum
  2. Swap USDC → ETH on Ethereum
  3. Stake ETH via Lido, wrap to wstETH
  4. User receives wstETH

The routing engine may choose a different sequence (e.g., swap on Arbitrum first, then bridge ETH) if it finds a better path.

Status Tracking

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;
};
For the full status and substatus reference, see Transaction Status Tracking.

Partial Failure Handling

Cross-chain flows are eventually consistent. In rare cases, the bridge step succeeds but the destination deposit fails. When this happens:
  • The user receives the bridged tokens on the destination chain rather than vault tokens
  • Source chain tokens are not at risk
  • Gas fees are consumed
Build your UI to handle the FAILED status and prompt users to check their destination chain balance if a cross-chain transfer does not complete.

Next Steps

API Integration

Full API integration guide with same-chain and cross-chain examples

SDK Integration

Managed cross-chain execution with the LI.FI TypeScript SDK

Deposit Recipes

Copy-paste recipes for common protocols and chains

Supported Protocols

Protocols available for cross-chain deposits