| name | pitfalls-blockchain |
| description | Blockchain RPC error handling, gas estimation, multi-chain config, and transaction management. Use when interacting with smart contracts, estimating gas, or managing transactions. Triggers on: RPC, contract call, gas, multicall, nonce, transaction, revert. |
Blockchain Pitfalls
Common pitfalls and correct patterns for blockchain interactions.
When to Use
- Making contract calls via RPC
- Estimating gas for transactions
- Handling reverts and errors
- Managing nonces for concurrent txs
- Configuring multi-chain support
- Reviewing blockchain code
Workflow
Step 1: Verify Error Handling
Check all contract calls are wrapped in try/catch.
Step 2: Check Gas Estimation
Ensure gas is estimated with buffer before sending.
Step 3: Verify Multicall Safety
Confirm multicall uses allowFailure: true.
RPC Error Handling
async function getQuote(tokenIn: Address, tokenOut: Address) {
try {
const quote = await quoter.quoteExactInput(...);
return quote;
} catch (error) {
console.warn(`Quote failed for ${tokenIn}->${tokenOut}:`, error.message);
return null;
}
}
if (!isAddress(tokenAddress)) {
throw new Error('Invalid token address');
}
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') {
} else {
}
});
Gas Estimation
const gasEstimate = await contract.estimateGas.swap(...args);
const gasLimit = gasEstimate.mul(120).div(100);
const feeData = await provider.getFeeData();
const tx = {
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
gasLimit,
};
try {
await contract.callStatic.swap(...args);
const tx = await contract.swap(...args);
} catch (e) {
}
if (feeData.maxFeePerGas > MAX_ACCEPTABLE_GAS) {
throw new Error('Gas too high, waiting...');
}
Multi-Chain Configuration
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',
},
};
Transaction Management
const receipt = await tx.wait(2);
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;
}
}
Rate Limiting & Retry
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('Max retries exceeded');
}
const RPC_ENDPOINTS = [
'https://eth-mainnet.alchemyapi.io/v2/KEY',
'https://mainnet.infura.io/v3/KEY',
'https://rpc.ankr.com/eth',
];
Quick Checklist