Normal behavior: _bonusShare is intended to distribute totalBonus among stakers using a k=2 time weighted formula, so that a deposit's reward scales with amount × (T − entry)² , rewarding stakers in proportion to both how much capital they committed and how long they held it at risk. DESIGN.md and the function's own natspec state this is specifically designed so that a large, late entering depositor is "crushed to ~zero" relative to smaller, long duration stakers, since (T − entry)² shrinks sharply as entry time approaches the resolution timestamp T.
The issue: the crushing term (T − entry)² is bounded, its maximum value is fixed by the pool's total risk-window duration D² = (riskWindowEnd − riskWindowStart)², and it shrinks toward 0 as entry → T. But it is multiplied by eligibleStake[u], a value with no upper bound anywhere in the contract. Because one factor in the product is bounded and the other is unbounded, any fixed exponent on the time term can always be outweighed by scaling the stake term. A depositor who stakes late can deterministically compute, from fully public onchain state (riskWindowStart, sumStakeTime, sumStakeTimeSq, totalEligibleStake are all public), the exact stake size required to capture any target percentage of the bonus pool, and deposit that amount before resolution.
Because _assertDepositsAllowed explicitly permits stake() throughout the UNDER_ATTACK state, a deliberate design choice defended in docs/DESIGN.md on the assumption that the k=2 term alone makes such deposits "near-zero-reward", this formula gap directly falsifies the safety property that deposit window is relying on to remain open.
Likelihood:
riskWindowStart, sumStakeTime, sumStakeTimeSq, and totalEligibleStake are all public state, so the exact stake size required to hit any target bonus share is computable off-chain with no privileged information, at any point before resolution.
The attack requires only a single ordinary stake() call with sufficient capital.
Impact:
Legitimate long duration stakers, the exact cohort the bonus mechanism is designed to reward, have their bonus share diluted in direct proportion to the attacker's capture.
The contract's own stated threat model guarantee, a late insider "should get vanishingly little bonus vs. a small but long-bearing staker") is falsified.
Add this test to test/unit/ConfidencePool.k2Bonus.t.sol
Run forge test --mt testLargeLateStakeCapturesDisproportionateBonusShare -vvvv
Mitigation should cap the maximum stake amount
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.