Normal behavior: each distinct failure condition in claimAttackerBounty() should map to a diagnostic custom error, so a caller (or off-chain tooling decoding the revert) can determine the actual root cause of the rejection. InvalidGoodFaithParams() exists specifically to signal "this pool has no bounty because it was flagged bad-faith."
Specific issue: for a bad-faith CORRUPTED pool, bountyEntitlement is 0 and bountyClaimed starts at its zero-initialized default, so the bountyClaimed == bountyEntitlement check (0 == 0) is always true and fires first — before the interpreter ever reaches the !goodFaith check. Every caller receives BountyAlreadyClaimed(), a selector whose plain meaning ("someone already claimed this bounty") is factually false, since no bounty ever existed to claim.
First-party confirmation that this branch is dead code on the honest path: the project's own regression test, testRevert_claimAttackerBounty_badFaith (test/unit/ConfidencePool.branches.t.sol), has to use the stdstore cheatcode to forcibly overwrite bountyEntitlement storage just to reach the InvalidGoodFaithParams() branch — a facility that exists only inside the Foundry test environment and has no equivalent in any real, permissionless transaction.
Likelihood:
Reason 1 // Any address calling claimAttackerBounty() against a bad-faith CORRUPTED pool receives BountyAlreadyClaimed() — this is the only reachable outcome for every such call, with no special preconditions beyond the pool having been flagged bad-faith by the moderator, which is an ordinary, expected step of the protocol's own resolution flow.
Reason 2 // The correct, semantically-accurate selector InvalidGoodFaithParams() is declared in IConfidencePool.sol and referenced by the code, yet is structurally unreachable through any real caller for this specific pool state.
Impact:
Impact 1 // Off-chain automation that decodes revert selectors to drive behavior (whitehat bounty-monitoring bots, claim-status indexers, wallet UIs surfacing human-readable failure reasons) is unable to distinguish "this pool never had a bounty" from "someone already took the bounty" using the interface's own error surface, defeating the purpose of having a dedicated selector for this case.
Impact 2 // No funds move incorrectly and no state is corrupted as a result of this issue — the function still correctly and safely rejects every bad-faith bounty claim attempt; the defect is confined to which diagnostic selector is returned, not whether the call is rejected.
Swapping the order of these two independent checks makes InvalidGoodFaithParams() fire first for bad-faith pools (correctly identifying the root cause), while preserving BountyAlreadyClaimed()'s correct, reachable behavior for good-faith pools whose bounty has genuinely been fully claimed. This is a pure reordering with no change to any fund-moving logic.
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.