sherlock-audit / 2024-05-napier-update-judging

8 stars 7 forks source link

zzykxx - `_stake()` function in `RenzoAdaper` and `RsETHAdapter` doesn't check if max TVL is reached #85

Closed sherlock-admin2 closed 3 months ago

sherlock-admin2 commented 3 months ago

zzykxx

medium

_stake() function in RenzoAdaper and RsETHAdapter doesn't check if max TVL is reached

Summary

Vulnerability Detail

RenzoAdapter

The function RenzoAdapter::_stake() never checks what's the maximum amount that can be currently staked in the Renzo protocol. The RENZO_RESTAKE_MANAGER.depositETH() implementation, called by _stake() to deposit assets into Renzo, is the following (can be verified here):

  function depositETH(uint256 _referralId) public payable nonReentrant notPaused {
      // Get the total TVL
      (, , uint256 totalTVL) = calculateTVLs();

      // Enforce TVL limit if set
@>    if (maxDepositTVL != 0 && totalTVL + msg.value > maxDepositTVL) {
          revert MaxTVLReached();
      }
      ...SNIP...
  }

It will revert if the amount to be deposited is greater than the current maxDepositTVL. This can be avoided by checking what's the current maxDepositTVL and only depositing the maximum possible amount.

RsETHAdapter

The same issue is present in the RsETHAdapter::_stake(), where funds are deposited via depositETH:

  function _stake(uint256 stakeAmount) internal override returns (uint256) {
      ...SNIP...
      RSETH_DEPOSIT_POOL.depositETH{value: stakeAmount}(0, REFERRAL_ID);
      ...SNIP...
  }

whose implementation ends up executing the following code (as can be verified here):

  function _beforeDeposit(address asset, uint256 depositAmount, uint256 minRSETHAmountExpected) private view returns (uint256 rsethAmountToMint) {
     ...SNIP...
@>    if (checkIfDepositAmountExceedesCurrentLimit(asset, depositAmount)) {
          revert MaximumDepositLimitReached();
      }
      ...SNIP...
  }

which will also revert if the maximum TVL is reached.

Impact

Stakers will be unable to deposit in Renzo and Kelp if the current amount to be deposited will pass the maximum TVL limits, but it could be possible to deposit the difference instead of reverting.

Code Snippet

Tool used

Manual Review

Recommendation

Change the RenzoAdapter::_stake() and RsETHAdapter::_stake() functions to only deposit the maximum amount that's possible to deposit.

Here's an example for the Renzo adapter:

  function _stake(uint256 stakeAmount) internal override returns (uint256) {
      if (RENZO_RESTAKE_MANAGER.paused()) revert ProtocolPaused();

      uint256 renzoMaxTVL = RENZO_RESTAKE_MANAGER.maxDepositTVL();
      if(renzoMaxTVL > 0) {
      (, , uint256 renzoTotalTVL) = RENZO_RESTAKE_MANAGER.calculateTVLs();
      if(stakeAmount + renzoTotalTVL > renzoMaxTVL) {
          uint256 maxToDeposit = renzoMaxTVL - renzoTotalTVL;
          if(stakeAmount > maxToDeposit) {
              stakeAmount = maxToDeposit;
          }
      }
      }
      if (stakeAmount == 0) return 0;

      uint256 balanceBefore = EZETH.balanceOf(address(this));
      IWETH9(Constants.WETH).withdraw(stakeAmount);
      RENZO_RESTAKE_MANAGER.depositETH{value: stakeAmount}(0);
      uint256 newBalance = EZETH.balanceOf(address(this));
      if (newBalance - balanceBefore == 0) revert InvariantViolation();

      return stakeAmount;
  }

Duplicate of #24