Ronin RPC After the Move to Ethereum L2: A Developer's Guide (Chain ID 2020)
Ronin spent four years as Sky Mavis's gaming sidechain, home of Axie Infinity. On May 12, 2026 it hard-forked into an Ethereum layer 2 built on the OP Stack, with Conduit running the migration and EigenDA providing data availability. The chain ID is still 2020, the token is still RON, and existing contracts kept their addresses. Almost everything an RPC client cares about changed anyway: block cadence, fees, finality, log limits and, most surprisingly, how far back you can read.
Most Ronin RPC guides were written for the sidechain. This one was measured on October 3, 2026 against the live chain, through the same endpoint you get from SwiftNodes.
The endpoint
https://rpc.swiftnodes.io/rpc/ronin?key=YOUR_API_KEY
import { createPublicClient, http, defineChain } from "viem";
const ronin = defineChain({
id: 2020,
name: "Ronin",
nativeCurrency: { name: "RON", symbol: "RON", decimals: 18 },
rpcUrls: { default: { http: ["https://rpc.swiftnodes.io/rpc/ronin?key=YOUR_API_KEY"] } },
});
const client = createPublicClient({ chain: ronin, transport: http() });
console.log(await client.getBlockNumber());
web3_clientVersion returns conduit-op-reth, the same client family as Katana. Our Ronin route is HTTP only for now, so poll for new blocks instead of subscribing; at a 2-second cadence, polling every 2 seconds misses nothing.
What we measured
| Property | Value |
|---|---|
| Chain ID | 2020 (0x7e4) |
| Block time | 2.00 s (1,000-block average) |
| Gas limit | 60,000,000 |
| Base fee | 20 gwei, unchanged across 2,000 sampled blocks |
eth_gasPrice |
21 gwei (base fee + 1 gwei tip) |
safe lag |
~252 blocks (~8.5 min) |
finalized lag |
~701 blocks (~23 min) |
eth_getLogs max span |
200 blocks |
| Earliest block served | 55,577,500 (2026-05-12 22:09:47 UTC) |
debug_traceTransaction / trace_block |
both available |
eth_getBlockReceipts |
available |
Two of those numbers will break code written for the old Ronin, and one will surprise anyone used to Ethereum.
Gotcha 1: no history before the migration
Ask the public RPC for block 50,000,000, or for block 1, and you get null. The first block it serves is 55,577,500, timestamped May 12, 2026; reports put the fork at 55,577,490, and the ten blocks in between aren't served either. State reads follow the same line: eth_getBalance at any earlier height returns no state found for block.
The layer 2's history simply begins at the fork. For an indexer this matters a lot:
- Backfills that start at block 0 fail immediately. Start your cursor at 55,577,500 if you only need post-migration data.
- Sidechain-era events (four years of Axie, Katana DEX and bridge history) are not reachable through this endpoint at any price tier. If you need them, you need an archive that preserved the old chain, and you should confirm that with your provider before you build on it.
- Block numbers did not reset. The layer 2 continued the old numbering, so a stored block number from 2025 still means something; you just can't fetch it here.
Gotcha 2: eth_getLogs is capped at 200 blocks
The cap is exact: a 199-block span succeeds and 200 fails with
requested block range 201 exceeds the limit of 200; narrow your fromBlock/toBlock
At 2 seconds per block that's under seven minutes of chain time per call, one of the tightest limits we've measured; our getLogs range-cap survey shows most chains allow thousands of blocks. A day of Ronin is 43,200 blocks, so catching up one day takes 216 calls. Page deliberately:
const STEP = 199n; // inclusive range: from..from+199 is 200 blocks
for (let from = start; from <= end; from += STEP + 1n) {
const to = from + STEP > end ? end : from + STEP;
const logs = await client.getLogs({ address, fromBlock: from, toBlock: to });
handle(logs);
}
Filter by address (and topics) wherever you can. The block span is the limit, but narrow filters keep each response small and quick.
Gotcha 3: the base fee doesn't move
Every block we sampled had a base fee of exactly 20 gwei, including a block that used 0.7% of its 60M gas limit. On Ethereum, a near-empty block would push the base fee down; here it sits at what behaves like a configured floor. eth_feeHistory agrees, returning the same 0x4a817c800 (20 gwei) for every block.
In practice:
maxFeePerGasof 21 to 25 gwei is plenty;eth_maxPriorityFeePerGasreturns 1 gwei.- Don't build fee logic that expects the base fee to fall when the chain is quiet. It won't.
- The 20 gwei is RON, not ETH. Gas is still paid in RON, so a simple transfer costs about 21,000 × 21 gwei = 0.00044 RON plus the L1 fee below.
Gotcha 4: every receipt carries an L1 fee
As an OP Stack chain, Ronin charges each transaction for its share of the cost of posting data to Ethereum, and receipts now include the OP Stack fee fields:
"l1Fee": "0x2aa7f2b4",
"l1GasUsed": "0xa0a",
"l1GasPrice": "0x3ec4efa",
"l1BaseFeeScalar": "0x558",
"l1BlobBaseFee": "0x3954d9",
"l1BlobBaseFeeScalar": "0xc3c9d"
If you compute a transaction's cost as gasUsed × effectiveGasPrice, you're now undercounting by l1Fee. Our guide to reading transaction receipts covers the standard fields; on Ronin, add l1Fee to get the full cost.
Each block also starts with a type 0x7e deposit transaction: the system transaction OP Stack chains use to carry L1 attributes. Code that iterates block transactions and assumes every one is type 0 or 2 should skip type 0x7e, or at least not crash on it. Coinbase is now 0x4200000000000000000000000000000000000011, the sequencer fee vault, not a validator address.
Finality: latest, safe and finalized are different now
The sidechain finalized under its own validator set. As a layer 2, Ronin's three block tags mean three different things:
latestis the sequencer's head, 2 seconds old at most.safe(about 8.5 minutes back) is data that has been posted to the data-availability layer and derived by the node.finalized(about 23 minutes back) follows Ethereum's own finality.
For game state and UI, latest is fine. For deposits, withdrawals and anything you credit to a user, wait for safe at least. Our 62-chain finality measurement and the soft vs hard finality explainer explain why rollups split these tags, and what a sequencer does covers what latest actually trusts.
Migration checklist for existing Ronin code
- Start backfills at 55,577,500, or point historical queries at a sidechain archive.
- Page
eth_getLogsin 200-block windows. - Treat the base fee as a constant 20 gwei; set
maxFeePerGasa little above it. - Add
l1Feeto per-transaction cost accounting. - Skip or handle type
0x7edeposit transactions when walking blocks. - Use
safeorfinalizedfor anything that moves money.
Summary
Ronin is the same chain ID and the same token on a completely different engine: an OP Stack layer 2 with 2-second blocks, a fixed 20 gwei base fee, L1 fees on every receipt, and an 8.5-minute safe and 23-minute finalized lag. For RPC clients, the two things that bite are the 200-block eth_getLogs cap and the hard floor at block 55,577,500: nothing from the sidechain era is served.
Ronin endpoints and live chain status are on our Ronin RPC page, alongside 127 other chains on one key. SwiftNodes has a free tier if you want 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
- opBNB RPC: A Developer's Guide (Chain ID 204)
opBNB is an OP Stack L2 settling to BNB Smart Chain, with 250 ms blocks. Measured finality, fees, the 50,000-block getLogs cap and a stale-finality trap.
- 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.