When a pool reaches expiry while the registry is still in an active-risk state, the protocol design says the pool survived its covered term. claimExpired() should resolve the pool as EXPIRED, allowing stakers to claim principal plus bonus.
The issue is that claimExpired() does not bind resolution to the registry state at expiry. When no one calls claimExpired() immediately at expiry and the registry later becomes CORRUPTED, the first later caller can resolve the already-expired pool as bad-faith CORRUPTED. This lets a post-term corruption retroactively forfeit stakers' expired claims.
Likelihood:
A pool expires while the registry is still active-risk.
No user calls claimExpired() before the registry later becomes CORRUPTED.
After the moderator grace period, any address can finalize the pool using the later CORRUPTED state.
Impact:
Stakers permanently lose principal and bonus even though the pool had already expired before the corruption.
The full pool balance is swept to recoveryAddress through claimCorrupted().
Add this test to test/unit/ClaimExpiredRegistryGate.t.sol:
Run:
Expected output:
Do not allow a CORRUPTED state first observed after expiry to retroactively choose the CORRUPTED branch. An expired pool should resolve from the state at expiry, or require the terminal CORRUPTED observation to have happened before expiry.
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.