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.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.