Skip to main content
Features

Transaction finality

A Linea transaction moves through two finality states before it is permanently settled on Ethereum.

Transaction lifecycle​

When you submit a transaction on Linea, it follows this path: the sequencer includes it in an L2 block (soft finality, ~2 seconds), then the Coordinator groups blocks into a batch and submits it to Ethereum. The prover generates a ZK proof for the batch, which is verified by the Linea rollup contract on L1. Once the L1 block containing the proof is finalized by Ethereum's consensus, the transaction reaches hard finality.

For the full protocol-level breakdown, see Architecture: Transaction lifecycle.

Soft finality​

When the sequencer orders, executes, and seals your transaction into an L2 block, it has reached soft finality. This happens within approximately 2 seconds—Linea's block time.

At soft finality:

  • The transaction is confirmed on Linea and visible in your wallet
  • Linea does not reorg—once a transaction reaches soft finality on Linea, it will not be removed or reordered by Linea
  • Ethereum (L1) may still reorg, but this does not affect Linea's confirmed state

For most application use cases—swaps, transfers, in-app interactions—soft finality is sufficient.

Hard finality​

Hard finality is reached when the ZK proof covering your transaction's batch has been verified by the Linea rollup contract on Ethereum, and the L1 block containing that verification transaction is itself finalized by Ethereum's consensus (two epochs / ~12.8 minutes).

At hard finality:

  • The transaction is cryptographically proven on Ethereum and inherits its security guarantees
  • It is anchored to Ethereum's security guarantees
  • The current median time to hard finality is under 1 hour 40 minutes, and is expected to reach approximately 30 minutes as proving performance improves
  • Hard finality should never exceed 16 hours under normal operating conditions

Hard finality is required for cross-layer withdrawals, CEX deposit confirmations, and any use case where Ethereum-level security guarantees are needed.

What you should care about​

Use caseFinality neededWhy
In-app transactions, swapsSoftOnce confirmed on Linea, the transaction is in the canonical chain and will not be reversed by the L2
Bridge withdrawals to L1HardFunds must be provably settled on Ethereum
CEX depositsHardExchanges require irreversibility
Onchain gaming, NFT mintsSoftSpeed matters, because Linea does not reorg after soft finality

How to check finality​

Use the finalized tag​

Use the finalized tag in JSON-RPC calls to target the latest hard-finalized block:

curl https://rpc.linea.build \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["finalized",false],"id":1}'

The finalized tag is supported by all applicable JSON-RPC methods on Linea Mainnet and Linea Sepolia, with the exception of eth_call. On Linea, eth_call does not currently support the finalized block tag.

Query the rollup contract​

As an alternative to the finalized tag, you can query the Linea L1 rollup contract to retrieve the value of the current finalized L2 block number stored in the currentL2BlockNumber variable.

note

Etherscan reads contract state from the latest L1 block, not the finalized one. The value shown may therefore be slightly ahead of the actual hard-finalized L2 block number. For exact finalized state, use the finalized JSON-RPC tag described above.

Prerequisites: Node.js installed.

  1. Initialize the project and install the web3 package:

    npm init -y && npm install web3
  2. Create a JavaScript file (for example index.js) and copy the following code:

    info

    Replace the Infura endpoint with your preferred Ethereum L1 RPC provider. Any L1 endpoint works—Infura, Alchemy, a self-hosted node, etc.

    index.js
    const { Web3 } = require("web3")
    const web3 = new Web3(new Web3.providers.HttpProvider(`https://mainnet.infura.io/v3/<YOUR-API-KEY>`))
    const lineaRollupAbi = [{"constant":true,"inputs":[],"name":"currentL2BlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}];
    const lineaRollupAddress = '0xd19d4b5d358258f05d7b411e21a1460d11b0876f';
    const lineaRollupContract = new web3.eth.Contract(lineaRollupAbi, lineaRollupAddress);
    async function getFinalizedL2BlockNumber() {
    try {
    const currentL2BlockNumber = await lineaRollupContract.methods.currentL2BlockNumber().call();
    const blockNumberHex = '0x' + BigInt(currentL2BlockNumber).toString(16);
    console.log('Finalized L2 Block Number:', currentL2BlockNumber.toString());
    console.log('Finalized L2 Block Number (Hex):', blockNumberHex);
    return { blockNumber: currentL2BlockNumber, blockNumberHex };
    } catch (error) {
    console.error('Error fetching L2 block number:', error);
    }
    }
    getFinalizedL2BlockNumber();
  3. Run the script:

    node index.js

Next steps​

Was this page helpful?