opBNB RPC: A Developer's Guide (Chain ID 204)
opBNB is BNB Chain's layer 2. It runs the OP Stack, the same software as Optimism and Base, with one structural difference: its layer 1 is BNB Smart Chain, not Ethereum. That one change explains most of what you will see over RPC. Blocks arrive every 250 milliseconds, finalized trails the head by well under a minute, and the L1 data fee is paid to BSC.
We measured it through our endpoint on 2026-09-29, and found one trap on the way that is worth the whole post: a public endpoint reporting a finalized block nearly four hours old.
The essentials
| opBNB mainnet | |
|---|---|
| Chain ID | 204 (0xcc) |
| Settles to | BNB Smart Chain |
| Gas token | BNB |
| Block time | 250 ms (1,000 blocks in 250 s; 79 newHeads in 20 s) |
| Gas limit | 100,000,000 per block |
eth_gasPrice |
0.001 gwei |
safe / finalized |
about 30 to 50 s behind latest on healthy nodes |
eth_getLogs range |
50,000 blocks at most (exceed maximum block range: 50000) |
| Recent state | about the last 100 blocks on full nodes |
| Tracing | debug_traceBlockByNumber and eth_getBlockReceipts yes; trace_* no |
| Node client | opBNB's op-geth (Geth/v0.5.9-stable) |
It is EVM-equivalent. viem, ethers, Foundry and Hardhat work with the chain ID set to 204, and viem ships an opBNB chain definition.
Four blocks a second
At 250 ms per block, habits from Ethereum stop scaling:
- Polling
latestevery block is 345,600 calls a day. Subscribe tonewHeadsor, better, to thelogsyou actually need. Theeth_subscribelogs guide shows the pattern. - Block counts are not time. "Wait 12 confirmations" is 3 seconds. Use block tags or timestamps.
- The 50,000-block
eth_getLogscap is about 3.5 hours. A one-day backfill is seven requests; a month is about 210. The error message states the limit, so a client can read it and adapt; eth_getLogs range caps covers chunking.
Finality, and the stale-head trap
Because BSC finalizes its own blocks within seconds, opBNB's finalized head moves quickly. On the official BNB Chain endpoint, dRPC and NodeReal, finalized trailed the newest block by 27 to 51 seconds across repeated checks.
One widely used public endpoint told a different story. On about half of our requests it returned a finalized block 13,631 seconds, about 3.8 hours, old, and a fresh one on the rest: a load-balanced pool with some backends stuck. Its WebSocket did the same, at 15,107 seconds. That endpoint was the primary opBNB upstream behind our own service, so we moved opBNB to the official endpoint and dRPC that morning; our endpoint now returns finalized 32 to 38 seconds behind on every request.
The lesson is general. A node that serves a stale finalized doesn't error. A deposit flow that waits for finalized just waits, for hours, and a load-balanced endpoint can even make finalized appear to jump backwards between two calls. Check the tag's timestamp, not just its number:
import { createPublicClient, http } from "viem";
import { opBNB } from "viem/chains";
const client = createPublicClient({
chain: opBNB,
transport: http("https://rpc.swiftnodes.io/rpc/opbnb?key=YOUR_API_KEY"),
});
const [latest, finalized] = await Promise.all([
client.getBlock({ blockTag: "latest" }),
client.getBlock({ blockTag: "finalized" }),
]);
const lagSeconds = Number(latest.timestamp - finalized.timestamp);
if (lagSeconds > 300) {
// opBNB normally finalizes in under a minute: this node's view of BSC is stuck
throw new Error(`finalized head is ${lagSeconds}s behind`);
}
The same idea for the head itself is in is your RPC node synced?.
Fees: the L1 part matters here
opBNB receipts carry the OP Stack L1 fee fields (l1Fee, l1GasPrice, l1GasUsed, l1BlobBaseFee and the scalars), and on opBNB they are not negligible. Across 12 recent user transactions:
| Total | |
|---|---|
L2 execution fee (gasUsed × effectiveGasPrice) |
289,556,149,631 wei |
L1 data fee (l1Fee) |
171,390,813,185 wei |
| L1 share of the total | 37% |
That is the opposite of Katana, where the L1 part was 0.2%. Both are tiny in absolute terms: the sample averaged about 0.00000004 BNB per transaction, a small fraction of a cent. But if you estimate cost as gasUsed × gasPrice alone, you will be about a third low. Use the receipt's l1Fee, or ask the GasPriceOracle predeploy at 0x420000000000000000000000000000000000000F with getL1Fee(bytes) before sending; for 100 bytes of calldata it returned 8,840,000,065 wei.
State and tracing
Full opBNB nodes keep very little history. Through our endpoint, eth_getBalance worked up to about 90 blocks back and failed with missing trie node beyond roughly 127 blocks: about 30 seconds of state. The official endpoint and dRPC also refused a state query 100,000 blocks back. If you need balances or contract state at older blocks, you need an archive node; plan for that before you build a backfill around eth_call at historical heights.
For execution detail, debug_traceBlockByNumber with callTracer works and eth_getBlockReceipts returns a block's receipts in one call. The trace_* namespace (trace_block, trace_filter) is not available, so tools built on trace_filter need to switch to debug_* tracing.
If you're coming from BSC or Optimism
- From BSC: same token for gas (BNB) and much of the same ecosystem, but a separate chain with its own chain ID, 250 ms blocks instead of BSC's, and OP Stack L1 fees on top of execution. The BNB Smart Chain RPC guide covers the L1 side.
- From Optimism or Base: the same predeploys at
0x4200…, the same deposit transactions (type0x7e) at the start of each block, and the samesafe/finalizedsemantics, but finality measured in seconds rather than the minutes an Ethereum-settled rollup takes. The sequencer's role is the same as described in what is a sequencer.
The short version
- Chain ID 204, BNB for gas, OP Stack settling to BSC.
- 250 ms blocks: subscribe, don't poll; count time, not blocks.
finalizedshould be under a minute old. If it's hours old, your node's view of BSC is stuck: check timestamps.- The L1 data fee was 37% of the total in our sample; include
l1Feein cost estimates. eth_getLogscaps at 50,000 blocks, full nodes keep about 100 blocks of state, anddebug_*works whiletrace_*doesn't.
opBNB HTTP and WebSocket endpoints are on our opBNB RPC page, with the live block height. The same key covers 83 chains, and SwiftNodes has a free tier to try it.
John Sullivan covers RPC infrastructure, node operations, and multi-chain development at SwiftNodes — what it actually takes to keep endpoints fast, fresh, and reliable across EVM and non-EVM networks.
Related posts
- Ronin RPC After the Move to Ethereum L2: A Developer's Guide (Chain ID 2020)
Ronin became an OP Stack layer 2 in May 2026. Measured: 2 s blocks, a fixed 20 gwei base fee, a 200-block getLogs cap, ~23 min to finalized, and no pre-migration history.
- Katana RPC: A Developer's Guide to the DeFi-First L2
Katana (chain ID 747474) is an OP Stack L2 with ZK proofs and 1-second blocks. Measured finality, fees, getLogs limits, archive access and code.
- Arbitrum RPC: A Developer's Guide
How to connect to Arbitrum One over JSON-RPC — chain ID 42161, why the block gas limit is a sentinel value, what safe and finalized actually mean here, zero priority fees, and where archive reads stop working.