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.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.