ConfidencePool assumes its token balance is fully transferable to claimants and sweep recipients. The root cause is that outbound settlement sends the nominal payout amount without accounting for sender-side transfer costs, so a fee-on-sender token can make claims or CORRUPTED sweeps revert despite apparently full reserves.
The repo does say only standard ERC20s are supported, and the factory comment acknowledges that fee-on-sender or negative-rebasing tokens can erode the pool below liabilities. But the broader protocol docs make a stronger claim: the protocol readme says the owner cannot freeze settlements because claim and sweep paths stay callable, and the design doc says a true stuck state would require a token that freezes the pool's own balance. Sender-fee semantics are materially worse than that description: the pool balance can remain healthy while outbound settlement is still impossible.
The survivor and expiry claim paths never check whether the requested payout is actually transferable under the token's sender-side semantics. claimSurvived() computes payout and immediately calls safeTransfer(msg.sender, payout), and claimExpired() does the same after auto-resolution.
For a sender-fee token, transferring payout requires the pool to fund payout + fee. A pool that nominally holds exactly payout therefore becomes unclaimable even though its accounting still says the user is fully covered. With one staker owed 100 and a 10% sender fee, the pool balance is 100, but claimExpired() still reverts because the token burns 10 from the pool before attempting the 100 transfer.
The bad-faith CORRUPTED path makes the same assumption in a stronger form. claimCorrupted() reads the pool's entire balanceOf(address(this)) and tries to sweep that exact amount to recoveryAddress.
That is impossible under sender-fee semantics whenever toSweep is the full pool balance, because paying toSweep requires more than toSweep to leave the sender. The result is a permanently frozen bad-faith CORRUPTED reserve even though the contract still holds the entire nominal balance.
The same root cause also creates claimant-ordering insolvency. If Alice and Bob each have 100 principal and the token burns 10% from the sender, Alice can claim first and receive her full 100, but the pool spends 110 to do it. Bob is still owed 100, yet only 90 remains, so his later claim reverts. This is stronger than the repo's documented "later claims may lock" warning: one rightful claimant can consume another claimant's reachable reserve simply by claiming first. The current test suite does not cover that surface. test/unit/ConfidencePool.fot.t.sol only validates inbound balance-diff accounting for stake() and contributeBonus(); it never exercises outbound claim or sweep settlement under non-standard sender-side fees.
If governance mistakenly allowlists a sender-fee token, or a previously allowed token upgrades into sender-fee behavior, the pool can become insolvent at settlement time even while totalEligibleStake, corruptedReserve, and the live token balance still look healthy. A lone claimant can be unable to withdraw principal, an early claimant can strand later claimants by burning the common reserve first, and bad-faith CORRUPTED recovery can be frozen permanently. The result is real fund loss by denial of payout, not just benign underpayment.
Reject any stake token whose outbound transfers are not exact-value sender-neutral ERC20 transfers, and never assume balanceOf(address(this)) is the maximum transferable amount for claim or sweep settlement.
I validated this locally with a minimal Foundry regression test.
Save the following as test/TempSenderFeeSemantics.t.sol:
Run:
Observed output:
This PoC shows the three concrete failure modes:
A lone staker can become completely unclaimable even though the pool still holds their full nominal balance.
A bad-faith CORRUPTED pool can become unsweepable even though corruptedReserve equals the full live balance.
With multiple stakers, the first claimant can burn sender fees out of the shared reserve and leave the next claimant permanently short.
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.