Liquid Staking

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

Loss of Rewards for Operators When Vaults Are Removed in `OperatorVCS`

Summary

When a vault is removed in OperatorVCS, the removeVault function is called, which triggers the withdrawal of rewards and assets from the vault via IOperatorVault::exitVault. However, there is no guarantee that all the operator rewards will be claimed before the vault is removed. If the OperatorVCS::withdrawOperatorRewards function cannot fully cover the rewards due to insufficient balance, the unclaimed rewards are lost permanently after the vault is removed, causing operators to lose their rightful rewards.

Vulnerability Details

When OperatorVCS::removeVault is called to remove a vault, the exitVault function of the vault is executed to withdraw the assets and rewards from the vault. The following function, _withdrawRewards, is responsible for attempting to claim any unclaimed operator rewards:

function _withdrawRewards() private {
uint256 rewards = getUnclaimedRewards();
uint256 balance = token.balanceOf(address(this));
uint256 amountWithdrawn = IOperatorVCS(vaultController).withdrawOperatorRewards(
rewardsReceiver,
rewards - balance // unclaimedRewards
);
unclaimedRewards -= SafeCast.toUint128(amountWithdrawn);
if (balance != 0) {
token.safeTransfer(rewardsReceiver, balance);
}
emit WithdrawRewards(rewardsReceiver, amountWithdrawn + balance);
}

The withdrawOperatorRewards function in OperatorVCS attempts to transfer the rewards to the rewardsReceiver. However, if the OperatorVCS contract does not have enough balance, it transfers only what it can afford and returns the amountWithdrawn:

function withdrawOperatorRewards(
address _receiver,
uint256 _amount
) external returns (uint256) {
if (!vaultMapping[msg.sender]) revert SenderNotAuthorized();
IERC20Upgradeable lsdToken = IERC20Upgradeable(address(stakingPool));
uint256 withdrawableRewards = lsdToken.balanceOf(address(this));
uint256 amountToWithdraw = _amount > withdrawableRewards ? withdrawableRewards : _amount;
unclaimedOperatorRewards -= amountToWithdraw;
lsdToken.safeTransfer(_receiver, amountToWithdraw);
return amountToWithdraw;
}

The problem arises when the vault is removed from the system, even if it hasn't fully paid out the operator's rewards. Once the vault is removed, any remaining unclaimed rewards are lost permanently because the vault no longer exists in the system. This results in operators losing their rewards with no recourse for recovery.

Impact

Operators may lose a significant portion of their rewards if the vault does not have enough balance to pay out the rewards before being removed. These lost rewards cannot be recovered once the vault is removed from the system, causing financial harm to the operators and reducing trust in the protocol.

Tools Used

Manual

Recommendations

  1. Prevent Vault Removal Until Full Rewards Are Claimed: Implement a check to ensure that all operator rewards have been fully claimed before allowing a vault to be removed. If sufficient balance is not available, the vault should not be removed until the rewards are fully paid out.

  2. Implement a Reserve Mechanism: Create a reserve or buffer in OperatorVCS that ensures there are always enough funds to pay out unclaimed rewards, even if the vault is removed.

  3. Defer Vault Removal: Defer the vault removal process until the protocol has sufficient balance to cover all operator rewards, ensuring no rewards are lost due to vault removal.

Updates

Lead Judging Commences

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

Support

FAQs

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