xanoutas/solidity-security-findings huggingface dataset:
Unfair Advantage through Oracle Front-Running in RestakeManager Contract
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/RestakeManager.sol#L491
https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/RestakeManager.sol#L507
https://github.com/code-423n4/2024-04-renzo/blob/1c7cc4e632564349b204b4b5e5f494c9b0bc631d/contracts/Oracle/RenzoOracle.sol#L71


# Vulnerability details

## Impact

The depegging of `stETH` from `ETH` creates a discrepancy between the actual value of `stETH ` and its expected parity with `ETH`. If an attacker `front-runs the oracle update`, they can mint `ezETH` at a stale price, obtaining an unfair advantage and potential profit. This action undermines the protocol's integrity and can lead to diminished user confidence and financial imbalances.

## Proof of Concept


Market Change: [stETH depegs](https://medium.com/huobi-research/steth-depegging-what-are-the-consequences-20b4b7327b0c). stETh down by 6% in terms of eth and the chailink deviation for `stEth/ETH` is `0.5%` which will trigger the price feed tom update but attacker frontrun the oracle and mint the at stale price for profit , reducing its value compared to ETH.

Oracle Update: Chainlink's deviation threshold for `stETH/ETH` is `0.5%`, triggering an impending price feed update.

Front-Running: The attacker observes the depegging event and anticipates the oracle update.

Minting at Stale Price: The attacker deposits `stETH` into the `RestakeManager::deposit` before the price feed updates.

Exploiting the Delay: The `RestakeManager` mints `ezETH` based on the outdated, more favorable price.

Oracle Update: The oracle adjusts the` stETH/ETH` price to reflect the current market value.

Profit Taking: The attacker sells the newly minted ezETH at the updated price, realizing a profit from the price difference.

[Similar Issue](https://github.com/code-423n4/2023-11-kelp-findings/issues/443).

## Tools Used
Manual Review

## Recommended Mitigation Steps

Implement a time-weighted average price (TWAP) oracle to mitigate front-running.

Slippage Checks: Add slippage checks to compare the expected mint amount with a threshold.



## Assessed type

Oracle
`OperatorDelegator::completeQueuedWithdrawal()` will not work if the max strategy tvl is reached
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L307-L309
https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/strategies/StrategyBaseTVLLimits.sol#L81


# Vulnerability details

## Impact

Protocol becomes insolvent as it becomes impossible to finalize withdrawals. It may be possible to finalize withdrawals if the withdrawal buffer is increased.

## Proof of Concept
`OperatorDelegator::completeQueuedWithdrawal()` finalizes withdrawals for strategy tokens and `ETH`. If the token balance of a given strategy exceeds the withdrawal buffer, it deposits to a strategy; however, the protocol may never finalize the withdrawal because when it tries to deposit, the max tvl of a strategy may be [reached](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/strategies/StrategyBaseTVLLimits.sol#L81) on this deposit attempt, reverting the transaction and not allowing any token or ETH to be withdrawn.

## Tools Used

Vscode

## Recommended Mitigation Steps

Check if the max tvl of the strategy is reached before depositing.


## Assessed type

Other
OperatorDelegator::completeQueuedWithdrawal() will never finalize if EigenLayer dewhitelists one of the strategies for deposit
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L307-L309
https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/core/StrategyManager.sol#L328


# Vulnerability details

## Impact

Protocol becomes insolvent as it becomes impossible to finalize withdrawals. It may be possible to finalize withdrawals if the withdrawal buffer is increased.

## Proof of Concept

`OperatorDelegator::completeQueuedWithdrawal()` finalizes withdrawals for strategy tokens and `ETH`. If the token balance of a given strategy exceeds the withdrawal buffer, it deposits to a strategy; however, the strategy may have been dewhitelisted to deposit in EigenLayer, in which case the transaction will revert and funds will not be finalized. [Here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/core/StrategyManager.sol#L328) is the whitelist modifier in EigenLayer.

## Tools Used

Vscode

## Recommended Mitigation Steps

Before depositing, check if the strategy is whitelisted for depositing. Withdrawals still work as the whitelist is only for depositing.





## Assessed type

Other
Withdrawals can fail due to deposits reverting in `completeQueuedWithdrawal()`
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L308
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L168
https://github.com/Layr-Labs/eigenlayer-contracts/blob/84ca899c715dbc577af1c8db5965188b061fe464/src/contracts/core/StrategyManager.sol#L332-L336
https://github.com/Layr-Labs/eigenlayer-contracts/blob/84ca899c715dbc577af1c8db5965188b061fe464/src/contracts/core/StrategyManager.sol#L297
https://github.com/Layr-Labs/eigenlayer-contracts/blob/0139d6213927c0a7812578899ddd3dda58051928/src/contracts/strategies/StrategyBaseTVLLimits.sol#L74-L75
https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/strategies/StrategyBaseTVLLimits.sol#L80-L81


# Vulnerability details

## Impact
The [`OperatorDelegator.completeQueuedWithdrawal()`](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L265) function serves to finalize a queued withdrawal consisting of different tokens, sending the withdrawn tokens to the `WithdrawQueue` contract up to the buffer amount, and depositing any excess tokens back into the corresponding EigenLayer strategy.

The issue is that the [call](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Delegation/OperatorDelegator.sol#L168) to `strategyManager.depositIntoStrategy()` used to deposit any amount of excess tokens back into EigenLayer may revert, which would cause the entire `completeQueuedWithdrawal()` transaction to revert.

There are a couple reasons why `depositIntoStrategy()` may revert:
1. In `StrategyManager.depositIntoStrategy()`, the number of shares returned by the strategy is [verified](https://github.com/Layr-Labs/eigenlayer-contracts/blob/84ca899c715dbc577af1c8db5965188b061fe464/src/contracts/core/StrategyManager.sol#L297) to be > 0. If the strategy mints shares at less than a 1:1 ratio, this can cause the transaction to revert, as the amount being deposited back may be as small as 1 wei. 
2. EigenLayer's stETH and wBETH [strategies](https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#strategies) both implement per-transaction and total deposit limits ([stETH](https://github.com/Layr-Labs/eigenlayer-contracts/blob/0139d6213927c0a7812578899ddd3dda58051928/src/contracts/strategies/StrategyBaseTVLLimits.sol#L74-L75), [wBETH](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/strategies/StrategyBaseTVLLimits.sol#L80-L81)):
	- if the deposit limit of a strategy has been reached, attempting to complete a queued withdrawal containing any amount over the `WithdrawQueue`'s buffer of the strategy's token will fail
	- if the amount being redeposited is large enough, it may surpass the per-deposit limit

This issue forces the `withdrawQueueAdmin` (which is a separate entity from the `nativeEthRestakeAdmin` affected) to set the buffer for the affected token high enough for the full amount being wit
removing collateral tokens and operator while having will cause loss of funds
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L244-L263
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L160-L184


# Vulnerability details

## Impact
lose of funds for users who deposit into protocol and holding ezETH if protocol remove collateral tokens and operators while collateral tokens is staking at eigen layer and operators having staked values .
## Proof of Concept
       ```function removeOperatorDelegator(
        IOperatorDelegator _operatorDelegatorToRemove
    ) external onlyRestakeManagerAdmin {
        // Remove it from the list
        uint256 odLength = operatorDelegators.length;
        //*@audit-issue -------->>> don't remove it when O.D have stake values 
        for (uint256 i = 0; i < odLength; ) {
            if (address(operatorDelegators[i]) == address(_operatorDelegatorToRemove)) {
                // Clear the allocation
                operatorDelegatorAllocations[_operatorDelegatorToRemove] = 0;
                emit OperatorDelegatorAllocationUpdated(_operatorDelegatorToRemove, 0);

                // Remove from list
                operatorDelegators[i] = operatorDelegators[operatorDelegators.length - 1];
                operatorDelegators.pop();
                emit OperatorDelegatorRemoved(_operatorDelegatorToRemove);
                return;
            }
            unchecked {
                ++i;
            }
        }

        // If the item was not found, throw an error
        revert NotFound();
    }```



       function removeCollateralToken(
        IERC20 _collateralTokenToRemove
    ) external onlyRestakeManagerAdmin {
        // Remove it from the list
        //*@audit-issue --------->>> don't remove it if collateral tokens are in staked value 
        uint256 tokenLength = collateralTokens.length;
        for (uint256 i = 0; i < tokenLength; ) {
            if (address(collateralTokens[i]) == address(_collateralTokenToRemove)) {
                //*@audit-info ------>>> check that after poping last index , previous last index went to where?
                collateralTokens[i] = collateralTokens[collateralTokens.length - 1];
                collateralTokens.pop();
                emit CollateralTokenRemoved(_collateralTokenToRemove);
                return;
            }
            unchecked {
                ++i;
            }
        }

        // If the item was not found, throw an error
        revert NotFound();
    }






Both remove functions for collateral tokens and operators have no check for that collateral tokens which gonna removed has staked values by each operators at eigen layer and tokens in the withdrawal queue or NOT . IF removed collateral tokens have values at protocol, it gonna make negative impact at calculating TVL.
When calculating TVL , each colla
Withdrawals and Claims are meant to be pausable, but it is not possible in practice
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details


## Impact

Administrator is not able to pause users' withdrawals and claims as expected.

## Proof of Concept

The `WithdrawQueue` contract inherits `PausableUpgradable` to provide pausing capabilities to the administrator on users' withdrawals and claims.
The contract correctly exposes the `_pause()` and `_unpause()` internal functions through access restricted external functions.

However, none of the functions implement the `whenNotPaused` modifier.
This is especially problematic for user-accessible functions: `withdraw` and `claim`.

```solidity
// @POC: WithdrawQueue inherits PausableUpgradeable
contract WithdrawQueue is
    Initializable,
    PausableUpgradeable,
    ReentrancyGuardUpgradeable,
    WithdrawQueueStorageV1
{
    // ...

    function initialize(
        IRoleManager _roleManager,
        IRestakeManager _restakeManager,
        IEzEthToken _ezETH,
        IRenzoOracle _renzoOracle,
        uint256 _coolDownPeriod,
        TokenWithdrawBuffer[] calldata _withdrawalBufferTarget
    ) external initializer {

        // ...

        __Pausable_init();

        // ...
    }

    function pause() external onlyWithdrawQueueAdmin {// @POC: pause is accessible to admin
        _pause();
    }

    function unpause() external onlyWithdrawQueueAdmin {// @POC: unpause is accessible to admin
        _unpause();
    }

    function withdraw(uint256 _amount, address _assetOut) external nonReentrant {// @POC: pause has no impact
        // ...
    }

    function claim(uint256 withdrawRequestIndex) external nonReentrant {// @POC: pause has no impact
        // ...
    }
}
```

## Tools Used
Manual review

## Recommended Mitigation Steps

Consider implementing `whenNotPaused` modifier on `claim` and `withdraw` functions.

The following patch implements such a fix.

```diff
diff --git a/contracts/Withdraw/WithdrawQueue.sol b/contracts/Withdraw/WithdrawQueue.sol
index 786238c..91ec77b 100644
--- a/contracts/Withdraw/WithdrawQueue.sol
+++ b/contracts/Withdraw/WithdrawQueue.sol
@@ -203,7 +203,7 @@ contract WithdrawQueue is
      * @param   _amount  amount of ezETH to withdraw
      * @param   _assetOut  output token to receive on claim
      */
-    function withdraw(uint256 _amount, address _assetOut) external nonReentrant {
+    function withdraw(uint256 _amount, address _assetOut) whenNotPaused external nonReentrant {
         // check for 0 values
         if (_amount == 0 || _assetOut == address(0)) revert InvalidZeroInput();
 
@@ -276,7 +276,7 @@ contract WithdrawQueue is
      * @dev     revert on claim before cooldown period
      * @param   withdrawRequestIndex  Index of the Withdraw Request user wants to claim
Withdrawals can't be paused as stated in the readme
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L13
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L139-L149


# Vulnerability details

## Impact
Withdraw queue is a pausable contract and also it has `pause` and `unpause` functions indicating that some functions should be pausable. Also, in Renzo C4 audit page it says:
"DEPOSIT_WITHDRAW_PAUSER allows the admin to pause/unpause deposits and withdraws" so it is intended that there will be a role such that can pause withdrawals which is consistent with pause/unpause and `Pausable` contract inheritance in withdraw queue. However, pausable modifier is not used in any of the functions, especially in withdraw function in `WithdrawQueue` contract. 

## Proof of Concept
As we can see the withdraw function has no `whenNotPaused` modifier which means pausing or unpausing has no impact in contract
```solidity
function withdraw(uint256 _amount, address _assetOut) external nonReentrant {
        // check for 0 values
        if (_amount == 0 || _assetOut == address(0)) revert InvalidZeroInput();

        // check if provided assetOut is supported
        if (withdrawalBufferTarget[_assetOut] == 0) revert UnsupportedWithdrawAsset();

        // transfer ezETH tokens to this address
        IERC20(address(ezETH)).safeTransferFrom(msg.sender, address(this), _amount);

        // calculate totalTVL
        (, , uint256 totalTVL) = restakeManager.calculateTVLs();

        // 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
        withdrawRequests[msg.sender].push(
            WithdrawRequest(
                _assetOut,
                withdrawRequestNonce,
                amountToRedeem,
                _amount,
                block.timestamp
            )
        );

        // add redeem amount to claimReserve of claim asset
        claimReserve[_assetOut] += amountToRedeem;

        emit WithdrawRequestCreated(
            withdrawRequestNonce,
            msg.sender,
            _assetOut,
            amountToRedeem,
            _amount,
            withdrawRequests[msg.sender].length - 1
The `WithdrawQueue` contract cannot be paused because it does not use `whenPaused` modifier in any of its functions
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The function calls in the `WithdrawQueue` contract cannot be paused, thus in case of sudden emergencies, pausing measures may not be taken.

## Proof of Concept
The `WithdrawQueue` contract inherits the `PausableUpgradeable` contract and implements the `pause` and `unpause` functions, allowing the `WithdrawQueueAdmin` role to modify the `_paused` variable.

[WithdrawQueue.sol#L11-L15](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L11-L15)

```solidity
contract WithdrawQueue is
    Initializable,
    PausableUpgradeable,
    ReentrancyGuardUpgradeable,
    WithdrawQueueStorageV1
```

[WithdrawQueue.sol#L135-L149](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L135-L149)

```solidity
    /**
     * @notice  Pause the contract
     * @dev     Permissioned call (onlyWithdrawQueueAdmin)
     */
    function pause() external onlyWithdrawQueueAdmin {
        _pause();
    }

    /**
     * @notice  Unpause the contract
     * @dev     Permissioned call (onlyWithdrawQueueAdmin)
     */
    function unpause() external onlyWithdrawQueueAdmin {
        _unpause();
    }
```

Since the `WithdrawQueue` contract inherits the `PausableUpgradeable` contract, it should, by design, have the ability to be paused.

`whenNotPaused` and `whenPaused` modifiers should be used to control whether functions can be called when the contract is paused.

[PausableUpgradeable.sol#L42-L64](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.9.3/contracts/security/PausableUpgradeable.sol#L42-L64)
```solidity
    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }
```

However, in the `WithdrawQueue` contract, none of the functions have a `whenNotPaused` or `whenPaused` modifier to control when they can be executed. This means that all functions can be called at any time, regardless of the paused state of the contract.


## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Review all the functions in the `WithdrawQueue` contract and then:
1. Add the `whenNotPaused` modifier to the functions requiring pause functionality.
2. Add the `whenPaused` modifier to make a function callable only when the contract is paused.


## Assessed type

Other
Incomplete implementation of pause functionality inside `WithdrawQueue.sol` results in it having no effect on the contract's methods
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description
The `WithdrawQueue.sol` contract [inherits](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L7) OpenZeppelin's `PausableUpgradeable.sol`. It correctly calls [__Pausable_init()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L81) inside `initialize()` and implements [pause()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L139) and [unpause()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L147) too **but misses to apply** the [relevant modifiers](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/utils/PausableUpgradeable.sol#L72-L87) `whenNotPaused` and `whenPaused` on any of the functions as prescribed clearly by OZ [here](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/utils/PausableUpgradeable.sol#L15-L16): 
```text
            /**
            * @dev Contract module which allows children to implement an emergency stop
            * mechanism that can be triggered by an authorized account.
            *
            * This module is used through inheritance. It will make available the
            * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
@------>    * the functions of your contract. Note that they will not be pausable by
@------>    * simply including this module, only once the modifiers are put in place.
            */
 ```
Thus pausing or unpausing the contract has no effect at all on any user flows, for example [withdraw()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L206) or [claim()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L279). 

## Impact
Pausing a contract is typically a special/emergency action taken by governance to remedy issues. This makes it important that certain user actions are suspended when the contract is paused. In the current scenario, it makes sense to not allow users to claim funds when the contract is paused (this could be applicable to `withdraw()` too). Hence the expected flow of events should be -
- The `onlyWithdrawQueueAdmin` role calls [pause()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L139) to pause the contract.
- All users should be unable to call [claim()](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L279) now.

However currently, all users are able to call `claim()` (or `withdraw()`). This could result in the users exiting with funds linked to suspicious activities which the protocol governance intended to block. 

## Tools Used
Manual review

## Recommended Mitigation Steps
Add the modifiers