Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt

Use this file to discover all available pages before exploring further.

For wallet teams adding or improving Monad support in an EVM wallet. Monad works with most EVM wallets out of the box — same addresses, transaction format, signatures, and dapp connection flow as Ethereum. But a handful of network-level differences — gas billed on the limit, asynchronous execution, no global mempool view — change how a wallet should quote gas, poll status, and surface pending state. For end-user instructions, see Add Monad to Wallet. For chain IDs, RPC URLs, tokens, and protocol contract addresses, see Network Information, the token-list, and protocols.

Tune gas handling

On Monad, users pay for the transaction’s gas limit, not the gas it actually uses. This is the single biggest wallet-side difference from Ethereum: large safety buffers significantly overcharge users and reserve block space the transaction never uses. Use a small, Monad-specific gas-limit margin, and warn users when they manually set a gas limit far above the estimate. Category Labs’ gas-limit analysis recommends eth_estimateGas with only a modest buffer. A fixed buffer is a good starting point; a dynamic buffer keyed on receiver address and function selector reduces unused gas further once you have enough history to calibrate. Quote fees from current Monad data, not Ethereum defaults:
  • eth_maxPriorityFeePerGas returns a hardcoded 2 gwei — not a live network recommendation.
  • eth_feeHistory duplicates the latest baseFeePerGas when newest_block is latest. Don’t count it twice in charts or averages.
See Gas Pricing and the JSON-RPC fee estimation notes.

Handle transaction lifecycle

A successful eth_sendRawTransaction means “accepted by this RPC node” — not a guarantee the transaction will land or succeed. The RPC node may accept it before checking nonce and balance against the latest state. Distinguish three stages:
StageWhat to showSignal
SubmittedPendingeth_sendRawTransaction returns a hash
Included and executed locallyCompleted or failedeth_getTransactionReceipt returns a receipt
FinalizedFinalizedThe receipt’s block number is at or below the block returned for the finalized tag
If an account just received MON and is about to spend it, wait until the receiving transaction’s receipt block is at least k blocks behind the current block. Currently k = 3 (~1.2 seconds), but this can change. If a spend drops an undelegated account below 10 MON, wait k blocks before another MON spend. After undelegating, wait k blocks before emptying the account. Monad has no global mempool. Don’t use txpool_content or newPendingTransactions for pending state. Instead:
  • Track local pending nonces for transactions you submitted.
  • Reconcile against receipts and eth_getTransactionCount.
  • Use txpool_statusByAddress or txpool_statusByHash for node-level pending status.
  • Scope pending lists to the user’s account, not the whole network.

Simulation and RPC differences

debug_trace* methods don’t return opcode-level struct logs. Use call-frame or prestate tracers for simulation and risk previews. WebSocket subscriptions: newHeads, logs, plus Monad-specific monadNewHeads and monadLogs for pre-finalization data. The syncing and newPendingTransactions WebSocket subscription types are not supported. See WebSocket subscriptions. Full nodes may serve recent state, but not arbitrary old state. Check RPC capability before showing past-state UI or old-block simulation, and link to an archive endpoint when needed. See Historical Data.

EIPs and EVM differences

Monad supports transaction types 0, 1, 2, and 4. Type 3 blob transactions are not supported.
FeatureStatusWallet implication
EIP-4844Not supportedReject type 3 transaction construction with a clear error.
EIP-7702Supported with Monad-specific restrictionsA delegated EOA can hold less than 10 MON, but any transaction that lowers its balance below 10 MON reverts. Delegated account code also cannot run CREATE or CREATE2.
Monad has larger contract-size limits than Ethereum and some opcode repricing. Use Monad-specific thresholds for deploy-size warnings, and re-estimate per chain rather than hardcoding opcode costs. See Differences from Ethereum.

Recipes

Apply a chain-specific gas-limit margin

Use Category Labs’ 7.5% fixed-buffer result as a Monad starting point, then tune from production data — success rates, retries, and out-of-gas events. Basis points avoid floating-point rounding errors.
const DEFAULT_GAS_LIMIT_MARGIN_BPS = 15_000n // 1.5x

const GAS_LIMIT_MARGIN_BPS_BY_CHAIN: Record<number, bigint> = {
  // 7.5% buffer. Measure against your wallet's transaction mix.
  143: 10_750n,
  10143: 10_750n,
}

export const applyGasLimitMargin = ({
  chainId,
  estimatedGas,
}: {
  chainId: number
  estimatedGas: bigint
}) => {
  const margin = GAS_LIMIT_MARGIN_BPS_BY_CHAIN[chainId] ?? DEFAULT_GAS_LIMIT_MARGIN_BPS
  return (estimatedGas * margin + 9_999n) / 10_000n
}

Warn on manual gas-limit overspend

const CHAINS_CHARGING_GAS_LIMIT = new Set([143, 10143])
// Warn only on egregious overrides — not normal 1.5–2x safety buffers.
const OVERSPEND_WARNING_MULTIPLIER = 10n

export const shouldWarnGasLimitOverspend = ({
  chainId,
  gasLimit,
  recommendedGasLimit,
}: {
  chainId: number
  gasLimit: bigint
  recommendedGasLimit: bigint
}) => {
  if (!CHAINS_CHARGING_GAS_LIMIT.has(chainId)) return false
  if (recommendedGasLimit <= 0n || gasLimit < 21_000n) return false

  return gasLimit > recommendedGasLimit * OVERSPEND_WARNING_MULTIPLIER
}
Show this inline and require explicit acknowledgement before signing. Reset the acknowledgement when the user changes the gas limit.

Wait for receipt and finality

The manual loop below works with standard RPC. If your RPC supports it, eth_sendRawTransactionSync is the shorter path.
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

const hexToBigInt = (hex: string) => BigInt(hex)

export const waitForReceipt = async (provider: EIP1193Provider, txHash: string) => {
  while (true) {
    // A receipt means the transaction has executed locally on this RPC node.
    const receipt = await provider.request({
      method: "eth_getTransactionReceipt",
      params: [txHash],
    })

    if (receipt) return receipt
    await sleep(400)
  }
}

export const waitUntilFinalized = async (provider: EIP1193Provider, receiptBlockNumber: string) => {
  while (true) {
    // Compare the receipt's block with the node's finalized commitment level.
    const finalizedBlock = await provider.request({
      method: "eth_getBlockByNumber",
      params: ["finalized", false],
    })

    if (hexToBigInt(finalizedBlock.number) >= hexToBigInt(receiptBlockNumber)) return
    await sleep(400)
  }
}
Use receipts for ordinary status. Wait for finality before crediting bridges, deposits, or irreversible off-chain settlement.

Learning resources

Gas Pricing

How Monad charges gas and how EIP-1559 works on Monad

JSON-RPC Overview

RPC differences, block tags, WebSockets, limits, and errors

Reserve Balance

When accounts can spend below the 10 MON reserve

EIP-7702 on Monad

Delegated EOA behavior and Monad-specific restrictions

Historical Data

Current-state and historical-state availability