ConfidencePool accepts extremely large standard ERC20 stakes without checking whether the accepted amount can later be processed by the k=2 bonus accounting formula.
The issue is in src/ConfidencePool.sol inside _bonusShare():
Because T is an absolute Unix timestamp, T * T is already a very large value. When it is multiplied by an extremely large accepted stake amount, the calculation can overflow during claimSurvived() or claimExpired().
The stake itself is accepted successfully by stake(), but later claims revert when _bonusShare() is evaluated.
This does not rely on a malicious token. The PoC uses standard ERC20 behavior only:
no fee-on-transfer
no rebasing
no hooks
no callback
no malformed return value
The attacker only needs to be a normal staker after the stake token has been allowlisted.
A normal staker can make both normal payout paths unusable:
claimSurvived()
claimExpired()
Once the overflow-triggering stake is accepted, honest stakers cannot claim their principal or bonus because the claim path reverts in _bonusShare().
This is submitted as Low severity because the required stake amount is astronomically large and the attacker also locks their own stake. However, the root cause is still valid: the contract accepts stake amounts that cannot safely be processed by its own later accounting logic.
PoC file:
Run:
Result:
The PoC proves:
A normal SURVIVED claim works with ordinary stake amounts.
An extremely large standard ERC20 stake is accepted by stake().
After that stake is accepted, claimSurvived() reverts due to _bonusShare() overflow.
The same accepted stake also makes claimExpired() revert.
Pool funds remain stuck after the failed claims.
The overflow threshold used in the PoC is calculated as:
For the PoC values:
the minimum total stake required to trigger the overflow is:
With a victim staking 1e18, the attacker stake is:
This is intentionally reported as Low because the amount is unrealistic for ordinary capped ERC20 assets.
The pool can enter a state where honest stakers cannot claim through either normal resolution path.
Locked funds include:
honest staker principal
honest staker bonus share
attacker stake
pool bonus funds
The attack is self-sacrificing and requires an extremely large token balance, so the practical likelihood is Low.
Manual review, Foundry, Slither.
Do not use absolute Unix timestamp squared values in the k=2 bonus formula.
Use bounded relative time values instead. For example, use elapsed time from riskWindowStart rather than the absolute timestamp:
Then calculate bonus weights from bounded elapsed values.
Also add a conservative total stake cap so every accepted stake is guaranteed to be claimable later:
Recommended changes:
Replace absolute timestamp squared accounting with bounded relative-time accounting.
Add a maximum total stake cap.
Add invariant tests proving that any accepted stake cannot later make claimSurvived() or claimExpired() revert.
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.