guide13 min read

Monad Mainnet RPC for Wallet Builders

Monad RPC checks for wallets: chain ID 143 / 0x8f, MON gas charged on the gas limit, 0.3 s blocks, and the public endpoints that passed 8 probes.

By RPC Directory•
#Monad#MON#RPC#Wallet#Chain ID#MetaMask#Rabby#EVM
$monad-mainnet-rpc.md

Monad is an EVM Layer 1 with the native MON token for gas. Blocks arrive about every 0.3 seconds and execution runs a few blocks behind consensus. The JSON-RPC surface looks like Geth, but Monad charges gas on the gas limit you sign, not the gas you use, and full nodes do not keep old state. Wallet builders who copy Ethereum defaults for gas padding, history reads, or pending transactions will overcharge users or show results that change a moment later.

This is not a ranking of providers. Use the Monad Mainnet hub to compare endpoints and paid rows, then run the checks below on the URL you configure. The Monad Mainnet providers page lists the keyed options side by side.

Chain ID 143 is mainnet, 10143 is testnet

  • Monad mainnet: chain ID 143, hex 0x8f. eth_chainId returned 0x8f on every URL that answered during our probe, and net_version returned "143". Monad's network information page lists the same chain ID.
  • Monad testnet: chain ID 10143, hex 0x279f. Chainlist (chainid.network) lists it as "Monad Testnet" with the same MON symbol. A wallet that shows MON without the network name can make a testnet balance look like real funds.
  • Ethereum mainnet: chain ID 1. Monad is its own Layer 1, not a rollup, so it needs its own network entry.

Wallet-add fields for mainnet, matching Monad's published network information:

  • Network name: Monad Mainnet (Chainlist uses Monad)
  • Chain ID: 143 (or 0x8f if the form wants hex)
  • Currency symbol: MON
  • RPC URL: a JSON-RPC HTTPS endpoint you have already probed, such as https://rpc1.monad.xyz
  • Block explorer: https://monadscan.com or https://monadvision.com

Native gas is MON with 18 decimals. Call eth_chainId on the exact RPC URL you will ship and expect 0x8f. A gateway that returns 0x279f is testnet. A timeout, an HTML page, an API-key error, or a rate-limit body is not Monad mainnet for that transport.

Public endpoints that answered eth_chainId

We probed every public Monad mainnet URL on the hub 8 times, about 55 seconds apart, on 2026-10-12 between 02:12 and 02:19 UTC. Each round called eth_chainId, eth_getBlockByNumber("latest"), eth_getBalance on the Wrapped MON contract, and an eth_call for Wrapped MON's totalSupply(), with a curl User-Agent on every request. The hub lists 19 HTTPS and 2 WSS transports. These passed all four calls in 8 of 8 rounds. On every one, the latest block's timestamp was within 1.5 seconds of our clock (Monad block timestamps are whole seconds):

EndpointTransportNotes
https://rpc1.monad.xyzHTTPSMonad docs list it as run by Alchemy, 15 requests per second, debug_ and trace_ disabled
https://rpc2.monad.xyzHTTPSMonad docs list it as run by Goldsky Edge, 300 requests per 10 seconds, historical eth_call supported
https://rpc3.monad.xyzHTTPSMonad docs list it as run by Ankr, 300 requests per 10 seconds, debug_ disabled
https://rpc4.monad.xyzHTTPSNot in the public RPC table on Monad's network information page
https://rpc-mainnet.monadinfra.comHTTPSMonad Foundation. Docs list 20 requests per second, batch limit 1, historical eth_call supported
https://monad-rpc.huginn.tech / wss://wss.monad-rpc.huginn.techHTTPS, WSSBoth transports passed all four calls
https://gm.monad.at.htw.techHTTPSThe WSS URL on the same host failed (see below)
https://infra.originstake.com/monad/evmHTTPS
https://monad-mainnet.rpc.sentio.xyz / https://rpc.sentio.xyz/monad-mainnetHTTPSTwo URLs for the same provider; both passed
https://monad-mainnet-rpc.spidernode.netHTTPS
https://rpc.swiftnodes.io/rpc/monadHTTPS

Some rows answered with caveats and stay listed:

  • https://monad-mainnet.drpc.org passed eth_chainId, the latest block and eth_getBalance in 8 of 8 rounds. Our eth_call sent no gas field and returned -32000 "user-specified gas exceeds provider limit" every time. A second 8-round run from 02:20 to 02:27 UTC with gas set to 10,000,000 passed all four calls in every round. Set an explicit gas value on eth_call against this URL.
  • https://monad-mainnet.gateway.tatum.io passed the first three calls in 8 of 8 rounds. eth_call returned -16401 "available for paid plans only" every time, so it cannot read token balances without a plan.
  • https://monad-mainnet.api.onfinality.io/public passed 4 of 8 rounds. The other rounds returned HTTP 429 with a request to use an API key.
  • https://rpc.monad.xyz (QuickNode, the first URL in Monad's docs) returned 0x8f and current blocks when it answered, but 19 of 32 requests from our probe host failed the TLS handshake after 10 seconds. A second run from 02:20 to 02:27 UTC failed the same way on 19 of 32 requests, while short manual checks in between all succeeded. We could not rule out our own network path, so it stays listed. Test it from your own servers before you rely on it.

The hub's Dwellir row is a keyed URL: https://api-monad-mainnet-full.n.dwellir.com/ followed by an API key. Without a key it returned HTTP 403 "API key does not exist" on every call, as expected for a keyed endpoint, so it is not in the table above.

Three rows failed all 8 rounds, and the directory now hides them:

  • https://lb.routeme.sh/rpc/evm/143 returned HTTP 429, JSON-RPC -32029 "public rate limit exceeded", in every round.
  • https://monad.rpc.blxrbdn.com does not resolve in DNS (NXDOMAIN).
  • wss://gm.monad.at.htw.tech accepted the WebSocket connection but answered -32601 "Method not found" to every call, including eth_chainId.

A row on a directory page is a starting point, not proof it serves Monad mainnet today.

What the Monad JSON-RPC does differently

Monad's JSON-RPC overview lists where it departs from Geth. The ones a wallet hits:

  • No pending transactions. eth_getTransactionByHash returns null until the transaction is in a block. Do not treat null right after a send as "dropped".
  • Deferred validation on send. eth_sendRawTransaction can accept a transaction with a nonce gap or too little MON, because the RPC node may not have the latest state yet. Check the receipt, not the send response.
  • No arbitrary historic state. eth_call or eth_getBalance at an old block number can fail on full nodes. Monad's docs list rpc2.monad.xyz and rpc-mainnet.monadinfra.com as supporting historical state lookups.
  • Small eth_getLogs ranges. Monad's docs list 100 blocks per call on rpc.monad.xyz and rpc-mainnet.monadinfra.com, and 1,000 on rpc1 and rpc3. At 0.3 seconds per block, 100 blocks is about 30 seconds of chain time, so activity history needs many calls or an indexer.
  • No blob transactions. Type 3 (EIP-4844) transactions are rejected.
  • WebSocket: newHeads and logs fire on proposed blocks. newPendingTransactions is not supported. Monad adds monadNewHeads and monadLogs, which report each block's commit state as it advances.

web3_clientVersion on rpc1.monad.xyz returned Monad/0.16.4 during the test.

Fees: charged on the gas limit, not gas used

On rpc1.monad.xyz at 02:20 UTC:

  • The latest block's baseFeePerGas was 0x174876e800 (100 gwei), the minimum base fee in Monad's gas pricing docs.
  • eth_gasPrice returned 0x17bfac7c00 (102 gwei).
  • eth_maxPriorityFeePerGas returned 0x77359400 (2 gwei). Monad's docs say this is a hardcoded suggestion for now.
  • eth_feeHistory over 5 blocks showed a base fee of 100 gwei in every block and median tips of 2 to 10 gwei.
  • eth_estimateGas for a plain MON transfer returned 0x5208 (21,000).
  • The block gas limit was 0x8f0d180 (150,000,000).

A plain transfer at 102 gwei costs 21,000 × 102 gwei, or 0.002142 MON. What makes Monad different is the charge: the sender pays gas_limit × price_per_gas, whatever the transaction actually uses. Monad's docs say this protects the network under asynchronous execution.

What that means for a wallet:

  • Do not pad the gas limit on transfers. A native MON transfer always uses exactly 21,000 gas. A 20% buffer on Ethereum costs nothing extra. On Monad it costs the user 20% more.
  • Keep buffers on contract calls small, and set known limits explicitly. Every unit of headroom is paid for. Monad's docs recommend that dapps set the gas limit themselves when it is known in advance.
  • Watch for reverted estimates. Monad's docs warn that some wallets, MetaMask included, set a very high gas limit when eth_estimateGas reverts. On Monad the user pays for that whole limit. Show the revert to the user instead of sending.
  • Show the fee as limit × price. There is no refund for unused gas, so the gas limit, not a usage estimate, sets what the user pays.

The 10 MON reserve balance

Because consensus runs ahead of execution, Monad keeps a reserve balance of 10 MON per account (reserve balance docs). Most users never notice it. A wallet does notice in two cases:

  • Quick sends from a small balance. An undelegated account can drop below 10 MON once in any 3-block window. A second send from the same account inside that window can revert, or not be included at all, if it would take the balance below 10 MON. Space back-to-back sends from low balances, or let the first one confirm.
  • EIP-7702 delegated accounts cannot drop below 10 MON at all. A transaction that would take them lower reverts but still pays gas.

Finality: latest, safe, finalized

In a single batch request to rpc3.monad.xyz and rpc2.monad.xyz, safe was 0 to 1 block behind latest, and finalized was 1 to 2 blocks behind. Across the last 10,000 blocks, timestamps spanned 3,019 seconds, about 0.30 seconds per block. That matches the 300 ms block time in Monad's docs.

Monad maps the tags to its block states:

  • latest is a proposed block, speculatively executed with no consensus vote yet. Data at latest can change on the next identical request.
  • safe is a voted block.
  • finalized is finalized and cannot be reverted without a hard fork.

For a wallet, latest is fine for balances on screen. For "payment received", deposits, or anything you credit, wait until the receipt's block number is at or below finalized. At these speeds that is under a second of extra wait.

Add Monad to MetaMask or Rabby

These are the values we checked on 2026-10-12, not values copied from a list:

  • eth_chainId returned 0x8f (143) on every passing URL in the table above.
  • Chainlist's entry for chain 143 is named "Monad", with MON and 18 decimals, and lists https://monadscan.com and https://monadvision.com as explorers. Monad's own docs list the same two explorers.
  • Both explorers returned HTTP 200 to a request with a browser User-Agent.

MetaMask: follow the MetaMask custom RPC guide. Check 'Additional networks' first. To add it by hand, open 'Networks', choose 'Add a custom network', and enter the five fields from the chain ID section with a URL from the passed table. MetaMask does not verify that a custom network is what it claims to be, so the probe is your check.

Rabby: follow the Rabby custom network guide. Check whether Monad already appears in Rabby's network list before you add a custom entry. If you add it by hand, enter chain ID 143 and an RPC URL that returned 0x8f in your own probe.

Dapps: for wallet_addEthereumChain, send chainId: "0x8f", chainName: "Monad Mainnet", nativeCurrency: { name: "Monad", symbol: "MON", decimals: 18 }, rpcUrls with your probed HTTPS endpoint, and blockExplorerUrls: ["https://monadscan.com"]. For wallet_switchEthereumChain, send only { chainId: "0x8f" }, and call wallet_addEthereumChain if MetaMask returns error 4902.

Wallet-add gotchas that show up in QA

  1. Testnet chain ID 10143 under a "Monad" label: balances look real but are testnet MON.
  2. Ethereum-style gas padding: every padded unit is charged on Monad.
  3. Huge gas limit after a reverted estimate: the user pays the full limit for a transaction that was going to fail.
  4. "Dropped" after send: eth_getTransactionByHash returns null until inclusion, which on Monad is a fraction of a second later.
  5. Credited too early: a latest result can change. Credit at finalized.
  6. History screens that call eth_getLogs over wide ranges: 100 or 1,000 block caps on the public RPCs.
  7. eth_call with no gas field against DRPC's public URL: -32000 until you set one.

Public vs dedicated for wallet traffic

Monad's docs describe the public endpoints as rate-limited but enough for basic usage, and point to RPC providers for higher limits.

Public endpoints are fine for:

  • Local development and wallet QA
  • Low-volume eth_chainId and eth_blockNumber probes
  • One-off scripts and docs examples

They are a poor primary path when you:

  • Run production wallet RPC for many users (balance, nonce, fee estimate, send, receipt polls)
  • Need historical state or wide eth_getLogs ranges
  • Depend on WebSocket subscriptions at volume
  • Cannot absorb shared-node rate limits during a launch, airdrop, or market spike

Probe the candidate URL before you call it ready:

  1. eth_chainId returns 0x8f.
  2. The latest block advances when you poll twice a second apart, and its timestamp is within a couple of seconds of wall-clock time.
  3. A plain transfer eth_estimateGas returns 0x5208 (21,000).
  4. An eth_call on a token contract you care about returns data, not a paid-plan or gas-limit error.
  5. finalized returns a block one or two behind latest.
  6. A burst of 20 to 50 quick requests does not return HTTP 429 or a JSON-RPC rate-limit error at the volume you expect per client.

For production wallet traffic, compare the paid rows on the same hub for method coverage, archive access, and quota. At the time of writing those include Alchemy, Chainstack, DRPC, Infura, and Dwellir. Dwellir's row is a keyed Monad mainnet endpoint. It is not the only option.

A cheaper URL that fails the probe above is not a savings. The public vs paid guide covers when a shared endpoint stops being enough, and the pricing calculator estimates monthly cost for a given request mix. Log-heavy indexers should also read the eth_getLogs caps guide, and the dead public endpoints guide explains why listed URLs stop answering.

Practical split for a wallet stack

  • User-facing Monad network row: chain ID 143 / 0x8f, MON symbol with the network name shown, Monadscan or MonadVision, an RPC you probed today.
  • Testnet row, if you need it: chain ID 10143 / 0x279f, a separate config key, never reused as the mainnet RPC.
  • Fee estimation: eth_gasPrice or base fee plus the 2 gwei tip, no padding on transfers, tight limits on contract calls, fee shown as limit × price.
  • Confirmations: show latest right away, credit at finalized.
  • Backend / indexer: a dedicated or paid endpoint with historical state and the eth_getLogs range you need. Do not share the browser wallet's public URL.

Ship the hub comparison and the probe first. A network called "Monad" in a dropdown does not replace eth_chainId returning 0x8f.