name: 区块链陷阱防范 description: “区块链RPC错误处理、Gas估算、多链配置和交易管理。适用于与智能合约交互、估算Gas或管理交易时使用。触发条件:RPC、合约调用、Gas、多链调用、Nonce、交易、回滚。”
区块链常见陷阱
区块链交互中的常见陷阱与正确模式。
使用时机
- 通过RPC进行合约调用
- 估算交易Gas费用
- 处理回滚和错误
- 管理并发交易的Nonce
- 配置多链支持
- 审查区块链代码
工作流程
步骤1:验证错误处理
检查所有合约调用是否都包裹在try/catch中。
步骤2:检查Gas估算
确保发送交易前已估算Gas并添加缓冲。
步骤3:验证多链调用安全性
确认多链调用使用allowFailure: true参数。
RPC错误处理
// ✅ 包裹所有合约调用
async function getQuote(tokenIn: Address, tokenOut: Address) {
try {
const quote = await quoter.quoteExactInput(...);
return quote;
} catch (error) {
// 低流动性代币会失败 - 这是预期行为
console.warn(`报价失败 ${tokenIn}->${tokenOut}:`, error.message);
return null; // 继续处理其他代币
}
}
// ✅ 调用合约前验证
if (!isAddress(tokenAddress)) {
throw new Error('无效的代币地址');
}
// ✅ 优雅处理"执行回滚"错误
if (error.message.includes('execution reverted')) {
// 池子不存在或流动性不足
return null;
}
// ✅ 带独立错误处理的多链调用
const results = await multicall({
contracts: tokens.map(t => ({ ... })),
allowFailure: true, // 关键参数
});
results.forEach((result, i) => {
if (result.status === 'success') {
// 使用result.result
} else {
// 记录日志并跳过该代币
}
});
Gas估算
// ✅ 发送前始终估算Gas
const gasEstimate = await contract.estimateGas.swap(...args);
// ✅ 为Gas估算添加10-20%缓冲
const gasLimit = gasEstimate.mul(120).div(100); // 20%缓冲
// ✅ EIP-1559 Gas定价
const feeData = await provider.getFeeData();
const tx = {
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
gasLimit,
};
// ✅ 执行前模拟
尝试 {
await contract.callStatic.swap(...args); // 试运行
const tx = await contract.swap(...args); // 实际执行
} catch (e) {
// 会回滚 - 不发送
}
// ✅ 处理Gas价格飙升
if (feeData.maxFeePerGas > MAX_ACCEPTABLE_GAS) {
throw new Error('Gas过高,等待中...');
}
多链配置
// ✅ 链特定配置
const CHAIN_CONFIG: Record<ChainId, ChainConfig> = {
ethereum: {
chainId: 1,
rpcUrl: process.env.ETHEREUM_RPC_URL,
blockTime: 12,
confirmations: 2,
nativeToken: 'ETH',
},
polygon: {
chainId: 137,
rpcUrl: process.env.POLYGON_RPC_URL,
blockTime: 2,
confirmations: 5, // 快速链需要更多确认
nativeToken: 'MATIC',
},
};
交易管理
// ✅ 等待确认
const receipt = await tx.wait(2); // 2个确认
// ✅ Nonce管理
class NonceManager {
private pending = new Map<Address, number>();
async getNextNonce(address: Address, provider: Provider): Promise<number> {
const onChain = await provider.getTransactionCount(address, 'pending');
const local = this.pending.get(address) ?? onChain;
const next = Math.max(onChain, local);
this.pending.set(address, next + 1);
return next;
}
}
速率限制与重试
// ✅ 指数退避
async function fetchWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) { // 速率限制
const delay = Math.pow(2, attempt) * 1000;
await sleep(delay);
continue;
}
throw error;
}
}
throw new Error('超过最大重试次数');
}
// ✅ 备用RPC端点
const RPC_ENDPOINTS = [
'https://eth-mainnet.alchemyapi.io/v2/KEY',
'https://mainnet.infura.io/v3/KEY',
'https://rpc.ankr.com/eth',
];
快速检查清单
- [ ] 所有合约调用包裹在try/catch中
- [ ] 多链调用使用
allowFailure: true - [ ] Gas估算添加20%缓冲
- [ ] 使用EIP-1559 Gas定价
- [ ] 发送前进行交易模拟
- [ ] 并发交易的非ce管理
- [ ] 根据链设置适当的确认数