Liquid Staking

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

`LSTRewardsSplitterController::removeSplitter` Will Consistently Revert Due to Miscalculation of Balance in Function Implementation

Summary

The LSTRewardsSplitterController::removeSplitter function will consistently revert due to an oversight in its implementation. Specifically, the line splitter.withdraw(balance, _account) attempts to withdraw the entire balance of the splitter, including both principal deposits and rewards. However, the function incorrectly handles the reward splitting and withdrawal process, leading to either underflow in principalDeposits -= _amount or insufficient funds for the transfer, causing the transaction to revert.

Vulnerability Details

The issue arises when the function attempts to withdraw the full balance of the splitter after calling splitRewards(). Here’s the problematic section of the removeSplitter function:

function removeSplitter(address _account) external onlyOwner {
ILSTRewardsSplitter splitter = splitters[_account];
if (address(splitter) == address(0)) revert SplitterNotFound();
uint256 balance = IERC20(lst).balanceOf(address(splitter));
@> uint256 principalDeposits = splitter.principalDeposits();
if (balance != 0) {
if (balance != principalDeposits) splitter.splitRewards();
@> splitter.withdraw(balance, _account);
}
delete splitters[_account];
uint256 numAccounts = accounts.length;
for (uint256 i = 0; i < numAccounts; ++i) {
if (accounts[i] == _account) {
accounts[i] = accounts[numAccounts - 1];
accounts.pop();
break;
}
}
IERC677(lst).safeApprove(address(splitter), 0);
}

The balance variable includes both the principal deposits and rewards (i.e., balance = principalDeposits + rewards). After splitRewards() distributes the rewards, the function attempts to withdraw the entire balance using splitter.withdraw(balance, _account). However, since the rewards have already been distributed, the contract no longer has sufficient funds to cover the entire balance, leading to one of the following issues:

  • Underflow: The line principalDeposits -= _amount may revert due to underflow if _amount exceeds principalDeposits.

  • Insufficient Funds: The line lst.safeTransfer(_receiver, _amount) may revert if the contract does not have enough funds to cover the withdrawal.

As a result, the function will consistently revert during the withdrawal process.

Impact

This issue prevents the proper removal of a splitter, causing the removeSplitter function to revert consistently when attempting to withdraw the splitter's balance. This can block the protocol's ability to manage and remove splitters, leading to operational inefficiencies and potential financial losses.

Tools Used

Manual

Recommendations

  1. Separate Principal and Reward Withdrawal: Modify the function to withdraw only the rewards after calling splitRewards() and then handle the withdrawal of the principalDeposits separately. This ensures that the contract does not attempt to withdraw more than it holds.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

In `removeSplitter` the `principalDeposits` should be used as an input for `withdraw` instead of balance after splitting the existing rewards.

Support

FAQs

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