`ConfidencePool` has two payout styles:
**Balance-aware:** `claimAttackerBounty`, `claimCorrupted`, `sweepUnclaimedCorrupted` cap the payout to `stakeToken.balanceOf(address(this))`. If the balance is short, they just pay less — never revert.
**Ledger-only:** `claimSurvived`, `claimExpired`, `withdraw` pay whatever the internal ledger (`eligibleStake` + bonus) says is owed, with no balance check.
If the pool's actual balance ever drops below what the ledger owes (e.g. a mistakenly-allowlisted rebasing token — already called out in `ConfidencePoolFactory.sol:26-30`), the two styles fail differently: balance-aware functions quietly pay less to everyone; ledger-only functions pay early claimants in full and then hard-revert for everyone after the balance runs out, until someone tops it up.
- Normal behavior: when a claim/withdraw entitlement is paid out, the contract should never attempt to transfer more than it actually holds. `claimAttackerBounty`, `claimCorrupted`, and `sweepUnclaimedCorrupted` all follow this. The transfer cap is capped to `stakeToken.balanceOf(address(this))`.
- The issue: `claimSurvived`, the claim tail of `claimExpired`, and `withdraw` skip this check entirely, they transfer whatever the internal ledger (`eligibleStake` + `_bonusShare`) says is owed, with no comparison against the contract's actual balance first.
Likelihood:
- Occurs whenever the pool's actual token balance falls below what the ledger tracks as owed - e.g. the factory owner allowlists a token that can lose balance independent of a transfer (already flagged in `ConfidencePoolFactory.sol:26-30` as a fee-on-sender/negative-rebase risk).
- Occurs on every claim made after that shortfall exists, for as long as the shortfall persists, not a one-time edge case but a recurring state once triggered.
Impact:
- Claimants who call after the balance is exhausted have their `safeTransfer` revert; checks-effects-interactions means the revert rolls back cleanly, so no funds are lost, but the claimant is blocked until someone restores the balance.
- Inconsistent behavior across the contract: the CORRUPTED-path functions degrade gracefully (pay less, never revert) for the identical trigger, while SURVIVED/EXPIRED/withdraw hard-fail for late claimants instead.
The poc can be run in a standalone test file against the ConfidencePool contract. Basically, what this does is to let the Factory allow a rebasing token (fee on transfer) to be on the allow list, so for every tx done, the fee is reduced and since the code does not check against the actual balance and depends solely on the internal ledger, there will be a point where the internal ledger is above the actual balance and will revert on claimSurvived, claimExpired and Withdraw. If the pool is topped, this can be avoided, but it's better to mitigate rather than allow it pass through
Adding the check to select min(freeBalance, payout) to pay out to users
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.