An early staker is meant to forfeit the exit option exactly when they begin earning the risk premium. DESIGN §9 states the two coincide: "a staker only forfeits the exit option once risk has actually materialized, which is exactly when they begin earning the risk premium." So the moment withdraw() locks should be the moment the staker starts earning bonus.
The two are gated on different signals. withdraw() is blocked the instant the registry reads UNDER_ATTACK (a live state check), independent of whether riskWindowStart has been sealed. But bonus accrual only exists once riskWindowStart != 0, and the window is sealed only by a pool interaction that observes an active-risk state. If the registry passes through the whole active-risk window with no such interaction and then resolves, riskWindowStart is never sealed — so by DESIGN §5 the bonus pays zero and the entire pool sweeps to recoveryAddress. The staker was locked in for that whole window (denied exit) yet earns nothing, contradicting §9.
Likelihood:
The registry passes through its active-risk window (UNDER_ATTACK) and no pool interaction observes it before a terminal state. Throughout it, the staker is locked on the state read while riskWindowStart stays 0.
The staker cannot rescue themselves by withdrawing: the exit is already locked. The only defense is to notice the transition and call pokeRiskWindow() .
Impact:
A staker who bore the full at-risk window is denied the exit and earns zero bonus. The lock and the reward do not coincide as §9 asserts.
The entire bonus pool (including third-party contributions) sweeps to recoveryAddress instead of to the risk-bearing stakers. Principal is still returned, so the loss is the full expected yield, not principal.
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
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.