π What's New
-
Auto Claim: automatic L1 β L2 bridge claiming (#1662): New opt-in
autoclaimcomponent. It discovers eligible exits froml1bridgesync, persists request/cursor/attempt state, prepares claim proofs withl1infotreesyncplus a per-claimerl2gersync, submitsclaimAsset/claimMessagethroughEthTxManager, and tracks each request to confirmation or failure.- Per-destination claimers with named policies:
allow-all,api-approve,no-message,basic-filter. - API split: public read endpoints (
GET /autoclaim/v1/bridges,GET /autoclaim/v1/bridges/:id) are served on the same public REST server as the bridge service (now[PublicREST]) under their own/autoclaim/v1prefix; admin write endpoints (POST /autoclaim/v1/bridges/:id/approve|reject) live on a separate[AdminREST]server, gated by[AutoClaim.API].Enabled. DryRunflag runs the full pipeline (discovery β policy β proof preparation) without submitting the transaction; requests end in the terminaldry-runstatus.- Per-claimer
BlockFinality/InitialBlockNumoverrides for the destination-L2 GER syncer (empty/zero falls back to the shared[L2GERSync]values). - Admin API hardening: max-length validation on
decider,decider_id,reasonand a 1 MiB body cap viaio.LimitReader. - β οΈ Ships a breaking config rename β see Breaking Changes.
- Per-destination claimers with named policies:
-
New tool:
exit_certificate(+exit_certificate_claimer) (#1582): CLI that generates, signs and submits an agglayerCertificatefor an aggchain exiting the Agglayer ecosystem. It scans L2 state from genesis to a target block, discovers every value-holding address (ETH + wrapped tokens), computes smart-contract-locked balances, detects unclaimed L1βL2 deposits, verifies balances against the agglayer, computes theNewLocalExitRoot(optionally cross-verified against an Anvil shadow-fork of the real bridge contract) and produces a signed certificate whoseBridgeExitentries transfer every balance to the destination network.- Runs as a resumable step pipeline (
CHECK β 0 β A β B β C β D β E β F β G β H β I β SIGN); every step persists its output so single steps, comma lists and ranges (a-c,g-,0-wait) can be re-run.SUBMIT/WAITare excluded from the default pipeline so the operator reviews the signed certificate first. exit_certificate_claimeris a companion HTTP service used after the certificate settles on L1: given a destination address it returns the available bridge exits plus every parameter needed to callAgglayerBridge.claimAsseton L1 (merkle proofs, global index, exit roots, leaf fields), with helper scripts and a docker-compose setup.- Scope is contained to
./tools/β the only changes outside are newMakefiletargets,Dockerfilecopies, and the new Kurtosis e2e workflow.
- Runs as a resumable step pipeline (
-
New tool:
force_ger_update(#1730): Standalone long-running CLI that guarantees the L1 Global Exit Root is updated at least every configurableX. It watches L1 forUpdateL1InfoTreeand, when none happens organically within the window, sends abridgeMessagewithforceUpdateGlobalExitRoot = true.- Motivation (OP-FEP / DA provability): every GER update appends an L1 info tree leaf embedding an L1 block hash, and the aggchain proof uses that block hash to attest to what happened on L1 β including data availability. Anything posted on L1 after the last L1 info root update is covered by no block hash in any L1 info root and therefore cannot be proven; certificate progress stalls, unbounded, until an organic GER update lands. This tool bounds that wait to
X. - Deliberately lightweight: no syncer, reorg detector or event DB β boot scan via chunked
FilterLogs, then WSWatchUpdateL1InfoTree(ifL1WSURLis set) orFilterLogspolling; sends via the standardzkevm-ethtx-manager; keys viago_signer(local keystore or GCP/AWS KMS). - Shipped in the Docker image at
/usr/local/bin/force_ger_update.
- Motivation (OP-FEP / DA provability): every GER update appends an L1 info tree leaf embedding an L1 block hash, and the aggchain proof uses that block hash to attest to what happened on L1 β including data availability. Anything posted on L1 after the last L1 info root update is covered by no block hash in any L1 info root and therefore cannot be proven; certificate progress stalls, unbounded, until an organic GER update lands. This tool bounds that wait to
-
Improved Backward/Forward LET tooling (#1616): Staging-only helper commands for controlled LET validation (
craft-cert,send-cert --no-db,cert-status), a newexport-cert-exitsthat generates override files from authoritative cert-ID maps via the AggLayer adminadmin_getCertificate, better diagnosis/recovery output and safe-stops for missing cert exits and bridge-service readiness. The in-repo runbook is replaced by a pointer to the public recovery runbook. -
New
bridgeservicefinderpackage (#1692): Resolves and serves the bridge service URL for every network attached to a rollup manager (New,Start,GetURL(networkID)). Resolution follows a strict priority β (1) static config map, (2) on-chainaggchainMetadata["BRIDGE_SERVICE_URL"], (3)trustedSequencerURL()with the port replaced by the bridge REST port5577β and a polling loop watchesSetTrustedSequencerURL/AggchainMetadataSetto refresh the cache live, with health gating. Not yet wired intocmd/run.goor the app config; it is intended for consumption by other components. -
Per-certificate L2 block range cap (#1673): New optional
MaxL2BlockRangecaps each certificate's span across PP, FEP, optimistic, builder and validator flows (0= unlimited, the default). -
Configurable batch block header retrieval (#1606):
RetrieveBlockHeadersmoves onto theCustomEthereumClienterinterface and a newBatchBlockHeaderRetrieval(defaulttrue) lets operators disable batch RPC requests for nodes that do not support them.BlockHeadersResult,ErrNotFoundandIsErrNotFoundmove fromethermantotypes(aliases kept for backward compatibility). -
Docs: log availability from genesis (#1610, #1664):
docs/usage_ethereum_full_node.mdnow documents that aggkit syncs contract state witheth_getLogsand therefore needs logs (receipts) available from genesis, not just a recent window.rethprunes receipts by default in full-node mode, so run it with--full --prune.receipts.before=0(or the equivalent[prune.segments]snippet). Historical state pruning remains acceptable.
β οΈ Breaking Changes
- π οΈ Config β
[REST]is split into[PublicREST]and[AdminREST](#1662): The single top-level[REST]section (the bridge service HTTP server) is replaced by[PublicREST](bridge service + Auto Claim public read routes) and[AdminREST](Auto Claim admin routes only). Existing configs must rename[REST]to[PublicREST]; there is no automatic migration. A leftover[REST]section is currently ignored and logs a deprecation warning at startup (warn-only for now because the e2e infra still generates configs with[REST]). - π οΈ Config: The new
[AutoClaim]section is opt-in β Auto Claim only runs ifautoclaimis in the component list. No migration needed. - π API/CLI: Additive only. New public routes
GET /autoclaim/v1/bridges,GET /autoclaim/v1/bridges/:idon[PublicREST]; new admin routesPOST /autoclaim/v1/bridges/:id/approve|rejecton the new[AdminREST]server. Existing/bridge/v1paths are unchanged. New tool binaries:exit_certificate,exit_certificate_claimer,force_ger_update. - ποΈ Deprecated Features:
[REST](see above). No features removed.
π Configuration Updates
- π§Ύ Config Diff:
v0.10.0...v0.11.0-rc1β seeconfig/config.goandconfig/default.go.
Required rename:
# Before # After
# [REST] [PublicREST]
[PublicREST]
Host = "0.0.0.0"
Port = 5577
ReadTimeout = "5m"
WriteTimeout = "5m"
MaxRequestsPerIPAndSecond = 10
# New β only used by Auto Claim's admin write routes
[AdminREST]
Host = "0.0.0.0"
Port = 5579
ReadTimeout = "5m"
WriteTimeout = "5m"
MaxRequestsPerIPAndSecond = 10New optional fields:
[AggSender]
MaxL2BlockRange = 0 # 0 = unlimited (default)
[Validator]
MaxL2BlockRange = "{{AggSender.MaxL2BlockRange}}"
# In [L1NetworkConfig.RPC] and [Common.L2RPC]
BatchBlockHeaderRetrieval = true # default; disable for nodes without batch RPC supportNew opt-in [AutoClaim] section (add "autoclaim" to the component list to enable):
[AutoClaim]
DryRun = false
StoragePath = "{{PathRWData}}/autoclaim.sqlite"
[AutoClaim.API]
Enabled = false
[AutoClaim.L1ToL2BridgeDetector]
Enabled = true
StartBlock = 0
PollInterval = "3s"
RetryAfterErrorPeriod = "1s"
MaxRetryAttemptsAfterError = -1
EtrogL1UpgradeBlock = 0
[AutoClaim.L2ToLxBridgeDetector]
Enabled = false
# One entry per destination network. Add as many [[AutoClaim.Claimers]] blocks as needed.
[[AutoClaim.Claimers]]
Enabled = true
ID = "my-l2"
NetworkType = "evm"
NetworkID = 1
URLRPC = "http://l2-rpc:8545"
BridgeAddr = "0x0000000000000000000000000000000000000000"
PolicyName = "allow-all" # allow-all | api-approve | no-message | basic-filter
GasOffset = 0
WaitPeriod = "5s"
RetryAfter = "30s"
MaxRetries = 3
# Optional: override the shared [L2GERSync] values for this claimer's destination-L2 GER syncer.
# BlockFinality = "LatestBlock"
# InitialBlockNum = 0
[AutoClaim.Claimers.Policy]
AllowMessageClaims = false # only relevant for "basic-filter"
AllowedOrigins = [] # empty = all origins allowed
AllowedTokens = [] # empty = all tokens allowed
ManualFallback = false # fall back to "api-approve" when the filter rejects
MaxGas = 0 # 0 = unlimited
[AutoClaim.Claimers.EthTxManager]
StoragePath = "{{PathRWData}}/autoclaim-claimer-my-l2-ethtx.sqlite"
# PrivateKeys, ForcedGas, MaxGasPrice, RetryInterval, WaitTxToBeMined, etc.
# See [EthTxManager] in the main config for the full list of fields.Standalone tool configs (separate files β they do not touch the aggkit config):
force_ger_update:[ForceGERUpdate]root section (+ nested[β¦EthTxManager]/[β¦EthTxManager.Etherman]). Mandatory and validated:L1URL,GlobalExitRootManagerAddr,BridgeAddr,DestinationNetwork(β 0),MaxTimeWithoutGERUpdate(> 0),CheckInterval(> 0),FilterLogsChunkSize(> 0),EventPollInterval(> 0, poll mode only). Seetools/force_ger_update/example-config.toml.exit_certificate: JSON or TOML (format selected by file extension). See the tool README.
π Bug Fixes
- #1709 β FEP certificates were retried forever after an L1 info root reorg. On an
InErrorcertificate the aggchain prover flow resent the exact same certificate, reusing the finalized L1 info tree root/leaf count and the cached aggchain proof; if the root had been reorged, that certificate (and the imported bridge exit proofs derived from it) was permanently invalid. TheInErrorretry path now validates the certificate's L1 info tree data against the current canonical tree first: still valid β resend unchanged; root mismatch or leaf reorged out β drop the stale proof and rebuild from scratch with fresh data; transient lookup errors β propagated so the retry happens later instead of needlessly discarding the cached proof. - #1673 β
AggSender.MaxL2BlockNumberwas not honored in the aggchain flow: the prover flow config was dropped when constructingAggchainProverBuilderFlow, so the limit never reached block-range adjustment for FEP and optimistic certificates. - #1645 (cherry-pick of #1639) β
l1infotreesyncstuck in a sync failure loop after upgrading from the legacy syncer (e.g. 0.8.x) to the multidownloader. The processor reported a last-processed checkpoint block the legacy syncer had persisted but the multidownloader's fresh storage never contained, soCheckValidBlockfailed withnot found in storage or blocks_reorgedwith no self-recovery (manual DB deletion was the only workaround). For blocks at or below the finalized block,CheckValidBlocknow validates against L1 by comparing the canonical header hash: match β valid, resume fromlastBlock+1; differ β error (severe inconsistency). Blocks above finalized keep the previous behaviour. - #1729 β
exit_certificatestate dumps were silently truncated. The cdk-erigon RPC behind a load balancer intermittently returned an HTTP 200debug_accountRangepage with an emptynextcursor before the trie ended (observed on zkEVM mainnet: 735,744 of 899,166 accounts), under-collecting native-ETH holders and under-reporting balances by ~152.7 ETH. The erigon dialect now re-seeks strictly past the highest key seen and only stops when a fresh re-seek genuinely returns no new account. Verified against mainnet: the full 899,166 accounts, matching an independent full-trie walk summing to exactly the 200,000,000 ETH genesis reserve. - #1605 β Debug-trace bridge call matching now only considers real
CALLframes inbridgesyncandclaimsync, so calldata is never extracted fromDELEGATECALL,STATICCALL,CALLCODEor type-less frames. Claim calldata extraction also rejects ambiguous distinct matching bridge calls while still accepting duplicate identical matches. - #1600 (cherry-pick of #1589) β
ParseMaxRangeFromErrordid not recognise thequery exceeds max block range <N>format emitted by some RPC providers, so the ClaimSyncer's RPC fallback returned immediately instead of retrying with chunked requests. - #1623 (cherry-pick of #1621) β
.git/was excluded from the Docker build context, sogit describe/git rev-parseproduced emptyVersion,GitRevandGitBranchin the built binary. - #1674 β Release candidates did not publish a
-dev(shell-enabled) image. The dev variant embedded-devinside the semver pattern, anddocker/metadata-actiondiscards a custom semver pattern for pre-releases; moving-devto thesuffix=attribute makes rc tags yieldX.Y.Z-rcN-dev. The same PR publishes a-no_shellalias for the current production image ahead of the shell-by-default migration (#1671), so consumers needing the hardened image can switch to*-no_shelltoday.
π·οΈ Image tags
| Variant | Shell | Final release (v0.11.0)
| Release candidate (v0.11.0-rc1)
|
|---|---|---|---|
| production | β | 0.11.0, 0.11, 0.11.0-no_shell, 0.11-no_shell
| 0.11.0-rc1, 0.11.0-rc1-no_shell
|
| development | β | 0.11.0-dev, 0.11-dev
| 0.11.0-rc1-dev
|
For pre-releases
docker/metadata-actiononly extends{{version}}, so{{major}}.{{minor}}tags are intentionally not produced.
β Testing & Validation
- Auto Claim (#1662): wired into the OP e2e environment alongside unit coverage for the component, policies and admin API.
exit_certificate(#1582): new Kurtosis-based e2e suite undertest/e2e-exit_certificate/with its own CI workflow.force_ger_update(#1730): Tier-1 unit + simulated-backend integration tests (go test -race ./tools/force_ger_update/..., both watch and poll modes) and a Tier-2 real-network e2e,TestForceGERUpdateE2E, now running in CI on its own isolated job (test-go-e2e-force-ger-update) instead of being permanently skipped. It proves causality β the new GER update's txFromequals the tool sender and the input starts with the0x240ff378selector.bridgeservicefinder(#1692): extensive simulated-backend unit tests covering each URL source in isolation, all priority combinations, config immunity to on-chain updates, live updates via both event types and health-gating edge cases, plus a smoke test asserting the new mock contracts are selector/event-compatible with the real bindings.- FEP reorg fix (#1709): unit tests for reorged L1 info root, leaf reorged out, and validation-error propagation.
- Legacy checkpoint fix (#1645):
TestEVMMultidownloader_CheckValidBlock_LegacyCheckpointAfterUpgradeagainst real empty storage, covering all three paths. - Backward/Forward LET (#1616): end-to-end staging validation completed; final no-override diagnosis returned
NoDivergence. - Local e2e ergonomics (#1591):
run-local-e2e.shnow uses${TMPDIR:-/tmp}and deterministic temp-dir names derived from the commit hash / repo URL, reusing an already-cloned repo instead of re-cloning on every run.
π§ Maintenance & Dependencies
- Grouped Dependabot updates (#1733, #1734): all Dependabot bumps now land as a single grouped PR. Notable in this release:
go-ethereum1.17.2 β 1.17.4,grpc1.80.0 β 1.81.0,go-sqlite31.14.42 β 1.14.48,zap1.27.1 β 1.28.0,go-toml/v22.3.0 β 2.4.3,prometheus/client_golang1.23.2 β 1.24.0,invopop/jsonschema0.13.0 β 0.14.0, plus theagglayerprotobuf/grpc modules.- β οΈ Note for maintainers:
buf.build/gen/go/agglayer/*modules are now ignored by Dependabot. Their pseudo-versions encode the protobuf-go generator version in the semver prefix while the actual schema is identified by the embedded commit timestamp, so av1.36.11module can carry an older schema than av1.36.10one. MVS picking the highest semver rolledinteropback to a 2025-05-19 snapshot predating theMultisig/ECDSAMultisig/AggchainProofWithMultisigtypes and broke the build; these first-party modules are now bumped deliberately, together.
- β οΈ Note for maintainers:
- SonarQube/SonarCloud removed (#1719): part of the org-wide deprecation, consolidating on CodeQL + GitHub Advanced Security. CI-only.
- Claude PR review is mention-only (#1695): the automatic review trigger was removed.
π¦ Full Changelog
- feat: improve local e2e script to reuse existing temp dirs by @joanestebanr in #1591
- fix: cherry-pick #1589 - add query exceeds max block range to ParseMaxRangeFromError (develop) by @joanestebanr in #1600
- fix: debug trace bridge call matching by @arnaubennassar in #1605
- feat: cherry-pick: configurable batch block header retrieval via RPCClientConfig by @joanestebanr in #1606
- chore(deps): bump github.com/ethereum/go-ethereum from 1.17.2 to 1.17.3 by @dependabot[bot] in #1617
- chore(deps): bump github.com/mattn/go-sqlite3 from 1.14.42 to 1.14.44 by @dependabot[bot] in #1615
- chore(deps): bump go.uber.org/zap from 1.27.1 to 1.28.0 by @dependabot[bot] in #1614
- chore(deps): bump github.com/pelletier/go-toml/v2 from 2.3.0 to 2.3.1 by @dependabot[bot] in #1613
- chore(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1 by @dependabot[bot] in #1612
- chore(deps): bump github.com/invopop/jsonschema from 0.13.0 to 0.14.0 by @dependabot[bot] in #1603
- docs: document log availability requirement and reth receipt pruning config by @joanestebanr in #1610
- fix: cherry-pick: include .git in Docker build context for version embedding by @joanestebanr in #1623
- feat: Improve Backward/Forward LET tooling by @arnaubennassar in #1616
- fix: cherry-pick: validate legacy checkpoint block against L1 in CheckValidBlock (#1638) (#1639) by @joanestebanr in #1645
- docs: document log availability requirement and reth receipt pruning by @joanestebanr in #1664
- fix(ci): fix -dev images for release candidates and add -no_shell aliases by @joanestebanr in #1674
- fix: honor MaxL2BlockNumber in aggchain flow by @arnaubennassar in #1673
- chore: remove automatic Claude PR review, keep mention-only trigger by @arnaubennassar in #1695
- feat: autoclaim plan by @arnaubennassar in #1662
- fix(aggsender): rebuild FEP cert from scratch on reorged l1inforoot by @arnaubennassar in #1709
- chore: remove SonarQube/SonarCloud integration by @joanestebanr in #1719
- feat: new tool - exit_certificate by @krlosMata in #1582
- feat: add bridgeservicefinder package by @arnaubennassar in #1692
- fix(exit-certificate): re-seek debug_accountRange to avoid truncated state dump by @krlosMata in #1729
- feat(tools): force_ger_update β force periodic L1 GER updates by @arnaubennassar in #1730
- group dependabot updates by @arnaubennassar in #1733
- chore(deps): bump the all-dependencies group with 16 updates by @dependabot[bot] in #1734
New Contributors
- @krlosMata made their first contribution in #1582
Full Changelog: v0.10.0-rc1...v0.11.0-rc1