Optimism mainnet is an OP Stack rollup on Ethereum. An RPC URL that answers eth_blockNumber is not enough for production. L2 teams that point indexers, keepers, bridges, or wallets at a public gateway without checking chain ID 10, sequencer vs replica behavior, and what safe / finalized return will ship the wrong confirmation policy or a rate-limited backplane. Before you send traffic, prove the chain ID, decide which head you trust, and probe the URL you intend to ship.
This is not a ranking of providers. Use the Optimism Mainnet hub to compare public endpoints and paid rows, then run the checks below on the URL you configure. For OP Stack on BNB as parent, see the opBNB mainnet RPC guide. That article is a different chain and a different parent; do not reuse its wallet-add checklist as an Optimism operator runbook.
Chain ID 10 is OP Mainnet, not Ethereum and not Sepolia
Mainnet chain ID is 10, hex 0xa. OP Sepolia is 11155420. Ethereum mainnet is 1. Those are different networks. A process that already talks to Ethereum still needs a separate Optimism transport.
Call eth_chainId on the exact RPC URL you will ship. Expect 0xa. A gateway that returns 0x1 is Ethereum. A gateway that returns OP Sepolia, times out, or answers HTML is not OP Mainnet for that transport. Do this after any load balancer or fallback so you are not checking a different backend than production.
Native gas is ETH with 18 decimals. Explorer for mainnet tx hashes is Optimistic Etherscan. Pointing users or ops runbooks at etherscan.io (L1) for L2 hashes is a support bug.
Sequencer RPC vs public / replica RPC
On OP Stack, the sequencer orders transactions and produces the unsafe (pre-confirmation) head. Public and third-party RPCs usually sit behind replicas or provider fleets that follow that head, with their own rate limits, caching, and lag.
That split matters for L2 teams more than the marketing line "EVM compatible":
- Sequencer-facing or low-latency paths see the tip first. They are the right shape for submit-and-watch UX when you accept sequencer reorg risk on the unsafe head. They are a poor sole dependency if the sequencer endpoint is rate-limited, authenticated, or not meant for open client traffic.
- Public / shared RPCs are fine for development, light reads, and probes. They are a poor primary path for production wallet balance/nonce/send loops, wide
eth_getLogs, or sustained WebSocket fan-out. Shared gateways 429 and brown out when everyone else spikes. - Dedicated or paid replicas are what most production L2 apps and indexers actually need: predictable quota, support when the gateway fails, and method coverage (archive, traces, filters) that matches the workload.
- Do not assume every URL labeled "Optimism" is the same head. Two providers can disagree on
latestby a few blocks during catch-up. If your confirmation policy compares heights across vendors, measure that gap.
Official docs still publish a public HTTP example such as https://mainnet.optimism.io. Treat published URLs as starting points. Always re-probe before baking one into a release. The hub lists current public and paid rows for OP Mainnet; use those rows, not a screenshot from last quarter.
Confirmation tags on OP Stack: unsafe, safe, finalized
OP Stack Bedrock-line nodes expose the same JSON-RPC block tags Ethereum uses, with rollup-specific meaning:
latest(unsafe head): the tip the node knows from the sequencer pipeline. It can reorg if the sequencer reorganizes unsafe blocks. Fine for tip UX, quotes, and "did my tx land in the sequencer stream?" checks. Not enough if your app treats the receipt as irreversible.safe: the head derived from L1 such that the rollup considers it safe under the network's L1 confirmation rules. Use this when "confirmed" must survive ordinary unsafe reorgs but you are not waiting for L1 finality.finalized: derived from L1 finalized batches. Use this when the app must not unwind under L1 finality assumptions (settlement, accounting, cross-chain messages that key off L1 finality).
Those are not informal synonyms for "wait N L2 blocks." Call each tag on your endpoint and log the block numbers:
eth_getBlockByNumberwithlatest,safe, andfinalized(full txsfalseis enough).- Confirm
safeandfinalizeddo not error if your policy depends on them. - Confirm the heights move over time and that
finalized≤safe≤latestunder normal conditions. Ifsafeequalslatestforever, orfinalizederrors, the node or proxy is not giving you the tags you think.
Deposit and withdrawal flows still depend on Ethereum L1 finality for the parent side. An L2 finalized tag is not a substitute for checking the L1 contract state your bridge docs require. Keep an L1 RPC policy next to the L2 one. Cross-chain workers: one confirmation policy per chain ID, stored next to each RPC URL.
If you also run Base or another OP Stack chain, do not copy Base Flashblocks/pending behavior onto OP Mainnet without measuring. Flashblocks are a Base-specific preconfirmation surface; this directory covers them in the Base Flashblocks guide. OP Mainnet confirmation for most teams is still latest / safe / finalized on ordinary eth_* calls.
What L2 teams usually get wrong
The failures that show up in ops and QA are usually config and policy, not cryptography:
- Wrong chain ID — Ethereum
1or OP Sepolia pasted into a mainnet config. - Treating sequencer tip as irreversible — labeling an unsafe receipt "final" in the database.
- One shared "op-stack" RPC key for Optimism, Base, and opBNB — separate keys per chain ID so a fallback cannot silently swap networks.
- L1 explorer or L1 RPC used for L2 hashes — Optimistic Etherscan vs Etherscan, and a dedicated Ethereum URL for deposit proofs.
- Public URL baked into every worker — fine for a docs example, poor as the only production path for receipt polls and log backfills.
- Assuming archive and wide
eth_getLogson every row — probe historical calls and filter ranges. The eth_getLogs caps guide covers the failure modes.
Public vs dedicated for L2 production traffic
Public endpoints are fine for:
- Local development and staging
- Low-volume
eth_chainId/eth_blockNumberprobes - One-off scripts and docs examples
They are a poor primary path when you:
- Run production RPC for many users or many indexer workers
- 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:
eth_chainIdreturns0xa.- 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.
eth_getBlockByNumberforlatest,safe, andfinalizedreturns three heights you can explain.safeandfinalizedmust not error if your app depends on them.- One balance or receipt read against a known OP Mainnet address (not an Ethereum or Sepolia address).
- If you need push updates, a WebSocket path that survives a disconnect (many public sockets do not).
- One historical
eth_call/eth_getBalanceat an old block if you need archive, and oneeth_getLogsfilter in the range your indexer uses.
Public rows on the hub are shared and rate-limited. Use them for the probe. For production L2 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, logs, 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.
Practical split for an L2 stack
- User-facing / low-latency path — chain ID 10 /
0xa, RPC you probed today, confirmation policy documented aslatest(unsafe) when that is intentional. - Settlement / accounting path — same chain ID, prefer
safeorfinalizedexplicitly; do not reuse the tip URL's mental model without the tag. - Ethereum L1 path — separate config for deposits, proofs, and withdrawal finality; never point L1 calls at the Optimism URL.
- Backend / indexer — dedicated or paid endpoint with explicit method and archive needs; do not share the browser or mobile client's public URL.
- Receipt policy — write down whether "confirmed" means
latest,safe, orfinalizedon this OP Stack chain, and store that next to the RPC URL.
Ship the hub comparison and the probe first. Marketing copy about "OP Stack throughput" does not replace eth_chainId returning 0xa and three confirmation tags you can explain.