Skip to main content
This recipe walks through a complete LI.FI Earn integration: use the Earn Data API to find the highest-yielding vaults for a specific token, display them to a user, and then execute a deposit via Composer.
Two layers, one flow. This recipe uses the Earn Data API for discovery and Composer for execution. The Earn Data API tells you where to deposit. Composer handles how.

Step 1: Find the Best USDC Vaults on Base

Query the Earn Data API for USDC vaults on Base, sorted by APY:
curl -X GET 'https://earn.li.fi/v1/earn/vaults?\
chainId=8453&\
asset=USDC&\
sortBy=apy&\
minTvlUsd=100000&\
limit=5'

Step 2: Filter for Depositable Vaults

Not all vaults support deposits via Composer. Filter using the isTransactional flag:
TypeScript
const depositableVaults = vaults.filter((vault) => vault.isTransactional);

// Display to user
depositableVaults.forEach((vault) => {
  console.log(`${vault.name} (${vault.protocol.name})`);
  console.log(`  APY: ${(vault.analytics.apy.total * 100).toFixed(2)}%`);
  console.log(`  TVL: $${Number(vault.analytics.tvl.usd).toLocaleString()}`);
  console.log(`  30d avg APY: ${(vault.analytics.apy30d * 100).toFixed(2)}%`);
  console.log(`  Deposit token: ${vault.address}`);
});
Use apy30d alongside apy.total to give users a sense of yield stability. A vault with a high current APY but low 30-day average may be experiencing a temporary spike.

Step 3: Get a Composer Quote

When the user selects a vault, use its contract address as the toToken in a Composer quote request. This is the handoff from Earn to Composer.
# User selected the first vault, deposit 100 USDC
curl -X GET 'https://li.quest/v1/quote?\
fromChain=8453&\
toChain=8453&\
fromToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&\
toToken=0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A&\
fromAddress=0xYOUR_WALLET_ADDRESS&\
toAddress=0xYOUR_WALLET_ADDRESS&\
fromAmount=100000000'
The Composer quote endpoint is https://li.quest/v1/quote. This is the Composer layer of LI.FI Earn, not the Earn Data API. See the Composer API Integration Guide for full details on parameters, token approvals, and transaction submission.

Step 4: Execute the Transaction

Submit the transaction from the quote response. See the Composer API Integration Guide for the full execution flow including token approvals and status tracking.
TypeScript
// Using ethers.js v6
import { ethers } from 'ethers';

const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

// Send the transaction from the Composer quote
const tx = await signer.sendTransaction(quote.transactionRequest);
console.log('Transaction sent:', tx.hash);

const receipt = await tx.wait();
console.log('Confirmed in block:', receipt.blockNumber);

Step 5: Verify the Position

After the deposit confirms, use the Earn portfolio endpoint to verify the user’s new position:
curl -X GET 'https://earn.li.fi/v1/earn/portfolio/0xYOUR_WALLET_ADDRESS/positions'

Cross-Chain Deposits

Vaults are chain-specific, but Composer handles cross-chain deposits seamlessly. To deposit from a different chain, change fromChain and fromToken in the quote request:
TypeScript
// Deposit ETH from Ethereum into a USDC vault on Base
// Note: fromChain differs from vault's chain. Composer handles the bridge automatically.
const params = new URLSearchParams({
  fromChain: '1',        // Ethereum (where the user's funds are)
  toChain: '8453',       // Base (where the vault lives)
  fromToken: '0x0000000000000000000000000000000000000000', // ETH (native)
  toToken: selectedVault.address,                           // Vault contract address on Base
  fromAddress: '0xYOUR_WALLET_ADDRESS',
  toAddress: '0xYOUR_WALLET_ADDRESS',
  fromAmount: '100000000000000000', // 0.1 ETH
});

const response = await fetch(`https://li.quest/v1/quote?${params}`);
const crossChainQuote = await response.json();
// Composer routes: bridge ETH to Base, swap to USDC, deposit into vault, all in one transaction
Composer’s routing engine automatically determines the optimal bridge and swap path. See Cross-Chain Compose for details.

Full Example

Here is the complete flow in a single function:
TypeScript
const discoverAndDeposit = async (
  chainId: number,
  asset: string,
  fromToken: string,
  fromAmount: string,
  userAddress: string
) => {
  // 1. Discover vaults
  const vaultParams = new URLSearchParams({
    chainId: String(chainId),
    asset,
    sortBy: 'apy',
    minTvlUsd: '100000',
    limit: '5',
  });
  const { data: vaults } = await fetch(
    `https://earn.li.fi/v1/earn/vaults?${vaultParams}`
  ).then((r) => r.json());

  // 2. Filter for depositable vaults
  const depositable = vaults.filter((v) => v.isTransactional);
  if (depositable.length === 0) throw new Error('No depositable vaults found');

  // 3. Pick the highest-APY vault
  const bestVault = depositable[0];
  console.log(
    `Best vault: ${bestVault.name} at ${(bestVault.analytics.apy.total * 100).toFixed(2)}% APY`
  );

  // 4. Get Composer quote
  const quoteParams = new URLSearchParams({
    fromChain: String(chainId),
    toChain: String(chainId),
    fromToken,
    toToken: bestVault.address,
    fromAddress: userAddress,
    toAddress: userAddress,
    fromAmount,
  });
  const quote = await fetch(
    `https://li.quest/v1/quote?${quoteParams}`
  ).then((r) => r.json());

  return { vault: bestVault, quote };
};

// Usage
const { vault, quote } = await discoverAndDeposit(
  8453,
  'USDC',
  '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  '100000000', // 100 USDC
  '0xYOUR_WALLET_ADDRESS'
);

Earn API Reference

Full endpoint reference with all parameters

Composer Vault Deposits

More vault deposit recipes with protocol-specific examples

Composer API Guide

Full Composer integration guide including approvals and status tracking

Cross-Chain Compose

Cross-chain deposit patterns and bridge selection