expiryLocked is set permanently on the first stake() call and never reset, including when that staker fully withdraws via withdraw(). This means any address, with no genuine staking intent, can call stake(minStake) then immediately withdraw(), permanently locking expiry while recovering their full deposit — denying the sponsor any future correction to a misconfigured deadline, even on a pool where zero real capital is currently at risk.
Likelihood:
Occurs whenever any address stakes minStake and withdraws it in quick succession on a freshly deployed pool — no privilege, no net capital, no coordination required.
Impact:
Sponsor permanently loses setExpiry, forcing full pool redeployment as the only remedy, while the attacker's net token cost is exactly zero.
The PoC stakes a griefer address for the minimum amount, withdraws immediately, and confirms the griefer's token balance is unchanged before and after — proving zero net cost — while pool.expiryLocked() is now true and the sponsor's subsequent setExpiry call reverts.
Reset the lock only when totalEligibleStake returns to exactly zero, preserving the original protection (no sponsor movement while genuine stake is outstanding) while closing the zero-stake griefing path.
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.