Root cause: sweepUnclaimedBonus sends the whole bonus to the sponsor's recoveryAddress and zeroes totalBonus without setting claimsStarted — so the moderator's re-flag window stays open — and flagOutcome then re-reads the now-zero totalBonus into snapshotTotalBonus on the CORRUPTED re-flag.
Impact: On a good-faith CORRUPTED outcome the whitehat is entitled to the entire pool (snapshotTotalStaked + snapshotTotalBonus, §12) but gets principal only; the bonus is permanently redirected to the sponsor. PoC: 40 of 140 tokens (28.6% of the pool) to the wrong party.
The moderator may re-flag an outcome until the first claim (flagOutcome gates on claimsStarted) — a deliberate pre-claim correction window (§4). The bug is that a sweepUnclaimedBonus in that window moves real value out but leaves the window open. When riskWindowStart == 0, sweep reserves only principal, sends the full bonus to recoveryAddress, zeroes totalBonus, and does not set claimsStarted:
flagOutcome then re-snapshots the drained bonus on the CORRUPTED re-flag:
This is not documented behavior: §5 says a CORRUPTED outcome sweeps "the pool whole, bonus included" (this does the opposite), and §4's no-claimsStarted rule is documented only for the dust-donation case, not a material bonus sweep.
Likelihood: Low — needs the moderator to flag SURVIVED then correct to good-faith CORRUPTED, with riskWindowStart == 0 and a sweep in between. The sponsor controls recoveryAddress, so has a direct incentive to be the sweeper.
Impact: Medium — a supported correction produces a wrong, permanent payout: the identical terminal state pays 140 via a direct CORRUPTED flag but only 100 here. Not High because no principal is destroyed and the bonus lands at the sponsor-controlled recoveryAddress (not stolen by a third party) — hence an honest Low floor, Medium arguable.
Executed against the contest suite with forge test. Both a control (the correct good-faith CORRUPTED payout) and the bug pass, so the two payouts are directly comparable on the same terminal state.
Where to place the file: create test/PoC_BountyUnderstatement.t.sol (any path under test/ works; it extends the contest's own test/helpers/BaseConfidencePoolTest.sol, so no extra setup is needed).
Run command:
Full PoC (complete file):
Executed result:
Option A (preferred, minimal) — lock the re-flag window once a material bonus has been swept, so a CORRUPTED correction after a real bonus sweep reverts instead of understating the bounty. The §4 dust-donation exemption is preserved (dust sweeps still don't lock the window):
Option B — don't release the bonus while the outcome is still re-flaggable. Reserve the bonus even when riskWindowStart == 0, and only pay recoveryAddress once claimsStarted == true, so a later CORRUPTED correction can still pay the whitehat the whole pool. (Larger change; Option A is the smaller, doc-consistent fix.)
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.