Liquid Staking

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

`canDeposit()` doesn't include `unusedDepositLimit` in StakingPool.sol

Summary

canDeposit() doesn't include unusedDepositLimit in StakingPool.sol

Vulnerability Details

stakingPool::canDeposit() returns the total deposit room available for deposit in the pool. Now the total deposit room for the pool will be totalStaked + unusedDepositLimit, but the problem is canDeposit() only includes the totalStaked while returning the depositRoom available for deposit.

@> function canDeposit() external view returns (uint256) {
uint256 max = getMaxDeposits();
if (max <= totalStaked) {
return 0;
} else {
return max - totalStaked;
}
}
function getMaxDeposits() public view returns (uint256) {
uint256 max;
for (uint256 i = 0; i < strategies.length; i++) {
uint256 strategyMax = IStrategy(strategies[i]).getMaxDeposits();
if (strategyMax >= type(uint256).max - max) {
return type(uint256).max;
}
max += strategyMax;
}
return max;
}

If you see the getMaxDeposits(), it returns the max token that can be deposited in the strategy. This max amount is compared with totalStaked only in canDeposit() ie it doesn't include the unusedDepositLimit that stakingPool have.

Lets see how this works(very simple example):

  1. Suppose we have one strategy ie strategyA, which has maxDeposit of 100e18 tokens & we have unusedDepositLimit of 20e18 tokens

  2. User deposited 110e18 tokens in the priorityPool:deposit() with _shouldQueue = true.

  3. Now, 100e18 tokens should be deposited to strategyA & 10e18 tokens should be available in stakingPool as unusedDepositLimit.

  4. How many tokens will be deposited to stakingPool is calculated by canDeposit(). So lets see, how many tokens canDeposit() returns: getMaxDeposits() will return 100e18 tokens & this is compared with totalStaked(which is 0 at this point) ie 100e18 <= 0. As result, max - totalStaked is returned ie 100e18 - 0 = 100e18 tokens

  5. As we see above, canDeposit() will only return 100e18. So priorityPool will send 100e18 tokens to the stakinPool & 10e18 will be queued in the priorityPool itself instead of sending it to the stakingPool as unusedDepositLimit

if (toDeposit != 0) {
@> uint256 canDeposit = stakingPool.canDeposit();
if (canDeposit != 0) {
uint256 toDepositIntoPool = toDeposit <= canDeposit ? toDeposit : canDeposit;
@> stakingPool.deposit(_account, toDepositIntoPool, _data);
toDeposit -= toDepositIntoPool;
}
}

Impact

unusedDepositLimit will never be used/filled

Tools Used

Manual Review

Recommendations

Use unusedDepositLimit in canDeposit(), which calculates how many tokens can be deposited in the strategy & sit in the pool as unusedDepositLimit

Updates

Lead Judging Commences

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.