The normal behavior: when the underlying agreement's registry enters an active-risk state (UNDER_ATTACK or PROMOTION_REQUESTED), ConfidencePool seals riskWindowStart, and stakers who held through that window earn a k=2 time-weighted share of the bonus pool on a SURVIVED/EXPIRED outcome.
The problem: ConfidencePool._isActiveRiskState() only recognizes UNDER_ATTACK and PROMOTION_REQUESTED as risk-bearing. The real AttackRegistry (BattleChain Safe Harbor) can resolve an agreement from ATTACK_REQUESTED directly to PRODUCTION via a 14-day DAO-approval-timeout backstop, skipping UNDER_ATTACK/PROMOTION_REQUESTED entirely. When this happens, riskWindowStart never seals — not through staking, not through withdrawing, not through pokeRiskWindow(), no matter how many times any party interacts with the pool — because the state that was actually observed (ATTACK_REQUESTED) is never classified as active-risk.
Likelihood:
Fires whenever the registry's registryModerator DAO does not call approveAttack() within PROMOTION_WINDOW (14 days) of the sponsor's protocol calling requestUnderAttack() — a documented DAO-latency backstop in the real AttackRegistry, not a misconfiguration. This requires no privileged action or timing attack by any pool participant; it is the default outcome of ordinary DAO governance latency.
Confirmed empirically that no amount of diligent pool interaction can prevent it: three separate pokeRiskWindow() calls spaced across the ATTACK_REQUESTED window each reverted with RiskWindowNotReached, since ATTACK_REQUESTED is never classified as active-risk and the registry can resolve straight to PRODUCTION from there.
Impact:
100% of the bonus pool is lost to every staker in the affected pool on an otherwise fully legitimate SURVIVED outcome, regardless of stake size or how early/long a staker held — the exact case the "confidence pool" product exists to reward.
The forfeited bonus is instead swept via sweepUnclaimedBonus() to recoveryAddress, which is sponsor-controlled, creating a perverse incentive where the sponsor benefits by default whenever DAO approval happens to be slow. Principal is unaffected.
Reproduces a real BattleChain agreement lifecycle where the registry DAO doesn't approve an
attack request within the 14-day PROMOTION_WINDOW. Two stakers hold through the entire
ATTACK_REQUESTED window while a third party attempts pokeRiskWindow() three times (all
revert, proving diligence can't seal the window). The registry then auto-resolves to
PRODUCTION exactly as the real AttackRegistry does on timeout. The pool resolves SURVIVED
legitimately, but riskWindowStart was never sealed, so both stakers receive 0 bonus and the
full bonus pool sweeps to recoveryAddress. Output from running this against the real,
compiled ConfidencePool.sol is included as a comment below the test.
Adding ATTACK_REQUESTED to the active-risk set closes the gap without touching deposit or
withdrawal eligibility, which are governed by separate checks unaffected by riskWindowStart.
After this fix, a pool resolving SURVIVED via the DAO-timeout path correctly pays stakers
their k=2-weighted bonus instead of forfeiting it to recoveryAddress. Add a regression test
confirming withdraw() still works during ATTACK_REQUESTED post-fix.
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.