Liquid Staking

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

Insufficient Validation of totalStaked Leading to Potential Overestimation of Withdrawable Funds

Summary

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/StakingPool.sol#L279
The canWithdraw() function in the Staking Pool contains a critical logic flaw where it does not ensure that the available withdrawal room calculation is correct. Specifically, the function assumes that the difference between totalStaked and the min (minimum required deposits) will always leave enough tokens for withdrawal, without validating if this is true. This can lead to overestimation of the withdrawable amount, resulting in potential operational issues, such as over-withdrawals, protocol instability, or liquidity depletion.

Vulnerability Details

if (min >= totalStaked) {
return 0;
} else {
// @audit: no guarantee that totalStaked - min >= min
return totalStaked - min;
}

The function attempts to calculate the available withdrawal room by subtracting the minimum amount of tokens that must remain in the pool (min=getMinDeposits()) from the total staked amount (totalStaked). However, there is no guarantee that totalStaked - min will always leave a sufficient remaining balance that respects the minimum required liquidity.(totalStaked - min >= min)
If totalStaked - min results in a value less than the minimum required deposits, this calculation could return a value that suggests there is moreliquidity available for withdrawal than there actually is, resulting in over-withdrawals.
This logic is then used in the Priority Pool::canWithdraw function:

uint256 stLINKCanWithdraw = MathUpgradeable.min(
stakingPool.balanceOf(_account),
stakingPool.canWithdraw() + totalQueued - canUnqueue
);

the faulty result from canWithdraw() is directly used to calculate the amount of tokens a user can withdraw, further propagating the incorrect withdrawal logic even in the WithdrawalPool::PerformUpKeep

function performUpkeep(bytes calldata _performData) external {
uint256 canWithdraw = priorityPool.canWithdraw(address(this), 0);
// rest of the function

Impact

The impact of this vulnerability is high:

  1. Excessive Withdrawals: In certain scenarios, users might be able to withdraw a disproportionately large amount of tokens, potentially more than half of the total staked amount.

  2. Liquidity Depletion: Since the system does not properly validate whether the withdrawal calculation leaves sufficient liquidity, large withdrawals could deplete the pool's funds, leaving it unable to fulfill further withdrawal requests or operate normally.

  3. Economic Instability: A sudden depletion of liquidity could cause larger systemic issues. If the protocol relies on maintaining a minimum level of liquidity (for staking, rewards, or other operations), draining this liquidity would result in collateral instability and could lead to under-collateralization risks, causing cascading failures across the system.

The severity is considered high because:

It affects core functionality (withdrawals) of the staking system.
It could potentially lead to significant fund loss or protocol instability.
The vulnerability spans multiple contracts, making it more complex to address and potentially more impactful.

Tools Used

Manual Review

Recommendations

Enforce Minimum Liquidity Validation:

if (min >= totalStaked || totalStaked - min < min) {
return 0;
} else {
return totalStaked - min;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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