Before the first successful stake, the pool owner may update expiry; the first stake then permanently locks the current value.
stake() does not bind the user’s transaction to the expiry they observed. A sponsor can front-run a pending first stake with a far-future expiry, causing the user’s principal to remain locked long after the advertised deadline.
Likelihood:
The pool owner changes expiry between a user signing and execution of the first stake() transaction.
The registry enters UNDER_ATTACK, disabling withdrawals before the user notices the changed deadline.
Impact:
withdraw() reverts after risk begins.
claimExpired() reverts at the originally advertised deadline, leaving principal locked until the replacement expiry, potentially decades later.
The PoC simulates a user signing a first stake while the pool shows a 31-day expiry. Before execution, the owner changes the
expiry to type(uint32).max. The stake then locks this new deadline; after risk begins, withdrawal is disabled, and claiming
at the original deadline reverts with PoolNotExpired, leaving the principal locked for decades.
Alternatively, prevent setExpiry() once the registry leaves pre-attack staging.
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.