The bonus is meant to be split with a k=2 time-weighted formula that crushes late entrants inside the observed risk window. Early, long-bearing stakers are supposed to dominate the bonus while a last-minute whale earns ≈0, because each deposit's score scales with (T − entry)² and a late deposit's entry is floored to riskWindowStart, so (T − entry)² ≈ 0.
riskWindowStart is not sealed when the registry actually enters UNDER_ATTACK; it is sealed lazily at the first pool interaction that observes active risk, using block.timestamp of that observation. When nobody pokes the pool after the transition, a late whale's own stake() becomes the first observer and seals riskWindowStart at its own late entry. _clampUserSums then floors every earlier staker up to that same late time, so (T − entry) is identical for everyone and the k=2 score degenerates to pure amount-weighting — letting the whale capture the bonus meant for the early risk-bearers.
Likelihood:
The registry transitions to UNDER_ATTACK and no participant interacts with the pool (no poke/stake/withdraw) before the whale — the common case, since passive stakers never poke. Pools in this state are trivially discoverable on-chain (riskWindowStart == 0 while the registry reads UNDER_ATTACK).
Deposits are intentionally allowed during UNDER_ATTACK (DESIGN §3), so the whale can freely stake late and be the first observer. It is an asymmetric race: the attacker acts on the transition block, while the victim must actively monitor the registry to defend.
Impact:
The k=2 time-weighting the protocol markets as its core mechanism is fully defeated; bonus distribution collapses to stake-share, so a last-minute insider siphons the premium meant for those who bore the risk.
PoC numbers (1000-token bonus, identical stakes/timing): early staker's bonus drops from 995.7 to 0.999 and the whale's flips from 4.3 to 999.0 (~232×). With larger whale/bonus ratios the whale captures arbitrarily close to 100% of the pool.
forge test --match-contract POC -vv : both tests pass, contrasting intended early-seal vs late-whale exploit:
Add the above test in test/unit/POC.t.sol
Run the following command in terminal.
You will see the output something simillar to below
Gate stake during UNDER_ATTACK entirely (as PROMOTION_REQUESTED already is).
Use Chainlink Automation to call pokeRiskWindow at fixed intervals. Create a new contract that first checks the registry state; if the state is UNDER_ATTACK, call pokeRiskWindow, else do nothing. Then have Chainlink Automation call this contract frequently — every 6 hours or 1 day, etc.
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.