Skip to content

feat(watch): finalize at the chain's finalized block, not a configured depth - #121

Merged
kyscott18 merged 5 commits into
mainfrom
feat/finalized-tag
Nov 2, 2026
Merged

kyscott18 merged 5 commits into
mainfrom
feat/finalized-tag

Conversation

@kyscott18

Copy link
Copy Markdown
Collaborator

Summary

Finalizes at the chain's "finalized" block instead of a configured confirmations.finalizedBlockDepth.

  • Watch (watch.ts): each poll requests "finalized" before "latest", so once the chain has caught up to latest it holds the finalized block. After reconciling (and after each catch-up batch) it drops the blocks up to that block and emits a new Finalized message. A finalized block not in the chain (a provider ahead of the one that served latest, or a fork not yet reorged out) is skipped until a later poll. maxChainDepth is removed.
  • Runtime (typewriter.ts): on Finalized, every settlement block at or below it is finalized, after the existing canonical-hash check. nextBlockStatus is removed.
  • Startup: next to the chainId check, every provider must serve the "finalized" block.
  • Config: confirmations.finalizedBlockDepth is removed (breaking).
  • RPC: a null "finalized" block now fails with BlockNotFoundError instead of throwing on BigInt("finalized").
  • Dev Anvil: --slots-in-an-epoch 1, so it finalizes 2 blocks behind latest instead of 64.
  • Sim: the depth is now a chain property, SIM.chain.finalizedBlockDepth. The sim chain answers "finalized" as the highest head at each node's view time minus that depth, which matches its reorg bound. (Anvil's own "finalized" doesn't bound reorgs.)

Testing

  • Typecheck, biome, unit tests (81 pass).
  • Simulation, reorgs off (reorgRate: [0], local edit only): 30/30 seeds (0x10–0x2d) pass. An earlier version that requested "finalized" after catching up stalled 6 of those seeds: a watch behind the chain by more than the finality distance always saw the finalized block ahead of its tip. The same 6 seeds pass on main.
  • Simulation with reorgs: 0x03, 0x05, 0x06, 0x07 pass. 0x01, 0x02, 0x04 fail with reorged/reverted settlements. Main also fails these error classes; they're handled on feat/reorg-handling.

Notes

  • One extra eth_getBlockByNumber per poll, including idle polls.
  • The watch still starts from latest, so a reorg of the start block is still unrecoverable, as before.
  • Conflicts expected with feat/reorg-handling in the runtime's watch consumer.

🤖 Generated with Claude Code

kyscott18 and others added 5 commits November 2, 2026 20:13
…d depth

The watch requests the "finalized" block after each new block (and each
catch-up batch), drops its local chain up to it, and emits a Finalized
message. The runtime finalizes settlement blocks at or below it.
Removes confirmations.finalizedBlockDepth. Dev Anvil uses
--slots-in-an-epoch 1 so it finalizes two blocks behind; the sim chain
answers "finalized" from its own reorg bound per node view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…startup

Also reports a missing "finalized" block as BlockNotFoundError instead of
throwing on BigInt("finalized"), and splits finalized settlement blocks
by number up front.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A watch behind the chain by more than the finality distance always saw a
finalized block ahead of its tip, so it never finalized. Seeds 0x10,
0x13, 0x20, 0x22, 0x23 and 0x2c stalled without reorgs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Requesting it before latest means the chain caught up to latest holds it,
so the watch needs no finalized target to wait on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyscott18
kyscott18 marked this pull request as ready for review November 2, 2026 20:50
@kyscott18
kyscott18 merged commit 1ca1fe8 into main Nov 2, 2026
7 checks passed
@kyscott18
kyscott18 deleted the feat/finalized-tag branch November 2, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant