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