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