Documentation Index
Fetch the complete documentation index at: https://docs.li.fi/llms.txt
Use this file to discover all available pages before exploring further.
Composer 使用标准的 LI.FI API 错误格式,但有一些特定于 Composer 的错误和考虑因素。
Composer 特定错误
| 错误代码 | 描述 | 常见原因 | 解决方案 |
|---|
NO_POSSIBLE_ROUTE | 没有找到路径 | toToken 不是支持的金库代币 | 验证 toToken 地址是否在支持的协议列表中 |
COMPOSER_EXECUTION_FAILED | Composer 执行失败 | 协议特定问题(如金库已满) | 检查协议状态,稍后重试 |
INSUFFICIENT_LIQUIDITY | 流动性不足 | 目标协议流动性不足 | 尝试较小金额或稍后重试 |
TRANSACTION_FAILED | 模拟失败 | 执行前模拟失败 | 检查 gas 限制、协议状态 |
UNSUPPORTED_TOKEN | 不支持的代币 | toToken 不是支持的 Composer 代币 | 使用支持的协议代币 |
常见失败模式
1. 协议级别失败
这些失败发生在目标协议层面:
- 金库容量限制:金库已达到最大容量
- 协议暂停:目标协议临时暂停
- 代币黑名单:特定代币被协议阻止
解决方案:
try {
const executedRoute = await executeRoute(route);
} catch (error) {
if (error.code === 'COMPOSER_EXECUTION_FAILED') {
console.error('协议级别失败,检查协议状态');
// 提示用户检查协议应用
}
}
2. 跨链失败
跨链 Composer 流程的特定失败:
- 跨链桥失败:跨链桥临时不可用
- 中间代币问题:跨链代币在目标链不可用
- Gas 不足:目标链 gas 费用变化
解决方案:
const trackCrossChainStatus = async (txHash: string, fromChain: number, toChain: number) => {
const status = await getStatus(txHash, fromChain, toChain);
if (status.status === 'FAILED' && status.substatus?.includes('bridge')) {
console.error('跨链失败,可能需要重试');
// 提示用户重试或使用不同跨链桥
}
};
3. 执行前模拟失败
模拟在链上执行前失败:
- Gas 估算错误:交易需要比预期更多的 gas
- 状态变化:模拟和执行之间状态发生变化
- Slippage 过高:价格变化超出可接受范围
解决方案:
const quote = await getQuote({
// ... 其他参数
options: {
slippage: 0.01, // 增加 slippage 容忍度
gasLimit: 'auto', // 自动 gas 限制
},
});
错误处理最佳实践
1. 预检查
在执行前验证条件:
const preCheck = async (toToken: string, amount: string) => {
// 检查代币是否受支持
const supportedProtocols = await getSupportedProtocols();
const isSupported = supportedProtocols.some(protocol =>
protocol.tokens.includes(toToken)
);
if (!isSupported) {
throw new Error('代币不受 Composer 支持');
}
// 检查最小/最大金额
const limits = await getProtocolLimits(toToken);
if (BigInt(amount) < limits.minDeposit) {
throw new Error('金额低于最小存款要求');
}
};
2. 重试逻辑
为临时失败实现重试:
const executeWithRetry = async (route: Route, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await executeRoute(route);
} catch (error) {
if (error.code === 'INSUFFICIENT_LIQUIDITY' && attempt < maxRetries) {
console.log(`流动性不足,重试 ${attempt}/${maxRetries}`);
await new Promise(resolve => setTimeout(resolve, 2000)); // 等待 2 秒
continue;
}
throw error; // 重新抛出不可重试的错误
}
}
};
3. 用户友好的错误消息
将技术错误转换为用户友好的消息:
const getErrorMessage = (error: any) => {
switch (error.code) {
case 'NO_POSSIBLE_ROUTE':
return '无法找到路径。请检查目标代币是否为支持的金库代币。';
case 'COMPOSER_EXECUTION_FAILED':
return '协议执行失败。请检查协议状态或稍后重试。';
case 'INSUFFICIENT_LIQUIDITY':
return '流动性不足。请尝试较小金额或稍后重试。';
case 'TRANSACTION_FAILED':
return '交易失败。请检查您的余额和网络状态。';
default:
return '发生未知错误。请稍后重试。';
}
};
监控和日志
1. 错误跟踪
const executeWithErrorTracking = async (route: Route) => {
try {
return await executeRoute(route);
} catch (error) {
// 记录错误以供分析
console.error('Composer 执行失败:', {
error: error.message,
code: error.code,
route: route.id,
timestamp: new Date().toISOString(),
});
// 发送到错误跟踪服务
if (typeof window !== 'undefined') {
window.analytics?.track('composer_error', {
error_code: error.code,
error_message: error.message,
route_id: route.id,
});
}
throw error;
}
};
2. 性能监控
const executeWithMetrics = async (route: Route) => {
const startTime = Date.now();
try {
const result = await executeRoute(route);
// 记录成功指标
const duration = Date.now() - startTime;
console.log('Composer 执行成功:', {
duration,
route_id: route.id,
steps: route.steps.length,
});
return result;
} catch (error) {
// 记录失败指标
const duration = Date.now() - startTime;
console.error('Composer 执行失败:', {
duration,
error: error.code,
route_id: route.id,
});
throw error;
}
};
测试错误场景
1. 单元测试
describe('Composer 错误处理', () => {
it('应该处理不支持的代币', async () => {
await expect(
getQuote({
toToken: 'UNSUPPORTED_TOKEN_ADDRESS',
// ... 其他参数
})
).rejects.toThrow('NO_POSSIBLE_ROUTE');
});
it('应该处理流动性不足', async () => {
await expect(
getQuote({
fromAmount: '999999999999999999999', // 极大金额
// ... 其他参数
})
).rejects.toThrow('INSUFFICIENT_LIQUIDITY');
});
});
2. 集成测试
describe('Composer 集成错误处理', () => {
it('应该优雅地处理跨链失败', async () => {
const route = await getCrossChainRoute();
// 模拟跨链失败
jest.spyOn(bridgeService, 'execute').mockRejectedValue(
new Error('BRIDGE_UNAVAILABLE')
);
await expect(executeRoute(route)).rejects.toThrow('BRIDGE_UNAVAILABLE');
});
});
下一步