Skip to content

fix(exit-certificate): AET-04: fail-fast when a token balanceOf batch errors in Step B#1675

Merged
joanestebanr merged 1 commit into
feature/exit-certificate-toolfrom
fix/fetch_all_token_balance-balanceOf_error
Jun 30, 2026
Merged

fix(exit-certificate): AET-04: fail-fast when a token balanceOf batch errors in Step B#1675
joanestebanr merged 1 commit into
feature/exit-certificate-toolfrom
fix/fetch_all_token_balance-balanceOf_error

Conversation

@joanestebanr

@joanestebanr joanestebanr commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

🔄 Changes Summary

  • Fix (High): fetchAllTokenBalances (Step B1) silently discarded per-token RPC errors (log.Warnf + return), leaving the failed token absent from the balance map. Step C then treated its entire LBT supply as SC-locked and Step D routed the whole supply to exitAddress, excluding the real EOA holders from the certificate.
  • Made it fail-fast, consistent with its sibling helpers (classifyAddresses, fetchETHBalances): fetchAllTokenBalances now returns an error and uses errgroup.WithContext (limited to tokenConcurrency) to cancel the remaining scans on the first failure. RunStepB1 propagates it and aborts the pipeline.
  • Added a doc comment explaining why the error must not be dropped.

⚠️ Breaking Changes

  • None. No config, API, or CLI changes. Behaviour change only: a persistent per-token RPC failure now aborts the pipeline (after the existing RPC retries) instead of producing a misrouted certificate.

📋 Config Updates

  • None.

✅ Testing

  • 🤖 Automatic: Added TestFetchAllTokenBalancesFailFast (a failing balanceOf batch must return an error, never omit the token). Updated TestFetchAllTokenBalances to the new signature. Full package suite passes (go test ./tools/exit_certificate/) and golangci-lint reports 0 issues.

🐞 Issues

🔗 Related PRs

  • N/A

📝 Notes

  • Chose fail-fast as the default without adding an opt-out flag (e.g. ignoreTokenBalanceFetchError) to keep the config surface minimal; can add one if a knowingly-degraded test mode is desired.

🤖 Generated with Claude Code

…in Step B

fetchAllTokenBalances swallowed per-token RPC errors (log.Warnf + return),
leaving the failed token absent from the balance map. Step C then treated its
entire LBT supply as SC-locked and Step D routed the whole supply to
exitAddress, excluding the real EOA holders from the certificate.

Make it fail-fast like its sibling helpers: fetchAllTokenBalances now returns
an error and uses errgroup.WithContext to cancel remaining scans on the first
failure; RunStepB1 propagates it and aborts the pipeline. Add a regression test
(TestFetchAllTokenBalancesFailFast) and update the existing test signature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jun 29, 2026

Copy link
Copy Markdown

Claude finished @joanestebanr's task in 1m 11s —— View job


Code Review Complete ✅


🚀 What's New

This PR fixes a critical bug in Step B1 of the exit-certificate tool where token balance fetch errors were silently ignored (logged as warnings and dropped). The fix makes fetchAllTokenBalances fail-fast using errgroup.WithContext, ensuring RPC errors abort the pipeline instead of producing misrouted certificates that exclude real EOA holders.

🐛 Bug Fixes

  • Critical Fix: fetchAllTokenBalances now returns errors instead of silently dropping failed tokens with log.Warnf. Previously, a persistent RPC error for a token's balanceOf batch would leave that token absent from the balance map, causing Step C to treat the entire LBT supply as SC-locked and misroute it to exitAddress, excluding real EOA holders from the certificate.
  • Migrated from manual WaitGroup + semaphore pattern to errgroup.WithContext for proper error propagation and context cancellation.

📋 Config Updates

None.

⚠️ Breaking Changes

None. No config, API, or CLI changes. Behavior change only: A persistent per-token RPC failure now aborts the pipeline (after existing RPC retries) instead of producing an incorrect certificate.


Review Feedback

🟢 Code Quality: Excellent

Strengths:

  1. Perfect use of errgroup - The migration from manual WaitGroup + buffered channel semaphore to errgroup.WithContext is exactly the right pattern for this use case. The code is cleaner and more maintainable.

  2. Excellent documentation - The doc comment on fetchAllTokenBalances (lines 261-267) clearly explains why failing fast is critical. This is exactly the kind of comment that prevents future regressions.

  3. Proper error wrapping - Error messages include context ("fetch balances for token %s: %w") which will make debugging much easier.

  4. Comprehensive test coverage - The new TestFetchAllTokenBalancesFailFast test specifically guards against the regression, and the existing test was properly updated.

  5. Consistent with sibling functions - Making this consistent with classifyAddresses and fetchETHBalances improves code maintainability.

🟢 Correctness: Solid

Implementation details verified:

  • tok := token loop variable capture is correct (prevents closure bug)
  • g.SetLimit(tokenConcurrency) properly limits concurrency to 4
  • ✅ Context propagation: goroutines use gctx (from errgroup) instead of ctx
  • ✅ Mutex protection on tokenBalances map is preserved
  • ✅ Error unwrapping chain is correct with %w format

🟢 Security: No Issues

No security concerns identified. The change actually improves security by preventing silent failures that could lead to incorrect certificate generation.

🟢 Performance: Equivalent or Better

  • Concurrency limit remains at 4 tokens in parallel (tokenConcurrency)
  • Errgroup provides early cancellation on first error, potentially faster failure detection
  • No additional allocations or overhead introduced

🟢 Testing: Well Covered

Test coverage analysis:

  • TestFetchAllTokenBalancesFailFast - New test ensures errors propagate
  • TestFetchAllTokenBalances - Updated to check error return
  • newErrorRPCServer - Reusable test helper for simulating RPC failures
  • ✅ All existing Step B1 integration tests pass with the new signature

Summary

This is a high-quality bug fix that addresses a critical correctness issue. The implementation is clean, well-documented, and properly tested. The choice to fail-fast without adding an opt-out flag is correct for this use case—a certificate with missing token balances is worse than no certificate.

Recommendation: Approve and merge. 🚀

No changes requested.


@sonarqubecloud

Copy link
Copy Markdown

@joanestebanr joanestebanr self-assigned this Jun 29, 2026
@joanestebanr joanestebanr merged commit 5c2fde5 into feature/exit-certificate-tool Jun 30, 2026
26 of 27 checks passed
@joanestebanr joanestebanr deleted the fix/fetch_all_token_balance-balanceOf_error branch June 30, 2026 08:02
joanestebanr added a commit that referenced this pull request Jun 30, 2026
…in Step B (#1675)

## 🔄 Changes Summary
- **Fix (High):** `fetchAllTokenBalances` (Step B1) silently discarded
per-token RPC errors (`log.Warnf` + `return`), leaving the failed token
absent from the balance map. Step C then treated its entire LBT supply
as SC-locked and Step D routed the whole supply to `exitAddress`,
excluding the real EOA holders from the certificate.
- Made it **fail-fast**, consistent with its sibling helpers
(`classifyAddresses`, `fetchETHBalances`): `fetchAllTokenBalances` now
returns an `error` and uses `errgroup.WithContext` (limited to
`tokenConcurrency`) to cancel the remaining scans on the first failure.
`RunStepB1` propagates it and aborts the pipeline.
- Added a doc comment explaining why the error must not be dropped.

## ⚠️ Breaking Changes
- None. No config, API, or CLI changes. Behaviour change only: a
persistent per-token RPC failure now aborts the pipeline (after the
existing RPC retries) instead of producing a misrouted certificate.

## 📋 Config Updates
- None.

## ✅ Testing
- 🤖 **Automatic:** Added `TestFetchAllTokenBalancesFailFast` (a failing
`balanceOf` batch must return an error, never omit the token). Updated
`TestFetchAllTokenBalances` to the new signature. Full package suite
passes (`go test ./tools/exit_certificate/`) and `golangci-lint` reports
0 issues.

## 🐞 Issues
- Closes #1679

## 🔗 Related PRs
- N/A

## 📝 Notes
- Chose fail-fast as the default without adding an opt-out flag (e.g.
`ignoreTokenBalanceFetchError`) to keep the config surface minimal; can
add one if a knowingly-degraded test mode is desired.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@joanestebanr

Copy link
Copy Markdown
Collaborator Author

AET-04 Silent Token Balance Fetch Failure In fetchAllTokenBalances

@joanestebanr joanestebanr added bug Something isn't working exit_certificate_tool Tool to create a final exit certificate aet labels Jul 2, 2026
@joanestebanr joanestebanr changed the title fix(exit-certificate): fail-fast when a token balanceOf batch errors in Step B fix(exit-certificate): AET-04: fail-fast when a token balanceOf batch errors in Step B Jul 2, 2026
joanestebanr added a commit that referenced this pull request Jul 10, 2026
…in Step B (#1675)

## 🔄 Changes Summary
- **Fix (High):** `fetchAllTokenBalances` (Step B1) silently discarded
per-token RPC errors (`log.Warnf` + `return`), leaving the failed token
absent from the balance map. Step C then treated its entire LBT supply
as SC-locked and Step D routed the whole supply to `exitAddress`,
excluding the real EOA holders from the certificate.
- Made it **fail-fast**, consistent with its sibling helpers
(`classifyAddresses`, `fetchETHBalances`): `fetchAllTokenBalances` now
returns an `error` and uses `errgroup.WithContext` (limited to
`tokenConcurrency`) to cancel the remaining scans on the first failure.
`RunStepB1` propagates it and aborts the pipeline.
- Added a doc comment explaining why the error must not be dropped.

## ⚠️ Breaking Changes
- None. No config, API, or CLI changes. Behaviour change only: a
persistent per-token RPC failure now aborts the pipeline (after the
existing RPC retries) instead of producing a misrouted certificate.

## 📋 Config Updates
- None.

## ✅ Testing
- 🤖 **Automatic:** Added `TestFetchAllTokenBalancesFailFast` (a failing
`balanceOf` batch must return an error, never omit the token). Updated
`TestFetchAllTokenBalances` to the new signature. Full package suite
passes (`go test ./tools/exit_certificate/`) and `golangci-lint` reports
0 issues.

## 🐞 Issues
- Closes #1679

## 🔗 Related PRs
- N/A

## 📝 Notes
- Chose fail-fast as the default without adding an opt-out flag (e.g.
`ignoreTokenBalanceFetchError`) to keep the config surface minimal; can
add one if a knowingly-degraded test mode is desired.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aet bug Something isn't working exit_certificate_tool Tool to create a final exit certificate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant