eth_estimateGas
eth_estimateGas(callObject, blockTag?) → quantity Returns an estimate of how much gas a transaction will need if it were executed at the given block. The node simulates the call (no state change, no transaction) and reports the gas used. Use this before signing to set a sane gas field; pad the result by 20–30% for production transactions because actual on-chain gas can drift slightly with state changes between simulation and inclusion. On this chain the estimate also covers PCL policy evaluation: the compliance check that runs after your call executes is metered against your transaction's gas, and eth_estimateGas runs the same check, so the number it returns already includes it. Do not budget for it separately, and do not set a gas limit from the contract call's cost alone.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
callObject | object | ✓ | Transaction call object. Common fields: from (sender address), to (target address — omit for contract creation), value (hex atokrw to forward), data (hex calldata), gasPrice / maxFeePerGas / maxPriorityFeePerGas (optional, used for base-fee-aware simulation). |
blockTag | string | - | Block to simulate against — latest (default), pending, finalized, or a hex block number. On Maroo's CometBFT consensus, pending and finalized resolve to the latest committed block (instant finality). earliest is accepted but also simulates against the latest state, not the first block; to simulate against an older block, pass its hex block number. The node rejects safe with -32602. |
Returns
string Estimated gas amount, hex-encoded with 0x prefix. On Maroo Testnet a plain transfer estimates at about 104,000 gas (e.g. 0x195c1 = 103,873), not Ethereum's 21,000 (0x5208): the compliance (PCL) and agent checks that run after every transaction are metered as its gas. That figure holds from an ordinary account to any address without code, an ERC-8004 agent's EOA wallet included. Sending from an agent wallet estimates higher, because the global policy looks up the owner of each agent that uses the wallet: about 283,000 when one agent uses it, and about 461,000–471,000 when two do. Sending to a contract wallet, such as a smart account, also estimates higher, since that wallet's own code runs. Use the returned value rather than a fixed number.
Errors
| Code | Name | Description |
|---|---|---|
3 | execution reverted | The simulated call reverted (the target contract threw, a PCL policy denied it, etc.). When the revert carries data, the node answers with code 3 and error.data holds the original revert reason — decode it to learn why the call would fail. A revert with no data comes back as -32000 with the same message and no data. |
-32000 | gas required exceeds allowance | The call needs more gas than the cap in force. That cap is the gas you passed — a plain transfer here needs about 104,000, so a gas of 21,000 always hits this — or, when you pass none, the node's RPC gas cap, further limited to your balance ÷ maxFeePerGas when you set a fee. Drop a hand-set gas and let the estimate decide; if no cap was set, look for an unbounded loop. The testnet's block gas limit is effectively unlimited, so it is not the cause. |
-32602 | Invalid params | The params are malformed: no callObject at all, a malformed address, or invalid hex in value/data. A missing from is not an error here; still pass it when the estimate depends on the sender (its balance, a PCL policy that reads it). |
Examples
ethers.js v6 — estimate before sending
ethers.js calls eth_estimateGas under the hood. For contract calls, pass data from iface.encodeFunctionData(...). Use what it returns as the basis for your gas field. A limit computed from the contract call alone can run out during the post-execution policy check, which draws on whatever the limit has left — the estimate already covers that check, a hand-rolled number does not.
import { JsonRpcProvider, parseEther } from "ethers";
const provider = new JsonRpcProvider("https://rpc-testnet.maroo.io");
const gas = await provider.estimateGas({
from: "0xSenderAddress",
to: "0xRecipientAddress",
value: parseEther("100"), // 100 OKRW
});
console.log("Estimated gas:", gas.toString());
// Pad by ~25% for headroom in production.
const safeGas = (gas * 125n) / 100n; Raw cURL
0x56BC75E2D63100000 is 10^20 atokrw = 100 OKRW. On Maroo Testnet this returns about { jsonrpc, id, result: "0x195c1" } — roughly 104,000 gas for a plain transfer between ordinary accounts (more when the sender is an agent wallet or the recipient is a contract wallet). On Ethereum the same request returns 0x5208 (21,000).
curl -X POST https://rpc-testnet.maroo.io \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [{
"from": "0xSenderAddress",
"to": "0xRecipientAddress",
"value": "0x56BC75E2D63100000"
}, "latest"],
"id": 1
}'