Liquid Staking

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

Inaccurate Calculation of Available Deposit and Withdrawal Amounts

Summary

The getMaxDeposits and getMinDeposits functions in StakingPool contract do not accurately account for the current state of deposits and withdrawals in each strategy, leading to incorrect values returned by the canDeposit and canWithdraw functions. This can result in users being unable to deposit funds even when there is available capacity or being allowed to withdraw more funds than what is actually available, potentially causing insufficient liquidity in the pool.

Vulnerability Details

canDeposit and canWithdraw functions, which are responsible for determining the available deposit and withdrawal capacities, rely on the getMaxDeposits and getMinDeposits functions. However, these functions simply sum up the corresponding values from all the strategies without considering the current state of deposits and withdrawals in each strategy.

The root cause of the issue lies in the implementation of the getMaxDeposits and getMinDeposits functions: StakingPool.sol# https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/StakingPool.sol#L201-L226

function getMaxDeposits() public view returns (uint256) {
uint256 max;
for (uint256 i = 0; i < strategies.length; i++) {
uint strategyMax = IStrategy(strategies[i]).getMaxDeposits();
if (strategyMax >= type(uint256).max - max) {
return type(uint256).max;
}
max += strategyMax; // <-- Vuln: Not considering current deposits in each strategy
}
return max;
}
function getMinDeposits() public view returns (uint256) {
uint256 min;
for (uint256 i = 0; i < strategies.length; i++) {
IStrategy strategy = IStrategy(strategies[i]);
min += strategy.getMinDeposits(); // <-- Vuln: Not considering current deposits in each strategy
}
return min;
}
// These vulnerabilities propagate to the `canDeposit` and `canWithdraw` functions, which rely on the incorrect values
// returned by `getMaxDeposits` and `getMinDeposits`, resulting in inaccurate calculations of available deposit and withdrawal amounts.

These functions iterate over all the strategies and sum up their getMaxDeposits and getMinDeposits values without taking into account the actual deposits and withdrawals made to/from each strategy. As a result, the canDeposit and canWithdraw functions return inaccurate values that do not reflect the true available deposit and withdrawal amounts based on the current state of the strategies.

Consider the following scenario:

  1. The StakingPool contract is deployed with two strategies, each having a maximum deposit limit of 1000 tokens and a minimum deposit limit of 100 tokens.

  2. A user deposits 800 tokens into the first strategy and 200 tokens into the second strategy.

  3. When the user calls the canDeposit function, it returns 1000 (the sum of the maximum deposit limits of both strategies), indicating that the user can deposit an additional 1000 tokens.

  4. However, when the user attempts to deposit 500 tokens into the pool, the transaction fails because the actual available deposit capacity is only 200 tokens (1000 - 800), not 1000 as reported by canDeposit.

Similarly, if the user withdraws 900 tokens from the first strategy and calls the canWithdraw function, it will return 1000 (the difference between the total staked amount and the sum of the minimum deposit limits of both strategies). However, the actual available withdrawal amount is only 100 tokens (1000 - 900), not 1000 as reported by canWithdraw.

Impact

Users may be unable to deposit funds even when there is available capacity in the strategies, effectively preventing them from participating in the staking pool.

Tools Used

Vs Code

Recommendations

The getMaxDeposits function should subtract the current total deposits of each strategy from its maximum deposit limit to accurately determine the available deposit capacity. Similarly, the getMinDeposits function should use the current total deposits of each strategy instead of the minimum deposit limit to accurately calculate the minimum amount of deposits that must remain in the pool.

function getMaxDeposits() public view returns (uint256) {
uint256 max;
for (uint256 i = 0; i < strategies.length; i++) {
- uint strategyMax = IStrategy(strategies[i]).getMaxDeposits();
+ uint strategyMax = IStrategy(strategies[i]).getMaxDeposits() - IStrategy(strategies[i]).getTotalDeposits();
if (strategyMax >= type(uint256).max - max) {
return type(uint256).max;
}
max += strategyMax;
}
return max;
}
function getMinDeposits() public view returns (uint256) {
uint256 min;
for (uint256 i = 0; i < strategies.length; i++) {
IStrategy strategy = IStrategy(strategies[i]);
- min += strategy.getMinDeposits();
+ min += strategy.getTotalDeposits();
}
return min;
}
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

Support

FAQs

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