feat(exit-certificate): state-dump Step A, zero-address & premint handling, ignoreAddresses, capMode (EAT-02)#1701
Draft
joanestebanr wants to merge 13 commits into
Conversation
Add an optional options.ignoreAddresses list of accounts whose balances must not be returned to them in the exit certificate. Step B1 still fetches their ETH and token balances (recorded in step-b-ignored-balances.json for traceability) but excludes them from both EOABalances and Accumulated via extractIgnoredBalances. Because their value no longer counts as EOA-held, it rolls into the per-token SC-locked total and Step D bridges it to exitAddress, so the certificate still balances against the LBT total (Step F stays green) and no exit is ever created back to the ignored address. LoadConfig validates each entry as a non-zero hex address. Documented in the tool CLAUDE.md and both parameters examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When Step F caps a certificate (ignoreBalanceMismatch=true), the new options.capMode selects how each token's budget is allocated to its exits: "appearance" (default, the previous behavior) serves exits in the order they appear; "amount" serves the largest-amount exits first, so big holders are kept intact and small ones are capped/dropped once the budget runs out. In both modes the surviving exits are emitted in their original order, so downstream ordering (Step G) is unaffected. Refactor capCertificateExits to compute the per-exit outcome over a priority order (capAllocationOrder) and emit in original order; extract capExitCopy. LoadConfig validates capMode. Docs and both parameters examples updated; stale "proportional scaling" wording corrected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flip the CapModeByAmount allocation order from largest-first to smallest-first, so the budget is consumed by the small exits and the largest-amount exits are the first to be capped/dropped once it runs out. Surviving exits are still emitted in their original order. Update tests and docs accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make "amount" the default cap allocation mode (largest exits capped first) instead of "appearance". Update the default, tests, docs and both parameters examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Step F now queries admin_getTokenBalance once at the very start whenever agglayerAdminURL is configured and writes the full response (the agglayer's local balance tree for l2NetworkId) to step-f-agglayer-lbt.json before any comparison runs. The dump therefore persists even when Step F later fails on a balance mismatch. In agglayer mode the same response is reused for the comparison to avoid a second RPC round-trip. Also adds scripts/get-agglayer-lbt.sh, a standalone curl helper that issues the same admin_getTokenBalance request from the command line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-agglayer-lbt filename Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r LBT dump in README Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ken is optional Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- stepAWindowSize default is 150000, not 5000 (README + stale struct comment) - document rollupManagerAddress config field (used by Step WAIT) - Step D also builds ERC-20 holder-bridge exits (3rd category) - Step C also writes step-c-holder-bridges.json - step-e-exit-certificate.json is conditional (not written on unclaimed-abort path) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…genesis-minted native in Step F Genesis pre-funded native tokens inflate the native-token LBT total without a matching agglayer deposit, forcing the Step F comparison to mismatch (and the certificate to be capped). The new options.genesisPrefundETHWei lets the operator declare that amount (in Wei); Step F subtracts it from the native LBT entry (the gas token, identified by a zero WrappedTokenAddress) before both the agglayer and offline comparisons — flooring at zero — so the check balances against the genuinely bridged amount. Affects the Step F comparison and cap budget only; the Step 0 LBT and Step C SC-locked totals are untouched. Validated by LoadConfig as a non-negative base-10 integer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…Transfer logs (from #1687) Step A now discovers value-holding addresses from the final state instead of replaying the whole chain history with debug_traceTransaction: - debug_accountRange state dump at the target block (geth/erigon dialect auto-detected) -> every ETH holder and contract in O(#accounts). - Transfer event logs per wrapped token from genesis -> every token holder, including token-only EOAs that tracing structurally misses. - options.addressDiscovery selects the strategy (auto | stateDump | logs | both); auto falls back to receipt harvesting when debug_accountRange is unavailable. The trace-based A1/A2 implementation is removed along with the a1/a2 sub-steps, ignoreOnTraceError and the step-a1/a2/failed-traces files. Step A writes the same step-a-addresses.json, so Steps B-D are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cate balances the LBT The zero address was unconditionally dropped from the collected address set (final Step A filter, Transfer-log topic extraction and receipt harvesting). But 0x000...000 can hold value like any other account: a plain transfer(0x0, amount) is not a burn (the tokens stay in totalSupply) and native ETH can be sent there too. Dropping it left that value uncovered by the certificate, so the per-token totals did not reconcile with the LBT. The zero address is now treated like any other address: collected in Step A, balance-scanned in Step B and covered by the certificate exits. Fixes #1700 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… native SC-locked from contract balances (from #1687) On chains with a native genesis premint, the Step C formula LBT − EOA_accumulated underflows for the native token (EOA balances include the premint but the LBT only measures bridge outflow), gets clamped to 0, and the ETH actually held by contracts silently disappears from the certificate. With options.nativeSCLockedFromContracts=true, Step C instead measures the native SC-locked value directly: it sums eth_getBalance of every Step B contract at the target block (excluding the L2 bridge, whose balance is the un-released native reserve) and uses that as the native token's SC-locked value. Wrapped tokens keep the LBT − EOA formula. Defaults to false. In this mode --step c needs the L2 RPC, the Step 0 target block and step-b-contract-addresses.json (run Step B first). Usually combined with options.genesisPrefundETHWei so the Step F comparison also accounts for the premint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



🔄 Changes Summary
debug_accountRangestate dump +Transferevent logs per wrapped token, instead ofdebug_traceTransactionover the whole chain history —O(#accounts)vsO(#txs), and it finds passive token-only holders that tracing structurally misses. Strategy selectable viaoptions.addressDiscovery(auto|stateDump|logs|both);autofalls back to receipt harvesting whendebug_accountRangeis unavailable. Unlike feat(exit-certificate): alternative Step A (state dump + Transfer logs) #1687 it lands as the Step A (no opt-inaaltvariant); the trace-based A1/A2 implementation is removed.0x00…00can hold value like any other account (a plaintransfer(0x0, amount)is not a burn and native ETH can be sent there); dropping it left that value uncovered and the certificate unbalanced against the LBT.options.nativeSCLockedFromContracts(imported from feat(exit-certificate): alternative Step A (state dump + Transfer logs) #1687): Step C measures the native token's SC-locked value from the actual ETH balances held by contracts (bridge reserve excluded) instead ofLBT − EOA, which underflows and clamps to 0 on chains with a native genesis premint, silently dropping contract-held ETH.options.genesisPrefundETHWei: Step F discounts genesis-minted native (no matching agglayer deposit) from the native LBT entry before comparing against the agglayer balance and certificate sum.options.ignoreAddresses: balances of the listed accounts are fetched (recorded instep-b-ignored-balances.json) but excluded from the EOA exits; their value rolls into SC-locked and is bridged toexitAddressinstead.options.capMode(amountdefault |appearance): allocation order when Step F caps a mismatched certificate (ignoreBalanceMismatch=true) —amountcaps the largest holders first.step-f-agglayer-lbt.jsonwheneveragglayerAdminURLis set, even when the check fails.options.ignoreOnTraceErrorremoved (the trace-based Step A no longer exists).--step a1/--step a2removed (ahas no sub-steps anymore);step-a1-*,step-a2-*andstep-a-failed-traces.jsonare no longer produced.📋 Config Updates
parameters.toml.example):addressDiscovery = "auto",nativeSCLockedFromContracts = false,genesisPrefundETHWei = "",ignoreAddresses = [],capMode = "amount".✅ Testing
go test ./tools/exit_certificate/...andgolangci-lintclean. New unit tests for Step A discovery (pagination, dialect detection, Transfer extraction, auto fallback, truncation/empty-dump guards), zero-address handling, native SC-locked from contracts, genesis prefund and cap allocation.🐞 Issues
🔗 Related PRs
aaltstep)🤖 Generated with Claude Code