Liquid Staking

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

Redundant logic in assets withdrawal flow

Summary

Allowing accounts to withdraw full balance in function StakingPool#withdraw() is redundant because caller does not have enough balance

Vulnerability Details

The function StakingPool#withdraw() allows the caller to withdraw all balance if setting _amount = type(uint256).max. The function can be triggered when transaction originated from WithdrawalPool#performUpkeep(). The function WithdrawalPool#performUpkeep() calls PriorityPool#executeQueuedWithdrawals(), which will pull _amount from WithdrawalPool and then call StakingPool#withdraw() with the mentioned amount.
The logic in function StakingPool#withdraw() below

if (_amount == type(uint256).max) {
toWithdraw = balanceOf(_account);
}

will be redundant. Because if _amount is set to type(uint256).max then the transaction always reverts because not enough balance.

function performUpkeep(bytes calldata _performData) external {
uint256 canWithdraw = priorityPool.canWithdraw(address(this), 0);
uint256 totalQueued = _getStakeByShares(totalQueuedShareWithdrawals);
if (
totalQueued == 0 ||
canWithdraw == 0 ||
block.timestamp <= timeOfLastWithdrawal + minTimeBetweenWithdrawals
) revert NoUpkeepNeeded();
timeOfLastWithdrawal = uint64(block.timestamp);
uint256 toWithdraw = totalQueued > canWithdraw ? canWithdraw : totalQueued; // @info get min
bytes[] memory data = abi.decode(_performData, (bytes[]));
@> priorityPool.executeQueuedWithdrawals(toWithdraw, data);
// @info asset token returned here
_finalizeWithdrawals(toWithdraw); // @info finalize withdrawals
}
function executeQueuedWithdrawals(
uint256 _amount,
bytes[] calldata _data
) external onlyWithdrawalPool {
// @info collect LST from withdrawalPool
// @info redeem token from stakingPool
// @info return asset token to caller
@> IERC20Upgradeable(address(stakingPool)).safeTransferFrom(
msg.sender,
address(this),
_amount
);
@> stakingPool.withdraw(address(this), address(this), _amount, _data);
// @info redeem LST to asset(LINK)
token.safeTransfer(msg.sender, _amount);
}
function withdraw(
address _account,
address _receiver,
uint256 _amount,
bytes[] calldata _data
) external onlyPriorityPool {
uint256 toWithdraw = _amount;
@> if (_amount == type(uint256).max) {
toWithdraw = balanceOf(_account);
}
uint256 balance = token.balanceOf(address(this));
if (toWithdraw > balance) { // @info withdraw from strategies if balance not enough
_withdrawLiquidity(toWithdraw - balance, _data);
}
require(
token.balanceOf(address(this)) >= toWithdraw,
"Not enough liquidity available to withdraw"
);
_burn(_account, toWithdraw);
totalStaked -= toWithdraw;
token.safeTransfer(_receiver, toWithdraw);
}

Impact

Redundant logic

Tools Used

Manual

Recommendations

Consider removing the logic

Updates

Lead Judging Commences

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

Appeal created

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

Support

FAQs

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