A confidence pool only underwrites the agreement until expiry.
When the agreement is still in an active-risk state at expiry, the pool has survived the complete period stakers agreed to underwrite. The intended result is therefore EXPIRED, returning staker principal and bonus.
However, expiration is not recorded automatically at the deadline. claimExpired() instead reads the agreement's current registry state whenever someone eventually calls it:
The moderator path has the same problem. flagOutcome() only verifies that the registry is currently CORRUPTED; it does not verify that the corruption occurred before the pool expired:
This permits the following sequence:
Stakers deposit principal and the risk window opens.
The pool reaches expiry while the agreement remains UNDER_ATTACK.
At this point, the pool has survived its complete term and should resolve as EXPIRED.
Nobody immediately calls claimExpired().
The agreement becomes CORRUPTED after the pool's expiry.
The moderator can now flag the expired pool as CORRUPTED.
If the moderator is unavailable, anyone can obtain the same result through claimExpired() after the 180-day grace period.
The entire principal and bonus balance is transferred to recoveryAddress or to a named good-faith attacker.
BattleChain's active states can subsequently transition to CORRUPTED, so remaining active after pool expiry does not imply that a later corruption occurred during the pool's term. :contentReference[oaicite:1]{index=1}
The implementation even caps a terminal observation made after expiry back to expiry:
Consequently, the pool cannot distinguish:
from:
Stakers are therefore required to race to settle immediately at expiry, although no such liveness requirement is imposed on their claims.
The repository defines expiry during an active-risk state as successful completion of the underwritten term, but the live-state resolution logic can retroactively reverse that result. :contentReference[oaicite:2]{index=2}
All staker principal and all contributed bonus can be confiscated because of an event that occurred only after the pool's coverage period ended.
A whitehat may also exploit an in-scope contract after the confidence pool has expired and then receive the entire expired pool if the moderator flags good-faith CORRUPTED.
For the permissionless fallback, the funds are sent to the sponsor-controlled recoveryAddress after the moderator grace period.
Consequences include:
complete loss of staker principal;
complete loss of the bonus pool;
payment to a whitehat for risk that was outside the agreed pool term;
a race requiring stakers to finalize exactly when the pool expires;
different outcomes for economically identical pools solely because one was settled sooner.
The maximum loss is the complete token balance held by the pool.
Add the following test to a contract inheriting BaseConfidencePoolTest:
The moderator path can be demonstrated without waiting for the grace period:
The outcome must be based on the registry state at the end of the insured term, not merely on the state visible when settlement is eventually submitted.
The robust solution is for the attack registry to expose a canonical timestamp for the transition to CORRUPTED:
The same check must be applied in both:
flagOutcome(CORRUPTED, ...);
the auto-CORRUPTED branch of claimExpired().
Do not use riskWindowEnd as a substitute for the canonical transition timestamp. It records only when the pool first observed the state, and the current implementation additionally caps all post-expiry observations to expiry.
If the upstream registry cannot provide historical transition timestamps, the protocol cannot safely infer whether a currently visible corruption occurred during the pool term. In that case, it should either:
remove the scope-blind automatic confiscation path and require an explicit moderator decision that includes temporal validation; or
introduce a trusted expiry keeper that finalizes pools at the deadline, while clearly documenting this as a required liveness dependency.
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.