Comprehensive development support for LI.FI DEX aggregator including SDK, Widget, and API integration for cross-chain swaps and bridging. Use when building applications with LI.FI for - (1) Cross-chain token swaps and bridges, (2) Multi-chain liquidity aggregation, (3) Trading widget integration, (4) Custom DEX aggregation UI, (5) Gas subsidy implementation, (6) Revenue monetization with integrator fees, (7) Route optimization across 60+ chains, (8) Intent-based trading systems. Covers SDK usage (TypeScript/JavaScript), Widget customization (React/Vue/Svelte), API integration (REST), and LI.FI-specific features like cross-chain routing, gas subsidies (LI.Fuel), and multi-protocol aggregation across Uniswap, 1inch, Stargate, Across, and 800+ protocols.
Comprehensive toolkit for building cross-chain swap and bridge applications with LI.FI - the leading DEX and bridge aggregation protocol supporting 60+ chains and 800+ protocols.
LI.FI offers three integration methods. Choose based on your use case:
Quick Decision:
Integration Complexity:
1. Add swap widget to your app:
2. Execute cross-chain swap programmatically:
3. Get best swap route via API:
4. Implement gas subsidy (LI.Fuel):
5. Monetize with integrator fees:
React/Next.js Integration:
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
// 1. Configure widget
const widgetConfig: WidgetConfig = {
integrator: 'your-app-name', // Required for analytics and fees
// Appearance
variant: 'expandable', // 'default' | 'wide' | 'drawer' | 'expandable'
theme: {
palette: {
primary: { main: '#3f51b5' },
secondary: { main: '#f50057' }
},
shape: { borderRadius: 12 }
},
// Chain & token filtering
fromChain: 1, // Ethereum
toChain: 137, // Polygon
fromToken: '0x...', // Specific token address (optional)
// Advanced configuration
fee: 0.03, // 0.03% integrator fee
slippage: 0.005, // 0.5% max slippage
// Event handlers
onRouteExecutionStarted: (route) => {
console.log('Swap started:', route);
},
onRouteExecutionCompleted: (route) => {
console.log('Swap completed:', route);
}
};
// 2. Render widget
function App() {
return (
<div style={{ width: '100%', maxWidth: 500 }}>
<LiFiWidget config={widgetConfig} />
</div>
);
}
Vue.js Integration:
<template>
<div class="widget-container">
<lifi-widget :config="widgetConfig" />
</div>
</template>
<script setup>
import { LiFiWidget } from '@lifi/widget';
const widgetConfig = {
integrator: 'your-app-name',
variant: 'wide',
theme: { palette: { primary: { main: '#42b883' } } }
};
</script>
Vanilla JavaScript:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/@lifi/widget/dist/widget.css">
</head>
<body>
<div id="lifi-widget"></div>
<script src="https://unpkg.com/@lifi/widget"></script>
<script>
window.lifi.createWidget({
container: document.getElementById('lifi-widget'),
config: {
integrator: 'your-app-name',
variant: 'default'
}
});
</script>
</body>
</html>
See widget_integration.tsx for complete examples.
Installation:
npm install @lifi/sdk
# or
yarn add @lifi/sdk
# or
pnpm add @lifi/sdk
Basic Swap Flow:
import { LIFI } from '@lifi/sdk';
import { createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';
// 1. Initialize SDK
const lifi = new LIFI({
integrator: 'your-app-name', // Required
apiKey: process.env.LIFI_API_KEY // Optional - higher rate limits
});
// 2. Get available chains and tokens
const chains = await lifi.getChains();
const tokens = await lifi.getTokens({ chains: [1, 137] }); // Ethereum & Polygon
// 3. Get best route for swap
const routeRequest = {
fromChainId: 1, // Ethereum
fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
fromAmount: '1000000000', // 1000 USDC (6 decimals)
toChainId: 137, // Polygon
toTokenAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // USDC on Polygon
fromAddress: '0x...', // User wallet address
// Optional optimizations
options: {
slippage: 0.005, // 0.5%
order: 'RECOMMENDED', // or 'FASTEST', 'CHEAPEST', 'SAFEST'
allowSwitchChain: true
}
};
const routes = await lifi.getRoutes(routeRequest);
const bestRoute = routes.routes[0]; // Pre-sorted by 'order' parameter
// 4. Check route details
console.log('Estimated time:', bestRoute.steps.reduce((t, s) => t + s.estimate.executionDuration, 0), 'seconds');
console.log('Gas cost:', bestRoute.gasCostUSD);
console.log('Output amount:', bestRoute.toAmountMin); // Minimum guaranteed
// 5. Execute route
const wallet = createWalletClient({
chain: mainnet,
transport: http()
});
// Approve token spending if needed
if (bestRoute.steps[0].action.fromToken.address !== '0x0000000000000000000000000000000000000000') {
const approvalTx = await lifi.approveToken({
walletClient: wallet,
token: bestRoute.steps[0].action.fromToken,
amount: bestRoute.steps[0].action.fromAmount,
spender: bestRoute.steps[0].estimate.approvalAddress
});
await approvalTx.wait();
}
// Execute swap
const execution = await lifi.executeRoute({
route: bestRoute,
walletClient: wallet,
// Event callbacks
updateRouteHook: (updatedRoute) => {
console.log('Route updated:', updatedRoute.status);
},
switchChainHook: async (requiredChainId) => {
// Handle chain switch in your UI
await wallet.switchChain({ id: requiredChainId });
},
acceptExchangeRateUpdateHook: (oldRate, newRate) => {
// Return true to accept rate changes
return confirm(`Rate changed from ${oldRate} to ${newRate}. Continue?`);
}
});
console.log('Swap completed!', execution);
Advanced Route Filtering:
// Filter by specific bridges and exchanges
const routes = await lifi.getRoutes({
...routeRequest,
options: {
bridges: { allow: ['stargate', 'across', 'hop'] },
exchanges: { allow: ['uniswap', '1inch'] },
// Exclude specific protocols
// bridges: { deny: ['multichain'] },
// Fee configuration
integrator: 'your-app',
fee: 0.03 // 0.03% fee on fromAmount
}
});
See sdk_swap_example.ts for production-ready implementation.
Base URL: https://li.quest/v1
Authentication (Optional):
# Higher rate limits with API key
curl -H "x-lifi-api-key: YOUR_API_KEY" https://li.quest/v1/...
Get Route Quote:
// GET /quote
const response = await fetch(
'https://li.quest/v1/quote?' + new URLSearchParams({
fromChain: '1',
toChain: '137',
fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
toToken: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // USDC on Polygon
fromAmount: '1000000000', // 1000 USDC
fromAddress: '0x...',
// Optional
integrator: 'your-app-name',
fee: '0.03', // 3% integrator fee (decimal format: 0.03 = 3%)
slippage: '0.005', // 0.5%
order: 'RECOMMENDED'
}),
{
headers: {
'x-lifi-api-key': process.env.LIFI_API_KEY // Optional
}
}
);
const quote = await response.json();
// Quote contains single optimized route
console.log('Estimated output:', quote.estimate.toAmount);
console.log('Execution time:', quote.estimate.executionDuration, 'seconds');
console.log('Gas cost:', quote.estimate.gasCosts);
Get Multiple Routes:
// GET /advanced/routes - Returns multiple route options
const response = await fetch(
'https://li.quest/v1/advanced/routes?' + new URLSearchParams({
fromChainId: '1',
toChainId: '137',
fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
toTokenAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
fromAmount: '1000000000',
fromAddress: '0x...'
})
);
const { routes } = await response.json();
// Compare multiple routes
routes.forEach((route, i) => {
console.log(`Route ${i + 1}:`);
console.log(' Steps:', route.steps.length);
console.log(' Time:', route.steps.reduce((t, s) => t + s.estimate.executionDuration, 0));
console.log(' Output:', route.toAmount);
});
Get Supported Chains:
// GET /chains
const response = await fetch('https://li.quest/v1/chains');
const chains = await response.json();
chains.forEach(chain => {
console.log(`${chain.name} (${chain.id}): ${chain.nativeToken.symbol}`);
});
Get Supported Tokens:
// GET /tokens
const response = await fetch(
'https://li.quest/v1/tokens?' + new URLSearchParams({
chains: '1,137,42161' // Ethereum, Polygon, Arbitrum
})
);
const { tokens } = await response.json();
// Returns: { [chainId]: Token[] }
Check Route Status:
// GET /status
const response = await fetch(
'https://li.quest/v1/status?' + new URLSearchParams({
txHash: '0x...',
bridge: 'stargate', // Optional - speeds up lookup
fromChain: '1',
toChain: '137'
})
);
const status = await response.json();
console.log('Status:', status.status); // 'DONE' | 'PENDING' | 'FAILED'
console.log('Destination tx:', status.receiving?.txHash);
See api_routes_example.ts and api-reference.md.
Problem: Users bridging to a new chain lack native gas tokens.
Solution: LI.Fuel converts a portion of bridged assets to destination chain's native token.
import { LIFI } from '@lifi/sdk';
const lifi = new LIFI({ integrator: 'your-app' });
// Request route with gas subsidy
const routes = await lifi.getRoutes({
fromChainId: 1, // Ethereum
fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
fromAmount: '1000000000', // 1000 USDC
toChainId: 137, // Polygon
toTokenAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // USDC on Polygon
fromAddress: userAddress,
// Enable gas subsidy
fromAmountForGas: '50000000' // Convert 50 USDC to MATIC on Polygon
});
// Route automatically includes gas conversion step
const route = routes.routes[0];
// Verify gas subsidy is included
const gasStep = route.steps.find(step => step.type === 'lifi');
if (gasStep?.includedSteps?.some(s => s.type === 'swap' && s.action.toToken.address === '0x0000000000000000000000000000000000000000')) {
console.log('Gas subsidy enabled: User will receive native tokens');
}
API Version:
const response = await fetch(
'https://li.quest/v1/quote?' + new URLSearchParams({
fromChain: '1',
toChain: '137',
fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
toToken: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
fromAmount: '1000000000',
fromAddress: userAddress,
// Add gas subsidy parameter
fromAmountForGas: '50000000' // 50 USDC → MATIC
})
);
Limitations:
/contractCalls endpoint/quote and /routes endpointsgetRoutes() methodEarn revenue by adding integrator fees to swaps:
// 1. Register at https://portal.li.fi to get your integrator account
// 2. Add fee parameter to SDK/API calls
const routes = await lifi.getRoutes({
...routeRequest,
integrator: 'your-app-name', // Required
fee: 0.03 // 3% fee (decimal: 0.03 = 3%, 0.001 = 0.1%)
});
// Fee is deducted from fromAmount automatically
// Example: User sends 1000 USDC
// → 30 USDC goes to you (3%)
// → 970 USDC is swapped/bridged
Fee Collection:
// Via API
const response = await fetch('https://li.quest/v1/integrators/fees', {
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'x-lifi-api-key': 'YOUR_API_KEY'
}
});
const { fees } = await response.json();
console.log('Collected fees:', fees);
// Withdraw via Portal or API
await fetch('https://li.quest/v1/integrators/fees/withdraw', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'x-lifi-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
token: '0x...', // Token to withdraw
amount: '1000000000' // Amount
})
});
Base Fee Structure:
Volume Discounts:
Important:
See monetization-guide.md for complete setup guide.
Never deploy without:
slippage parameter (default 0.5% = 0.005)toAmountMin in routes before executionWidget Security:
const widgetConfig = {
integrator: 'your-app',
// Prevent unlimited approvals (recommended)
infiniteApproval: false,
// Set maximum slippage
slippage: 0.005, // 0.5%
// Disable risky bridges (optional)
bridges: { deny: ['bridge-with-issues'] },
// Add transaction confirmation
onRouteExecutionStarted: (route) => {
// Log for monitoring
analytics.track('swap_started', {
fromChain: route.fromChainId,
toChain: route.toChainId,
amount: route.fromAmount
});
}
};
SDK Security:
// ✅ GOOD: Exact approval
await lifi.approveToken({
token: route.steps[0].action.fromToken,
amount: route.steps[0].action.fromAmount, // Exact amount
spender: route.steps[0].estimate.approvalAddress
});
// ❌ BAD: Unlimited approval
await lifi.approveToken({
token: route.steps[0].action.fromToken,
amount: ethers.MaxUint256, // Dangerous!
spender: route.steps[0].estimate.approvalAddress
});
// ✅ GOOD: Rate limiting
const rateLimit = {
maxRequests: 10,
perSeconds: 60
};
// ✅ GOOD: Timeout handling
const routePromise = lifi.getRoutes(request);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Route timeout')), 30000)
);
try {
const routes = await Promise.race([routePromise, timeoutPromise]);
} catch (error) {
// Handle timeout or error
}
See security.md for complete security checklist.
┌─────────────────────────────────────────────┐
│ Your Application (dApp) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Widget │ │ SDK │ │ API │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼───────────┼────────────┼───────────┘
│ │ │
└───────────┴────────────┘
│
┌───────────▼────────────┐
│ LI.FI Smart Router │
│ (Route Optimization) │
└───────────┬────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───▼────┐ ┌────▼─────┐ ┌────▼────┐
│ Bridges│ │ DEX Agg │ │ Solvers │
│ │ │ │ │ │
│Stargate│ │ Uniswap │ │ 1inch │
│ Across │ │ Sushiswap│ │ 0x │
│ Hop │ │ Curve │ │ Kyber │
│Connext │ │ Balancer │ │ ParaSwap│
└────────┘ └──────────┘ └─────────┘
│ │ │
└──────────────┴──────────────┘
│
┌────────▼─────────┐
│ 60+ Blockchains │
│ 800+ Protocols │
└──────────────────┘
Start: Need cross-chain swap/bridge functionality
│
├─ Need UI component?
│ │
│ ├─ Yes → Use Widget
│ │ │
│ │ ├─ Basic theme customization → Widget default config
│ │ ├─ Advanced customization → Widget + custom theme
│ │ └─ Need custom logic → Widget + SDK hooks
│ │
│ └─ No → Need programmatic control?
│ │
│ ├─ TypeScript/JavaScript → Use SDK
│ │ │
│ │ ├─ Frontend → SDK + wallet provider
│ │ └─ Backend → SDK + private key signer
│ │
│ └─ Other language/platform → Use REST API
│ │
│ ├─ Python/Go/Ruby → Direct HTTP
│ └─ Mobile native → HTTP client
User Swap: 1000 USDC (Ethereum) → USDT (Arbitrum)
│
├─ Step 1 (SWAP on Ethereum):
│ └─ 1000 USDC → 1000 USDT via Uniswap V3
│
├─ Step 2 (BRIDGE):
│ └─ 1000 USDT (Ethereum) → 998 USDT (Arbitrum) via Stargate
│ (2 USDT bridge fee)
│
└─ Result: 998 USDT on Arbitrum
Optional: Gas Subsidy (LI.Fuel)
│
├─ Step 2a (before bridge):
│ └─ 50 USDT → 0.05 ETH (for gas on Ethereum)
│
└─ Step 2b (on destination):
└─ Bridge converts 50 USDT → ~40 ARB native tokens
└─ User receives: 948 USDT + 40 ARB (for gas)
Without API Key (per IP):
├─ /quote, /routes: 10 requests/minute
├─ /chains, /tokens: 20 requests/minute
└─ /status: 30 requests/minute
With API Key (per key):
├─ /quote, /routes: 100 requests/minute
├─ /chains, /tokens: 200 requests/minute
└─ /status: 300 requests/minute
Enterprise (contact LI.FI):
└─ Custom limits + dedicated infrastructure
Widget Installation:
# React
npm install @lifi/widget
# Includes all peer dependencies
# Vue
npm install @lifi/widget vue
# Svelte
npm install @lifi/widget svelte
# Vanilla JS (via CDN)
# No installation needed - use <script> tag
SDK Installation:
npm install @lifi/sdk viem
# or with ethers
npm install @lifi/sdk ethers
# TypeScript types included
Additional Tools:
# For API calls (if not using SDK)
npm install axios
# or use native fetch (Node 18+, all modern browsers)
# For wallet integration
npm install wagmi @wagmi/core viem
# or
npm install @web3-react/core ethers
# .env
LIFI_API_KEY=your_api_key_here # Optional - get from portal.li.fi
INTEGRATOR_NAME=your-app-name # Required for analytics
INTEGRATOR_FEE=0.03 # Optional - your revenue %
# Wallet (for backend/scripts)
PRIVATE_KEY=0x... # For automated execution
RPC_URL_ETHEREUM=https://eth.llamarpc.com
RPC_URL_POLYGON=https://polygon.llamarpc.com
# ... other chains
EVM Chains (40+):
Non-EVM Chains (20+):
Testnet Support:
LI.FI uses partner protocols (Uniswap, Stargate, etc.) - no proprietary contracts to deploy.
Integration Contracts (for advanced use):
0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE (multi-chain)Token Addresses (commonly used):
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480x3c499c542cEF5E3811e1192ce70d8cC03d5c33590xaf88d065e77c8cC2239327C5EDb3A432268e58310x0b2C639c533813f4Aa9D7837CAf62653d097Ff850x0000000000000000000000000000000000000000See api-reference.md for complete address list.
import { render, screen, waitFor } from '@testing-library/react';
import { LiFiWidget } from '@lifi/widget';
describe('LiFi Widget Integration', () => {
it('should render widget with custom config', () => {
const config = {
integrator: 'test-app',
variant: 'wide',
fromChain: 1,
toChain: 137
};
render(<LiFiWidget config={config} />);
expect(screen.getByTestId('lifi-widget')).toBeInTheDocument();
});
it('should call onRouteExecutionCompleted', async () => {
const onComplete = jest.fn();
const config = {
integrator: 'test-app',
onRouteExecutionCompleted: onComplete
};
render(<LiFiWidget config={config} />);
// Simulate swap completion
// ... trigger swap in test environment
await waitFor(() => {
expect(onComplete).toHaveBeenCalled();
});
});
});
import { LIFI } from '@lifi/sdk';
import { describe, it, expect, beforeEach } from 'vitest';
describe('LI.FI SDK', () => {
let lifi: LIFI;
beforeEach(() => {
lifi = new LIFI({
integrator: 'test-app'
});
});
it('should fetch available chains', async () => {
const chains = await lifi.getChains();
expect(chains.length).toBeGreaterThan(0);
expect(chains[0]).toHaveProperty('id');
expect(chains[0]).toHaveProperty('name');
});
it('should get routes for valid swap', async () => {
const routes = await lifi.getRoutes({
fromChainId: 1,
fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
fromAmount: '1000000000', // 1000 USDC
toChainId: 137,
toTokenAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // USDC Polygon
fromAddress: '0x1234567890123456789012345678901234567890'
});
expect(routes.routes.length).toBeGreaterThan(0);
expect(routes.routes[0]).toHaveProperty('fromAmount');
expect(routes.routes[0]).toHaveProperty('toAmount');
});
it('should handle invalid route request', async () => {
await expect(
lifi.getRoutes({
fromChainId: 1,
fromTokenAddress: '0xinvalid',
fromAmount: '1000000000',
toChainId: 137,
toTokenAddress: '0x...',
fromAddress: '0x...'
})
).rejects.toThrow();
});
});
# Test chain availability
curl https://li.quest/v1/chains | jq '.[0]'
# Test quote endpoint
curl "https://li.quest/v1/quote?fromChain=1&toChain=137&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&fromAmount=1000000000&fromAddress=0x1234567890123456789012345678901234567890" \
-H "x-lifi-api-key: YOUR_API_KEY" | jq '.estimate'
# Test with integrator fee
curl "https://li.quest/v1/quote?fromChain=1&toChain=137&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&fromAmount=1000000000&fromAddress=0x...&integrator=test-app&fee=0.03" | jq '.estimate.feeCosts'
import { test, expect } from '@playwright/test';
test('complete swap flow with widget', async ({ page }) => {
await page.goto('http://localhost:3000');
// Wait for widget to load
await page.waitForSelector('#lifi-widget');
// Configure swap
await page.fill('input[name="fromAmount"]', '100');
await page.selectOption('select[name="fromChain"]', '1'); // Ethereum
await page.selectOption('select[name="toChain"]', '137'); // Polygon
// Click swap button
await page.click('button:has-text("Swap")');
// Verify route display
await expect(page.locator('.route-summary')).toBeVisible();
await expect(page.locator('.estimated-output')).toContainText('USDC');
// In testnet: complete transaction
// In production: verify swap flow without execution
});
Missing integrator name: Required for analytics and fee collection
// ❌ BAD
const lifi = new LIFI({});
// ✅ GOOD
const lifi = new LIFI({ integrator: 'your-app-name' });
Ignoring route execution status: Always monitor multi-step routes
// ❌ BAD - Fire and forget
await lifi.executeRoute({ route, walletClient });
// ✅ GOOD - Monitor status
const execution = await lifi.executeRoute({
route,
walletClient,
updateRouteHook: (route) => {
console.log('Step', route.steps.findIndex(s => s.execution?.status === 'PENDING'));
}
});
Not handling chain switches: Cross-chain swaps require chain switching
// ✅ GOOD
const execution = await lifi.executeRoute({
route,
walletClient,
switchChainHook: async (chainId) => {
await walletClient.switchChain({ id: chainId });
}
});
Unlimited token approvals: Security risk
// ❌ BAD
await token.approve(spender, ethers.MaxUint256);
// ✅ GOOD
await lifi.approveToken({
token: route.steps[0].action.fromToken,
amount: route.steps[0].action.fromAmount, // Exact amount
spender: route.steps[0].estimate.approvalAddress
});
Exposing API keys in frontend: Use backend proxy
// ❌ BAD - Frontend
const lifi = new LIFI({
apiKey: 'pk_live_...' // Exposed in browser!
});
// ✅ GOOD - Backend proxy
// Frontend: Call your backend
const routes = await fetch('/api/lifi/routes', {
method: 'POST',
body: JSON.stringify(routeRequest)
});
// Backend: Use API key securely
const lifi = new LIFI({
apiKey: process.env.LIFI_API_KEY
});
Not handling bridge delays: Some bridges take hours
// ✅ GOOD - Set expectations
console.log('Estimated time:', route.steps.reduce(
(total, step) => total + step.estimate.executionDuration,
0
), 'seconds');
if (route.steps.some(s => s.estimate.executionDuration > 3600)) {
alert('This route may take over 1 hour. Continue?');
}
Wrong fee format: Use decimal, not percentage
// ❌ BAD
fee: 3 // SDK interprets as 300%!
// ✅ GOOD
fee: 0.03 // 3%
Ignoring rate limits: Will get throttled
// ✅ GOOD - Implement rate limiting
const rateLimiter = new RateLimiter({
maxRequests: 10,
perSeconds: 60
});
await rateLimiter.throttle();
const routes = await lifi.getRoutes(request);
Not validating user inputs: Can cause API errors
// ✅ GOOD
if (!ethers.isAddress(fromAddress)) {
throw new Error('Invalid address');
}
if (BigInt(fromAmount) <= 0) {
throw new Error('Amount must be positive');
}
Gas subsidy with contractCalls: Not supported
// ❌ BAD - Will fail
const routes = await lifi.getRoutes({
...request,
contractCalls: [...],
fromAmountForGas: '50000000' // Not supported together
});
// ✅ GOOD - Use separately
const routes = await lifi.getRoutes({
...request,
fromAmountForGas: '50000000' // Works
});
When the user needs up-to-date documentation or specific implementation details not covered in this skill:
Example:
User: "How do I customize the widget theme for dark mode?"
→ Check widget-guide.md for theme basics
→ Use Context7: query-docs with "@lifi/widget" for latest theme options
→ Apply security patterns from security.md for safe integration
Track user balances across all chains:
import { LIFI } from '@lifi/sdk';
async function getMultiChainBalances(userAddress: string) {
const lifi = new LIFI({ integrator: 'portfolio-app' });
// Get all supported chains
const chains = await lifi.getChains();
// Get token balances for each chain
const balances = await Promise.all(
chains.map(async (chain) => {
const tokens = await lifi.getTokenBalances(userAddress, [chain.id]);
return {
chain: chain.name,
tokens: tokens.filter(t => parseFloat(t.amount) > 0)
};
})
);
return balances.filter(b => b.tokens.length > 0);
}
// Get multiple routes and compare
const routes = await lifi.getRoutes({
...baseRequest,
options: {
// Get fastest, cheapest, and recommended routes
order: 'RECOMMENDED'
}
});
// Compare routes
const comparison = routes.routes.map(route => ({
id: route.id,
outputAmount: route.toAmount,
executionTime: route.steps.reduce((t, s) => t + s.estimate.executionDuration, 0),
gasCostUSD: route.gasCostUSD,
steps: route.steps.length
}));
// Sort by custom criteria
const bestByOutput = [...comparison].sort((a, b) =>
parseFloat(b.outputAmount) - parseFloat(a.outputAmount)
)[0];
const bestByTime = [...comparison].sort((a, b) =>
a.executionTime - b.executionTime
)[0];
const widgetConfig = {
integrator: 'analytics-app',
// Track all widget events
onRouteHighValueLoss: (route, loss) => {
analytics.track('high_value_loss_warning', {
expectedOutput: route.toAmount,
actualOutput: route.toAmountMin,
lossAmount: loss
});
},
onRouteExecutionStarted: (route) => {
analytics.track('swap_started', {
fromChain: route.fromChainId,
toChain: route.toChainId,
fromToken: route.fromToken.symbol,
toToken: route.toToken.symbol,
amount: route.fromAmount
});
},
onRouteExecutionCompleted: (route) => {
analytics.track('swap_completed', {
routeId: route.id,
executionTime: Date.now() - route.executionStartedAt,
actualOutput: route.toAmountMin
});
},
onRouteExecutionFailed: (route, error) => {
analytics.track('swap_failed', {
routeId: route.id,
error: error.message,
step: route.steps.findIndex(s => s.execution?.status === 'FAILED')
});
}
};
import express from 'express';
import { createHmac } from 'crypto';
const app = express();
app.post('/webhooks/lifi', express.json(), (req, res) => {
// Verify webhook signature
const signature = req.headers['x-lifi-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = createHmac('sha256', process.env.LIFI_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
// Process webhook
const { event, data } = req.body;
switch (event) {
case 'route.completed':
// Update database, notify user, etc.
console.log('Route completed:', data.routeId);
break;
case 'route.failed':
// Handle failure, refund user, etc.
console.log('Route failed:', data.routeId, data.error);
break;
case 'fee.collected':
// Track revenue
console.log('Fee collected:', data.amount, data.token);
break;
}
res.status(200).send('OK');
});
// Cache chain and token data (rarely changes)
import { LRUCache } from 'lru-cache';
const chainCache = new LRUCache({
max: 100,
ttl: 1000 * 60 * 60 // 1 hour
});
async function getChainsWithCache() {
const cached = chainCache.get('chains');
if (cached) return cached;
const lifi = new LIFI({ integrator: 'your-app' });
const chains = await lifi.getChains();
chainCache.set('chains', chains);
return chains;
}
// Cache routes for same parameters (short TTL)
const routeCache = new LRUCache({
max: 50,
ttl: 1000 * 30 // 30 seconds
});
async function getRoutesWithCache(request) {
const cacheKey = JSON.stringify(request);
const cached = routeCache.get(cacheKey);
if (cached) return cached;
const lifi = new LIFI({ integrator: 'your-app' });
const routes = await lifi.getRoutes(request);
routeCache.set(cacheKey, routes);
return routes;
}
// Fetch routes for multiple token pairs in parallel
async function getMultipleRoutes(requests) {
const lifi = new LIFI({ integrator: 'your-app' });
const routePromises = requests.map(request =>
lifi.getRoutes(request).catch(err => ({
error: err.message,
request
}))
);
const results = await Promise.all(routePromises);
return results.filter(r => !r.error);
}
// Usage
const routes = await getMultipleRoutes([
{ fromChainId: 1, toChainId: 137, ... },
{ fromChainId: 1, toChainId: 42161, ... },
{ fromChainId: 137, toChainId: 10, ... }
]);
// Lazy load widget
import { lazy, Suspense } from 'react';
const LiFiWidget = lazy(() => import('@lifi/widget').then(m => ({ default: m.LiFiWidget })));
function App() {
return (
<Suspense fallback={<div>Loading swap widget...</div>}>
<LiFiWidget config={widgetConfig} />
</Suspense>
);
}
// Before (0x)
const response = await fetch(
`https://api.0x.org/swap/v1/quote?` + new URLSearchParams({
sellToken: 'ETH',
buyToken: 'DAI',
sellAmount: '1000000000000000000'
})
);
// After (LI.FI)
const lifi = new LIFI({ integrator: 'migrated-from-0x' });
const routes = await lifi.getRoutes({
fromChainId: 1,
fromTokenAddress: '0x0000000000000000000000000000000000000000', // ETH
fromAmount: '1000000000000000000',
toChainId: 1,
toTokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
fromAddress: userAddress
});
const bestRoute = routes.routes[0];
// Execute with SDK or display in Widget
// Before (1inch)
const response = await fetch(
`https://api.1inch.dev/swap/v5.2/1/quote?` + new URLSearchParams({
src: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // ETH
dst: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
amount: '1000000000000000000'
}),
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
// After (LI.FI) - Includes 1inch + other aggregators
const routes = await lifi.getRoutes({
fromChainId: 1,
fromTokenAddress: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
toChainId: 1,
toTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
fromAddress: userAddress,
// Optional: Prefer 1inch if migrating gradually
options: {
exchanges: { allow: ['1inch', 'uniswap', 'sushiswap'] }
}
});
For high-volume applications, contact LI.FI for:
Contact: [email protected]