> ## 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.

# Wallet Developer Integration Guide

> Guidance and recipes for wallet teams improving Monad support

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](/guides/add-monad-to-wallet).

For chain IDs, RPC URLs, tokens, and protocol contract addresses, see
[Network Information](/developer-essentials/network-information), the
[`token-list`](https://github.com/monad-crypto/token-list), and
[`protocols`](https://github.com/monad-crypto/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](https://www.category.xyz/blogs/setting-your-gas-limit-on-monad)
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](/developer-essentials/gas-pricing) and the JSON-RPC
[fee estimation notes](/reference/json-rpc/overview#fee-estimation).

## 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:

| Stage                         | What to show        | Signal                                                                               |
| ----------------------------- | ------------------- | ------------------------------------------------------------------------------------ |
| Submitted                     | Pending             | `eth_sendRawTransaction` returns a hash                                              |
| Included and executed locally | Completed or failed | `eth_getTransactionReceipt` returns a receipt                                        |
| Finalized                     | Finalized           | The 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](/reference/json-rpc/overview#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](/developer-essentials/historical-data).

## EIPs and EVM differences

Monad supports transaction types 0, 1, 2, and 4. Type 3 blob transactions are not supported.

| Feature  | Status                                     | Wallet implication                                                                                                                                                         |
| -------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EIP-4844 | Not supported                              | Reject type 3 transaction construction with a clear error.                                                                                                                 |
| EIP-7702 | Supported with Monad-specific restrictions | A 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](/developer-essentials/differences).

## 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.

```ts theme={null}
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

```ts theme={null}
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.

```ts theme={null}
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

<CardGroup cols={2}>
  <Card title="Gas Pricing" href="/developer-essentials/gas-pricing">
    How Monad charges gas and how EIP-1559 works on Monad
  </Card>

  <Card title="JSON-RPC Overview" href="/reference/json-rpc/overview">
    RPC differences, block tags, WebSockets, limits, and errors
  </Card>

  <Card title="Reserve Balance" href="/developer-essentials/reserve-balance">
    When accounts can spend below the 10 MON reserve
  </Card>

  <Card title="EIP-7702 on Monad" href="/developer-essentials/eip-7702">
    Delegated EOA behavior and Monad-specific restrictions
  </Card>

  <Card title="Historical Data" href="/developer-essentials/historical-data">
    Current-state and historical-state availability
  </Card>
</CardGroup>
