xanoutas/solidity-security-findings huggingface dataset:
Eigen rewards can be sandwiched to earn more ezETH
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/RenzoOracle.sol#L123-L149
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/RenzoOracle.sol#L152-L164


# Vulnerability details

## Impact

Native rewards initiated from `OperatorDelegator::verifyAndProcessWithdrawals` and `OperatorDelegator::withdrawNonBeaconChainETHBalanceWei` can be sandwiched from users to steal assets when the exchange rate of `ezETH` increases.

## Proof of Concept

The protocol handles ETH rewards by sending them to the `OperatorDelegator’s`. There are at least 3 flows that end up sending funds there:

1. When the function `withdrawNonBeaconChainETHBalanceWei` is called to scrape non-beacon chain ETH from an Eigenpod.
2. When a validator receives rewards via partial withdrawals after the function [`EigenPod::verifyAndProcessWithdrawals`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/v0.2.1-goerli-m2/src/contracts/pods/EigenPod.sol#L232) is called.
3. When a validator exists and has more than 32ETH the excess will be sent as rewards after the function [`EigenPod::verifyAndProcessWithdrawals`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/v0.2.1-goerli-m2/src/contracts/pods/EigenPod.sol#L232) is called.

All of these 3 flows end up queuing a withdrawal to the `OperatorDelegator`. After a delay the rewards can claimed by calling the permissionless function [`DelayedWithdrawalRouter::claimDelayedWithdrawals`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/v0.2.1-goerli-m2/src/contracts/pods/DelayedWithdrawalRouter.sol#L99) this call will instantly increase the `totalTVL` of the protocol.

Following the mint and redeem flows in `Renzo` we can see that the exchange rate of `ezETH` is dependent on the `totalTVL`, which will be increased when rewards are sent to the system:

- Minting:

[RenzoOracle.sol#L123-L149](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Oracle/RenzoOracle.sol#L123-L149)

```solidity
function calculateMintAmount(
    uint256 _currentValueInProtocol,
    uint256 _newValueAdded,
    uint256 _existingEzETHSupply
) external pure returns (uint256) {
    // For first mint, just return the new value added.
    // Checking both current value and existing supply to guard against gaming the initial mint
    if (_currentValueInProtocol == 0 || _existingEzETHSupply == 0) {
        return _newValueAdded; // value is priced in base units, so divide by scale factor
    }

    // Calculate the percentage of value after the deposit
    uint256 inflationPercentaage = (SCALE_FACTOR * _newValueAdded) /
        (_currentValueInProtocol + _newValueAdded);

    // Calculate the new supply
    uint256 newEzETHSupply = (_existingEzETHSupply * SCALE_FACTOR) /
        (SCALE_FACTOR - inflationPercentaage);

    // Subtract the old supply from the new supply to get the amount to mint
    uint256 mintAmount = newEzETHSupply - _existingEzETHSupply;

    // Sanity check
    if (mintAmount
Attackers can steal ETH rewards by front-running `DelayedWithdrawalRouter.claimDelayedWithdrawals()`.
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/Layr-Labs/eigenlayer-contracts/blob/v0.2.1-goerli-m2/src/contracts/pods/DelayedWithdrawalRouter.sol#L99-L104


# Vulnerability details

## Impact

Attackers can take a part of rewards by front-running the call to `DelayedWithdrawalRouter.claimDelayedWithdrawals()` of EigenLayer.

## Proof of Concept

Rewards that are generated in EigenLayer are sent to `DelayedWithdrawalRouter` contract of EigenLayer first. After a delay, the rewards can be claimed by calling the permissionless function `DelayedWithdrawalRouter.claimDelayedWithdrawals()` of EigenLayer. This call will instantly increase the TVL of the protocol.

An attacker can take advantage of this to steal a part of the rewards. Let's consider following scenario:
1. Initial sate:
    - Total supply of `ezETH`: `1,000e18`,
    - TVL: `1,000 ETH`,
    - Exchange rate: 1.
2. The protocol is about to call the `DelayedWithdrawalRouter.claimDelayedWithdrawals()` function of EigenLayer and receive `50 ETH` as the reward.
3. The attacker deposits `9,000 ETH` by front-running. Then the state becomes:
    - Total supply of `ezETH`: `10,000e18`,
    - TVL: `10,000 ETH`,
    - Exchange rate: 1,
    - The attacker's `ezETH` balance: `9,000e18`.
4. After the protocol's transaction, the state becoems:
    - Total supply of `ezETH`: `10,000e18`,
    - TVL: `10,500 ETH`,
    - Exchange rate: 1.05,
    - The attacker's `ezETH` balance: `9,000e18`.

Finally, the attacker's asset becomes `1.05 * 9,000 = 9450 ETH`, results in taking `45 ETH`, that is the `90%` of protocol's reward.

## Tools Used

Manual review

## Recommended Mitigation Steps

Recommend not send `DepositQueue` contract all the reward at once. It would be better distribute the reward following linear function on time.


## Assessed type

Other
Reward ETH can be exploited by front runner 
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Rewards/RewardHandler.sol#L58-L70
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Deposits/DepositQueue.sol#L163-L183


# Vulnerability details

## Impact
Reward distribution from eigen layer can be exploited with front running 

## Proof of Concept
Protocol implement that all rewards from eigen layer send forward to deposit queue to fill the withdrawal buffer deficit and restake again at eigen layer .Cause of sending native eth in deposit queue , total TVL will be increased .But ezETH was not minted . ezETH value is calculated based on total TVL / total supply of ezETH.This means valuation of ezETH is increased due to reward distribution from eigen layer .However malicious user can see that reward distribution transaction in the mempool .So before reward transaction is executed , malicious user front run and deposit into restake manager to get back ezETH before it's price is increased.After reward distribution trasaction is executed , ezETH will be increased .Then malicious user burn the ezETH which become more valuable to get back ETH.


POC
1.Reward handler send reward to deposit queue
2.malicious user see that in mempool and mint ezETH with front running
3.ezETH value is increased due to rewards
4.then malicious user burn ezETH and take profits 


Malicious profits from sandwiching the reward distribution is directly proportional to rewards send from reward handler .


## Tools Used
manual view
## Recommended Mitigation Steps
make unprofitable for front runner with just  minting and burning 


## Assessed type

MEV
ETH rewards can be partially stolen by sandwiching `forwardRewards`
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Rewards/RewardHandler.sol#L52-L54
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Rewards/RewardHandler.sol#L58-L60
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Rewards/RewardHandler.sol#L62-L70


# Vulnerability details

## Vulnerability Detail
The protocol's accrued rewards are sent to the `RewardHandler` contract.

This will end up calling the `RewardHandler::receive()` function which in turn calls `_forwardETH()`, which forwards the ETH to the DepositQueue.

This will instantly increase the total TVL of the protocol, since the balance of the deposit queue is included in [`RestakeManager::calculateTVL()`](https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/RestakeManager.sol#L352)

An attacker can take advantage of this to partially steal the ETH rewards using the following steps:

1. Attacker identifies an incoming rewards Tx that will increase the protocol's TVL
2. Attacker frontruns the rewards Tx by minting ezETH at the current TVL level
3. Rewards Tx goes through and increases the TVL of the protocol
4. Attacker backruns the rewards Tx by redeeming all their minted ezETH at the higher exchange rate (since TVL increased), obtaining ether (or LSTs) at a confirmed profit

Consensus layer rewards will be credited to the `RewardHandler` contract without a transaction, so the `NativeEthRestakeAdmin` will have to manually call `forwardRewards()` to send the ETH to the DepositQueue. This call by the `NativeEthRestakeAdmin` will increase the protocol's TVL, therefore it is also vulnerable to the previously described sandwich attack.

## Impact
A portion of the ETH rewards can be stolen from honest users with 0 risk. The attacker makes profit while reducing the yield of honest stakers.

This significantly reduces the incentive for honest users to stake their funds for continuous periods since any non-participant can simply front-run the forwarding of rewards from `RewardHandler` to `DepositQueue` and steal rewards.

## Proof of Concept
Refer to Vulnerability Detail

## Tools Used
Manual Review

## Recommended Mitigation Steps
This does not have a simple solution. It will require a redesign of the reward distribution.


## Assessed type

Other
Attacker can sandwich ETH received by the `OperatorDelegator` for a profit
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/Layr-Labs/eigenlayer-contracts/blob/ef2ea4a7459884f381057aa9bbcd29c7148cfb63/src/contracts/pods/EigenPod.sol#L235-L280
https://github.com/Layr-Labs/eigenlayer-contracts/blob/ef2ea4a7459884f381057aa9bbcd29c7148cfb63/src/contracts/pods/EigenPod.sol#L750-L752
https://github.com/Layr-Labs/eigenlayer-contracts/blob/ef2ea4a7459884f381057aa9bbcd29c7148cfb63/src/contracts/pods/DelayedWithdrawalRouter.sol#L100-L105
https://github.com/Layr-Labs/eigenlayer-contracts/blob/ef2ea4a7459884f381057aa9bbcd29c7148cfb63/src/contracts/pods/DelayedWithdrawalRouter.sol#L190-L220
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Delegation/OperatorDelegator.sol#L501-L525


# Vulnerability details

## Vulnerability Detail
When `EigenPod.verifyAndProcessWithdrawals` is called, the funds are sent to the `DelayedWithdrawalRouter` and attributed to the `OperatorDelegator`. This can be seen in the below two code snippets:


``` solidity
function verifyAndProcessWithdrawals(
    ...
    ...
    _sendETH_AsDelayedWithdrawal(podOwner, withdrawalSummary.amountToSendGwei * GWEI_TO_WEI);
    ...
```


``` solidity
function _sendETH_AsDelayedWithdrawal(address recipient, uint256 amountWei) internal {
        delayedWithdrawalRouter.createDelayedWithdrawal{value: amountWei}(podOwner, recipient);
    }
```
When calling `_sendETH_AsDelayedWithdrawal()`, the `recipient` is set to `podOwner` which is the `OperatorDelegator` contract.


Now to redeem the funds (to the recipient), any user can call the following permissionless function in EigenLayer `DelayedWithdrawalRouter::claimDelayedWithdrawals`

<Details>
<summary>claimDelayedWithdrawals</summary>

``` solidity
/**
     * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period.
     * @param recipient The address to claim delayedWithdrawals for.
     * @param maxNumberOfDelayedWithdrawalsToClaim Used to limit the maximum number of delayedWithdrawals to loop through claiming.
     * @dev
     *      WARNING: Note that the caller of this function cannot control where the funds are sent, but they can control when the
     *              funds are sent once the withdrawal becomes claimable.
     */
    function claimDelayedWithdrawals(
        address recipient,
        uint256 maxNumberOfDelayedWithdrawalsToClaim
    ) external nonReentrant onlyWhenNotPaused(PAUSED_DELAYED_WITHDRAWAL_CLAIMS) {
        _claimDelayedWithdrawals(recipient, maxNumberOfDelayedWithdrawalsToClaim);
    }
```

</Details>


This will end up sending ETH to the set `recipient` through the internal function `_claimDelayedWithdrawals`

<Details>
<summary>_claimDelayedWithdrawals</summary>

``` solidity
function _claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) internal {
        ...
        ...
        // actually send the ETH
        if (amountToSend != 0) {
Attacker can profit from sandwiching protocol reward deposits
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L506-L523
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Deposits/DepositQueue.sol#L163-L183


# Vulnerability details

The [`DepositQueue`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Deposits/DepositQueue.sol) contract receives ETH rewards from various sources, such as execution layer rewards and MEV from native staking, likely via the [`OperatorDelegator`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol) contracts. These rewards are immediately sent to either the deposit queue or withdrawal queue by the [`receive()`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Deposits/DepositQueue.sol#L163) function, after deducting a protocol fee if configured.

The core issue lies in the fact that the ETH rewards sent to the deposit or withdrawal queue are counted towards the protocol's TVL. Consequently, when a reward is received, it instantaneously increases the exchange rate between ETH and ezETH.

An attacker can exploit this vulnerability by sandwiching the reward transaction. The attack would involve first minting ezETH for a given amount of ETH at the current exchange rate. Subsequently, after the reward is received and the exchange rate increases, they could immediately withdraw their ezETH for ETH at the higher rate, thereby profiting from the difference.

The root cause of this vulnerability stems from the fact that external rewards directly affect the ETH/ezETH exchange rate before being distributed to ezETH holders.

## Impact
An attacker can extract value from the protocol by sandwiching reward transactions. This would be at the expense of other ezETH holders, as the attacker mints ezETH at a lower rate and withdraws at a higher rate. 

The attacker's profit would be proportional to the amount of the reward relative to the TVL. The higher the reward as a percentage of TVL, the more value they could extract.

## Proof of Concept
Here is a step-by-step illustration of how an attacker could exploit this vulnerability:

1. Assume the ETH/ezETH exchange rate is 1:1 and the TVL is 1000 ETH. 
   
2. Attacker mints 100 ezETH by depositing 100 ETH at the 1:1 rate.
   1. The function `calculateMintAmount()` will be called with the following arguments:
      1. `_currentValueInProtocol` -> `1000e18`
      2. `_newValueAdded` -> `100e18`
      3. `_existingEzETHSupply` -> `1000e18`
      4. It will return `100 ETH (100e18)`, which will be the amount of `ezETH` that the attacker will be minted.
   
3. The totalTVL will be increased by `100 ETH`. So it'll be `1100 ETH` now.

4. The protocol receives a `100 ETH` reward from EigenLayer. This increases the TVL to `1200 ETH`.

5. The new ETH/ezETH exchange rate is 1.2:1.1. For 1 ezETH you can now withdraw ~1.09 ETH.

6. Attacker immediately withdraws their 100 ezETH and receives 109 ETH due to th
Withdrawal queue balance included in TVL leads to inaccurate mint and redeem rates
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L316-L321
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L565-L569
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L241-L253


# Vulnerability details

## Impact
The [`RestakeManager.calculateTVLs()`](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L274) function serves to calculate the protocol's TVL, which is used together with the total ezETH supply to determine the mint and redeem rates. This function iterates through all `OperatorDelegator` contracts and sums up the value of their staked collateral tokens and ETH, and adds the balance of collateral tokens held in the `WithdrawQueue` contract.

However, when a user queues a withdrawal request in [`WithdrawQueue.withdraw()`](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L206), the corresponding `ezETH` amount is not immediately burned, but rather locked in the `WithdrawQueue` until the user claims the withdrawal after the cooldown period. The collateral amount to be redeemed is [calculated](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L229-L232) based on the `ezETH` to collateral ratio at the time of queuing the withdrawal.

So while the `ezETH` is still included in the total supply and TVL calculation, it is essentially out of circulation as it acts as a liability that the protocol must satisfy at the predetermined rate. Including this "locked" `ezETH` in the TVL leads to inaccurate mint and redeem rates as calculated by [`RenzoOracle.calculateMintAmount()`](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Oracle/RenzoOracle.sol#L123) and [`calculateRedeemAmount()`](https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Oracle/RenzoOracle.sol#L152) respectively.

In other words, since the "burn + remove collateral" operation is calculated immediately but executed later, it can be executed at a time when the calculation would be different, so initiated withdrawals represent a "promise of burning + removing collateral" that should be taken into account in the TVL calculation.

In the worst case, this could lead to a situation where the protocol becomes undercollateralized and unable to satisfy all withdrawal requests at the promised rates. Let's illustrate the issue an example.

## Proof of Concept
The below Foundry POC demonstrates the issue by performing the following sequence of steps:
1. Alice and Bob deposit 100 stETH and 100 cbETH respectively, receiving 100 ezETH each.
2. Alice init
allowing any range of output tokens could put pressure on some tokens than others
Audit: 2024-04-renzo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L206-L263


# Vulnerability details

## Impact
According to the `withdraw` token having enough `ezETH` can allow a user to choose the output token to receive his equivalent value. This brings up an issue

A case of some tokens like native ETH having more withdrawal pressure than say stETH withdrawals. This could lead to serious DOS on withdrawals once availableToWithdraw for ETH gets lower than the withdrawal amount or is zero while a lot of value is stuck in other tokens like stETH.

A large base of users could decide to only withdraw in ETH for example which could be due to preference, issue on pegged token like depegging issues, and others.

## Proof of Concept
Any approved token is accepted for withdrawal as seen in

```solidity
function withdraw(uint256 _amount, address _assetOut) external nonReentrant {
...
// check if provided assetOut is supported
        if (withdrawalBufferTarget[_assetOut] == 0) revert UnsupportedWithdrawAsset();
```

and we see a check for withdrawal redeemable at
```solidity
// revert if amount to redeem is greater than withdrawBufferTarget
        if (amountToRedeem > getAvailableToWithdraw(_assetOut)) revert NotEnoughWithdrawBuffer();
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
- Track tokens deposited by users and use that determine the token withdrawable by a user.
- Implement swaps between approved tokens based on the tokens with more availableToWithdraw value to reduce withdrawal pressure on tokens across boards


## Assessed type

Other
Users can mint `xEZETH` for less than `1 to 1` ratio
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Bridge/L2/xRenzoDeposit.sol#L204


# Vulnerability details

## Description
On the supported L2's, a user can call one of the two `deposit` functions to deposit either `native ETH` or a supported `ERC20` token to mint `xezETH`.

```javascript
    function deposit(
        uint256 _amountIn,
        uint256 _minOut,
        uint256 _deadline
    ) external nonReentrant returns (uint256) {
        //.. ommitted code
    }

    function depositETH(
        uint256 _minOut,
        uint256 _deadline
    ) external payable nonReentrant returns (uint256) {
        //.. ommitted code
    }
```

During the `deposit` flow, `getMintRate()` gets called:
```javascript
    function _deposit(
        uint256 _amountIn,
        uint256 _minOut,
        uint256 _deadline
    ) internal returns (uint256) {
        // Fetch price and timestamp of ezETH from the configured price feed
        (uint256 _lastPrice, uint256 _lastPriceTimestamp) = getMintRate();
    }
```

The problem here is a timing issue inside `getMintRate()`:
```javascript
    function getMintRate() public view returns (uint256, uint256) {
        // revert if PriceFeedNotAvailable
        if (receiver == address(0) && address(oracle) == address(0)) revert PriceFeedNotAvailable();
        if (address(oracle) != address(0)) {
->          (uint256 oraclePrice, uint256 oracleTimestamp) = oracle.getMintRate();
->          return
->              oracleTimestamp > lastPriceTimestamp
->                  ? (oraclePrice, oracleTimestamp)
->                  : (lastPrice, lastPriceTimestamp);
        } else {
            return (lastPrice, lastPriceTimestamp);
        }
    }
```

`lastPrice` and `lastPriceTimestamp` can be updated by a `CCIP call` from `L1` inside `_updatePrice`:
```javascript
    // Call made by `receiver`
    function updatePrice(uint256 _price, uint256 _timestamp) external override {
        if (msg.sender != receiver) revert InvalidSender(receiver, msg.sender);
        _updatePrice(_price, _timestamp);
    }
    
    // Call made by owner
    function updatePriceByOwner(uint256 price) external onlyOwner {
        return _updatePrice(price, block.timestamp);
    }

    function _updatePrice(uint256 _price, uint256 _timestamp) internal {
        //.. ommitted code
        lastPrice = _price;
        lastPriceTimestamp = _timestamp;
        //.. ommitted code
    }
```

When `lastPrice` gets updated, it does not get checked if the exchange rate is less than `1 ether`, as done in `RenzoOracleL2.getMintRate()`:
```javascript
    function getMintRate() public view returns (uint256, uint256) {
        (, int256 price, , uint256 timestamp, ) = oracle.latestRoundData();
        if (timestamp < block.timestamp - MAX_TIME_WINDOW) revert OraclePriceExpired();
        // scale the price to have 18 decimals
        uint256 _scaledPrice = (uint256(price)) * 10 ** (18 - oracle.decimals())