The original implementation used block-by-block scanning to find token transfers:
// Old approach: SLOW
for (let block = currentBlock; block > 0; block -= 1000) {
const logs = await provider.getLogs({
fromBlock: block - 1000,
toBlock: block,
// ... filters
});
// Process logs...
}Issues:
- Extremely slow: 1000+ API calls for wallets with old transfers
- Rate limits: Quickly hits API limits
- Inefficient: Scanning blocks that might have no transfers
- Time-consuming: Can take 5-10 minutes for a single wallet
Alchemy maintains an indexed database of all token transfers, similar to how Basescan works.
const response = await alchemy.core.getAssetTransfers({
toAddress: walletAddress,
contractAddresses: [tokenAddress],
category: ['erc20'],
fromBlock: '0x0',
toBlock: '0xlatest',
});Benefits:
- ⚡ Instant: Returns all transfers in milliseconds
- 📊 Indexed: Pre-indexed database, no block scanning
- 🎯 Precise: Only returns relevant transfers
- 💰 Efficient: Single API call instead of thousands
Wallet with transfers from 6 months ago:
- Blocks to scan: ~1,000,000
- API calls needed: ~1,000 (with 1000-block chunks)
- Time: 5-10 minutes
- Rate limit risk: HIGH
Same wallet:
- API calls needed: 1
- Time: < 1 second
- Rate limit risk: LOW
Result: 300-600x faster!
The app automatically uses Alchemy's fast API when:
- Provider is set to "Alchemy"
- Alchemy SDK is available
- Network is supported
if (this.alchemy) {
// Use fast indexed API
return await this.getTransactionsForAddressAlchemy(...);
} else {
// Fallback to standard RPC
return await this.getTransactionsForAddressRPC(...);
}If Alchemy API fails or isn't available:
- Automatically falls back to standard RPC
- Still works with QuickNode and Infura
- Graceful degradation
Alchemy's fast API works on:
- ✅ Ethereum Mainnet
- ✅ Ethereum Sepolia
- ✅ Polygon Mainnet
- ✅ Arbitrum Mainnet
- ✅ Optimism Mainnet
- ✅ Base Mainnet (your use case!)
No changes needed! The optimization is automatic.
If you're using Alchemy:
- Configure your Alchemy API key
- Select your network (e.g., base-mainnet)
- The app automatically uses the fast API
You'll see in the logs:
Using Alchemy fast API for transfers (much faster than block-by-block)
Alchemy API returned 45 transfers instantly
// The method signature stays the same
await blockchain.getTransactionsForAddress(
walletAddress,
tokenAddress,
fromBlock,
toBlock
);
// But internally:
// - Alchemy users get instant results
// - Other providers use standard RPCimport { Alchemy, Network, AssetTransfersCategory } from 'alchemy-sdk';
// Initialize
const alchemy = new Alchemy({
apiKey: config.apiKey,
network: Network.BASE_MAINNET,
});
// Query
const response = await alchemy.core.getAssetTransfers({
toAddress: address,
contractAddresses: [tokenAddress],
category: [AssetTransfersCategory.ERC20],
fromBlock: `0x${fromBlock.toString(16)}`,
toBlock: `0x${toBlock.toString(16)}`,
maxCount: 1000,
});{
transfers: [
{
blockNum: '0x2155eae',
hash: '0x...',
from: '0x...',
to: '0x...',
value: 1234.56,
asset: 'FULA',
category: 'erc20',
rawContract: {
address: '0x...',
decimal: '0x12'
}
},
// ... more transfers
]
}Alchemy returns up to 1000 transfers per request. For wallets with more:
// Future enhancement: pagination support
let pageKey = undefined;
do {
const response = await alchemy.core.getAssetTransfers({
// ... params
pageKey: pageKey,
});
transfers.push(...response.transfers);
pageKey = response.pageKey;
} while (pageKey);Block explorers like Basescan use the same approach:
- Indexed database: All transfers pre-indexed
- Direct queries: No block scanning needed
- Pagination: Efficient data retrieval
Our app now works the same way when using Alchemy!
| Provider | Fast API | Fallback |
|---|---|---|
| Alchemy | ✅ getAssetTransfers | Standard RPC |
| QuickNode | ❌ | Standard RPC |
| Infura | ❌ | Standard RPC |
Recommendation: Use Alchemy for best performance, especially when tracing old transactions.
Trace a wallet that received tokens 3 months ago (block 35000000 → 36230000)
Blocks to scan: 1,230,000
Chunk size: 1000 blocks
API calls: 1,230
Time per call: ~500ms
Total time: ~10 minutes
API calls: 1
Time: ~200ms
Total time: < 1 second
600x faster!
The Alchemy SDK is included in dependencies:
{
"dependencies": {
"alchemy-sdk": "^3.1.0"
}
}Install with:
npm install- Free tier: 300 requests/second
- Growth tier: Higher limits
- Max 1000 transfers per request
- Standard RPC still slow for old transfers
- Block range limits still apply
- Rate limits still a concern
- Pagination Support: Handle wallets with >1000 transfers
- Caching: Cache transfer results locally
- QuickNode Support: Add QuickNode's similar API if available
- Parallel Queries: Query multiple addresses simultaneously
By using Alchemy's indexed API, we've made the app:
- ⚡ 600x faster for tracing
- 💰 More efficient with API calls
- 🎯 More reliable (fewer rate limits)
- 🚀 Production-ready for real-world use
This is the same technology that powers Basescan and other block explorers!