Liquid Staking

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

Failure to Withdraw Liquidity from External Contracts Causes Complete Withdraw Transaction Reversion

Summary

A vulnerability has been identified in the _withdrawLiquidity function of the StakingPool.sol contract, where if a strategy contains withdrawable liquidity but fails to complete the withdrawal (due to issues with contract from external projects), the entire transaction will revert. As a result, no liquidity can be withdrawn from any strategies, leading to a complete failure of the withdrawal process. This issue is particularly significant since the liquidity is often held in external contracts that are beyond the control of the strategy contract.

Vulnerability Details

The _withdrawLiquidity function iterates over a list of strategies in descending order to withdraw the required liquidity. If a strategy has available liquidity (checked via strategyCanWithdraw), the withdraw function of the strategy is called. However, if the withdraw function fails (e.g., due to an unexpected error or issue in external contracts where the liquidity is held), the entire transaction is reverted, even if other strategies could have fulfilled the withdrawal.

function _withdrawLiquidity(uint256 _amount, bytes[] calldata _data) private {
uint256 toWithdraw = _amount;
for (uint256 i = strategies.length; i > 0; i--) {
IStrategy strategy = IStrategy(strategies[i - 1]);
uint256 strategyCanWithdrawdraw = strategy.canWithdraw();
if (strategyCanWithdrawdraw >= toWithdraw) {
strategy.withdraw(toWithdraw, _data[i - 1]);
break;
} else if (strategyCanWithdrawdraw > 0) {
strategy.withdraw(strategyCanWithdrawdraw, _data[i - 1]);
toWithdraw -= strategyCanWithdrawdraw;
}
}
}

The core issue here is that the liquidity is often stored in external contracts or protocols (e.g., DeFi platforms) which are not directly controlled by the strategy contract. External contracts may experience failures, bugs, or governance decisions that could prevent the strategy from withdrawing liquidity. In such cases, if one external contract fails to return liquidity, the entire transaction will be reverted, preventing users from accessing any of their funds.

Impact

If external contracts or projects holding the liquidity experience issues or failures, the strategy contract might not be able to withdraw liquidity. This results in users being unable to withdraw any of their staked funds, even if other strategies are functional.

Tools Used

Manual

Recommendations

Use try-catch mechanism around the external withdraw call to ensure that the failure of one external contract does not prevent withdrawals from other strategies. This will allow the system to continue withdrawing liquidity from other available strategies, even if one fails due to issues with external liquidity sources.

if (strategyCanWithdraw >= toWithdraw) {
try strategy.withdraw(toWithdraw, _data[i - 1]) {
break;
} catch {
// Log failure and continue to next strategy
}
} else if (strategyCanWithdraw > 0) {
try strategy.withdraw(strategyCanWithdraw, _data[i - 1]) {
toWithdraw -= strategyCanWithdraw;
} catch {
// Log failure and continue to next strategy
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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