Liquid Staking

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

Insufficient Queued Tokens To Fully Unqueue Users Queued Tokens Can Prevent User From Receiving/Queueing Withdrawals On Some Amounts

Summary

The withdraw function in the priotiy pool takes in the _amountToWithdraw parameter which is the amount of LINK a user wishes to withdraw. Including both their unqueued tokens and LST tokens.
When a users queued tokens cannot be fully unqued before taking the LST the withdrawal would fail to execute.

Vulnerability Details

function withdraw(
uint256 _amountToWithdraw, // <---- here
uint256 _amount,
uint256 _sharesAmount,
bytes32[] calldata _merkleProof,
bool _shouldUnqueue,
bool _shouldQueueWithdrawal
) external

When a users queued tokens cannot be fully unqued before taking the LST the transaction would revert
Lets take a scenario where a user has 80 link tokens to withdraw (50 LST and 30 Queued tokens).

Inside the withdraw function in the priorityPool we have the if statement for the shouldUnqueue
We'll take totalQueued tokens to be 20

if (_shouldUnqueue == true) {
// .. some code here....
uint256 queuedTokens = getQueuedTokens(account, _amount);
uint256 canUnqueue = queuedTokens <= totalQueued ? queuedTokens : totalQueued;
uint256 amountToUnqueue = toWithdraw <= canUnqueue ? toWithdraw : canUnqueue;
if (amountToUnqueue != 0) {
accountQueuedTokens[account] -= amountToUnqueue;
totalQueued -= amountToUnqueue;
toWithdraw -= amountToUnqueue;
emit UnqueueTokens(account, amountToUnqueue);
}
}
// ....some code here....

The function gets the users queuedTokens which we'll take to be 30 and passes it to the canUnqueue
The canUnqueue returns the totalQueued = 20 because its less than the users queuedTokens = 30.
The amountToUnqueue also returns the canUnqueue =20 since its less than the toWithdraw= 80.

Inside the amountToUnqueue != 0 check
We subtract the amountToUnqueue from the toWithdraw which is (80 - 20) = 60
The toWithdraw = 60.

Moving further down the code..

// attempt to withdraw whats left if tokens remain after unqueueing
if (toWithdraw != 0) {
IERC20Upgradeable(address(stakingPool)).safeTransferFrom(account, address(this), toWithdraw);
toWithdraw = _withdraw(account, toWithdraw, _shouldQueueWithdrawal);
}
token.safeTransfer(account, _amountToWithdraw - toWithdraw);

Since the toWithdraw is not zero we perform a safeTransferFrom on the LST from the user with the toWithdraw amount passed down which is 60
But remember the user wanted to withdraw his balance which was, 80 tokens (50 LST and 30 Queued).
So this safeTransferFrom function would fail due to insufficient LST balance preventing the user from receiving/queuing their tokens for withdrawal via this function.

Impact

On some user amounts user would not be able to process their withdrawals since the leftover unqueued tokens were added to the amount of LST that's to be transferred from them.

Recommendations

Handling the unqueued tokens amount seprarate from the amount of LST that's to be transferred from the users balance.

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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