The protocol defines exactly three terminal outcomes, each with a fully specified settlement: SURVIVED/EXPIRED return funds to stakers (or, with no observed risk window, route the bonus to recoveryAddress under a stable SURVIVED), and good-faith CORRUPTED sends the whole pool — principal and bonus — to the named attacker. The moderator may re-flag the outcome pre-claim to correct a scope judgement, which is a documented, supported path.
While the outcome transiently reads SURVIVED but is not yet stable, the permissionless sweepUnclaimedBonus() irreversibly transfers the bonus to recoveryAddress. When the moderator then corrects the outcome to good-faith CORRUPTED, the funds are already gone, and the pool settles into outcome = CORRUPTED, bounty -> attacker, bonus -> recoveryAddress — a distribution no defined outcome produces. The final attacker bounty therefore depends on whether the sweep executed before the corrective re-flag, not solely on the final outcome.
Likelihood:
When the registry reaches a terminal state without the pool observing the active-risk window (riskWindowStart == 0), the full bonus is unreserved and sweepable in a single call.
When the moderator flags SURVIVED and then corrects to good-faith CORRUPTED — the out-of-scope -> in-scope judgement revision the pre-claim re-flag window exists to support — a transient SURVIVED window is opened.
When any unprivileged account calls sweepUnclaimedBonus() during that transient window (the pending correction is mempool-observable), the bonus leaves permanently before the correction lands; the moderator cannot prevent the front-run of their own correction.
Impact:
The good-faith attacker (white-hat) is permanently denied the entire bonus portion of the bounty the corrected outcome awards them (PoC: 100e18 instead of 600e18). No theft occurs — the funds go to recoveryAddress (trusted) — so this is a misallocation of a prescribed payment, which bounds the severity to Low.
The pool settles into a final distribution — CORRUPTED outcome with the bonus at recoveryAddress — that matches none of the protocol's three defined outcomes.
The final settlement depends on transaction ordering rather than solely on the final outcome. DESIGN.md §4 keys resolution finality on value movement ("once value has left the contract, a corrective re-flag cannot be honored without breaking balance accounting"); the sweep is value leaving, yet the corrective re-flag is honored and accounting breaks — though the same section also calls a claim "the distribution-locking event," so the specification is internally ambiguous on whether this is prohibited.
Paste into a new file test/OrderDependencePoC.t.sol and run forge test --match-test test_orderDependenceOfReflagAndSweep -vv. Same initial state, same final outcome — only the ordering differs; both assertions pass:
Prevent an irreversible bonus movement while the outcome remains subject to correction. The branch that moves the bonus itself (not donation excess) is the one that must not race a re-flag, so finalize when it fires:
Alternatively, on a corrective re-flag to good-faith CORRUPTED, derive bountyEntitlement from the pool's actual balance so the final distribution always matches the final outcome regardless of any prior operation. Either approach removes the ordering dependence.
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.