Skip to main content
Monad supports a JSON-RPC interface for interacting with the blockchain. Monad aims to match the RPC behavior of Geth as closely as possible, but due to fundamental architectural differences, some behaviors deviate from Ethereum.

Supported methods

Differences from Ethereum

Monad is Geth-compatible, but its architecture — including asynchronous execution and sub-second block times — introduces behavioral differences in several areas.

Transaction lifecycle

Deferred nonce/balance validation. eth_sendRawTransaction may not immediately reject transactions with a nonce gap or insufficient gas balance. Because Monad’s RPC server is designed for asynchronous execution, it may not have the latest account state at the time of submission. These transactions are initially accepted because they may become valid during block creation. No pending transaction queries. eth_getTransactionByHash only returns transactions that have been included in a block. Querying a transaction that is still in the mempool returns null.

State availability

eth_call invocations that reference old state (i.e. with an old block number) may fail, because full nodes do not provide access to arbitrary historic state. See Historical Data for details on what state is available and how to access it.

Fee estimation

eth_maxPriorityFeePerGas currently returns a hardcoded suggested fee of 2 gwei. This is temporary. eth_feeHistory with newest_block = latest: by convention, this method returns the fee history for the requested range plus one extra projected fee for the next block. Monad does not have all the inputs required to compute the next block’s base fee, so when the newest requested block is latest, the latest baseFeePerGas is returned twice.

Debug / tracing

Trace options parameter is required. debug_traceCall, debug_traceTransaction, and related debug_trace* methods require the trace options object to be explicitly provided. Unlike standard EVM clients where this parameter is optional, Monad RPC returns error -32602 Invalid params if it is omitted. Always include the parameter, even if empty:
Default tracer is callTracer. When an empty trace options object {} is provided, Monad defaults to the callTracer instead of the struct logs tracer typical in other EVM clients. Monad does not currently support opcode-level struct logs at the VM level.

Unsupported features

Block tags

Monad blocks progress through four commitment states: Proposed, Voted, Finalized, and Verified. The JSON-RPC API exposes these through standard Ethereum-compatible block tags.
The "pending" tag is supported but behaves the same as "latest". Learn more.

Unfinalized data

Any RPC response that includes data from a non-finalized block can change on a subsequent identical request.
  • Methods with a block number/tag parameter (eth_call, eth_getBalance, eth_getLogs, etc.) — can return non-finalized data when called with "latest" or a non-finalized block number.
  • Transaction-hash lookups (eth_getTransactionByHash, eth_getTransactionReceipt) — can match a transaction in a non-finalized block. The returned blockNumber, log indices, or even the result itself (null) can change.
  • Implicit-latest methods (eth_gasPrice, eth_maxPriorityFeePerGas) — always return non-finalized data using the "latest" tag.
Treat data from non-finalized blocks as provisional. For transaction-hash lookups, compare the returned blockNumber against the "finalized" block height (tracked client-side) before acting on the result.

Limits

eth_call / eth_estimateGas

Gas limit per call

Node operators. Use --eth-call-provider-gas-limit (default 30M) and --eth-estimate-gas-provider-gas-limit (default 30M) to configure these limits.

Gas limit resolution

When the caller specifies a gas price (gasPrice or maxFeePerGas), the effective gas limit is min(gas limit allowance, provider gas limit), where the gas limit allowance is the maximum gas given the caller’s balance and specified price. When no gas price is specified, the provider gas limit applies directly. This is consistent with Geth’s behavior.

Dual-pool execution model

eth_call and eth_estimateGas requests are routed to one of two execution pools based on the caller-specified gas limit: When the caller does not specify a gas limit, the request is first attempted in the low-gas pool. If it runs out of gas, it is automatically retried in the high-gas pool.
Node operators. Use --eth-call-max-concurrent-requests (default 1000) and --eth-call-high-max-concurrent-requests (default 20) to configure pool concurrency.

eth_getLogs

Block range limit per call

Node operators. Use --eth-get-logs-max-block-range to configure the block range limit.

Why are block range limits low?

Monad produces a block every 400ms and can accommodate up to 5,000 transactions per block with computation up to 200M gas. Blocks are both extremely frequent and significantly larger than Ethereum blocks, which is the main motivation for keeping per-call block range limits low.

Errors

Monad’s JSON-RPC error codes aim to be equivalent to Ethereum’s, but some codes deviate due to lack of standardization across Ethereum clients.

Request-level errors (-32601)

Parameter errors (-32602)

Execution errors (-32603)

WebSocket subscriptions

Monad’s RPC server supports JSON-RPC over WebSocket connections, enabling persistent connections and real-time data via eth_subscribe. See the Geth documentation for general eth_subscribe behavior. Monad extends the standard subscription types with two speculative variants (monadNewHeads and monadLogs) that publish data sooner — approximately one second earlier on average — on a speculative basis. See Speculative Real-Time Data for background on speculative execution and Block States for the full block lifecycle.

Subscription types

The subscription types syncing and newPendingTransactions are not supported.

Speculative subscription behavior

monadNewHeads and monadLogs updates include two additional fields not present in the standard variants:
  • blockId — a unique identifier for this specific block proposal (distinct from block number, since multiple proposals can exist for the same height).
  • commitState — the block’s current commit state: Proposed, Voted, Finalized, or Verified.
The same block will typically produce multiple updates as its commitState advances through the lifecycle. A block may skip Voted and go directly from Proposed to Finalized when consensus is ahead of execution. When a block fails to finalize, it is abandoned implicitly — the finalization of a different block at the same height supersedes it, but no explicit abandonment event is published.