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
Behavioral 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:
{
"method": "debug_traceCall",
"params": [
{
"to": "0x6b175474e89094c44da98b954eedeac495271d0f"
},
"latest",
{}
]
}
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
| Feature | Affected methods | Details |
|---|
| EIP-4844 (blob transactions) | eth_sendRawTransaction, eth_call, eth_estimateGas | Blob transaction type is rejected |
syncing subscription | eth_subscribe | Not supported |
newPendingTransactions subscription | eth_subscribe | Not supported |
Limits
eth_call / eth_estimateGas
Gas limit per call
| Provider | Public RPC | Gas limit |
|---|
| QuickNode | rpc.monad.xyz | 200M gas |
| Alchemy | rpc1.monad.xyz | 200M gas |
| Ankr | rpc3.monad.xyz | 1B gas |
| Monad Foundation | rpc-mainnet.monadinfra.com | 200M gas |
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:
| Pool | Gas limit | Purpose |
|---|
| Low-gas | ≤ 8,100,000 | Most calls; higher throughput |
| High-gas | > 8,100,000 | Large simulations; limited concurrency |
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
| Provider | Public RPC | Block range limit |
|---|
| QuickNode | rpc.monad.xyz | 100 blocks |
| Alchemy | rpc1.monad.xyz | 1,000 blocks and 10,000 logs (whichever is more constraining) |
| Ankr | rpc3.monad.xyz | 1,000 blocks |
| Monad Foundation | rpc-mainnet.monadinfra.com | 100 blocks |
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)
| Message | Explanation | Common cause |
|---|
| Parse error | Unable to parse the JSON-RPC request | Malformed JSON |
| Invalid request | The request is structurally invalid | Request exceeds size limit |
| Method not found | The method is not part of the JSON-RPC spec | Typo in method name |
| Method not supported | The method exists in the spec but is not yet supported by Monad | Calling an unimplemented method |
Parameter errors (-32602)
| Message | Explanation | Common cause |
|---|
| Invalid block range | The requested eth_getLogs block range exceeds the provider limit | See block range limits |
| Invalid params | Incorrect parameters for the method | Wrong types, missing required fields, omitted trace options |
Execution errors (-32603)
| Message | Explanation | Common cause |
|---|
| Internal error | The request could not be fulfilled | Server-side failure |
| Execution reverted | The simulated transaction reverted | Failed eth_call or eth_estimateGas |
| Transaction decoding error | The raw transaction could not be decoded | Invalid RLP in eth_sendRawTransaction |
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
| Type | Fires when |
|---|
newHeads | A new header is appended, once the block is Voted |
logs | Matching logs appear in a new block, once the block is Voted |
monadNewHeads | A new header is available, once the block is Proposed and speculatively executed |
monadLogs | Matching logs are available, once the block is Proposed and speculatively executed |
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.