Confidence Pools allow the moderator to correct or reflag an outcome before the first claim. This is intentional: claimsStarted is the finality latch, and the pre-claim window exists so an incorrect SURVIVED/CORRUPTED decision or attacker address can be corrected before value distribution is locked.
In a no-observed-risk-window case, sweepUnclaimedBonus() treats the whole bonus as surplus when the pool is flagged SURVIVED, transfers that bonus to recoveryAddress, and decrements totalBonus. However, the function intentionally does not set claimsStarted. As a result, the moderator can still correct the outcome to good-faith CORRUPTED after the bonus has already been swept. The corrected CORRUPTED bounty then snapshots the reduced totalBonus, causing the named good-faith attacker to receive only staked principal instead of principal plus bonus.
Likelihood:
The pool observes registry state lazily. A quiet pool can first observe the agreement when the registry is already terminal CORRUPTED, leaving riskWindowStart == 0.
The moderator-supported pre-claim correction window remains open after sweepUnclaimedBonus(), and any caller can trigger the sweep before the moderator corrects an initial SURVIVED decision to good-faith CORRUPTED.
Impact:
A named good-faith attacker can be underpaid by up to the entire bonus pool.
The swept bonus is diverted to recoveryAddress, even though the later corrected good-faith CORRUPTED outcome should have included that bonus in the attacker bounty.
The PoC demonstrates a pool with 300 stake and 1000 bonus. The registry is already CORRUPTED, but the pool never observed an active risk state, so riskWindowStart == 0.
The moderator first flags SURVIVED, leaving the pre-claim correction window open because claimsStarted == false. A permissionless caller then calls sweepUnclaimedBonus(). Since riskWindowStart == 0, the function treats the whole 1000 bonus as surplus, transfers it to recoveryAddress, and reduces totalBonus to zero.
The moderator then validly corrects the outcome to good-faith CORRUPTED. Because the bonus was already swept, the corrected bounty snapshots snapshotTotalBonus == 0, so the attacker receives only 300 stake instead of the expected 1300 stake + bonus. The 1000 bonus remains with recovery.
Ensure that value needed by any still-possible corrected outcome cannot be moved before the outcome becomes final.
In particular, sweepUnclaimedBonus() should not be able to remove accounted bonus while the pre-claim reflag window remains open and a later corrected CORRUPTED outcome can still depend on that bonus.
Possible approaches include:
Make sweepUnclaimedBonus() close the reflag window when it removes accounted bonus, for example by setting claimsStarted = true.
Or reserve accounted bonus while claimsStarted == false, so only true raw excess/donations can be swept before finality.
Or otherwise prevent reflagging after any sweep that changes accounting used by a later outcome snapshot.
The key invariant is that a corrected outcome must not be computed from accounting that was already depleted by a pre-finality sweep.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.