eth
The eth namespace is the foundational and most commonly used API set in Ethereum's JSON-RPC interface. It provides core functionality for interacting with the Ethereum blockchain, enabling users and applications to read blockchain state and submit transactions.
Key methods within this namespace allow you to check an account's balance (eth_getBalance), get the current block number (eth_blockNumber), retrieve transaction details (eth_getTransactionByHash), and send signed transactions to the network (eth_sendRawTransaction, eth_sendRawTransactionSync). Essentially, the eth namespace contains all the fundamental tools needed to observe and participate in the life of the chain.
API usage
For API usage refer to the below official resources:
The sections below cover only the places where Erigon adds a method that is not in that standard set, or where its behaviour differs from it. They are not an exhaustive compliance statement: anything not listed here is expected to follow the spec, but a deviation that has not yet been documented may still exist.
execution-apis is a living specification rather than a frozen standard — methods are
still being added, and some long-standing eth behaviour was never specified at all — so
parts of what follows are differences from a moving target rather than defects. Erigon
runs the specification's own conformance vectors in continuous integration (CI), using Hive's rpc-compat suite against
a pinned execution-apis revision, and works with the other client teams on closing
these gaps, so this page will change as the two converge.
Pending state
Erigon does not support the pending block tag for eth_call, eth_createAccessList, eth_getProof, eth_getWitness, eth_getTxWitness, or eth_simulateV1. These methods need a block header and state from the same view, and Erigon cannot currently acquire a matching pending-state view. They return pending state is not supported instead of executing against a different block. Other eth methods keep their existing pending behavior.
Fork-gated call fields
Methods that execute a call object — among them eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1, eth_callMany, debug_traceCall and the trace_call family — validate it against the fork active at the effective execution block, as a transaction would be. For eth_simulateV1 that is each simulated block, which runs after the selected parent, and block overrides can move it to a different fork than the parent's:
| Call carries | Rejected when | Error |
|---|---|---|
accessList | the block is before Berlin | eip-2930 transactions require Berlin |
blobVersionedHashes | the block is before Cancun | BlobTx transactions require Cancun |
blobVersionedHashes and no to | from Cancun on | txn: field 'To' can not be 'nil' |
an empty blobVersionedHashes list | from Cancun on | blob txn must contain at least one blob versioned hash |
| a blob versioned hash with the wrong version byte | from Cancun on | blob txn versioned hash has invalid version byte |
On 3.7 the trace_call family takes no blobVersionedHashes, so the blob rows do not apply to it.
eth_createAccessList therefore rejects every request on a block before Berlin, including a plain transfer.
eth_syncing and eth_subscribe("syncing")
eth_syncing returns false when the current block is fewer than 8 blocks from the highest block seen, in either direction, unless snapshots are downloading or handing off to execution; otherwise an object with startingBlock, currentBlock, highestBlock and stages (per-stage progress). While snapshots download, currentBlock advances with the bytes downloaded, scaled to the block the snapshots reach; during the handoff to execution it stays at the snapshots' commitment block. In both phases stages is empty, as it is while the highest block is not yet known. A node 8 or more blocks ahead of a stale highest block therefore reports syncing, with highestBlock equal to currentBlock. On 3.7 eth_syncing reports startingBlock as 0x0; the subscription reports the block it was at when it first saw the node syncing.
eth_subscribe("syncing") (WebSocket or inter-process communication (IPC)) sends the last known state when you subscribe, if the node has reported one, then a message on each change: the same object with syncing: true while syncing, or false once synced. A slow client can miss intermediate states: each subscription buffers 8 messages and drops the oldest first, so the latest state always arrives.
eth_getProof
eth_getProof returns Merkle proofs for account state and storage slots, as defined in EIP-1186. It is stable and production-ready as of Erigon v3.4.
To enable historical proof support, activate commitment history storage at startup:
--prune.include-commitment-history=true
RAM requirement: Historical eth_getProof requires at least +32 GB RAM to operate efficiently. Running without sufficient memory will severely degrade node performance.
This enables faster retrieval of Merkle proofs for any executed block.
eth_getStorageValues
eth_getStorageValues reads several storage slots for one or more accounts in a single call, reducing round-trips compared to multiple eth_getStorageAt calls. It originated as an Erigon extension and has since been adopted into the Ethereum execution APIs (April 2026). It takes two parameters.
Parameters
| Parameter | Type | Description |
|---|---|---|
| requests | OBJECT | Maps each account address to an array of 32-byte storage slot keys (hex-encoded) |
| blockNumber | STRING or NUMBER | Optional. Block number, block hash or tag ("latest", "earliest", etc.). Defaults to "latest" |
Example
curl --data '{"jsonrpc":"2.0","method":"eth_getStorageValues","params":[{"0xdAC17F958D2ee523a2206206994597C13D831ec7":["0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002"],"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":["0x0000000000000000000000000000000000000000000000000000000000000002"]},"latest"],"id":1}' -H "Content-Type: application/json" -X POST http://localhost:8545
Returns
An object mapping each requested address to an array of 32-byte values, in the same order as the slot keys requested for that address. Slots that were never written return zero.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"0xdac17f958d2ee523a2206206994597c13d831ec7": [
"0x000000000000000000000000c6cde7c39eb2f0f0095f41570af89efc2c1ea828",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": [
"0x0000000000000000000000000a06be16275b95a7d2567fbdae118b36c7da78f9"
]
}
}
Limits
- A single call may request at most 1024 slots in total, counting all addresses together. Larger requests are rejected.
- An empty
requestsobject returns error code-32602with the messageempty request. - When
blockNumberis a block hash, the block must be canonical.
eth_getWitness and eth_getTxWitness
Neither method exists in the standard set. They return a state witness for the pre-state of a block: the serialized trie node stream needed to re-execute it without a full state database.
Parameters
| Method | Parameter | Type | Description |
|---|---|---|---|
eth_getWitness | blockNumberOrHash | STRING, NUMBER or OBJECT | Block to build the witness for |
eth_getTxWitness | blockNumberOrHash | STRING, NUMBER or OBJECT | Block containing the transaction |
eth_getTxWitness | txIndex | QUANTITY | Index of a transaction within that block |
eth_getTxWitness currently only bounds-checks txIndex — an index past the end of the
block is an error, but the witness it returns is the same whole-block witness
eth_getWitness produces. Two cases are not rejected: the check narrows the index with
int(txIndex), so on a 64-bit build a value from 0x8000000000000000 upward wraps
negative and passes; and at the genesis block the check never runs at all, because both
methods share one implementation that returns the empty witness as soon as the block
number resolves to 0.
Returns
DATA — the serialized witness. On the normal path Erigon decodes the bytes it is about
to return and checks that they rebuild the parent state root, so the witness is
self-verified. Two early returns skip that check because they produce no witness to
verify: the genesis block, and any block whose access set turns out to be empty. Both
return the empty witness.
Requirements
Both methods need commitment history for any block above genesis, so start the node with:
--prune.include-commitment-history
The genesis block is the one exception: the empty witness is returned before the
commitment-history check, so eth_getWitness and eth_getTxWitness succeed at block 0
on a node without commitment history. For every other block, without it the call fails
with an error starting with eth_getWitness requires commitment history; the message
continues with a restart hint that names the
--prune.experimental.include-commitment-history alias rather than the canonical flag
above. If the requested block is older than the retained commitment history, the call
fails with an error starting with commitment history pruned, followed by the retained
range.
eth_fillTransaction
eth_fillTransaction takes a partially specified transaction, fills in what is
missing, and returns it unsigned — it neither signs nor submits anything. The method
was added to the execution-apis specification in July 2026; Erigon (matching geth)
deviates from it in the return shape, which keeps the raw field the spec dropped.
Parameters
Object— a transaction object in the same shapeeth_callaccepts.
The standard type field is ignored. CallArgs has no field for it and unknown JSON
properties are accepted silently, so the envelope is inferred from whichever fee,
access-list and blob fields are present. On a post-London head, a request such as
{
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
"gas": "0x5208",
"type": "0x0"
}
comes back as a type 0x2 transaction rather than the legacy type requested, because no
gasPrice was supplied. The to field is not decoration: a fill with neither to nor
contract-creation data fails with contract creation without any data provided before a
transaction is built at all.
Fills in
nonce— from the pending nonce offrom, or0whenfromis absentvalue—0when absentgas— frometh_estimateGasagainstlatestchainId— the node's chain ID; a mismatching supplied value is an error. It is used for validation and carried by every typed transaction, but it does not appear in the returnedtxwhen the fill infers legacy type0x0:CallArgs.ToTransactionbuilds aLegacyTxwith no chain-ID field, and the response encoder skips chain-ID derivation whilevis zero. That is thedefaultarm of the type switch — reached only when none ofauthorizationList,blobVersionedHashes,maxFeePerGasoraccessListis present. AnaccessListfill before London and a blob fill after it are typed and carrychainId, even whengasPriceis supplied. An authorization-list fill is typed and carrieschainIdtoo, but never alongsidegasPrice— that pair is rejected outright, as the fee-field rules below explain- fee fields — an explicitly supplied
gasPriceis preserved, before or after London, though after London it must be non-zero (gasPrice must be non-zero after london fork); before London a zero is accepted. It cannot be combined withmaxFeePerGasormaxPriorityFeePerGas, and a supplied pair must satisfymaxFeePerGasnon-zero and>= maxPriorityFeePerGas. It also cannot be combined with anauthorizationList(both gasPrice and authorizationList specified) — a set-code transaction has nogasPricefield, so honouring both would silently reinterpret it as the dynamic pair. WhengasPriceis absent: before LondongasPricecomes from the gas oracle; after London the dynamic fields are filled independently,maxPriorityFeePerGasfrom the oracle andmaxFeePerGasas twice the base fee plus the tip. The dynamic fields are rejected before London maxFeePerBlobGas— for blob transactions, twice the current blob gas price
Rejected
Besides the fee-field rules above, a fill fails outright when:
authorizationListis present but empty —need at least one authorization for a set-code transaction- a derived fee field would not fit 256 bits.
maxFeePerGasis computed as twice the base fee, plus the tip when one was supplied, andmaxFeePerBlobGasas twice the blob gas price; each overflow is reported separately (maxFeePerGas overflow: 2*baseFee ...,maxFeePerGas overflow: maxPriorityFeePerGas ... + 2*baseFee ...,maxFeePerBlobGas overflow: 2*blobGasPrice ...)
Returns
An object with raw, the unsigned transaction in its canonical binary encoding, and
tx, the same transaction in JSON form. raw comes from MarshalBinary, so it is bare
Recursive Length Prefix (RLP) only for a legacy transaction; a typed one is the EIP-2718 type byte followed by the
encoded payload, and feeding that straight to an RLP decoder will fail. The standardized
result carries only tx; the extra raw field is the geth-compatible shape.
tx also carries four fields the standardized unsigned schemas do not define. v, r
and s are placeholders — SignTransactionResult.MarshalJSON writes "0x0" for each —
and a typed result adds yParity, likewise 0x0. Nothing is signed here, so none of
them is a signature.
The hash field is computed before signing and changes once the transaction is
signed. It is not the hash the transaction will have when submitted, so it must not be
used to track it. Take the hash from eth_sendRawTransaction instead.
A dynamic-fee result — types 0x2, 0x3 and 0x4 — also carries gasPrice: null.
FillTransaction passes no base fee to the response encoder, so computeGasPrice
returns nothing and the missing key is filled with null — deliberate geth parity,
pinned by rpc/ethapi/api_test.go. The standardized type-2 unsigned schema requires
gasPrice to be a hex quantity, so a schema-validating client will reject the result;
read maxFeePerGas and maxPriorityFeePerGas instead.
Legacy (0x0) and access-list (0x1) results are not affected: NewRPCTransaction
takes their gasPrice from the tip cap, so it is a real hex quantity. Those two carry
maxFeePerGas: null and maxPriorityFeePerGas: null instead, filled in by the same
rule. A type 0x1 fill is reached by supplying gasPrice together with an
accessList — Erigon keeps the list where geth drops it to a legacy transaction.
KZG commitment and proof generation from raw blobs is not implemented. Blob
transactions must already carry their blobVersionedHashes.
The sidecar fields the specification defines — blobs, commitments and proofs — are
not supported: CallArgs has no field for them, so they are dropped from the request
without an error even when a complete precomputed sidecar is supplied, and they are
absent from the returned tx and raw. Callers that need the sidecar must keep it
themselves and reattach it before submitting.
Block number parameter format
Erigon accepts more block-number forms than the standard schema, which allows only a
0x-prefixed hex quantity string or a named tag.
The rules below govern parameters decoded as BlockNumber or BlockNumberOrHash — the
selector of eth_getBlockByNumber, eth_getBalance, eth_call, trace_block and the
rest of that family. They do not apply to methods that select a block through a fixed
common.Hash parameter: eth_getBlockTransactionCountByHash,
eth_getTransactionByBlockHashAndIndex, eth_getRawTransactionByBlockHashAndIndex,
eth_getUncleByBlockHashAndIndex and eth_getUncleCountByBlockHash take a 32-byte hash
and nothing else. eth_getBlockByHash is not one of them — despite the name its
parameter is a BlockNumberOrHash, so it follows the rules here.
Accepted:
- a
0x-prefixed hex string without leading zeros —"0x3","0x2ed119" - a bare, unquoted JSON integer —
3 - the named tags
"earliest","latest","safe","finalized","pending" null, whose handling depends on the parameter's type, not on the method:- a plain block-number parameter accepts it —
BlockNumber.UnmarshalJSONmaps top-levelnulltolatest, soeth_getBlockByNumber,trace_blockand the rest of that family take it even though the parameter is required; - an optional block-or-hash parameter accepts it too: the selector is left unset
and the method's own default applies,
latestfor most; - a required block-or-hash parameter rejects it —
BlockNumberOrHash.UnmarshalJSONdecodesnullinto an empty struct and then fails withat least one of BlockNumber or BlockHash is needed if a dictionary is provided(rpc/types.go). This holds for every such method, includingeth_getBlockReceipts,eth_getBlockAccessList,eth_simulateV1,eth_getWitness,eth_getTxWitness,eth_getBlockByHashandtrace_replayBlockTransactions
- a plain block-number parameter accepts it —
Rejected by both parameter types:
- a quoted decimal string —
"3","100" - a block number above
2^63-1— out of range in both parameter types, though the error differs: a plain block-number parameter fails withblock number larger than int64, while a top-level numeric or hex block-or-hash value fails withblocknumber too high(rpc/types.go)."0x8000000000000000"and the equivalent bare integer are rejected either way - hex with leading zeros —
"0x01" - hex without the prefix —
"ff"
Breaking change in v3.6. Quoted decimal strings such as "3" used to be accepted
and are now rejected with hex string without 0x prefix, returned as an
invalid params error. Callers relying on that form must switch to "0x3", the bare
integer 3, or a named tag.
Two further forms exist: the Erigon-specific tag "latestExecuted", and the string
"null", which means latest. Both are parsed by BlockNumber.UnmarshalJSON, so they are
accepted unconditionally by methods whose parameter is a plain block number — such as
eth_getBlockByNumber, eth_getBlockTransactionCountByNumber and trace_block.
On methods taking a block-number-or-hash selector — eth_call, eth_getBalance,
eth_getProof and the rest — the two behave differently.
BlockNumberOrHash.UnmarshalJSON lists latestExecuted in its own top-level string
switch, so the bare string works there as well. "null" is not in that switch: it falls
through to the hex parser and fails. The object form accepts both, because that path
decodes the blockNumber field through BlockNumber:
{"blockNumber": "latestExecuted"}
{"blockNumber": "null"}
So {"blockNumber": "null"} is the only way to reach latest by that spelling on a
block-number-or-hash method.
Filter lifetime
The standard spec does not say how long a filter lives. In Erigon, a filter created by
eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter is evicted
once it has gone 5 minutes without being polled. Only eth_getFilterChanges counts
as a poll: it calls TouchSubscription, which resets the clock, so a filter stays alive
on a quiet chain as long as the client keeps calling it. eth_getFilterLogs does not
— it reads the stored criteria and serves them through the same path as eth_getLogs,
without touching the deadline — so a client that only ever calls eth_getFilterLogs
loses its filter after five idle minutes. A call against an evicted filter returns
filter not found.
Logs returned by eth_getFilterChanges for a log filter, and logs pushed by
eth_subscribe("logs"), carry the block time as blockTimestamp, as eth_getLogs does.
Change the window, or turn eviction off entirely, with:
--rpc.subscription.filters.timeout 15m # longer window
--rpc.subscription.filters.timeout 0 # keep filters indefinitely
Eviction applies only to these polling filters. WebSocket subscriptions live as long as their connection does.