跨链 Composer 让用户将资产存入与他们的资产所在链不同的协议中,全部在单个流程中完成。本指南涵盖跨链 Composer 操作的模式、示例和注意事项。
跨链 Composer 工作原理
当 fromChain 和 toChain 不同时,LI.FI 的路由引擎自动组合:
- 源链操作 — 如果需要则兑换代币
- 跨链桥 — 将资产转移到目标链
- 目标链操作 — 兑换到所需代币并通过 Composer 存入目标协议
从开发者的角度来看,API 调用与同链 Composer 完全相同。您只需使用不同的链 ID。LI.FI 处理跨链桥选择、中间兑换和最终的 Composer 存款。
下面的模式说明了可能的执行路径。您不控制路由引擎选择哪种模式。它会自动优化成本、速度和流动性。只有在收到路由响应时您才会知道确切的执行路径。
源链(例如 Ethereum) 目标链(例如 Base)
┌──────────────────────────┐ ┌──────────────────────────┐
│ 1. 兑换 ETH → USDC │ │ 4. 接收跨链 USDC │
│ (如果需要) │ │ 5. 将 USDC 存入 │
│ 2. 授权跨链桥 │─── 跨链桥 ──▶│ Morpho 金库 │
│ 3. 发送到跨链桥 │ │ 6. 返还金库代币 │
└──────────────────────────┘ └──────────────────────────┘
常见跨链模式
这些模式描述路由引擎可能满足跨链 Composer 请求的典型方式。实际路径在报价时根据可用流动性、跨链桥选项和 gas 成本确定。
跨链桥 + 存款
用户在错误的链上有正确的代币。
示例: Arbitrum 上的 USDC → Base 上的 Morpho 金库
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'
可能发生的情况:
- 将 USDC 从 Arbitrum 跨链到 Base
- 将 USDC 存入 Base 上的 Morpho 金库
- 用户收到金库代币
确切的步骤取决于 API 返回的路由。如果产生更好的结果,路由引擎可能会选择不同的跨链桥或插入中间兑换。
兑换 + 跨链桥 + 存款
用户在不同链上有不同的代币。
示例: Ethereum 上的 ETH → Optimism 上的 Aave USDC 借贷
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'
可能发生的情况:
- 在 Ethereum 上将 ETH 兑换为 USDC
- 将 USDC 从 Ethereum 跨链到 Optimism
- 将 USDC 存入 Optimism 上的 Aave
- 用户收到 aUSDC 代币
多跳跨链
当没有直接的跨链桥可用时,路由引擎可能使用多跳路径。
示例: Polygon 上的 MATIC → Avalanche 上的 Yield Yak 金库
const quote = await getQuote({
fromChain: 137, // Polygon
toChain: 43114, // Avalanche
fromToken: '0x0000000000000000000000000000000000001010', // MATIC
toToken: 'YIELD_YAK_VAULT_ADDRESS_ON_AVALANCHE', // Avalanche 上的 Yield Yak 金库
fromAmount: '1000000000000000000', // 1 MATIC
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
可能发生的情况:
- 在 Polygon 上将 MATIC 兑换为桥接代币
- 跨链到中间链(如 Ethereum)
- 从中间链跨链到 Avalanche
- 在 Avalanche 上兑换为目标代币
- 存入 Yield Yak 金库
执行跨链 Composer
API 集成
import axios from 'axios';
const executeCrossChainComposer = async () => {
// 1. 获取跨链 Composer 报价
const quote = await axios.get('https://li.quest/v1/quote', {
params: {
fromChain: 1, // Ethereum
toChain: 8453, // Base
fromToken: '0x0000000000000000000000000000000000000000', // ETH
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A', // Morpho 金库
fromAmount: '100000000000000000', // 0.1 ETH
fromAddress: '0xYOUR_WALLET_ADDRESS',
toAddress: '0xYOUR_WALLET_ADDRESS',
},
});
const quoteData = quote.data;
// 2. 设置授权(如果需要)
if (quoteData.estimate.approvalAddress) {
// 授权逻辑...
}
// 3. 执行源链交易
const tx = await signer.sendTransaction(quoteData.transactionRequest);
console.log('源链交易:', tx.hash);
// 4. 跟踪跨链状态
const trackStatus = async () => {
const status = await axios.get('https://li.quest/v1/status', {
params: {
txHash: tx.hash,
fromChain: 1,
toChain: 8453,
},
});
const statusData = status.data;
console.log('状态:', statusData.status, statusData.substatus);
if (statusData.status !== 'DONE' && statusData.status !== 'FAILED') {
setTimeout(trackStatus, 5000); // 5 秒后重试
} else {
console.log('跨链 Composer 完成!');
}
};
trackStatus();
};
SDK 集成
import { getQuote, convertQuoteToRoute, executeRoute } from '@lifi/sdk';
const executeCrossChainComposerWithSDK = async () => {
// 1. 获取跨链报价
const quote = await getQuote({
fromChain: 1, // Ethereum
toChain: 8453, // Base
fromToken: '0x0000000000000000000000000000000000000000', // ETH
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A', // Morpho 金库
fromAmount: '100000000000000000', // 0.1 ETH
fromAddress: '0xYOUR_WALLET_ADDRESS',
});
// 2. 转换并执行 - SDK 处理所有复杂性
const route = convertQuoteToRoute(quote);
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
console.log('路由更新:', updatedRoute);
},
onChainSwitch(chainId) {
console.log('切换到链:', chainId);
},
});
console.log('跨链 Composer 完成:', executedRoute);
};
状态跟踪
跨链 Composer 需要状态跟踪来监控两阶段执行:
| 阶段 | 状态 | 描述 |
|---|
| 源链 | PENDING | 源链交易进行中 |
| 跨链桥 | PENDING | 资产正在跨链 |
| 目标链 | PENDING | Composer 执行进行中 |
| 完成 | DONE | 所有步骤完成 |
const trackCrossChainStatus = async (txHash: string, fromChain: number, toChain: number) => {
const status = await getStatus(txHash, fromChain, toChain);
switch (status.status) {
case 'PENDING':
if (status.substatus?.includes('bridge')) {
console.log('跨链进行中...');
} else if (status.substatus?.includes('composer')) {
console.log('Composer 执行进行中...');
} else {
console.log('源链交易进行中...');
}
break;
case 'DONE':
console.log('跨链 Composer 完成!');
break;
case 'FAILED':
console.error('执行失败:', status.substatus);
break;
default:
console.log('状态:', status.status);
}
};
成本和速度考虑
成本因素
- Gas 费用:两笔交易(源链 + 目标链)
- 跨链桥费用:跨链桥收取的费用
- DEX 费用:中间兑换的费用
- Slippage:跨链和兑换的价格影响
速度因素
- 跨链桥时间:从几秒(LayerZero)到几分钟(传统跨链桥)
- 目标链确认:取决于目标链的出块时间
- 拥堵:网络拥堵可能延迟执行
优化建议
// 在报价请求中包含选项以优化
const quote = await getQuote({
fromChain: 1,
toChain: 8453,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',
fromAmount: '100000000000000000',
fromAddress: '0xYOUR_WALLET_ADDRESS',
options: {
slippage: 0.005, // 0.5% slippage
bridges: {
allow: ['layerzero', 'stargate'], // 偏好快速跨链桥
},
},
});
错误处理
常见跨链 Composer 错误:
| 错误 | 原因 | 解决方案 |
|---|
BRIDGE_NOT_AVAILABLE | 没有可用的跨链桥 | 尝试不同的源链或代币 |
INSUFFICIENT_BRIDGE_LIQUIDITY | 跨链桥流动性不足 | 等待或使用较小的金额 |
COMPOSER_EXECUTION_FAILED | 目标链 Composer 失败 | 检查协议状态,重试 |
TIMEOUT | 跨链时间过长 | 检查状态,可能已完成 |
try {
const executedRoute = await executeRoute(route);
} catch (error) {
if (error.code === 'BRIDGE_NOT_AVAILABLE') {
console.error('此路径没有可用的跨链桥');
} else if (error.code === 'COMPOSER_EXECUTION_FAILED') {
console.error('目标链执行失败,请检查协议状态');
}
}
最佳实践
- 显示进度:向用户显示两阶段执行进度
- 设置合理超时:跨链可能需要几分钟
- 处理部分失败:源链成功但目标链失败的情况
- 提供反馈:清晰的状态更新和错误消息
- 测试跨链桥:在主网之前在测试网上测试
const executeWithProgress = async (route) => {
try {
const executedRoute = await executeRoute(route, {
updateRouteHook(updatedRoute) {
// 向用户显示进度
const currentStep = updatedRoute.steps.find(step =>
step.execution?.status === 'PENDING'
);
if (currentStep) {
updateUI(`正在执行:${currentStep.tool} 在链 ${currentStep.action.toChainId}`);
}
},
});
updateUI('跨链 Composer 完成!');
return executedRoute;
} catch (error) {
updateUI(`错误:${error.message}`);
throw error;
}
};
下一步