Normally, every amount accepted by stake() remains claimable after a valid
SURVIVED resolution. claimSurvived() calculates the staker's k=2 bonus
weight and transfers principal plus bonus.
_bonusShare() instead evaluates the centered second moment through expanded
uint256 terms. At ConfidencePool.sol:704-710, the first addition can
overflow before the later subtraction even when each stored moment and the
final mathematical score fit:
The final Math.mulDiv at line 719 cannot protect these earlier additions. An
ordinary staker can therefore push an allowlisted token's aggregate base-unit
stake one unit above the exact boundary. Once SURVIVED is flagged, every
claimSurvived() call panics and the pool retains all principal and bonus.
Affected code:
src/ConfidencePool.sol:382-400 (claimSurvived)
src/ConfidencePool.sol:696-719 (_bonusShare)
The 2026-07-11 public review of repository issues and pull requests,
docs/DESIGN.md, source history, and upstream tests found no issue with this
root. Existing high-magnitude tests cover the final Math.mulDiv, not the
preceding raw-moment addition.
The pool must use a factory-allowlisted standard ERC20 with enough base-unit
granularity and supply. The PoC uses 60 decimals; the contest assumptions
exclude fee-on-transfer and rebasing behavior but state no decimals or supply
cap.
No privilege is required after pool creation. The attacker is an ordinary
staker and does not control the owner, moderator, registry, or token.
At the PoC timestamps, the first failing aggregate stake is less than 0.02
whole tokens at 60 decimals.
All stakers' eligible principal and bonus become permanently unclaimable in
the affected pool.
withdraw() is disabled after the risk latch and resolution.
sweepUnclaimedBonus() reserves the outstanding principal and bonus.
Re-flagging CORRUPTED is invalid in terminal PRODUCTION; re-flagging
SURVIVED recreates the overflowing snapshot.
Pool clones are non-upgradeable, so a factory upgrade cannot repair an
affected pool.
High impact with a low-likelihood token prerequisite supports Medium severity.
Requirements: Foundry on PATH, repository dependencies installed, and commit
58e8ba4ce3f3277866e4926f3140e597f9554a1e checked out.
Create test/poc/ConfidencePoolOverflow.poc.t.sol:
Run:
Observed result:
The positive test uses the first failing aggregate amount and confirms that both
claims panic while the full balance remains in the pool. The adjacent lower
amount succeeds, pays principal plus bonus, and clears eligible stake.
Compute the centered moment with 512-bit intermediates and downcast only after
the final non-negative score is proven to fit. Alternatively, enforce a
conservative token-decimals and aggregate-stake bound during allowlisting and
pool creation.
Apply the same change to the per-user score and add regression tests at the
exact failing boundary and the adjacent successful value.
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.