# 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](https://swiftnodes.io/blog/json-rpc-batching) 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](https://swiftnodes.io/blog/zksync-era-rpc-differences) and [Scroll](https://swiftnodes.io/blog/scroll-rpc-bytecode-equivalent-zkevm), 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 `finalized` was about **3.8 hours** old on roughly half of its answers (healthy opBNB nodes: about 40 seconds), covered in our [opBNB guide](https://swiftnodes.io/blog/opbnb-rpc-developer-guide);
    
*   a Celo endpoint whose `finalized` and `safe` were about **41 days** old;
    
*   a Blast endpoint whose `finalized` was 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?](https://swiftnodes.io/blog/is-your-rpc-node-synced-stale-endpoint) 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:

```js
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](https://swiftnodes.io/blog/l2-finality-soft-vs-hard).

## The short version

*   `finalized` **ranges 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.
    
*   `safe` **is less universal than** `finalized`**:** 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** `finalized` for 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](https://swiftnodes.io/docs/supported-chains) or [get a free key](https://swiftnodes.io/) to run the same check yourself.

* * *

*Originally published on the* [*SwiftNodes blog*](https://swiftnodes.io/blog/finalized-block-tag-62-chains)*. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering.* [*Grab a free key*](https://swiftnodes.io/)*.*
