For a good-faith CORRUPTED outcome, DESIGN §12 and the README guarantee the entire pool (snapshotTotalStaked + snapshotTotalBonus) becomes the named whitehat attacker's bounty, and the moderator may re-flag a wrong outcome any time before the first claim (claimsStarted == false), including correcting an out-of-scope SURVIVED judgement to an in-scope good-faith CORRUPTED.
sweepUnclaimedBonus is permissionless and deliberately does not latch claimsStarted, but when riskWindowStart == 0 it treats the whole bonus as unowed and sweeps it to recoveryAddress, zeroing totalBonus; a following good-faith CORRUPTED re-flag then re-snapshots snapshotTotalBonus = totalBonus = 0, so the whitehat's bounty is only the principal and the bonus is stranded at the sponsor-controlled recoveryAddress.
Likelihood:
Occurs when the registry reaches CORRUPTED with no active-risk state ever observed by the pool (riskWindowStart == 0, a state DESIGN §5/§7 treat as reachable), the moderator first flags SURVIVED (a valid judgement on a CORRUPTED registry), and any account calls the permissionless sweepUnclaimedBonus before the moderator corrects to good-faith CORRUPTED.
The front-run is directly incentivized: the recoveryAddress beneficiary (the sponsor) profits by calling sweepUnclaimedBonus themselves during the open correction window.
Impact:
The named whitehat attacker receives strictly less than the "entire pool" the spec guarantees — the full snapshotTotalBonus, which can be the majority of the pool, is routed to recoveryAddress instead of the whitehat.
Breaks the DESIGN §12 invariant that bountyEntitlement == snapshotTotalStaked + snapshotTotalBonus for good-faith CORRUPTED, and the design's own premise that "genuine reliance only comes from claim entrypoints."
Two tests, same setup, one variable changed — both pass. The focused exploit is shown first for reading; the full self-contained file follows for running.
Focused exploit (the vulnerable sequence):
Full runnable test — test/poc/SweepReflagStandalone.t.sol
Self-contained: depends only on the in-scope src/ contracts + OpenZeppelin + forge-std (both vendored in the repo). Drop into test/poc/ and run:
Result: 2 passed; 0 failed. The exploit pays the whitehat only 100e18 (principal), stranding the 100e18 bonus at recovery; the control (no sweep) pays the full 200e18, isolating sweepUnclaimedBonus as the sole cause.
Latch outcome finality when a bonus sweep removes real reliance value (the full-bonus, no-observed-risk-window branch), so a later re-flag cannot promise a good-faith CORRUPTED bounty the pool can no longer pay.
This preserves the existing intent that a dust donation must not block the moderator's re-flag window: ordinary excess/donation sweeps (observed risk window, live stakers) never enter this branch, so they still do not latch finality.
Impact – Medium Up to the entire bonus pool can be routed to the wrong party for good, with no on-chain way to unwind it, and in a stakerless pool the effect is total since bountyEntitlement computes to zero and claimAttackerBounty reverts outright. This impact is definitely not High: the pool stays solvent throughout with no principal ever at risk, and the money ends up at the sponsor's own recoveryAddress, which in most pools means a sponsor recovering a bonus they funded themselves. The whitehat's claim on that bonus also only exists because the moderator changed their mind after the fact. Likelihood – Low Four separate things have to coincide: nobody touches the pool for the whole active-risk window (or there are no stakers to begin with), the moderator flags SURVIVED and later reverses to CORRUPTED, that reversal is good-faith with a named attacker, and a sweep lands between the two flags. The first cuts against staker self-interest, since skipping the poke costs them their entire bonus share, and the second asks the moderator to overturn a scope judgement, which is a bigger deal than the typo fix DESIGN.md #4 offers the window for. The sweep itself I'd treat as near-certain once the rest holds, given it's permissionless and the sponsor has an obvious reason to make the call, but assembling the first three in one pool lifecycle is where this stays rare.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.