xanoutas/solidity-security-findings huggingface dataset:
`RestakeManager.sol::chooseOperatorDelegatorForDeposit` underestimate the threshhold calculation
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
This calculation error can lead to no operator delegators being selected under normal conditions because the calculated threshold is unrealistically low. This could disrupt the normal operation of depositing into the protocol.

The second issue is that the system will default to the first operator delegator for all deposits when all do not meet the threshold. This is unfair to the other delegators. This could lead to the first delegator receiving higher deposits when all other delegators do not meet the threshold.

This is a 2 in 1 issue caused by the double basis point division.

## Proof of Concept
    `function chooseOperatorDelegatorForDeposit(
        uint256[] memory tvls,
        uint256 totalTVL
    ) public view returns (IOperatorDelegator) {
        // Ensure OperatorDelegator list is not empty
        if (operatorDelegators.length == 0) revert NotFound();


        // If there is only one operator delegator, return it
        if (operatorDelegators.length == 1) {
            return operatorDelegators[0];
        }


        // Otherwise, find the operator delegator with TVL below the threshold
        uint256 tvlLength = tvls.length;
        for (uint256 i = 0; i < tvlLength; ) {
            if (
                tvls[i] <
                (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
                    BASIS_POINTS /
                    BASIS_POINTS
            ) {
                return operatorDelegators[i];
            }


            unchecked {
                ++i;
            }
        }


        // Default to the first operator delegator
        return operatorDelegators[0];
    }`


Suppose:
BASIS_POINTS is 10,000
totalTVL is 1,000,000 units.
operatorDelegatorAllocations[operatorDelegators[i]] is 1,000 basis points (or 10%).
The expected calculation for a 10% allocation should be:

1,000 (basis points for 10%) * 1,000,000 (totalTVL) / 10,000 = 100,000

This result represents 10% of the `totalTVL`, which is the intended threshold.

However, with the double division:

1,000 * 1,000,000 / 10,000 / 10,000 = 10



## Tools Used
Manual Review

## Recommended Mitigation Steps
`+` (operatorDelegatorAllocations[operatorDelegators[i]] totalTVL) / BASIS_POINTS
Make the allocation random and not just default to the first one in the operator delegator list to make it fair.





## Assessed type

Context
Wrong Operator Delegator Choice For Deposit and Withdrawal Due to Implementation Error
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L378-L381
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L420-L424


# Vulnerability details

## Impact
1. Break of Protocol Functionality as only the first operator Delegator would always be chosen for Deposit by the chooseOperatorDelegatorForDeposit(...) function in Restake Manager contract
2. Break of Protocol Functionality as operator Delegator with tvl below proper threshold would be chosen for Withdrawal by the chooseOperatorDelegatorForWithdraw(...) function as against Protocol expectation at [L416](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L416) of the Restake Manager contract
## Proof of Concept
Due to the Similarity in Error causing both impacts, for the purpose of this report major focus would be given to the error in first impact.
```solidity
 function depositTokenRewardsFromProtocol(
        IERC20 _token,
        uint256 _amount
    ) external onlyDepositQueue {
        // Get the TVLs for each operator delegator and the total TVL
>>>        (, uint256[] memory operatorDelegatorTVLs, uint256 totalTVL) = calculateTVLs();

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

        // 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);

        // Deposit the tokens into EigenLayer
        operatorDelegator.deposit(_token, _amount);
    }
```
The depositTokenRewardsFromProtocol(...) function above from the RestakeManager contract shows how Token Reward is Deposited from the Protocol, as noted from the pointers above the point of interest is how the Operator Delegator to be used is determined from the list operators, each operators tvls and the cumulative total tvl is used to make a choice of of one operator.
The break down of how an operator tvl is calculated at [L307](https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/RestakeManager.sol#L307) of the calculateTVLs(...) function shows that, an Operator tvl is based on an Operator actual token values in the contract while totalTVL is the the total.
The problem is in the chooseOperatorDelegatorForDeposit(...) functiion code below that shows how a choice is made.
```solidity
 function chooseOperatorDelegatorForDeposit(
        uint256[] memory tvls,
        uint256 totalTVL
    ) public view returns (IOperatorDelegator) {
        // Ensure OperatorDelegator list is not empty
        if (operatorDelegators.length == 0) revert NotFound();

        // If there is only one operator delegator, return it
        if (operatorDelegators.length == 1) {
            return operatorDelegator
Incorrect OperatorDelegator Allocation Calculation while choosing operator delegator in `RestakeManager` contract
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

The `chooseOperatorDelegatorForDeposit` and `chooseOperatorDelegatorForWithdraw` functions in `RestakeManager` incorrectly calculates the allocation threshold by dividing by `BASIS_POINTS` twice, which should only be divided once.

## Impact
This error could lead to the consistent selection of the first OperatorDelegator in the list, regardless of their actual TVL compared to their target allocation. This could result in an imbalance in the distribution of deposits and withdrawals across OperatorDelegators.

## Proof of Concept
The issue is located in the `chooseOperatorDelegatorForDeposit` and `chooseOperatorDelegatorForWithdraw` functions within the `RestakeManager` contract. The relevant code snippet is:

```solidity
    if (
        tvls[i] <
@-->    (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
            BASIS_POINTS /
            BASIS_POINTS
    ) {
        return operatorDelegators[i];
    }
```
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L377C13-L384C14

```solidity
    if (
        operatorDelegatorTVLs[i] >
@-->    (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
            BASIS_POINTS /
            BASIS_POINTS &&
        operatorDelegatorTokenTVLs[i][tokenIndex] >= ezETHValue
    ) {
        return operatorDelegators[i];
    }
```
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L419C13-L427C14

A numerical example is provided to illustrate the issue with the current logic:

- `BASIS_POINTS` is defined as 10000.
- Alice's OperatorDelegator has an allocation of 3000 basis points (30%).
- Bob's OperatorDelegator has an allocation of 7000 basis points (70%).
- The total TVL (`totalTVL`) is 1000 ETH.
- Alice's current TVL (`tvls[Alice]`) is 200 ETH.
- Bob's current TVL (`tvls[Bob]`) is 800 ETH.

The correct allocation threshold calculation for Alice should be:
- (3000 * 1000) / 10000 = 300 ETH

However, with the current logic, the calculation erroneously divides by `BASIS_POINTS` twice:
- (3000 * 1000) / 10000 / 10000 = 0.03 ETH

This incorrect calculation would always result in Alice's TVL being below the threshold, leading to her constant selection for new deposits and withdrawals, regardless of the actual TVLs.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Modify the calculation to divide by BASIS_POINTS only once to correctly convert the allocation basis points to a proportion. This ensures that the OperatorDelegator's TVL is compared against the correct allocation threshold.


## Assessed type

Math
Incorrect Handling of Basis Points in Allocation Calculations
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Root of the bug and details 

The bug is arise from the incorrect division by BASIS_POINTS twice in the allocation calculations within the functions chooseOperatorDelegatorForDeposit and chooseOperatorDelegatorForWithdraw. 
This incorrect handling leads to significantly smaller values than intended for the threshold comparisons.
here in function chooseOperatorDelegatorForDeposit
```solidity 
 if (
                tvls[i] <
                (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
                    BASIS_POINTS /
                    BASIS_POINTS
            ) {
                return operatorDelegators[i];
            }

```
and in the function chooseOperatorDelegatorForWithdraw
```solidity
 if (
                operatorDelegatorTVLs[i] >
                (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
                    BASIS_POINTS /
                    BASIS_POINTS &&
                operatorDelegatorTokenTVLs[i][tokenIndex] >= ezETHValue
            ) {
                return operatorDelegators[i];
            }
```
in this functions the calculations of the threshold for TVL comparison is incorrect due to double division by BASIS_POINTS and this incorrect calculation significantly reduces the threshold value, making it nearly impossible for any operator delegator to have a TVL below this value.

## Impact
Deposits might not be allocated to the correct operator delegator, leading to imbalanced TVLs.
and Withdrawals might fail unnecessarily because the  logic not find a suitable operator delegator due to incorrect threshold calculations.


## Proof of Concept
i test with this scenario let's say taht : 
- BASIS_POINTS is 100.
- operatorDelegatorAllocations[operatorDelegator] is 50 (representing 50%).
- totalTVL is 1000.
- TVL of an operator delegator (tvls[i]) is 400.

in the function chooseOperatorDelegatorForDeposit the calculation is Simplifies :
```solidity 
tvls[i] < (50 * 1000) / 100 / 100
// tvls[i] < 5
```
- here the result is incorrect Threshold: 5
and in chooseOperatorDelegatorForWithdraw function the calculation is Simplifies :
```solidity 
tvls[i] < (50 * 1000) / 100
// tvls[i] < 500
```
- the result is Correct Threshold: 500
## Tools Used
manual review 
## Recommended Mitigation Steps
The logic should only divide by BASIS_POINTS once to correctly compute the allocation threshold 


## Assessed type

Other
deposits can be made with ODs that have exceeded their max TVL
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
choosing an `OpereatorDelegator` that has exceeded its maxTVL can lead to loss of funds

## Proof of Concept
When `RestakeManager::deposit(...)` is called, an `OperatorDelegator` (OD) is chosen based on whether or not they have exceeded their max TVL allocation by calling `RestakeManager::chooseOperatorDelegatorForDeposit(...)` however due to a logical flaw in the implementation, an OD that has exceeded its max TVL can be chosen. for deposit.

```solidity
File: RestakeManager.sol
362:     function chooseOperatorDelegatorForDeposit(
363:         uint256[] memory tvls,
364:         uint256 totalTVL
365:     ) public view returns (IOperatorDelegator) {
366:         // Ensure OperatorDelegator list is not empty
367:         if (operatorDelegators.length == 0) revert NotFound();
368: 
369:         // If there is only one operator delegator, return it
370:         if (operatorDelegators.length == 1) {
371:             return operatorDelegators[0];
372:         }

376:         for (uint256 i = 0; i < tvlLength; ) {
377:             if (
378:                 tvls[i] <
379:                 (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
380:                     BASIS_POINTS /
381:                     BASIS_POINTS
482:              ) {   
483:                 return operatorDelegators[i];
402:              }      
403:                 
404:             unchecked {
405:                 ++i;   
407:             }
408:         }
409:             
        

```

The downside to this is that the chosen `OperatorDelegator` may have exceeded it `maxTVL` and shares may not be assigned by EigenLayer Strategy as most strategy implementations assign shares based on whether or not the OD has reached its max TVL and as such user will make deposit that becomes dormant in the protocol diluting the rewards of the protocol.

## Tools Used
Manual review

## Recommended Mitigation Steps
Modify the  `RestakeManager::chooseOperatorDelegatorForDeposit(...)` as shown  below

```solidity
File: RestakeManager.sol
362:     function chooseOperatorDelegatorForDeposit(
363:         uint256[] memory tvls,
364:         uint256 totalTVL
365:     ) public view returns (IOperatorDelegator) {
366:         // Ensure OperatorDelegator list is not empty
367:         if (operatorDelegators.length == 0) revert NotFound();
368: 
369:         // If there is only one operator delegator, return it
370:         if (operatorDelegators.length == 1) {
371:             return operatorDelegators[0];
372:         }

376:         for (uint256 i = 0; i < tvlLength; ) {
377:             if (
378:                 tvls[i] <
379:                 (operatorDelegatorAllocations[operatorDelegators[i]] * totalTVL) /
380:   -                 BASIS_POINTS /
381:   -                 BASIS_POINTS
380:   +                 B
Cut is paid on eigenpod ETH withdrawals through ``verifyAndProcessWithdrawals`` 
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Delegation/OperatorDelegator.sol#L405-L425


# Vulnerability details


## Impact
The ``receive()`` function has the following specs:
```solidity
//     * @dev If msg.sender is eigenPod then forward ETH to deposit queue without taking cut //

```

This means that the eth transfers coming from the EigenPod should be sent directly to the deposit queue, however this is not the case, as Withdrawals coming from ``OperatorDelegator.verifyAndProcessWithdrawals()``  which calls ``eigenPod.verifyAndProcessWithdrawals``. But this does not send available withdrawals directly from the Eigenpod contract. Instead it sends the ETH through the ``delayedWithdrawalRouter``. 
Since the ETH transfer is finalized through ``delayedWithdrawalRouter``, it does not skip the necessary step in the ``receive`` function.
## Proof of Concept

```solidity
    /**
     * @notice Users should NOT send ETH directly to this contract unless they want to donate to existing ezETH holders.
     *        This is an internal protocol function.
     * @dev Handle ETH sent to this contract - will get forwarded to the deposit queue for restaking as a protocol reward
     * @dev If msg.sender is eigenPod then forward ETH to deposit queue without taking cut (i.e. full withdrawal from beacon chain)
     */
    receive() external payable nonReentrant {
        // check if sender contract is EigenPod. forward full withdrawal eth received

        //@audit it does not skip this step
        if (msg.sender == address(eigenPod)) {
            restakeManager.depositQueue().forwardFullWithdrawalETH{ value: msg.value }();
        } else {
            // considered as protocol reward
            uint256 gasRefunded = 0;
            uint256 remainingAmount = msg.value;
            if (adminGasSpentInWei[tx.origin] > 0) {
                gasRefunded = _refundGas();
                // update the remaining amount
                remainingAmount -= gasRefunded;
                // If no funds left, return
                if (remainingAmount == 0) {
                    return;
                }
            }
            // Forward remaining balance to the deposit queue
            address destination = address(restakeManager.depositQueue());
            (bool success, ) = destination.call{ value: remainingAmount }("");
            if (!success) revert TransferFailed();

            emit RewardsForwarded(destination, remainingAmount);
        }
    }
```
## Tools Used

Manual Review, Josephdara

## Recommended Mitigation Steps
Ensure that if ``msg.sender`` is the ``delayedWithdrawalRouter``, no fee is paid.



## Assessed type

ETH-Transfer
Lack of latency handling can lead to incorrect amount minted
Audit: 2024-04-renzo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Bridge/L1/xRenzoBridge.sol#L210


# Vulnerability details

## Summary
As per [Sponsor](https://github.com/code-423n4/2024-04-renzo/blob/main/README.md?plain=1#L329):
```javascript
* ezETH should be minted or redeemed based on current supply and TVL.
```
Simultaneous `CCIP calls` are possible which can lead to fresh data being overwritten, resulting in wrong data that leads to a wrongly calculated TVL, which breaks the invariant above. 
## Description
The project uses `CCIP` mechanism of Chainlink to send cross-chain messages from `L1 -> L2` to update the L2 with the current price. The `priceFeedSender` will call `sendPrice()` to initiate the `CCIP call`:

```javascript
    function sendPrice(
        CCIPDestinationParam[] calldata _destinationParam,
        ConnextDestinationParam[] calldata _connextDestinationParam
    ) external payable onlyPriceFeedSender nonReentrant {
    //.. ommitted code
        for (uint256 i = 0; i < _destinationParam.length; ) {
            Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
                receiver: abi.encode(_destinationParam[i]._renzoReceiver), // ABI-encoded xRenzoDepsot contract address
                data: _callData, // ABI-encoded ezETH exchange rate with Timestamp
                tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
                extraArgs: Client._argsToBytes(
                    // Additional arguments, setting gas limit
                    Client.EVMExtraArgsV1({ gasLimit: 200_000 })
                ),
                // Set the feeToken  address, indicating LINK will be used for fees
                feeToken: address(linkToken)
            });
    //.. ommitted code
```

On L2, `CCIPReceiver.sol` will handle the incoming `CCIP calls`. Renzo added custom logic inside `_ccipReceive()`:
```javascript
    function _ccipReceive(
        Client.Any2EVMMessage memory any2EvmMessage
    ) internal override whenNotPaused {
        address _ccipSender = abi.decode(any2EvmMessage.sender, (address));
        uint64 _ccipSourceChainSelector = any2EvmMessage.sourceChainSelector;
        // Verify origin on the price feed
        if (_ccipSender != xRenzoBridgeL1) revert InvalidSender(xRenzoBridgeL1, _ccipSender);
        // Verify Source chain of the message
        if (_ccipSourceChainSelector != ccipEthChainSelector)
            revert InvalidSourceChain(ccipEthChainSelector, _ccipSourceChainSelector);
        (uint256 _price, uint256 _timestamp) = abi.decode(any2EvmMessage.data, (uint256, uint256));
        xRenzoDeposit.updatePrice(_price, _timestamp);
        emit MessageReceived(
            any2EvmMessage.messageId,
            _ccipSourceChainSelector,
            _ccipSender,
            _price,
            _timestamp
        );
    }
```

The problem is that there is currently no way to handle late
Missing Tick Range Validation in create_position Function
Audit: 2024-08-superposition-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-08-superposition/blob/main/pkg/seawater/src/pool.rs#L75-L87


# Vulnerability details

In the `create_position` function, there is no validation to check whether the provided *lower tick is less than the upper tick*, which is a crucial condition for ensuring proper tick range management. 

When this validation is missing, it becomes possible for users to provide tick ranges where the lower tick is greater than the upper tick, leading to unexpected behaviors.

- Uniswap V3 (https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol#L127)
```solidity
    /// @dev Common checks for valid tick inputs.
    function checkTicks(int24 tickLower, int24 tickUpper) private pure {
@>>     require(tickLower < tickUpper, 'TLU');
        require(tickLower >= TickMath.MIN_TICK, 'TLM');
        require(tickUpper <= TickMath.MAX_TICK, 'TUM');
    }
```
- Superposition (https://github.com/code-423n4/2024-08-superposition/blob/main/pkg/seawater/src/pool.rs#L75-L87)
```rust
    pub fn create_position(&mut self, id: U256, low: i32, up: i32) -> Result<(), Revert> {
        assert_or!(self.enabled.get(), Error::PoolDisabled);
        let spacing = self.tick_spacing.get().sys() as i32;
        assert_or!(low % spacing == 0, Error::InvalidTickSpacing);
        assert_or!(up % spacing == 0, Error::InvalidTickSpacing);
        let spacing: u8 = spacing.try_into().map_err(|_| Error::InvalidTickSpacing)?;
        let min_tick = tick_math::get_min_tick(spacing);
        let max_tick = tick_math::get_max_tick(spacing);

@>>     assert_or!(low >= min_tick && low <= max_tick, Error::InvalidTick);
@>>     assert_or!(up >= min_tick && up <= max_tick, Error::InvalidTick);

        self.positions.new(id, low, up);
        Ok(())
    }
```

## Impact

The lack of validation for tick range order introduces potential inconsistencies in liquidity positions. When the ticks are inverted, the position behaves differently, affecting the token amounts that users must provide to establish or update their position.
This can also have some other issues too.

## Proof of Concept
```rust
pub fn update_position(&mut self, id: U256, delta: i128) -> Result<(I256, I256), Revert> {
        // the pool must be enabled
        assert_or!(self.enabled.get(), Error::PoolDisabled);

        let position = self.positions.positions.get(id);
        let lower = position.lower.get().sys();
        let upper = position.upper.get().sys();

        // update the ticks
        let cur_tick = self.cur_tick.get().sys();
        ...

        if delta != 0 {
            flipped_lower = self.ticks.update(
@>>             lower,
                cur_tick,
                ..
            )?;

            flipped_upper = self.ticks.update(
@>>             upper,
                cur_tick,
                ..
            )?;

            ...
        }
        ...

        // calculate liquidity change and the amount of each token we need
        if delta != 0 {