Liquid Staking

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

Wrong Update in PriorityPool::_depositQueuedTokens function will lead to missing of yield and rewards for users when performUpkeep is invoked

Summary

`PriorityPool::checkUpkeep()` is supposed to do some checks to ensure as to whether performUpkeep() function can be called or not. Now, during the execution of PriorityPool::performUpkeep(), the logic in the checkUpkeep() function is inculcated into the depositQueuedTokens function by the protocol team. However, this logic is wrongly executed within the _depositQueuedTokens function.

Due to this, even though, checkUpkeep confirms that a call should be made to deposit the queued tokens into the strategies contract, the amount to deposit will not be the actual intended amount, resulting in some of the tokens still idle, and missing on yield and rewards.

Vulnerability Details

  • Have a look at the checkUpkeep() function below:

```solidity


uint256 strategyDepositRoom = stakingPool.getStrategyDepositRoom();
uint256 unusedDeposits = stakingPool.getUnusedDeposits();
if (poolStatus != PoolStatus.OPEN) return (false, "");
if (
strategyDepositRoom < queueDepositMin ||
(totalQueued + unusedDeposits) < queueDepositMin
) return (false, "");
return (
true,
abi.encode(
MathUpgradeable.min(
MathUpgradeable.min(strategyDepositRoom, totalQueued + unusedDeposits),
queueDepositMax
)
)
);

```

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/priorityPool/PriorityPool.sol#L412-L419

Look at the _depositQueuedTokens() function attempting to apply the same checkUpkeep logic, but doing it wrongly.
`PriorityPool::_depositQueuedTokens()` .

Instead of uint256 toDepositFromStakingPool = MathUpgradeable.min(MathUpgradeable.min(unusedDeposits + totalQueued, strategyDepositRoom), ``depositMax);* as done in the checkUpkeep function, it uses uint256 toDepositFromStakingPool = MathUpgradeable.min(MathUpgradeable.min(unusedDeposits, strategyDepositRoom), _depositMax);* which is flawed. #see below:

uint256 strategyDepositRoom = stakingPool.getStrategyDepositRoom();
if (strategyDepositRoom == 0 || strategyDepositRoom < _depositMin)
revert InsufficientDepositRoom();
uint256 _totalQueued = totalQueued;
uint256 unusedDeposits = stakingPool.getUnusedDeposits();
uint256 canDeposit = _totalQueued + unusedDeposits;
if (canDeposit == 0 || canDeposit < _depositMin) revert InsufficientQueuedTokens();
//<@audit-issue: getting minimum of (unusedDeposits, strategyDepositRoom, _depositMax) instead of (unusedDeposits + totalQueued, strategeyDepositRoom, _depositMax)
uint256 toDepositFromStakingPool = MathUpgradeable.min(
MathUpgradeable.min(unusedDeposits, strategyDepositRoom),//@audit should be minimum of (unusedDeposits + totalQueued, strategyDepositRoom) as done in checkUpkeep
_depositMax
);

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/priorityPool/PriorityPool.sol#L700-L712

  • Now, with this current implementation, the value of uint256 toDepositFromStakingPool variable could be lesser than the actual value, which will in turn make the uint256 toDepositFromQueue variable have a wrong value. This will ultimately result in a wrong amount being deposited into the staking Pool. See Below:

    uint256 toDepositFromQueue = MathUpgradeable.min(
    MathUpgradeable.min(_totalQueued, strategyDepositRoom - toDepositFromStakingPool),
    _depositMax - toDepositFromStakingPool
    );
    stakingPool.deposit(address(this), toDepositFromQueue, _data);
    _totalQueued -= toDepositFromQueue;

Impact

  • Actual intended full amount will not be deposited into the staking Pool even though there is enough room in the strategies contracts, leaving some of the tokens still idle missing on yield and rewards distribution.

Tools Used

Manual Review

Recommendations

Follow the logic in the PriorityPool::checkUpkeep() function, and ensure that this same logic is applied in PriorityPool::_depositQueuedTokens() function .

Within PriorityPool::_depositQueuedTokens() function,

Change

From:

uint256 toDepositFromStakingPool = MathUpgradeable.min(MathUpgradeable.min(unusedDeposits, strategyDepositRoom),_depositMax);

To:

uint256 toDepositFromStakingPool = MathUpgradeable.min(MathUpgradeable.min(unusedDeposits + totalQueued, strategyDepositRoom),_depositMax);
Updates

Lead Judging Commences

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.