Root cause: riskWindowStart seals on the first on-chain observation of an active-risk registry state (_markRiskWindowStart, ConfidencePool.sol#L801), not on the true attack-start block. _clampUserSums then floors every earlier staker's effective entry up to that seal (#L677), and _bonusShare weights each deposit by (T − effectiveEntry)² (#L696).
Impact: A staker who deposits late during an UNDER_ATTACK phase nobody has observed yet makes their own deposit the first observation, sealing riskWindowStart late. Every earlier staker's entry floors up to it, erasing the at-risk time they actually bore. The k=2 score spread collapses toward an amount-weighted split, transferring the early honest stakers' earned bonus premium to the late (larger) staker — a ~88% loss of the early staker's fair bonus in the PoC.
The bonus rewards time at risk: each deposit scores amount × (T − entryTime)², so a longer-underwriting staker earns more. But riskWindowStart — which all pre-seal entries floor to — is set by the first party to touch the pool during an active-risk state, not by the true transition. A passive early staker doesn't interact during an unobserved attack, so the window doesn't seal on its own; a late staker makes their own deposit the first observation, seals riskWindowStart late, and wipes out earlier stakers' real at-risk time.
This is not the timing residual the docs accept: that residual is for the end anchor T (riskWindowEnd), framed as a symmetric public race. A4 manipulates the start anchor and is not symmetric — the loss falls entirely on a passive early staker. The time it erases is genuinely at-risk time (true UNDER_ATTACK → late first-observation), which the docs never address.
Likelihood: Medium — occurs whenever an active-risk interval passes with no pool interaction to seal the window (normal for passive stakers); the late large staker is economically incentivised to withhold the poke and stake late.
Impact: Medium — a specific honest staker loses the majority of their fair bonus to the attacker; redistribution among stakers, no principal loss.
test/audit/AdversarialA4.t.sol (both tests pass). Alice stakes 10 early, bears ~21 days at risk; a whale stakes 90 one day before resolution, its late stake sealing riskWindowStart. Baseline is identical but seals at the true start via one pokeRiskWindow().
| Scenario | Alice (early, ~21d) | Whale (late, ~1d) |
|---|---|---|
| Attack (late seal) | 10 | 90 |
| Honest baseline (early poke) | 98 | 2 |
Seal riskWindowStart from the registry's canonical active-risk transition timestamp if available, so a late observer cannot move it. If no on-chain transition timestamp exists, credit at-risk time from min(depositTime, earliest-known-active-risk) rather than flooring up to a possibly-late riskWindowStart, or auto-seal the window on the registry transition instead of lazily on first interaction.
For example, replace the lazy first-observation seal with a value derived from the transition itself:
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.