ZK Finality Gates, a design, not a deployment

Correction, 2026-08-01. This page used to say that all eight optimistic rollups already had a companion finality gate, and it listed sixteen contract addresses plus a shared verifier. That was not true, and the page contradicted itself: it carried a "coming soon, contracts not yet deployed" banner in the same screen. We re-read the chain instead of the page. Measured with eth_getCode at the chain head on 2026-08-01: the shared verifier address had zero bytes of code, ten of the sixteen table addresses had zero bytes of code, and the remaining six were entirely unrelated live contracts that happened to be pasted into the table, among them an NFT contract, an oracle and a compliance screening contract. No ZK finality gate is deployed on chain 2800. The addresses have been removed rather than corrected, because there is nothing correct to point them at. The design below is real and is kept; the claim that it was live is gone.

The AereL2ZKFinalityGate design gives an optimistic rollup batch a second way to become final. Instead of waiting out the fraud window, anyone holding a valid Groth16 proof of the batch's state transition calls submitProof(), the gate reads the batch's old and new state roots from the rollup's own verifier, builds the public signals from them, and asks a Groth16 verifier to check the proof. A proof for one batch cannot be replayed onto another, because the roots it is bound to are the ones the chain already recorded for that batch. The optimistic path stays in place underneath, so a rollup can adopt this one sequencer at a time.

What exists today, measured

Finality gates deployed on chain 28000
Shared Groth16 verifier deployedno, zero bytes of code at the address this page used to print
Optimistic rollups live on chain 28000, see the L2 page
Batches ever marked ZK-final0

The circuit side of this work is real: the gate is designed against a snarkjs Groth16 verifier generated from the transfer8.circom circuit, a depth-3 Poseidon Merkle tree with one transfer per batch, and all gates would share that one verifier so the trusted setup is shared rather than repeated. What has not happened is deployment. When a gate is deployed, its address will appear here with a code size and a link, and not before.

How the conversion works

Optimistic rollup (status quo)            +     ZK Finality Gate (this layer)
────────────────────────────                    ───────────────────────────────
Sequencer posts batch:                          Anyone holding a valid Groth16 proof
  - blockRange, batchHash                       of the batch's state transition calls:
  - oldStateRoot, newStateRoot                    submitProof(batchId, sIdx, rIdx, amount,
Fraud window: 7 days                              pA, pB, pC)
   (anyone can challenge with a fault proof)    The gate fetches (oldRoot, newRoot) from the
                                                rollup verifier, builds the public signals,
                                                and asks AereZKRollup8Verifier to verify.
                                                On success: zkFinalAt[batchId] = block.timestamp.

Result:                                          Result:
  Batch is "soft-final" until day 7              Batch is HARD-final immediately, 
  (some apps may delay withdrawals)              instant settlement, no fraud window,
                                                 verified entirely on-chain.
    

One gate template would serve every rollup, and a sequencer can be upgraded on its own to start producing proofs alongside its batch posts, so the conversion is incremental and the optimistic path stays as the fallback. None of that has been deployed yet, so read this box as a design and not as a description of chain 2800 today.