When the registry enters UNDER_ATTACK, the pool opens a risk window at that moment. Every staker's entry time is floored to riskWindowStart, and the k=2 scoring formula rewards earlier entrants with a squared-time bonus: later stakers earn proportionally less because their (T − entryTime)² term is smaller.
The implementation performs the global accumulator reset before the attacker's stake is added to totalEligibleStake. The reset uses only the pre-existing honest stake, recording it as if every honest position had just entered at the attacker's arrival time. When the attacker's own stake is then credited at the same riskWindowStart, both parties end up with identical effective entry times and the k=2 formula collapses to a plain amount-weighted split.
The honest stakers' independently accrued time — potentially months — is silently erased.
Likelihood: Medium
The registry reaches UNDER_ATTACK as part of normal BattleChain operation — this is the trigger the protocol is designed around, not an edge case.
No stakeholder has called pokeRiskWindow() ahead of time (the pool explicitly supports lazy observation and does not require proactive sealing).
Impact: High
Honest stakers who bore the agreement's full risk term receive a negligible bonus share (0.099% at 1000× ratio).
The attacker captures near-total bonus with capital deployed for a single block, creating an asymmetric extraction that undercuts the pool's incentive design.
The exploitation scales linearly with the stake ratio — any attacker who can source more capital than the honest pool size will dominate the bonus distribution.
The test below uses the real ConfidencePool implementation deployed via Clones (same pattern as the factory). One honest staker deposits early in NOT_DEPLOYED state. The registry transitions to UNDER_ATTACK without any pool interaction. At expiry − 1 the attacker stakes 1000× the honest amount; their transaction becomes the first risk observation and resets all time accrual. At expiry both parties claim — the attacker receives 99.9% of the bonus.
Key observations from the trace:
_observePoolState() reads registry state 3 (UNDER_ATTACK), locks the scope, and emits RiskWindowStarted(timestamp: 7776000) — all before the attacker's token transfer completes.
The honest staker's claim emits bonusShare: 999000999000999000999 (0.099% of the pool bonus), while the attacker's claim emits bonusShare: 999000999000999000999000 (99.9%).
Both stakers recover their full principal. No fund is permanently locked.
Include the incoming stake in the accumulator reset so the true post-stake total is used:
Even stronger: compute _markRiskWindowStart after crediting the current stake, or cap individual stake relative to the pre-existing pool size during active risk so a single entrant cannot dominate the k=2 denominator.
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.