_assertDepositsAllowed intentionally allows deposits during UNDER_ATTACK while blocking PROMOTION_REQUESTED/PRODUCTION/CORRUPTED:
DESIGN.md §3 justifies this as safe because such a deposit's "bonus share is crushed to ~zero by the k=2 weighting — entry is floored at riskWindowStart ≈ now, so (T − entry)² ≈ 0 ... There is no late-join advantage to gate against."
That claim does not hold for the first account to interact once the registry is UNDER_ATTACK. T in the k=2 score is riskWindowEnd, and a first-observer's stake() itself seals riskWindowStart via _observePoolState(), then floors the caller's own entry to it:
So the first UNDER_ATTACK depositor's weight is (riskWindowEnd − riskWindowStart)² — the maximum early-entrant weight, identical to a staker who committed blind for the entire pre-risk term (who also floors to riskWindowStart per §7). Per DESIGN.md §1, UNDER_ATTACK is the normal, long-lived "legally attackable" operating mode that usually resolves to SURVIVED, so an opportunist who deposits only once UNDER_ATTACK is visible on-chain (with far better survival information, and locking capital for only the short observed-risk window rather than the full blind term) captures a full early-entrant bonus share and dilutes honest long-term stakers toward parity.
DESIGN.md §7 in fact endorses the actual behavior ("every deposit made before the seal, including the one that triggers it, floors to riskWindowStart"), so §3's stated safety invariant contradicts both §7 and the code. This is reported because §3's specific justification for allowing UNDER_ATTACK deposits is demonstrably false — the "no late-join advantage" it relies on does not exist for the first observer.
Likelihood:
The opportunist must be the first account to interact with the pool after the registry enters UNDER_ATTACK (any earlier honest stake or pokeRiskWindow seals riskWindowStart first, after which a later deposit floors to a real, later entry and is correctly crushed). Reachable whenever a pool has parked bonus liquidity but no active pre-seal interaction.
Impact:
Bonus-pool redistribution: an informed late entrant captures the same time-weight as blind long-term stakers, diluting their bonus share. No principal loss (the late stake is genuinely locked and forfeit on CORRUPTED), so this is a fairness/economic-incentive issue, not theft.
test/audit/DesignDivergence.t.sol (test_underAttackDepositor_earns_full_not_zero_bonus) — passes under forge test.
Result: PASS — bob (the JIT UNDER_ATTACK depositor) earns 50 ether bonus, exactly equal to the 20-day committed staker, not "~zero."
Option (a) — if UNDER_ATTACK deposits carry no intended late-join advantage (per §3), close them like PROMOTION_REQUESTED, removing the first-observer edge entirely:
Note the first-observer edge cannot be fixed by changing the entry-floor at the stake site: a first-observer's own stake() seals riskWindowStart to their block.timestamp in _observePoolState() before the floor line runs, so newEntry < start is already false for them — their entry equals riskWindowStart because the seal is their timestamp, not because of the floor. Any fix that preserves UNDER_ATTACK deposits must therefore decouple "who seals the window" from "who gets credited from the seal" — e.g. require the risk window to already be sealed (by a prior interaction or pokeRiskWindow) before an UNDER_ATTACK deposit is accepted, so the entrant's real (later) block.timestamp is used and correctly crushed. Blocking the state (option a) is the simpler removal.
At minimum, correct DESIGN.md §3 to match the §7/code behavior so the "no late-join advantage" safety claim is not relied upon by future integrators or auditors.
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.