guide7 min read

Base Mainnet RPC for Wallet Builders

What to check before a wallet points at Base mainnet: chain ID 8453 / 0x2105, OP Stack vs Ethereum L1, Flashblocks vs finality, and public vs dedicated endpoints.

By RPC Directory•
#Base#ETH#RPC#Wallet#Chain ID#OP Stack
$base-mainnet-rpc.md

Base is an OP Stack Layer 2 on Ethereum, not "Ethereum with a different RPC URL." Wallet builders who paste chain ID 1, point an Ethereum gateway at a Base network row, or treat Flashblocks (~200ms preconfirmations) as finality will ship the wrong network, the wrong explorer, or a soft receipt users treat as irreversible. Before you wire MetaMask, Rabby, Rainbow, Phantom, or your own wallet_addEthereumChain flow, prove chain ID 8453, separate L1 from L2, and probe the URL you intend to ship.

This is not a ranking of providers. Use the Base Mainnet hub to compare endpoints and paid rows, then run the checks below on the URL you configure. For the pending-vs-confirmed Flashblocks method matrix, see the Base Flashblocks guide. This article is the wallet-add and confirmation-policy companion, not a rewrite of that matrix.

Chain ID 8453 is not Ethereum and not Base Sepolia

Mainnet chain ID is 8453, hex 0x2105. Base Sepolia is 84532. Ethereum mainnet is 1. Those are different networks. A wallet that already has Ethereum added still needs a separate Base network entry.

Wallet-add fields for mainnet:

  • Network name: Base (or Base Mainnet if the product already scopes the list)
  • Chain ID: 8453 (or 0x2105 if the form wants hex)
  • Currency symbol: ETH
  • RPC URL: a JSON-RPC HTTPS endpoint you have already probed
  • Block explorer: https://basescan.org

Native gas is ETH with 18 decimals, same symbol as Ethereum L1. The shared ticker is a common UX trap: users think they are on Ethereum when the network list says "ETH." Show the network name and chain ID in the UI, not the symbol alone.

Call eth_chainId on the exact RPC URL you will ship. Expect 0x2105. A gateway that returns 0x1 is Ethereum. A gateway that returns Base Sepolia (0x14a34), times out, or answers HTML is not Base mainnet for that transport. Do this after any load balancer or fallback so you are not checking a different backend than production.

OP Stack quirks that matter for wallets

Base uses the Optimism OP Stack with Ethereum as the parent chain. That matters for wallets more than the marketing line "EVM compatible":

  • Deposits and withdrawals go through the Base bridge against Ethereum L1, not as a simple same-chain transfer. Bridge UX needs an L1 RPC policy even when the dapp only calls Base for day-to-day sends.
  • Finality language follows OP Stack semantics (safe / finalized and sequencer vs L1 confirmation). Soft / sequencer tip is fine for tip UX. Do not copy "wait N Ethereum blocks" heuristics into a Base receipt poller without checking what your RPC returns for those tags.
  • Contract addresses are not portable from Ethereum. The same dapp on Ethereum and Base uses different deployments. Hard-coding an Ethereum USDC or Uniswap address into a Base wallet path is a funds bug.
  • Parent Ethereum still needed for bridge. Keep chain ID 1 configured for deposit and withdraw flows. Never reuse the Base RPC URL for L1 calls.
  • Flashblocks (~200ms) are preconfirmations, not finality. On Flashblocks-aware Base RPC, "pending" is in-progress sequencer state. For a UI checkmark, that is useful. For a database write you will not unwind, wait for "finalized". Use "safe" only when the system can handle a reorganization. Details and the method matrix live in the Base Flashblocks guide; do not treat that article's pending probe as a substitute for the wallet-add checks here.

Wallet-add gotchas that show up in QA

The failures that show up in support and QA are usually config, not cryptography:

  1. Wrong chain ID - Ethereum 1 or Base Sepolia 84532 pasted into a mainnet form.
  2. Ethereum RPC on a Base network row - eth_chainId will not return 0x2105.
  3. Explorer mismatch - pointing users at etherscan.io for Base tx hashes, or at a Sepolia explorer for mainnet hashes.
  4. One shared "base" RPC bucket in app config - separate keys for base and base-sepolia so a fallback cannot silently swap networks.
  5. Missing parent Ethereum - bridge and withdraw flows need L1 configured even when the product UI lives on Base.

wallet_addEthereumChain vs wallet_switchEthereumChain

When you implement wallet_addEthereumChain, send a full EIP-3085 payload: chainId: "0x2105", a complete nativeCurrency object ({ name: "Ether", symbol: "ETH", decimals: 18 }), rpcUrls pointing at your probed HTTPS endpoint, and blockExplorerUrls: ["https://basescan.org"]. Wallets that receive only symbol on add can reject the request.

For wallet_switchEthereumChain, send only { chainId: "0x2105" }. Metadata fields belong on add, not switch. Prefer one primary RPC in the wallet payload; put failover in your own client, not as five public URLs the wallet may pick at random.

Public vs dedicated for wallet traffic

Public endpoints are fine for:

  • Local development and wallet QA
  • Low-volume eth_chainId / 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, send, receipt polls)
  • Need predictable latency and a support channel when the gateway 429s
  • Depend on historical state, wide eth_getLogs, or sustained WebSocket traffic
  • Cannot absorb shared-node brownouts during a mint, airdrop, or market spike

Probe the candidate URL before you call it ready:

  1. eth_chainId returns 0x2105.
  2. The latest block or head advances when you poll twice a few seconds apart. Do not use an unchanged account balance as proof the node is live.
  3. One balance or receipt read against a known Base mainnet address (not an Ethereum or Base Sepolia address).
  4. If you need push updates, a WebSocket path that survives a disconnect (many public sockets do not; public Base HTTP in particular does not serve application WSS).
  5. Optional: compare safe / finalized block tags if your confirmation policy depends on them, and document which tag "confirmed" means next to the RPC URL. If your product also reads "pending" for Flashblocks UX, keep that policy separate from settlement.

Public rows on the hub are shared and rate-limited. Use them for the probe. For production wallet traffic, compare the paid rows on the same hub for method coverage, archive or historical access if you need it, and quota. Dwellir is one of those rows: a flat response-credit model that is easier to budget when the mix of reads and writes is not stable. 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. The pricing calculator is there for the rest. Log-heavy indexers should also read the eth_getLogs caps guide before assuming a wide filter will pass on every URL.

Practical split for a wallet stack

  • User-facing Base network row - chain ID 8453 / 0x2105, ETH symbol, Basescan, RPC you probed today.
  • Base Sepolia row - chain ID 84532, separate config key, never reused as the mainnet RPC.
  • Parent Ethereum row - chain ID 1, kept for bridge and withdraw flows, never reused as the Base RPC.
  • Backend / indexer - dedicated or paid endpoint with explicit method and archive needs; do not share the browser wallet's public URL.
  • Receipt policy - decide whether "confirmed" means latest, safe, or finalized on this OP Stack chain, and store that next to the RPC URL. Treat Flashblocks / "pending" as a separate UX signal, not as that settlement tag.

Ship the hub comparison and the probe first. Marketing copy about "cheap Base gas" does not replace eth_chainId returning 0x2105.