Distribution._withdraw
function will revert if it's called after the pool is permanently locked (expired), resulting in stakers of public pools losing their staked assets.
The protocol allows stakers to withdraw their staked asset via withdraw
function if the withdrawal window is open; which is after some locking time from the latest deposit or when the payout hasn't started:
So the user will be able to withdraw if the following a || (b && c)
condition returns true, where:
a
is block.timestamp < pool.payoutStart
, and it depends on the pool configuration only.
b
is block.timestamp > pool.payoutStart + pool.withdrawLockPeriod
, and it depends on the pool configuration only.
c
is userData.lastStake + pool.withdrawLockPeriodAfterStake
, and it depends on the pool configuration and the time of the last staking made by the user.
a
and b
conditions are static as they are not affected by the user staking time, while c
is dynamic and will change whenever the user stakes.
And to get the a || (b && c)
check permanently returns false:
a
should be always false, and this is met when block.timestamp >= pool.payoutStart
.
b && c
check should be always false, and this is met when b
is false where block.timestamp <= pool.payoutStart + pool.withdrawLockPeriod
false || (false && true)
will return false
.
As can be noticed from the above condition; the pool will be permanently locked (expired) if these two conditions are met:
block.timestamp >= pool.payoutStart
and block.timestamp <= pool.payoutStart + pool.withdrawLockPeriod
where:
pool.payoutStart
represents the timestamp when the pool starts to pay out rewards.
pool.withdrawLockPeriod
represents the period in seconds when the user can't withdraw his staked assets.
So when the pool becomes permanently locked (expired); stakers of this pool will not be able to withdraw their staked assets as the withdraw
transaction will always revert (withdraw
will be DoS'd).
Since there is no mechanism to enable stakers of expired pools to withdraw their assets; these staked assets will be locked in the Distribution
contract as they can't be withdrawn from the pool.
Note that this loss of funds is limited for stakers in public pools , as private pools don't require the staker to actually deposit any assets (staking in private pools is accessible by the contract owner only, and it's meant by it to reward these participants with MOR
tokens on Arbitrum).
Distribution._withdraw function/L243-L248
Manual Review.
Add a mechanism that enables users from withdrawing their staked assets from expired pools.
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.