A swap simulation fails because the test account has no USDC. With an eth_call state override, you can inject a token balance for one call and see whether the swap succeeds. The chain state stays unchanged.
The third eth_call parameter maps addresses to temporary account state. This guide shows how to patch storage, test provider support, and avoid a successful response that ignored your override.
For plain read pricing and batch sizing, see the eth_call pricing comparison and the JSON-RPC batch sizing guide. For gas estimation context, see the Glamsterdam eth_estimateGas notes.
What the state override set does
Geth documents eth_call with up to four parameters:
- The transaction call object (
to,data, optionalfrom,value,gas, and fee fields) - A block number, tag, or hash (defaults to
latest) - An optional state override set
- An optional block override set
The state override object maps each address to fields you want to change for that call only:
| Field | Type | What it changes |
|---|---|---|
balance | Quantity | Fake ETH balance for the account |
nonce | Quantity | Fake nonce for the account |
code | Binary | Fake EVM bytecode at the address |
state | Object | Replace all storage slots with the provided map |
stateDiff | Object | Patch individual storage slots |
movePrecompileToAddress | Address | Move a precompile to another address |
Nethermind documents the same parameter order: [transactionCall, blockParameter, stateOverride, blockOverride]. Chainstack documents state overrides on Geth and Erigon. QuickNode's eth_call reference lists balance, nonce, code, state, and stateDiff on the override object.
Overrides never appear in eth_sendRawTransaction. They exist only for simulation and estimation paths that the client chooses to expose.
Block overrides, where the client supports them
The optional fourth parameter customizes the block context for the call. Geth lists these fields for eth_call, eth_simulateV1, and debug_traceCall:
| Field | Purpose |
|---|---|
number | Block number seen by NUMBER |
time | Block timestamp |
gasLimit | Block gas limit |
feeRecipient | Coinbase / fee recipient |
baseFeePerGas | EIP-1559 base fee |
blobBaseFee | EIP-4844 blob base fee |
prevRandao | Previous randomness beacon value |
Although Geth's general block-override docs list withdrawals for other methods, Geth rejects it on eth_call (block override "withdrawals" is not supported for this RPC method).
Use block overrides when the contract reads block.timestamp, block.number, or the base fee. Do not assume every provider accepts the fourth parameter. Confirm state-override support with the state probe below; use the separate block-context probe after it before shipping client code that depends on block overrides.
Production uses for state overrides
Simulate a transfer or swap with a hypothetical balance. Patch the token balance slot or the caller's ETH balance, then call transfer or the router. The node returns the result or revert data without broadcasting a transaction.
Estimate gas under forced state. Geth's JSON-RPC eth_estimateGas accepts the same state and block override parameters as eth_call in go-ethereum's BlockChainAPI. That lets you quote gas for a path that only exists after you inject a balance or allowance. Confirm your provider forwards those optional params; some gateways strip unknown fields.
Override a mapping slot for a token balance. ERC-20 balanceOf usually reads balances[user]. Set that slot with stateDiff, then call balanceOf or a dependent contract. You need the correct slot, not a guessed index (see pitfalls below).
Test patched contract code without deploying. Put replacement bytecode in code for an existing address, keep the storage layout compatible, and call a new view method. Geth's own override example rewrites a checkpoint oracle to expose a field that the live contract did not return.
Pre-flight wallet and backend flows. Wallets, simulation services, and CI jobs can ask "what happens if this account had X?" on a shared RPC endpoint instead of maintaining a private Anvil fork for every scenario.
Common override mistakes
state vs stateDiff
state replaces the account's storage with only the slots you supply. Slots you omit are empty for that call. stateDiff patches the listed slots and leaves the rest of live storage intact.

In go-ethereum, both fields on the same address are rejected: the override layer returns an error if an account has both state and stateDiff. Prefer stateDiff when you only need a few slots. Use state when you intentionally want a blank storage map plus your keys.
Mapping slots need keccak, not the map key
Solidity stores a mapping value at keccak256(h(k) . p), where p is the mapping's base slot and h pads value-type keys to 32 bytes (Solidity storage layout). For a typical ERC-20 mapping(address => uint256) balances at slot p, the balance slot for user is:
slot = keccak256(pad32(user) || pad32(p))
If you write the padded address into slot p itself, balanceOf still reads the real mapping entry and your override appears to do nothing. Check the slot before you trust a simulation. eth_getStorageAt at the computed slot should return the same value that balanceOf(user) returns for a real holder. Some tokens use a different base slot or a non-standard layout.
Client and provider support is not uniform
Support depends on the execution client behind the URL and on whether the gateway forwards optional parameters. Documented examples exist for Geth, Erigon (via Chainstack), Nethermind, and QuickNode's method reference. Other stacks may accept eth_call while ignoring or rejecting the third parameter. Treat "the method exists" as insufficient. Probe the endpoint.
Large override payloads
Injecting full contract code plus wide state maps grows the JSON body and the node's temporary state work. Prefer stateDiff with the minimum slots. Cap code size to what you need for the call path. Oversized bodies fail with gateway limits or timeouts that look like generic RPC errors.
Caching and silent no-ops
Do not cache an override result as if it were live chain state. Two identical call objects with different override maps are different requests. If a provider strips the third parameter, you get a successful response against real state, which is worse than an error. Include a negative control. The same call without the override should fail or return a different value when your scenario depends on overridden state.
Archive vs recent block tags
Geth notes that calls against blocks older than 128 require archive state on a default full node. An override does not recreate missing historical trie nodes. If eth_call at an old block fails with a missing-state error, switch to an archive endpoint or a recent tag. The hidden cost of archive nodes covers that trade-off.
eth_estimateGas override support
Even when eth_call accepts overrides, the estimate path may not. Geth implements overrides on EstimateGas. Your SaaS URL might run a different client or a proxy that only forwards two parameters. Test both methods on the same endpoint before you rely on override-based gas quotes in production.
Verify that a provider supports overrides
A balance-only check is unreliable. Some clients skip the sender balance check in eth_call, so a transfer from an empty account can succeed with or without an override. Use a probe whose output can only come from the override.
The probe targets an empty address. The override injects 15 bytes of bytecode that returns two 32-byte words: storage slot 0 (SLOAD) and the contract's own balance (SELFBALANCE). It also sets balance to 42 and patches slot 0 to 7 with stateDiff. One call exercises code, balance, and stateDiff.
# Control: the address has no code, so the call returns 0x
curl -sS https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_call",
"params": [
{ "to": "0x00000000000000000000000000000000000000aa", "data": "0x" },
"latest"
]
}'
# Probe: inject code, balance, and one storage slot for this call only
curl -sS https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "eth_call",
"params": [
{ "to": "0x00000000000000000000000000000000000000aa", "data": "0x" },
"latest",
{
"0x00000000000000000000000000000000000000aa": {
"code": "0x6000546000524760205260406000f3",
"balance": "0x2a",
"stateDiff": {
"0x0000000000000000000000000000000000000000000000000000000000000000":
"0x0000000000000000000000000000000000000000000000000000000000000007"
}
}
}
]
}'
An endpoint that applies all three fields returns slot 0 (7) followed by the balance (42, hex 0x2a):
{
"jsonrpc": "2.0",
"id": 2,
"result": "0x0000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000002a"
}
| Probe result | Meaning |
|---|---|
| Both words match (7 and 42) | code, balance, and stateDiff are applied |
0x | The third parameter was dropped. The call ran against real state |
| First word is 0, second is 42 | stateDiff was ignored |
| Invalid params error | The endpoint rejects the override parameter |
Treat a 0x result as the dangerous case. The call succeeds, so a simulation built on it reports success against the wrong state.

The state probe above does not test the fourth parameter. To test block overrides separately, inject bytecode that returns NUMBER, then compare a control with only the code override to a call with the same code and a distinct fourth-parameter number:
# Control: code is injected, but NUMBER uses the provider's current block
curl -sS https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "eth_call",
"params": [
{ "to": "0x00000000000000000000000000000000000000bb", "data": "0x" },
"latest",
{
"0x00000000000000000000000000000000000000bb": {
"code": "0x4360005260206000f3"
}
}
]
}'
# Probe: the fourth parameter should make NUMBER return 0x1 instead
curl -sS https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "eth_call",
"params": [
{ "to": "0x00000000000000000000000000000000000000bb", "data": "0x" },
"latest",
{
"0x00000000000000000000000000000000000000bb": {
"code": "0x4360005260206000f3"
}
},
{ "number": "0x1" }
]
}'
The control should return the current block number, while the block-override probe should return 0x1. If both responses match, the provider likely dropped the fourth parameter.
Run the same state probe through eth_estimateGas with the override as the third parameter. A supporting endpoint returns a gas figure for the injected code. An endpoint that drops the override estimates a call to an empty account instead, and the number still looks plausible. Compare it with an estimate taken without the override.
Dwellir counts each RPC response as one API credit, including eth_call with overrides. There are no method multipliers. The Ethereum eth_call method page shows the request shape for Dwellir endpoints.
Code examples in viem and ethers
Token balance with stateDiff (curl)
Replace the placeholders with a token address, a holder, and a slot you checked with eth_getStorageAt. 0xCOMPUTED_MAPPING_SLOT must be the keccak slot for the holder, not the raw address.
curl -sS https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_call",
"params": [
{
"to": "0xTOKEN_ADDRESS",
"data": "0x70a08231000000000000000000000000HOLDER_ADDRESS_WITHOUT_0x"
},
"latest",
{
"0xTOKEN_ADDRESS": {
"stateDiff": {
"0xCOMPUTED_MAPPING_SLOT": "0x00000000000000000000000000000000000000000000000000000000000f4240"
}
}
}
]
}'
0x70a08231 is the balanceOf(address) selector. The override sets the holder's balance to 1,000,000 base units (0xf4240).
viem
viem's call action takes stateOverride as an array of per-address objects. stateDiff is a list of { slot, value } pairs. This is the same probe:
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http('https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY'),
})
const probe = '0x00000000000000000000000000000000000000aa'
const { data } = await client.call({
to: probe,
data: '0x',
stateOverride: [
{
address: probe,
code: '0x6000546000524760205260406000f3',
balance: 42n,
stateDiff: [
{
slot: '0x0000000000000000000000000000000000000000000000000000000000000000',
value: '0x0000000000000000000000000000000000000000000000000000000000000007',
},
],
},
],
})
// data: slot 0 (7) followed by the balance (42)
ethers v6
ethers v6 provider.call takes a transaction request and has no override argument. Send the JSON-RPC params directly with provider.send:
import { JsonRpcProvider } from 'ethers'
const provider = new JsonRpcProvider(
'https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY',
)
const probe = '0x00000000000000000000000000000000000000aa'
const result = await provider.send('eth_call', [
{ to: probe, data: '0x' },
'latest',
{
[probe]: {
code: '0x6000546000524760205260406000f3',
balance: '0x2a',
stateDiff: {
'0x0000000000000000000000000000000000000000000000000000000000000000':
'0x0000000000000000000000000000000000000000000000000000000000000007',
},
},
},
])
If you batch these checks, match responses by request id. A batch can return HTTP 200 while one member fails, as the batch sizing guide explains.
Operator checklist
- Run the code, balance, and
stateDiffprobe on every endpoint in your failover list. Treat a0xresult as a failure. - Prefer
stateDifffor token balances and allowances; reservestatefor full storage replacement. - Compute mapping slots with
keccak256(pad32(key) || pad32(slot))and check them witheth_getStorageAtagainst a real holder. - Keep override payloads small. Large
codeand widestatemaps raise timeout risk. - Do not treat override results as cacheable chain state.
- Run the probe through
eth_estimateGasseparately if gas quotes need the same overridden state. - Use archive endpoints only when the block tag needs historical state; overrides do not invent missing tries.
- Record which client family sits behind each provider URL in your failover list.
When you need Ethereum endpoints that treat every method as one credit, including simulation-heavy eth_call traffic, start from Dwellir's Ethereum network page or the rate limits docs. Run the override check on the URL you will bill against, not only on a public demo endpoint.


