Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: medium
Invalid

PriorityPool::canWithdraw() incorrectly calculates the assets that can be withdrawn

Summary

The amount of assets that can be withdrawn immediately from prioritypool comes from two sources:

a. Assets inside the stakingpool

b. assets queued in the prioritypool

So, ideally the amount that can be withdrawn should be the sum of amount that can be unqueued and assets that can be withdrawn from stakingpool.

But the way it is done inside the canWithdrawfunction is incorrect.

Vulnerability Details

function canWithdraw(
address _account,
uint256 _distributionAmount
) external view returns (uint256) {
uint256 canUnqueue = paused()
? 0
: MathUpgradeable.min(getQueuedTokens(_account, _distributionAmount), totalQueued);
uint256 stLINKCanWithdraw = MathUpgradeable.min(
stakingPool.balanceOf(_account),
stakingPool.canWithdraw() + totalQueued - canUnqueue
);
return canUnqueue + stLINKCanWithdraw;
}

If we look carefully, totalQueued - canUnqueueare the amount of assets that are not queued by the account. This result is added with stakingPool.canWithdraw()which is the total amount of assets that can be withdrawn from stakingPool. If the balance of _account in stakingPool is greater then stakingPool.canWithdraw() + totalQueued - canUnqueuethen stLINKCanWithdrawis basically:

Total assets that can be withdrawn from stakingPool + Assets not deposited by _account in priorityPool

POC:

Assuming the account has deposited 1000 LINK, totalQueued tokens are 5000 LINK, total LINK in stakingpool is 10000 LINK and balance of _account is also 15000 LINK.

So,

uint256 stLINKCanWithdraw = MathUpgradeable.min(stakingPool.balanceOf(_account),stakingPool.canWithdraw() + totalQueued - canUnqueue);

putting in the values assumed

uint256 stLINKCanWithdraw = MathUpgradeable.min(15000, 10000 + 5000 - 1000);

= MathUpgradeable.min(15000, 14000) = 14000 LINK

So the final amount returned is

return canUnqueue + stLINKCanWithdraw;= 1000 + 14000 = 15000 LINK

But Ideally it should be the assets that the account has in staking and prioritypool which is 15000 LINK + 1000 LINK = 16000 LINK. But the stakingpool can accomadate to withdraw only 10000 LINK (apart from strategies) and 5000 from queue. So this will empty the entire assets inside the stakingpool and prioritypool.

Impact

Medium

Likelihood: Medium (The balance of an account being greater than the totalStaked amount has a medium likelihood as the account can always choose not to stake or deposit; the amount queued is highly likely that will be less than the totalQueued amount)

Impact: Medium (though the user will never be able to withdraw more than what he has, a big withdrawl can effect the amount of assets both in staking and priority pool and will effect the future withdrawls)

Tools Used

Manual Review

Recommendations

We propose that the code should be:

function canWithdraw(
address _account,
uint256 _distributionAmount
) external view returns (uint256) {
uint256 canUnqueue = paused()
? 0
: MathUpgradeable.min(getQueuedTokens(_account, _distributionAmount), totalQueued);
uint256 stLINKCanWithdraw = MathUpgradeable.min(
stakingPool.balanceOf(_account),
stakingPool.canWithdraw()
);
return canUnqueue + stLINKCanWithdraw;
}

The final amount returned is the assets that account can unqueue and withdraw from the stakingpool.

From POC:

Assuming the account has deposited 1000 LINK, totalQueued tokens are 5000 LINK, total LINK in stakingpool is 10000 LINK and balance of _account is also 15000 LINK.

Amount returned is: 1000 + 10000 = 11000 LINK

This will not much effect the remaining amount of assets in the staking and prioritypool.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

eshumanohare_cyfrin Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.