xanoutas/solidity-security-findings huggingface dataset:
The user received less money than expected
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L270-L358


# Vulnerability details

## Impact

The user will receive less money than they should have.

## Proof of Concept

The user's withdrawal process involves two steps:

1. Calling withdraw to initiate a withdraw request and calculate the amount to be received using the formula `share * totalAsset / totalShare`.
2. Calling claim to burn ezETH (share) and receive the desired collateral token.

share: user own ezETH amount

totalAsset: TVL

totalShare: ezETH totalSupply

In this process, the amount received includes the funds from rewards. The calculation of `TVL` does not take into account the amount waiting to be claimed stored in the `claimReserve` state variable. Therefore, if a user initiates a withdrawal request but does not call the `claim` function, the TVL will always include the amount waiting to be claimed.

TVL:

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L270-L358



Here's an example:

1. User A calls `withdraw` to initiate a withdrawal request.
2. After `claimReserve` seconds, User A still does not call `claim` to receive the amount.
3. Another day passes, and User B initiates a withdrawal request. However, when calculating the amount to be received, TVL still includes the amount waiting to be claimed by User A and totalSupply. In this process, rewards generated by RENZO continue to include User A, but User A cannot claim them (because User A's claimable amount has already been calculated), resulting in User B receiving less reward than expected when claiming the total amount, as User A is included in this process.

poc coding :

mock/mockOracle:

```solidity
pragma solidity 0.8.19;


import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract TestOracle is AggregatorV3Interface{
    constructor() {

    }

    function decimals() external override view returns (uint8) {
        return 18;
    }

    function description() external override view returns (string memory) {
        return "none";
    }

    function version() external override view returns (uint256) {
        return 1;
    }

    function getRoundData(
        uint80 _roundId
    ) external override view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) {
        return (0, 0, 0, 0, 0);
    }

    function latestRoundData() external override view returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        ){
            return (0, 1000, 0, block.timestamp, 0);
    }
}
```



mock/mockStrategyManager.sol:

```solidity
pragma solidity 0.8.19;
import "../../contracts/EigenLayer/interfaces/IStrategyManager.sol";

contract MockStrategyManager {
    constructor() {
Inclusion of Pending Withdrawals in WithdrawQueue in TVL Calculation Causes Users to Lose Staking Rewards
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L315-L321
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L564-L569
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L605-L609
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L355


# Vulnerability details

## Impact
When users apply for a withdrawal, they deposit ezETH into the `WithdrawQueue`. In `RestakeManager.sol`, the function `renzoOracle.calculateMintAmount` calculates how much new ezETH to mint, taking into account the ezETH in the `WithdrawQueue`, and `calculateTVLs()` also accounts for the collateral in the WithdrawQueue.

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L605-L609

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L315-L321

The issue here is that the exchange ratio between ezETH and collateral is fixed at the time of withdrawal application. Any rewards earned by the protocol after this application do not affect this ratio. However, including these deposited ezETH in the TVL calculation can dilute subsequent users' staking rewards and can be manipulated to influence the price of ezETH.

Based on this vulnerability, an further attack scenario can be constructed, assuming the protocol's TVL is initially 0:
1. An attacker deposits ETH and immediately applies for a withdrawal without claiming it.
2. A victim deposits ETH, earning staking rewards for the protocol.
3. The attacker deposits more ETH and immediately executes the claim from step (1).

Through this attack process, the attacker can realize an instant profit in step three, derived from the staking rewards earned by the victim.

## Proof of Concept
https://gist.github.com/StillFantastic/26338c38774d9e1dfe87d2420737c010

## Tools Used
Manual review

## Recommended Mitigation Steps
In the `calculateTVLs` function of `RestakeManager.sol`, the calculation should exclude `claimReserve` from the `WithdrawQueue`, and the total supply of ezETH should also deduct the ezETH in the WithdrawQueue.





## Assessed type

Context
METH Exchange Rate Retrieval Method Can Lead To Major Losses 
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/Oracle/Mantle/METHShim.sol#L69


# Vulnerability details

## **Vulnerability Details:**

The METHShim contract uses the **[_getMETHData](https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/Oracle/Mantle/METHShim.sol#L69)** function to retrieve the price of 1 mETH in ETH from the mETH staking contract. Although the Meth token is not yet supported, "it can technically be supported using the METHShim contract if required". However, there is currently an unhandled edge case that can cause losses for both users and the protocol.

As mentioned in the [Mantle docs](https://docs.mantle.xyz/meth/concepts/risk-management/slashing-event-handling#:~:text=Slashing%20Event%20Handling-,Exchange%20Rate%20During%20Major%20Slashing%20Event,-Description):

- If a major slashing event occurs for the protocol's validators, which causes a large loss of ETH controlled by the protocol, the exchange rate will not reflect this slashing until the next Oracle update. As the Oracle is configured to run every 8 hours, it is possible for another party to detect the slashing event, and request an unstake of all of their ETH at an elevated exchange rate before the oracle data "catches up" and the correct rate is quoted.
- This leads to a loss of ETH for all remaining stakers, and would cause a rush to exit the protocol.

To mitigate this, the mantle staking contract implements the following mechanism:

- All unstake requests have a property called "finalization" which determines whether or not they can be claimed by the user. Requests will only finalize when an Oracle update has been posted which covers the block in which they were requested, plus some buffer.

However, in the RestakeManager contract, users can create a withdraw request at any time as long as there are currently enough funds for one of the collateral tokens or ETH in the withdrawal queue contract. This withdraw request will lock in the amount at the current exchange rate and be able to be claimed once a cooldown period has passed.

```solidity
    function withdraw(uint256 _amount, address _assetOut) external nonReentrant {
        ...
        // Calculate amount to Redeem in ETH
        uint256 amountToRedeem = renzoOracle.calculateRedeemAmount(_amount, ezETH.totalSupply(), totalTVL);
        // update amount in claim asset, if claim asset is not ETH
        if (_assetOut != IS_NATIVE) {
            // Get ERC20 asset equivalent amount
            amountToRedeem = renzoOracle.lookupTokenAmountFromValue(IERC20(_assetOut), amountToRedeem);
        }
        // revert if amount to redeem is greater than withdrawBufferTarget
        if (amountToRedeem > getAvailableToWithdraw(_assetOut)) revert NotEnoughWithdrawBuffer();
        // increment the withdrawRequestNonce
        withdrawRequestNonce++;
        // add withdraw request for msg.sender
        w
Depositors can avoid validator penalties at other depositors' expense
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L274-L358
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/Mantle/METHShim.sol#L80
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/RenzoOracle.sol#L75


# Vulnerability details

There are validator penalties and slashings that can decrease the value of staked ETH. This can affect all of Renzo's supported collateral tokens, wbETH, mETH, stETH and ETH.

There is typically a delay between the update of an Oracle and the penalty. In [mETH](https://docs.mantle.xyz/meth/concepts/risk-management/slashing-event-handling#exchange-rate-during-major-slashing-event), for example:

> If a major slashing event occurs for the protocol's validators, which causes a large loss of ETH controlled by the protocol, the exchange rate will not reflect this slashing until the next Oracle update. As the Oracle is configured to run every 8 hours, it is possible for another party to detect the slashing event, and request an unstake of all of their ETH at an elevated exchange rate before the oracle data "catches up" and the correct rate is quoted.

The described issue above has been mitigated in mETH. However, the issue still applies to Renzo.

The Oracle update in mETH is done every 8 hours. During a major slashing event and before the next Oracle update, depositors can withdraw their ETH from Renzo. The withdrawal is done while TVL has not yet decreased due to the slashing. Users unable to withdraw before the huge decrease in TVL will shoulder the losses of the slashing event.

## Impact
This leads to a loss of ETH for all remaining depositors and would cause a rush to exit the protocol. The damage is worse when the assets withdrawn are unaffected by the slashing.

## Proof of Concept
The [TVL](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L274-L358) of the protocol is used for computing the minting and withdrawal amounts and is calculated with:
```
totalTVL = sum(operatorTVL) + DepositQueue ETH Balance + WithdrawQueue TVL
```
A decrease in the price of any of its collateral tokens will decrease Renzo's TVL.

In the case of mETH, its price is fetched from an [Oracle](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/RenzoOracle.sol#L75) which calls [`methStaking.mETHToETH(1 ether)`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/Mantle/METHShim.sol#L80) to get its price.

ref: [mETH Staking contract on mainnet](https://etherscan.io/address/0xdecacc56fc347274d3df2b709602632845611d39#code)
```solidity
    function mETHToETH(uint256 mETHAmount) public view returns (uint256) {
        if (mETH.totalSupply() == 0) {
            return mETHAmount;
        }
        return Math.mulDiv(mETHAmount, totalControlled(), mETH.totalSupply());
    }

    /// @notice The total amount of ETH controlled by the protocol.
    /// @dev Sums over the balances of various c
stETH 1-wei transfer issue may lead to reverts due to insufficient balance
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L491-L576


# Vulnerability details

## Impact
`RestakeManager#deposit` treats stETH as any other token, which is prone to the known stETH rounding issue. 

https://docs.lido.fi/token-guides/steth-on-aave-caveats/

As `RestakeManager#deposit` relies on its balance, and assumes it has received exactly `_amount` from the depositor, it then splits `_amount` into `bufferToFill` and `_amount - bufferToFill`. If `_amount` is transferred from the user to the RestakeManager with a precision loss, but both `bufferToFill` and `_amount - bufferToFill` are transferred without precision loss, the contract will not be able to execute the latter transaction, as it would own only `_amount - 1` of stETH, but would attempt to transfer `_amount - bufferToFill + bufferToFill = _amount` of stETH.

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L546-L562

## Recommended Mitigation Steps

Use `balanceOf(address(this))` instead of `_amount`.
































## Assessed type

Other
stETH Rebasing Token Issue: Minting and Redeeming Discrepancies
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L491-L576


# Vulnerability details

## Impact
Rebasing tokens, such as stETH, have a dynamic supply that can complicate their integration with DeFi platforms. To address this issue, it is recommended to wrap the rebasing token (e.g., swap it for a non-rebasing equivalent like wstETH) before deploying it to DeFi. Note that this process may add another layer of complexity and risk.



## Proof of Concept

When a user deposits stETH, the protocol calculates the collateralAmount based on an oracle call. However, during the token transfer to restakeAddress, the amount transferred is less than the protocol assumes. This discrepancy causes the total value to be updated based on the actual amount in the contract, resulting in users potentially redeeming more collateral tokens than they initially deposited.

```solidity
  function deposit(
        IERC20 _collateralToken,
        uint256 _amount,
        uint256 _referralId
    ) public nonReentrant notPaused {
        // Verify collateral token is in the list - call will revert if not found
        uint256 tokenIndex = getCollateralTokenIndex(_collateralToken);

        // Get the TVLs for each operator delegator and the total TVL
        (
            uint256[][] memory operatorDelegatorTokenTVLs,
            uint256[] memory operatorDelegatorTVLs,
            uint256 totalTVL
        ) = calculateTVLs();

        // Get the value of the collateral token being deposited
        uint256 collateralTokenValue = renzoOracle.lookupTokenValue(_collateralToken, _amount);

        // Enforce TVL limit if set, 0 means the check is not enabled
        if (maxDepositTVL != 0 && totalTVL + collateralTokenValue > maxDepositTVL) {
            revert MaxTVLReached();
        }

        // Enforce individual token TVL limit if set, 0 means the check is not enabled
        if (collateralTokenTvlLimits[_collateralToken] != 0) {
            // Track the current token's TVL
            uint256 currentTokenTVL = 0;

            // For each OD, add up the token TVLs
            uint256 odLength = operatorDelegatorTokenTVLs.length;
            for (uint256 i = 0; i < odLength; ) {
                currentTokenTVL += operatorDelegatorTokenTVLs[i][tokenIndex];
                unchecked {
                    ++i;
                }
            }

            // Check if it is over the limit
            if (currentTokenTVL + collateralTokenValue > collateralTokenTvlLimits[_collateralToken])
                revert MaxTokenTVLReached();
        }

        // Determine which operator delegator to use
        IOperatorDelegator operatorDelegator = chooseOperatorDelegatorForDeposit(
            operatorDelegatorTVLs,
            totalTVL
        );

        // Transfer the collateral token to this address
        _collateralToken.safeTransferFrom(msg.sender, address(this), _amount);

        // Check the withdraw buffer and fill if below
Deposits of rebasing stETH can revert
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L491-L576
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L143-L154
https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/core/StrategyManager.sol#L323-L342


# Vulnerability details

Due to the rebasing mechanism in stETH, stETH transfers will frequently result in [1-2 wei less](https://docs.lido.fi/guides/lido-tokens-integration-guide/#1-2-wei-corner-case) than the expected transfer amount. 

In Renzo, depositing into the Restake Manager leads to a string of token transfers.

```
caller ---> RestakeManager ---> OperatorDelegator ---> Strategy
```

Every transfer will result in 1-2 wei less stETH for the recipient. However, each transfer expects to transfer the same amount. If one of the contracts (RestakeManager or OperatorDelegator) do not have extra stETH, the deposit will revert. This is the most likely outcome since RestakeManager and OperatorDelegator are not meant to hold token balances beyond the length of a function executing. Also, the Operator Delegator would [deposit its token balances](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L307-L309) back to Eigenlayer every time it completes a queued withdrawal, emptying its balance of stETH.


## Impact
This issue prevents users from depositing stETH into Renzo since it will frequently fail.

## Proof of Concept
Below are the relevant steps in a transaction that reproduces the issue:
1. Alice deposits 1000 stETH into Restake Manager. The 1000 stETH are [transferred](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L540) from Alice to Restake Manager. The Restake Manager only receives 998 stETH.
2. The Restake Manager then [deposits](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L562) 1000 stETH into the Operator Delegator. The Delegator [transfers](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L151) the 1000 stETH from the Restake Manager to itself. The transaction could revert here if the Restake Manager did not have 1-2 extra wei on top of the 998 stETH received from Alice. The Operator Delegator receives only 998 stETH.
3. The Operator Delegator [deposits](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L168) the 1000 stETH into the Eigenlayer Strategy Manager. The Strategy Manager then [transfers](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/core/StrategyManager.sol#L330) the 1000 stETH from the Delegator to itself. The transaction reverts here since the Operator Delegator only has 998 stETH because its balance of stETH was recently emptied with a [`completeQueuedWithdrawal()`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L3
Protocol is not compatible with rebasing tokens as it claims
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L592
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L491


# Vulnerability details

## Summary

According to Docs:
```solidity
Renzo allows users to deposit either ETH or LSTs (like stETH and wbETH), 
and in return, it mints an equivalent amount of ezETH, a reward-bearing token.
```
From this, the protocol accepts `stETH` as collateral that a user can deposit to mint `ezETH`. However, as a `rebasing token`, the protocol needs to integrate them into the system. But as it is now the system is definitely not compatible with such tokens.

## Proof of Concept
`deposit()`, `depositTokenRewardsFromProtocol()` and any other accounting related functions perform operations using inputed/recorded `amounts`. They don't query the `existing balance` of tokens before or after receiving/sending in order to properly account for tokens that shift balance over time (`rebasing`).

> [deposit()](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L491-L576)
```solidity
    function deposit(
        IERC20 _collateralToken,
        uint256 _amount,
        uint256 _referralId
    ) public nonReentrant notPaused {
        // Verify collateral token is in the list - call will revert if not found
        uint256 tokenIndex = getCollateralTokenIndex(_collateralToken);

        // @audit No balance query before transfer

        // Transfer the collateral token to this address
        _collateralToken.safeTransferFrom(msg.sender, address(this), _amount);

        //...

        // Mint the ezETH
        ezETH.mint(msg.sender, ezETHToMint);


        //...
    }
```

> [depositTokenRewardsFromProtocol()](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L647-L668)
```solidity
    function depositTokenRewardsFromProtocol(
        IERC20 _token,
        uint256 _amount
    ) external onlyDepositQueue {
        //...

        // @audit No balance query before transfer

        // Transfer the tokens to this address
        _token.safeTransferFrom(msg.sender, address(this), _amount);


        // Approve the tokens to the operator delegator
        _token.safeApprove(address(operatorDelegator), _amount);


        //...
    }
```

Depositing `rebasing` tokens into a system that does not support them can lead to several issues:
- Incorrect Balance Tracking: 
The system may not account for balance changes due to rebases, leading to incorrect display or accounting of the user's holdings.
- Value Discrepancies: 
The value of the user's deposit could be misrepresented, as the system might not update the user's balance in response to the token's supply changes.

## Impact
Impact - High - It simply won't work.

## Tools Used
Manual review

## Reco
The price of ezETH could be manipulated by the first staker.
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Oracle/RenzoOracle.sol#L121-L149


# Vulnerability details

## Impact
In `RestakeManager.sol`, the `calculateTVLs()` function includes the amount of Ether in the `DepositQueue` contract when calculating the TVL. This calculation is used to determine the price of ezETH, providing an opportunity for the protocol's first staker to manipulate the price of ezETH.

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L352

The first staker can manipulate the price significantly by depositing 1 wei of ETH to receive 1 ezETH and then transferring ETH into the DepositQueue contract as a donation. This can greatly increase the price per ezETH, potentially causing economic losses for subsequent stakers due to precision losses when the protocol calculates how much new ezETH to mint.

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Oracle/RenzoOracle.sol#L121-L149

## Proof of Concept
https://gist.github.com/StillFantastic/c4c9381ffd00d45189ec6f4386a4a64d

## Tools Used
Manual review

## Recommended Mitigation Steps
It is recommended to pre-mint a certain amount of ezETH at the time of protocol deployment to significantly increase the cost of potential attacks.


## Assessed type

Context