How Old Is 'finalized'? We Measured 62 Chains
eth_getBlockByNumber("finalized") looks like a universal answer to "is this transaction safe to act on?" Wait until the transaction's block is at or below finalized, then credit the deposit. The method name is the same on every EVM chain. What it returns is not.
We asked all 62 EVM chains we serve for their latest, safe and finalized blocks on 2026-09-30. finalized trailed the newest block by zero seconds on some chains and by over four hours on others. Four chains rejected both tags, one answered safe but not finalized, and one returned block 0, from 2020, as its finalized block.
If you write multi-chain code that waits for finality, this is the table to check your assumptions against.
How we measured
For each chain we sent one JSON-RPC batch containing eth_getBlockByNumber for latest, safe and finalized, so all three answers came from the same node. The lag is latest.timestamp − tag.timestamp, with the block count alongside. We took three samples per chain and report the median. Everything went through our endpoint, so a chain's numbers reflect the healthy upstreams we route it to; they move with network conditions, and a single day is a snapshot, not a guarantee.
The results
Final in under 10 seconds (single-slot or BFT finality)
On these chains finalized is the newest block, or within a few seconds of it. Consensus finalizes each block as it is produced, so waiting for finalized costs almost nothing.
| Lag | Chains |
|---|---|
| 0 s | Sonic, Sei, Cronos, Linea, Kava, Story, Kaia, Flare, ZetaChain, Aurora, Beam, IoTeX, WEMIX, Arc, HAQQ, Hyperliquid, Somnia, Tempo |
| 1 to 3 s | BNB Smart Chain, Monad, 0G, Telos, Berachain, Plasma, Polygon PoS |
| 6 to 7 s | Core, Avalanche C-Chain |
Under 5 minutes
| Chain | safe |
finalized |
|---|---|---|
| Astar | 36 s | 36 s |
| opBNB | 39 s | 39 s |
| peaq | 42 s | 42 s |
| Tron (EVM JSON-RPC) | not supported | 54 s |
| Gnosis | 125 s | 205 s |
| Shibarium | not answered | 211 s |
Ethereum and chains that settle to it: about 13 to 65 minutes
Ethereum finalizes every two epochs, about 13 minutes. Rollups that post to Ethereum can only call a block finalized once the L1 data it depends on is itself finalized, so they sit at or beyond Ethereum's number:
| Chain | safe |
finalized |
|---|---|---|
| Ethereum | 432 s | 816 s (13.6 min) |
| PulseChain | 520 s | 840 s |
| Optimism | 58 s | 898 s |
| Arbitrum One | 606 s | 918 s |
| Boba | 1,128 s | 1,130 s |
| Robinhood Chain | 754 s | 1,136 s |
| Unichain | 369 s | 1,180 s |
| Base | 120 s | 1,244 s |
| Mantle | 428 s | 1,328 s |
| Katana | 74 s | 1,334 s |
| Soneium | 220 s | 1,520 s |
| Ink | 731 s | 1,588 s |
| Fraxtal | 566 s | 1,646 s |
| Celo | 174 s | 1,682 s |
| Syscoin | 568 s | 1,807 s |
| Blast | 806 s | 2,292 s |
| Mode | 776 s | 2,530 s |
| Taiko | 2,562 s | 2,562 s |
| Arbitrum Nova | 3,603 s | 3,603 s |
| Zora | 274 s | 3,838 s (64 min) |
On OP Stack chains safe usually arrives within a few minutes (the batch is posted to Ethereum) while finalized waits for Ethereum to finalize that batch, which is why Base, Optimism and Katana show a small safe and a much larger finalized.
Hours
| Chain | safe |
finalized |
|---|---|---|
| Manta Pacific | 210 s | 7,240 s (2.0 h) |
| Scroll | not answered | 8,888 s (2.5 h) |
| zkSync Era | 2,158 s | 14,823 s (4.1 h) |
On zkSync Era and Scroll, a block is finalized once the validity proof covering it is verified on Ethereum, and proofs are batched. A deposit flow that waits for finalized there is waiting hours by design.
Not supported, partial, or wrong
| Chain | What happened |
|---|---|
| Ethereum Classic | safe block not found, finalized block not found |
| Immutable zkEVM | safe block not found, finalized block not found |
| Rootstock | invalid blocknumber safe, invalid blocknumber finalized |
| Fuse | Unknown block error for both |
| Metis | safe answered (6.4 h behind); finalized returned element not found |
| WEMIX, Tron, Polygon PoS, Shibarium | finalized works; safe is rejected or unanswered |
| Chiliz | safe and finalized both return block 0, the genesis block from 2020-04-20, on Chiliz's official RPC as well |
The Chiliz case is the dangerous one. Nothing errors. Code that waits for finalized.number >= txBlock never finishes, and code that reads state "at the finalized block" reads the genesis state.
Two more ways it goes wrong: stale nodes
The table shows each chain's normal behaviour. Individual nodes can also report a finalized block that is far older than it should be, while answering everything else correctly. Checking our own upstreams this week, we found:
an opBNB endpoint whose
finalizedwas about 3.8 hours old on roughly half of its answers (healthy opBNB nodes: about 40 seconds), covered in our opBNB guide;a Celo endpoint whose
finalizedandsafewere about 41 days old;a Blast endpoint whose
finalizedwas about 64 days old.
We now check every upstream's tag lag against the other upstreams for the same chain and take outliers out of rotation. But the lesson applies to any provider, including a node you run yourself: a stale finalized does not raise an error. It just makes your code wait, or act on old state. Is your RPC node synced? covers the same problem for the head block.
Seconds or blocks?
Arbitrum Nova's finalized was 3,603 seconds behind but only 3 blocks behind: Nova produces blocks when there is activity, so blocks can be minutes apart. Syscoin showed 1,807 seconds but 11 blocks. zkSync Era showed 14,823 seconds and 1,584 blocks. A threshold written in blocks means very different things on different chains; one written in seconds breaks on chains with sparse blocks. Check both.
Code that survives all of this
A finality check that works across chains has to handle four cases: the tag works, the tag is unsupported, the tag returns something absurd, and the tag is stale. Fall back to a confirmation count when the tag can't be trusted:
import { createPublicClient, http } from "viem";
// Per-chain policy: how old `finalized` may be before we distrust it, and how many
// confirmations to require when the tag is unusable. Values here are examples.
const POLICY = {
1: { maxLagSec: 3600, confirmations: 64 }, // Ethereum: finalized normally ~13 min
324: { maxLagSec: 21600, confirmations: 2000 }, // zkSync Era: finalized normally ~4 h
88888: { maxLagSec: 0, confirmations: 30 }, // Chiliz: finalized returns genesis
30: { maxLagSec: 0, confirmations: 12 }, // Rootstock: tag unsupported
};
async function finalizedBlockNumber(client, chainId) {
const { maxLagSec, confirmations } = POLICY[chainId] ?? { maxLagSec: 7200, confirmations: 64 };
const latest = await client.getBlock({ blockTag: "latest" });
const fallback = latest.number - BigInt(confirmations);
if (maxLagSec === 0) return fallback;
try {
const fin = await client.getBlock({ blockTag: "finalized" });
const lag = Number(latest.timestamp - fin.timestamp);
if (fin.number === 0n || lag < 0 || lag > maxLagSec) return fallback; // absurd or stale
return fin.number;
} catch {
return fallback; // tag not supported by this chain or node
}
}
const client = createPublicClient({ transport: http("https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY") });
const safeToCredit = (txBlock) => finalizedBlockNumber(client, 1).then((f) => txBlock <= f);
The confirmation counts are policy, not physics: pick them from the chain's documentation and your own risk tolerance. The point of the function is that a missing, zero or stale finalized never silently becomes "never" or "genesis". For what soft and hard finality mean on rollups in general, see L2 finality: soft vs hard.
The short version
finalizedranges from 0 seconds to over 4 hours across the 62 EVM chains we serve, and depends on the chain's consensus and, for rollups, on Ethereum's own finality and on proof batching.safeis less universal thanfinalized: 9 chains rejected it or didn't answer it.Five chains gave no usable
finalized, and Chiliz returns genesis. Handle both.Individual nodes can serve a stale
finalizedfor hours or weeks. Compare its timestamp with the clock.Write thresholds in seconds and blocks, per chain.
Every chain in this table is available through one key; see the chain list or get a free key to run the same check yourself.
Originally published on the SwiftNodes blog. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. Grab a free key.

