The LI.FI SDK handles the full Composer lifecycle (allowance checks, chain switching, transaction submission, and status tracking) so you can focus on your application logic. This guide shows how to use Composer through the SDK.
Quick Example
Deposit USDC into a Morpho vault on Base using the SDK:
import { createConfig, getQuote, convertQuoteToRoute, executeRoute } from '@lifi/sdk';
// 1. Configure the SDK (once, at app startup)
createConfig({
integrator: 'YourAppName',
});
// 2. Get a Composer quote
const quote = await getQuote({
fromChain: 8453, // Base
toChain: 8453, // Base
fromToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A', // Morpho vault token
fromAmount: '1000000', // 1 USDC
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
// 3. Convert quote to route and execute - SDK handles allowance, submission, and tracking
const route = convertQuoteToRoute(quote);
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
console.log('Route updated:', updatedRoute);
},
});
console.log('Done!', executedRoute);
That’s it. The SDK internally manages:
- Allowance checks and approvals
- Transaction data retrieval (if using
/advanced/routes)
- Transaction submission
- Status tracking and polling
- Chain switching (for cross-chain flows)
Step-by-Step Guide
Set up the SDK once at application startup. You must configure EVM providers for the chains you want to use.
import { createConfig } from '@lifi/sdk';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
createConfig({
integrator: 'YourAppName',
providers: [
// Configure your EVM provider - see SDK docs for full setup
],
});
For full provider configuration, see Configure SDK Providers.
2. Request a Composer Quote
Use getQuote for a single best route (includes transaction data) or getRoutes for multiple options.
Using getQuote
import { getQuote } from '@lifi/sdk';
const quote = await getQuote({
fromChain: 8453,
toChain: 8453,
fromToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',
fromAmount: '1000000',
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
Using getRoutes
import { getRoutes } from '@lifi/sdk';
const result = await getRoutes({
fromChainId: 8453,
toChainId: 8453,
fromTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
toTokenAddress: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',
fromAmount: '1000000',
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
const route = result.routes[0]; // Select the best route
The toToken / toTokenAddress is always the vault token address of the target protocol. This is what triggers Composer. Find vault token addresses on the protocol’s own app or documentation.
3. Execute the Route
The executeRoute function handles the entire execution lifecycle:
import { executeRoute } from '@lifi/sdk';
const executedRoute = await executeRoute(route, {
// Called whenever the route object is updated during execution
updateRouteHook(updatedRoute) {
const step = updatedRoute.steps[0];
const process = step?.execution?.process;
const lastProcess = process?.[process.length - 1];
console.log(`Step: ${step?.tool}`);
console.log(`Status: ${lastProcess?.status}`);
console.log(`Tx: ${lastProcess?.txHash || 'pending'}`);
},
});
4. Monitor Execution
The updateRouteHook callback fires on every state change. Use it to update your UI:
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
for (const step of updatedRoute.steps) {
if (!step.execution) continue;
for (const process of step.execution.process) {
switch (process.status) {
case 'STARTED':
console.log(`${process.type} started`);
break;
case 'PENDING':
console.log(`${process.type} pending - tx: ${process.txHash}`);
break;
case 'DONE':
console.log(`${process.type} complete`);
break;
case 'FAILED':
console.error(`${process.type} failed: ${process.error?.message}`);
break;
}
}
}
},
});
Cross-Chain Composer via SDK
Cross-chain Composer works identically. Just use different fromChain and toChain values:
const crossChainQuote = await getQuote({
fromChain: 1, // Ethereum
toChain: 8453, // Base
fromToken: '0x0000000000000000000000000000000000000000', // ETH (native)
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A', // Morpho vault on Base
fromAmount: '100000000000000000', // 0.1 ETH
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
const route = convertQuoteToRoute(crossChainQuote);
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
console.log('Route updated:', updatedRoute.steps.map(s => s.tool));
},
});
The SDK automatically handles:
- Bridge selection and execution
- Waiting for bridge completion
- Chain switching to the destination chain
- Executing the Composer deposit on the destination chain
- Status tracking throughout
Execution Options
All execution options are optional but can be useful for advanced use cases:
| Option | Type | Description |
|---|
updateRouteHook | (route) => void | Called on every route state change. Use for UI updates. |
updateTransactionRequestHook | (txRequest) => Promise<tx> | Modify transaction requests before submission (e.g., custom gas). |
acceptExchangeRateUpdateHook | (params) => Promise<boolean> | Called if the exchange rate changes during execution. Return true to accept. |
infiniteApproval | boolean | If true, approves max uint256 instead of the exact amount. |
executeInBackground | boolean | If true, execution continues even if the user navigates away. |
For the full execution options reference, see Execute Routes/Quotes.
Error Handling
The SDK throws errors that you can catch and handle:
try {
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
// Track progress
},
});
} catch (error) {
console.error('Execution failed:', error.message);
// Check the route's step execution for detailed failure info
for (const step of route.steps) {
if (step.execution) {
for (const process of step.execution.process) {
if (process.status === 'FAILED') {
console.error(`Step ${step.tool} failed:`, process.error?.message);
}
}
}
}
}
Next Steps