eth_estimateGas: Size a Transaction's Gas Limit
eth_estimateGas•Read method•Ethereum JSON-RPCeth_estimateGas simulates a transaction and returns the gas limit it would need. It takes the same call object as eth_call, executes it against real state, and answers with a number you put in the gas field of the transaction you are about to sign. The estimate is a measurement of one moment, not a guarantee, and treating it as a guarantee is how transactions run out of gas.
Try eth_estimateGas
curl -s https://ethereum-rpc.publicnode.com \
-X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_estimateGas",
"params": [
{
"from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"data": "0x095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d0000000000000000000000000000000000000000000000000000000005f5e100"
}
]
}'Runs the example straight from your browser against the endpoint above. The prefilled URL is a third-party public endpoint, not run by BLAZED.sh.
What eth_estimateGas does
The node does not compute the answer analytically; it searches for it. Starting from a range bounded by the intrinsic cost and the node's gas cap, it executes your call repeatedly with different gas limits until it finds the lowest limit under which execution succeeds. That means one eth_estimateGas is several full EVM executions, which is why it is noticeably heavier than a single eth_call for the same input.
The estimate covers intrinsic gas (21,000 for a plain transaction plus the calldata cost of every byte) and everything the EVM spends during execution. Storage costs dominate the variance: writing a slot from zero to non-zero costs far more than overwriting a non-zero slot, and the first touch of an address or slot in a transaction is charged as cold access. The same call therefore estimates differently depending on state that has nothing to do with your inputs.
Like eth_call, execution is discarded. Nothing is signed, nothing is broadcast and no gas is paid; if the call would revert, you get an error instead of a number, with the revert data attached. Geth also accepts an optional block parameter and the same state-override object eth_call takes, so you can estimate against historical state or against a hypothetical world where your account already holds the tokens.
Parameters
| # | Name | Type | Description |
|---|---|---|---|
| 1 | callObject | object | Transaction-shaped call: to, from, value, data (or input) and optionally gas and fee fields. Omitting gas lets the node search the whole range up to its cap. |
| 2 | blockParameteroptional | string | Hex block number or latest, pending, safe, finalized, earliest. Defaults to the client's notion of the latest state; Geth accepts pending to estimate against its own mempool view. |
| 3 | stateOverridesoptional | object | Geth-style per-address overrides applied only for this estimate: balance, nonce, code and storage slots, exactly as in eth_call. |
What it returns
A single hex quantity: the gas limit the node believes the transaction needs. It is a limit, not a price, and it is not a fee; multiply it by a gas price of your choosing to get a cost in wei.
On a call that would revert you get a JSON-RPC error rather than a number, carrying the same revert payload eth_call would return. "gas required exceeds allowance" is different: it means the search hit the node's simulation cap rather than the contract reverting.
Example response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0xdc29"
}0xdc29 is 56,361 gas for a USDC approve of 100 USDC to the Uniswap V2 router. Run it again after the allowance is set and the number drops, because overwriting a non-zero storage slot is cheaper than filling an empty one; that sensitivity to state is the whole story of this method.
eth_estimateGas with ethers.js
import { JsonRpcProvider, Contract } from "ethers";
const provider = new JsonRpcProvider("https://ethereum-rpc.publicnode.com");
const usdc = new Contract(
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
["function approve(address spender, uint256 amount) returns (bool)"],
provider,
);
// .estimateGas on any method sends eth_estimateGas
const estimate = await usdc.approve.estimateGas(
"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
100_000_000n,
{ from: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
);
// pad it: state can move between estimating and mining
const gasLimit = (estimate * 120n) / 100n;
console.log(estimate, "->", gasLimit);Gotchas and common errors
An estimate is a snapshot, so pad it
The estimate describes execution against the state of one block. By the time your transaction mines, someone else may have flipped a storage slot from zero to non-zero, an oracle may have updated, or a code path that was warm may be cold. All of those raise real consumption above the estimate and the transaction fails with out-of-gas, having paid for the privilege. Adding 10% to 30% headroom is standard practice, and unused gas is refunded anyway, so the only cost of padding is a higher balance requirement up front.
Reverts come back as errors, not as zero
If the simulated call reverts, the node returns a JSON-RPC error with the revert data attached, exactly as eth_call does. In wallets and bots this surfaces as "cannot estimate gas; transaction may fail", which almost always means the transaction genuinely would fail: a missing approval, insufficient balance, a slippage guard, or a paused contract. Estimating a swap before the token approval exists is the single most common instance.
from matters more than for eth_call
Left unset, the call runs as the zero address, which owns nothing and is approved for nothing, so anything balance- or allowance-gated reverts. Set from to the account that will actually sign. Be aware of the other side of that coin: once from is set and you also pass fee fields, the node checks that the account can afford value plus gas, so an unfunded account gets "insufficient funds" instead of an estimate. Omitting the fee fields skips that check.
The 63/64 rule breaks exact estimates
A call frame may forward at most 63/64 of its remaining gas to a child call. When a contract makes a nested call near the end of its budget, an estimate that is exactly right for the outer frame can leave the inner call one 64th short. Contracts that check the gas they forward, or that loop over a dynamic number of sub-calls, are where this shows up; padding covers it.
Gas limit is not gas price
eth_estimateGas answers how much work the transaction does. What you pay per unit of that work comes from eth_feeHistory, eth_maxPriorityFeePerGas or eth_gasPrice, and the total cost is the product of the two. Setting the estimate as a fee, or a fee as a gas limit, produces transactions that either never mine or that fail at once.
It costs more work than eth_call
Because the answer comes from a binary search, the node runs your call several times over. On a shared endpoint that shows up as slower responses and heavier rate-limit consumption for what looks like one request. Bots that already know the shape of the transactions they send usually skip estimation entirely and use a hardcoded limit with a safety margin, keeping eth_estimateGas for unfamiliar contracts.
What eth_estimateGas costs on BLAZED.sh
eth_estimateGas costs 1 credit per call on BLAZED.sh, the standard rate, and the hex number it returns will never approach the 100KB response threshold. What differs from a plain read is the work behind the credit: the node executes your call repeatedly to bisect the answer, so estimation-heavy flows feel the difference in response time long before they feel it in credits. On a co-located node that work happens on the machine your code is already running on, one sub-10ms local round-trip away, rather than behind a gateway that is also metering it.
See the full credit price listeth_estimateGas: frequently asked questions
Why does eth_estimateGas fail with "execution reverted"?
Because the transaction would revert if you sent it. The node simulates the call, so a failing estimate is a failing transaction: usually a missing token approval, insufficient balance, a slippage or deadline guard, or a paused contract. Decode the revert data in the error to see which.
How much buffer should I add to the estimate?
Between 10% and 30% is the common range, larger for contracts whose gas depends on storage that other people are writing to. Unused gas is refunded, so padding costs nothing beyond needing the balance to cover the ceiling.
What is the difference between eth_estimateGas and eth_call?
Both simulate the same call object against the same state. eth_call returns the function's return data; eth_estimateGas returns the gas the execution needed, found by running it repeatedly under different limits. Use eth_call to read a result and eth_estimateGas to fill in a gas limit.
Why does the estimate change between calls?
Gas depends on state. Writing a storage slot that is currently zero costs far more than overwriting a non-zero one, and the first access to an address or slot in a transaction is charged at the cold rate. Anything that changes those conditions between two estimates changes the answer.
What does "gas required exceeds allowance" mean?
The search ran into the node's simulation gas cap rather than into a revert. Geth's cap is configurable and defaults to 50 million; heavy multi-hop calls can genuinely need more than a shared endpoint allows. On your own node it is a setting rather than a wall.
Call eth_estimateGas from the node itself
Deploy your container or script next to a synced Ethereum node. No rate limits, no compute units, sub-10ms local RPC.