DESIGN.md section 3 says deposits during UNDER_ATTACK are acceptable because their bonus share is crushed to near-zero by the k=2 formula: their entry time is floored at riskWindowStart ≈ now, so (T - entry)^2 is tiny. The protocol also tests this in testQuadraticCrushesInsiderLastMinuteStaker, where a 50,000-token late insider should receive less than aliceBonus / 1000.
riskWindowStart is not set when the registry actually enters an active-risk state. It is set lazily when the pool first observes that state. When the pool is quiet while the registry is already UNDER_ATTACK, a late staker can be the first observer through their own stake() call. That call seals riskWindowStart at the late staker's entry time, and existing stakers are clamped to the same timestamp. The k=2 time weighting no longer differentiates long-term risk from last-minute risk; the bonus split degenerates to amount-weighting.
This contradicts the stated safety property for UNDER_ATTACK deposits. DESIGN.md section 7 documents the first-observation mechanic and frames the timing as a public race that only redistributes bonus among stakers. That explains the mechanism, but it does not reconcile the result with section 3 and testQuadraticCrushesInsiderLastMinuteStaker, which promise that a late depositor is crushed. A documented mechanism does not make the broken late-entry penalty the intended outcome.
Likelihood:
This occurs when the registry enters UNDER_ATTACK and no one calls pokeRiskWindow() before a later staker calls stake().
pokeRiskWindow() is permissionless but unrewarded, so the pool can remain unsealed until the next user interaction. Every pool has a fresh first-observer window.
Impact:
The bonus pot is redistributed away from long-term stakers and toward late capital. With the protocol's own test parameters, Alice stakes 100 tokens for 21 days of risk, while a 50,000-token insider stakes 10 minutes before resolution. Instead of being crushed, the insider receives about 99.8% of the bonus pot.
With equal stake sizes, a 1-day and 30-day commitment split the bonus 50/50. Principal is not lost, but the pool's intended reward mechanism is defeated.
Attack path:
Alice stakes 100 tokens.
The registry enters UNDER_ATTACK.
No one calls pokeRiskWindow(), so riskWindowStart remains 0.
21 days later, Eve stakes 50,000 tokens and becomes the first observer.
Eve's stake call seals riskWindowStart at her own late entry time.
The pool later resolves SURVIVED.
Alice and Eve claim; Eve receives almost the entire bonus pot.
Add this test to a fresh PoC file and run:
The full PoC suite also includes a control test showing that an early pokeRiskWindow() restores the intended behavior and crushes the late entrant:
The best fix is to anchor riskWindowStart to the registry's actual active-risk transition timestamp instead of the pool's first observation timestamp. If that timestamp is not available from the registry, the local mitigation is to disallow new principal deposits after active risk is observed, while still allowing bonus contributions.
This removes the first-observer deposit path. The tradeoff is that it also removes the intended ability to stake during UNDER_ATTACK; preserving that feature safely requires the registry to expose the real transition timestamp or another non-manipulable risk-window anchor.
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.