Overview
Declared eth_calls are a performance optimization technique that allows you to pre-declare contract calls in your subgraph manifest. Instead of making synchronous RPC calls during event processing, the indexer executes these calls ahead of time and caches the results in memory. This dramatically improves indexing performance by eliminating RPC latency from your handler execution.How it works
When you declare an eth_call in your subgraph manifest, the indexing engine follows this optimized workflow:- Pre-execution: Before processing events, the indexer identifies all declared eth_calls for the current block
- Batch fetching: All declared calls are executed in parallel against the blockchain
- In-memory caching: Results are stored in an efficient in-memory cache
- Handler access: When your event handler code binds to a contract and makes a call, it retrieves the cached result instantly instead of making an actual RPC call
Requirements
To use declared eth_calls, ensure yoursubgraph.yaml specifies the correct version:
subgraph.yaml
Syntax
Declared eth_calls are added as acalls property on an event handler entry in your subgraph manifest. The calls property is an object where each key is an arbitrary name and the value describes the eth_call to make.
The general syntax is:
calls:— an object of key-value pairs nested under the event handler that triggers the eth_calls.callName:— an arbitrary name you choose for the declared call. Use something self-documenting, e.g. if your eth_call isgetLiquidity, name itgetLiquidity: ....ABI— the name of the ABI as defined in theabissection of your manifest. This corresponds to how you useABI.bind(address)in your mapping code. You can search your source mapping for.bind(calls to identify where declared eth_calls can be applied.[address]— the address passed to the ABI when binding. This can beevent.address,event.params.<name>, or a constant hex address..method(...)— the contract method to call. Use the actual method name even if your mapping usestry_syntax (e.g. if your code callstry_getSymbol, declare it as.getSymbol()).(args)— the arguments passed to the contract method. These can beevent.address,event.params.<name>, or constant values.
Implementation guide
Step 1: Identify candidate calls
Look for contract calls in your event handlers that:- Are called frequently during event processing
- Access the same contract method repeatedly
- Contribute to slow indexing performance
- Getting token metadata (name, symbol, decimals)
- Checking user balances or allowances
- Reading contract configuration values
- Fetching pool or pair addresses from a factory
Step 2: Declare the calls in your manifest
Add acalls property to the relevant event handler. Here’s an example for a Uniswap V3 pool that pre-fetches token0 and token1 addresses on every swap:
subgraph.yaml
Step 3: Use cached results in your event handler
No separate handler functions are needed for declared eth_calls. The results are automatically cached and returned when your event handler binds to the contract and calls the same method. Your existing mapping code works as-is:src/mapping.ts
try_token0, the declared call uses just token0 — omit the try_ prefix in the manifest.
Address and argument patterns
Thecalls syntax supports several ways to specify the contract address and method arguments. You can use event.address, event.params.<name>, or constant hex addresses in any combination:
Best practices
Only declare calls that are actually needed
Only declare calls that are actually needed
Each declared call adds overhead during the pre-execution phase. Only declare calls that your event handlers will actually use during event processing.
Use try_ patterns for safety
Use try_ patterns for safety
Always use the
try_ prefix when calling contract methods in your handlers. This ensures your subgraph continues indexing even if a call fails or reverts. The declared call in the manifest always uses the base method name (without try_).Consider call frequency vs. overhead
Consider call frequency vs. overhead
Declared eth_calls are most effective for calls that happen frequently. If a call only happens once per block or less, the overhead of declaring it may outweigh the benefits.
Match the call signature exactly
Match the call signature exactly
The address and arguments in your declared call must match what your event handler actually passes when binding and calling the contract method. If they don’t match, the cache won’t be used and a standard RPC call will be made instead.
Performance impact
The performance benefits of declared eth_calls depend on several factors:- Call frequency: More frequent calls see greater benefits
- RPC latency: Higher latency networks benefit more from caching
- Handler complexity: Handlers with multiple contract calls see the most improvement
After implementing declared eth_calls, you should see faster indexing speeds and reduced RPC load in your deployment metrics.
Example implementation
Check out our complete example implementation showing declared eth_calls in action with an ERC-20 token subgraph on the Taiko network.Troubleshooting
Calls still seem slow after declaring
Calls still seem slow after declaring
Ensure your
specVersion is set to 1.2.0 or higher. Older spec versions don’t support declared eth_calls and will fall back to standard synchronous calls.Cache not being used
Cache not being used
Verify that the address and arguments in your declared call match exactly what your event handler passes. For example, if you declare
Factory[event.address].get(event.params.address) but your handler binds to a different address, the cached result won’t be used.Indexing errors after adding declared calls
Indexing errors after adding declared calls
Check that the contract ABI includes all the functions you’ve declared. Missing function definitions will cause indexing to fail.