contractcops G

## [G-01] Better variable layout in Message struct
We are able to re-position the current Message struct variables in a way which packs them in a more memory-efficient way:
```
    // Current structure in the project
    struct MessageBad {
        bytes32 id;
        bytes payload;
        uint256 timestamp;
        bytes signature;
    }

    // Modified struct for better memory packing
    struct MessageGood {
        bytes32 id;
        uint256 timestamp;
        bytes payload;
        bytes signature;
    }

    // ---------------- Testing functions ------------------
    // Uses ~22888 gas
    function TestGood() external returns (MessageGood memory){
        MessageGood memory messageGood;
        messageGood.id = 0x0;
        messageGood.timestamp = 1;
        messageGood.payload = "1";
        messageGood.payload = "1";
        return messageGood;
    }
    // Uses ~22910 gas
    function TestBad() external returns (MessageBad memory){
        MessageBad memory messageBad;
        messageBad.id = 0x0;
        messageBad.timestamp = 1;
        messageBad.payload = "1";
        messageBad.payload = "1";
        return messageBad;
    }
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/interfaces/IStolenNftOracle.sol#L6-L13

## [G-02] x = x + y costs less then x += y (and x -= y) in PrivatePool.sol
```230:        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
...
231:       virtualNftReserves -= uint128(weightSum);
...
247:       royaltyFeeAmount += royaltyFee;
...
252:       netInputAmount += royaltyFeeAmount;
...
323:       virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);
...
324:       virtualNftReserves += uint128(weightSum);
...
341:       royaltyFeeAmount += royaltyFee;
...
355:       netOutputAmount -= royaltyFeeAmount;
...
678:       sum += tokenWeights[i];

```

// Code block 1 (unknown):
// Current structure in the project
    struct MessageBad {
        bytes32 id;
        bytes payload;
        uint256 timestamp;
        bytes signature;
    }

    // Modified struct for better memory packing
    struct MessageGood {
        bytes32 id;
        uint256 timestamp;
        bytes payload;
        bytes signature;
    }

    // ---------------- Testing functions ------------------
    // Uses ~22888 gas
    function TestGood() external returns (MessageGood memory){
        MessageGood memory messageGood;
        messageGood.id = 0x0;
        messageGood.timestamp = 1;
        messageGood.payload = "1";
        messageGood.payload = "1";
        return messageGood;
    }
    // Uses ~22910 gas
    function TestBad() external returns (MessageBad memory){
        MessageBad memory messageBad;
        messageBad.id = 0x0;
        messageBad.timestamp = 1;
        messageBad.payload = "1";
        messageBad.payload = "1";
        return messageBad;
    }


0x11singh99 Q

# Low-Level and Non-Critical issues

## Summary

### Low Level List

| Number | Issue Details                                                                                | Instances |
| :----: | :----------------------------------------------------------------------------------------------------------------- | :-------: |
| [L-01] | Amounts should be checked for 0 before calling a transfer                                                                 |     2     |
| [L-02] |   Check address 0 before approving.                        |     5     |
| [L-03] |   Check address 0 before transferring to it or minting to it  |     5     |
Total 3 Low Level Issues

### Non Critical List

| Number  | Issue Details                                                       | Instances |
| :-----: | :------------------------------------------------------------------ | :-------: |
| [NC-01] | Some Contracts are not following proper solidity style guide layout |     4     |
| [NC-02] | Typos                                                               |     4     |
| [NC-03] | Getter function should not revert                                   |     1     |
| [NC-04] | Misleading comments                                               |     1     |
| [NC-05] | Check amount is NON-ZERO      |     2     |

Total 5 Non Critical Issues

# LOW FINDINGS

## [L-01] Amounts should be checked for 0 before calling a transfer

In Solidity, the transfer() function is commonly used to transfer ether or tokens from one address to another. When calling the transfer() function, it is important to check the amount being transferred to ensure that it is greater than 0, as transferring 0 amounts is not necessary and can consume unnecessary gas.
It is a security best practice to validate that the amount being transferred is greater than zero to prevent unnecessary gas costs and ensure that transfers are only executed when there is a valid amount to send. Implementing this che

File : contracts/amo/UniV2LiquidityAmo.sol

142: function emergencyWithdraw(
    address[] calldata tokens
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    IERC20WithBurn token;

    for (uint256 i = 0; i < tokens.length; i++) {
      token = IERC20WithBurn(tokens[i]);
149:  token.safeTransfer(msg.sender, token.balanceOf(address(this)));///@audit check
    }

     emit LogEmergencyWithdraw(msg.sender, tokens);
153:  }

File : contracts/amo/UniV3LiquidityAmo.sol
274: function swap(
      address _tokenA,
      address _tokenB,
      uint24 _fee_tier,
      uint256 _amountAtoB,
      uint256 _amountOutMinimum,
      uint160 _sqrtPriceLimitX96
      )  public onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256) {
     // transfer token from rdpx v2 core
     IERC20WithBurn(_tokenA).transferFrom(///@audit check
      rdpxV2Core,
      address(this),
      _amountAtoB
287:    );


The Seraphs Q

## [QA/Low-01] Use `flipped` in `Pools::removeLiquidity()` function so that the users do not have to order the tokens correctly in the function call, similar to how its done for all other functions

### Code references

https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L175

### Description
The function `Pools::removeLiquidity()` retrieves the pool ID and if the token IDs are flipped.

```solidity
(bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB);
```

The pool ID is determined as follows:

```solidity
function _poolIDAndFlipped( IERC20 tokenA, IERC20 tokenB ) internal pure returns (bytes32 poolID, bool flipped)
{
    // See if the token orders are flipped
    if ( uint160(address(tokenB)) < uint160(address(tokenA)) )
        return (keccak256(abi.encodePacked(address(tokenB), address(tokenA))), true);

    return (keccak256(abi.encodePacked(address(tokenA), address(tokenB))), false);
}
```

Flipped is returned as true if `tokenB < tokenA`.

This ensures that the `tokenA`, `tokenB` and the corresponding amounts provided in the function call matches the tokens for `reserve0` and `reserve1` of the pool.

However, `flipped` is not used in the function at all, and its the user's responsibility to provide the tokens in the right order.

The other functions like `Pools::addLiquidity()` use `flipped` and flip the tokens if needed.

### Recommendation

Use `flipped` in the code to flip the tokens if the user provides the tokens in the wrong order.

---

## [QA/Low-02] Add checks in `DAO::_executeSetContract()` to ensure the same price feed is not set for different price feeds

### Code references

https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/DAO.sol#L135-L142

### Description

In `DAO::_executeSetContract()`, based on the `nameHash`, the price feed is set to `priceFeedNum`.

```solidity
if ( nameHash == keccak256(bytes( "setContract:priceFeed1_confirm" )) )
    priceAggregator.setPriceFeed( 1, IPriceFeed(ba

// Code block 1 (solidity):
(bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB);

// Code block 2 (solidity):
function _poolIDAndFlipped( IERC20 tokenA, IERC20 tokenB ) internal pure returns (bytes32 poolID, bool flipped)
{
    // See if the token orders are flipped
    if ( uint160(address(tokenB)) < uint160(address(tokenA)) )
        return (keccak256(abi.encodePacked(address(tokenB), address(tokenA))), true);

    return (keccak256(abi.encodePacked(address(tokenA), address(tokenB))), false);
}

(bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB);

function _poolIDAndFlipped( IERC20 tokenA, IERC20 tokenB ) internal pure returns (bytes32 poolID, bool flipped)
{
    // See if the token orders are flipped
    if ( uint160(address(tokenB)) < uint160(address(tokenA)) )
        return (keccak256(abi.encodePacked(address(tokenB), address(tokenA))), true);

    return (keccak256(abi.encodePacked(address(tokenA), address(tokenB))), false);
}


cryptonue G

# CheckContract optimization

CheckContract is used in many contracts to verify an address provided is indeed a contract, not just a random address or an EOA. This CheckContract contract is only contains a single `checkContract()` function which is a view function and could easily be implemented as an internal library call. This would result in slightly larger contract bytecode but should be far more gas efficient than an external contract call as is the current case.

If a smart contract is consuming a library which have only internal functions than EVM simply embeds library into the contract. Instead of using delegate call to call a function, it simply uses JUMP statement(normal method call). There is no need to separately deploy library in this scenario.

```js
File: CheckContract.sol
11:     function checkContract(address _account) internal view {
12:         require(_account != address(0), "Account cannot be zero address");
13:
14:         uint256 size;
15:         // solhint-disable-next-line no-inline-assembly
16:         assembly { size := extcodesize(_account) }
17:         require(size > 0, "Account code size cannot be zero");
18:     }
```

Further more, if Ethos decided to use Solidity v0.8 and above, we can just use `address.code.length` > 0 to detect if an address is a contract or just EOA, this will surely will save alot of gas which the current `checkContract()` function is being called everywhere.

# Remove `_scaleTellorPriceByDigits` function and just return the `_price` (its parameter)

In `PriceFeed.sol`, the `TARGET_DIGITS` and `TELLOR_DIGITS` are both a constant `18`.
By looking at this same value, then `_scaleTellorPriceByDigits` function is actually unnecessary, as it would just return the `_price` (it's parameter) since the conditional `if-else` statement will not being entered (nothing matched)

The `_scaleTellorPriceByDigits` function is being used in on of function which fetch a collateral price `fetchPrice()`, thus removing this function

File: PriceFeed.sol
38:     // Use to convert a price answer to an 18-digit precision uint
39:     uint constant public TARGET_DIGITS = 18;
40:     // legacy Tellor "request IDs" use 6 decimals, newer Tellor "query IDs" use 18 decimals
41:     uint constant public TELLOR_DIGITS = 18;
...
521:     function _scaleTellorPriceByDigits(uint _price) internal pure returns (uint) {
522:         uint256 price = _price;
523:         if (TARGET_DIGITS > TELLOR_DIGITS) {
524:             price = price.mul(10**(TARGET_DIGITS - TELLOR_DIGITS));
525:         } else if (TARGET_DIGITS < TELLOR_DIGITS) {
526:             price = price.div(10**(TELLOR_DIGITS - TARGET_DIGITS));
527:         }
528:         return price;
529:     }

// Code block 1 (js):
File: CheckContract.sol
11:     function checkContract(address _account) internal view {
12:         require(_account != address(0), "Account cannot be zero address");
13:
14:         uint256 size;
15:         // solhint-disable-next-line no-inline-assembly
16:         assembly { size := extcodesize(_account) }
17:         require(size > 0, "Account code size cannot be zero");
18:     }


SBSecurity Analysis

# ****🛠 Analysis - KelpDAO - rsETH****

---

## Overview

---

KelpDAO aims to launch a new and advanced Liquid Restaked Token called rsETH. rsETH represents a consolidated, re-staked token that can be generated using LSTs accepted as collateral within the EigenLayer ecosystem. The idea is to make restaking and diving into DeFi super easy, playing well with other DeFi stuff. KelpDAO is here to fix tricky reward systems and those pesky high gas fees, making a big splash in the web3 scene. To break it down, the Kelp protocol takes in deposits, mainly in the form of LSTs recognized as EigenLayer collateral. In return, users receive $rsETH – KelpDAO's distinctive reward token, symbolizing each individual's deposited assets.

**The key contracts of KelpDAO - $rsETH protocol for this Audit are**:

- LRTConfig: This upgradeable contract plays a pivotal role in storing the configuration settings for the Kelp product. Additionally, it serves as a repository for the addresses of other contracts within the Kelp product, establishing a centralized hub for managing various aspects of the system's functionality.
- LRTDepositPool: Operating as an upgradeable contract, LRTDepositPool functions as a receiving point for funds deposited by users into the Kelp product. Subsequently, these funds are channeled to NodeDelegator contracts, facilitating the efficient movement of resources within the Kelp ecosystem.
- NodeDelegator: These contracts serve as intermediaries, receiving funds from the LRTDepositPool and strategically delegating them to the EigenLayer strategy. The allocated funds are then utilized to provide liquidity within the EigenLayer protocol. Notably, NodeDelegator is designed as an upgradeable contract, allowing for adaptability and future enhancements.
- LRTOracle: Functioning as another upgradeable contract, LRTOracle is tasked with the responsibility of fetching accurate price data for Liquid Staking Tokens from external oracle services. This ensures that the Kelp pr

modifier onlyLRTManager() {
        if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.MANAGER, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigManager();
        }
        _;
    }


PoeAudits Analysis

# Contract Overview

#### AiArenaHelper:

The AiArenaHelper contract is a helper contract that generates and manages Ai Arena fighter's physical attributes. The contract includes functionality to handle ownership, attribute probabilities, and the creation of physical attributes based on a fighter's DNA. It is used in other contracts to facilitate the generation of Ai fighters.

#### FighterOps:

This library manages the creation and attributes of Fighter NFTs within the game. The library is designed to be used by other contracts to retrieve the relevant data for Ai fighters. The library includes the data structures that define each fighter and includes relevant functions to expose this data publicly.

#### GameItems:

The GameItems smart contract inherits from OpenZeppelin's ERC-1155 standard allowing for the creation of fungible and non-fungible tokens within a single contract. The contract is designed to manage game items for Ai Arena. The GameItems smart contract allows for trusted admins to create unique game items, and allows anyone to mint these items as long as pre-set requirements are fulfilled. When creating an item, the admin can set the transferability, price, daily allowance, and supply of the game items. The supply can be infinite if desired. The contract also defines the logic for transferring and burning game items, while complying with uri requirements set by ERC-1155. Payment for game items is done through the Neuron ERC-20 token.

#### MergingPool:

The MergingPool contract includes functionality for managing points associated with fighter tokens, selecting winners, and claiming rewards. Users earn points by winning rounds through the RankedBattle contract, which calls the addPoints function of the MergingPool contract. This contract also allows admin accounts to select the winners of a round, and allows the winners to claim rewards, which are additional Ai fighters.

#### Neuron:

The Neuron contract is the payment system for Ai Arena, and is an

    /// @notice Transfers ownership from one address to another.
    /// @dev Only the owner address is authorized to call this function.
    /// @param newOwnerAddress The address of the new owner
    function transferOwnership(address newOwnerAddress) external {
        require(msg.sender == _ownerAddress);
        _ownerAddress = newOwnerAddress;
    }


Tomio G

Title: Unnecessary variable declaration

Proof of Concept:
[RewardsManager.sol#L436](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L436) [RewardsManager.sol#L444](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L444)
Consider declare this variable directly instead, which can save gas costs by reducing unnecessary variable declarations.

Recommended Mitigation Steps:

```
for (uint256 i = 0; i < positionIndexes_.length; ) {
            uint256 bucketIndex = positionIndexes_[i]; //declare here
            BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];

            if (epoch_ != stakingEpoch_) {

                // if staked in a previous epoch then use the initial exchange rate of epoch
                uint256 bucketRate = bucketExchangeRates[ajnaPool_][bucketIndex][epoch_]; //declare here
            } else {
```
________________________________________________________________________

// Code block 1 (unknown):
for (uint256 i = 0; i < positionIndexes_.length; ) {
            uint256 bucketIndex = positionIndexes_[i]; //declare here
            BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];

            if (epoch_ != stakingEpoch_) {

                // if staked in a previous epoch then use the initial exchange rate of epoch
                uint256 bucketRate = bucketExchangeRates[ajnaPool_][bucketIndex][epoch_]; //declare here
            } else {


Franfran G

## Findings summary
Name | Finding | Instances | Gas saved
--- | --- | --- | ---
[G-1] | Duplicated require()/revert()/assert() checks should be refactored to a modifier or an internal function | 3 | 60282
[G-2] | Use custom errors instead of revert strings | 10 | 2540
[G-3] | Setting the constructor to payable | 4 | 864
[G-4] | Avoid using public for immutable/constant state variables | 11 | 272899
[G-5] | Use calldata instead of memory for external function parameters | 2 | /
[G-6] | Functions guaranteed to revert for normal users should be marked as `payable` | 17 | 408
[G-7] | use ternary operators rather than `if/else` | 3 | 39
[G-8] | No need to initialize variables to their default values | 7 | /
[G-9] | Cache rocket-style string encoded into immutables | 6 | 864

## Findings details
### [G-1] Duplicated require()/revert()/assert() checks should be refactored to a modifier or an internal function

Duplicated require, revert, or assert messages should be refactored to an internal or private function in order to save some gas on deployment. The more duplicated it is, the greater the savings, but this always saves gas starting from two duplication.

`contracts/SafEth/derivatives/Reth.sol`
200:12

https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts/SafEth/derivatives/Reth.sol#L200

```solidity
              uint256 rethBalance2 = rocketTokenRETH.balanceOf(address(this));
>             require(rethBalance2 > rethBalance1, "No rETH was minted");
              uint256 rethMinted = rethBalance2 - rethBalance1;
```

### Gas saved (per instance on deployment)

20094

Locations

- https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts/SafEth/derivatives/WstEth.sol#L66

- https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts/SafEth/derivatives/WstEth.sol#L77

### [G-2] Use custom errors instead of revert strings

Solidity 0.8.4 added the custom errors functionality, which can be us

      function stake() external payable {
>         require(pauseStaking == false, "staking is paused");
          require(msg.value >= minAmount, "amount too low");

// Code block 1 (solidity):
uint256 rethBalance2 = rocketTokenRETH.balanceOf(address(this));
>             require(rethBalance2 > rethBalance1, "No rETH was minted");
              uint256 rethMinted = rethBalance2 - rethBalance1;


camdengrieh G

1) SmartAccount.sol - Require Strings More than 32 bytes -  4 instances

Saves 3 gas if string length is 32 characters or less

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77-L78

```solidity
        require(msg.sender == owner, "Smart Account:: Sender is not authorized");

```

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L110-L111

```solidity
        require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

```

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L128-L129

```solidity
        require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");
        require(Address.isContract(_newEntryPoint), "Smart Account:: new entry point address must be a contract"
```

// Code block 1 (solidity):
require(msg.sender == owner, "Smart Account:: Sender is not authorized");

// Code block 2 (solidity):
require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

// Code block 3 (solidity):
require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");
        require(Address.isContract(_newEntryPoint), "Smart Account:: new entry point address must be a contract"

require(msg.sender == owner, "Smart Account:: Sender is not authorized");

require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");
        require(Address.isContract(_newEntryPoint), "Smart Account:: new entry point address must be a contract"


0xmystery Q

## Gas estimate on SourceBridge.burnAndCallAxelar
The UI may have the gas estimate taken care of. Nonetheless, users interacting with this function have no clue what amount of estimated `msg.value` to send along with the call. Apparently, `burnAndCallAxelar()` only checks if `msg.value` is not zero.

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L72-L74

```solidity
    if (msg.value == 0) {
      revert GasFeeTooLow();
    }
```
While this prevents the latter from calling the function without sending any ether, it does not ensure the amount of ether sent is adequate to cover the gas fees.

Consider implementing the [Axelar gas estimate](https://docs.axelar.dev/dev/general-message-passing/gas-services/pay-gas) to the function logic where possible.

## rUSDY.approve poses a threat to unsavvy users
The contract already has [`increaseAllowance()`](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L327-L337) and [`decreaseAllowance()`](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L353-L364) implemented as an alternative to [`approve()`](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L276-L279) that can be used as a mitigation to spender frontrunning the revised allowance. Where possible, remove `approve()` since user can always use `increaseAllowance()` for the same intended purpose.

## Missing crucial check in RWADynamicOracle.simulateRange
Consider adding the following check:

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L119-L128

```diff
    } else {
      Range memory lastRange = ranges[ranges.length - 1];
      uint256 prevClosePrice = derivePrice(lastRange, lastRange.end - 1);
+      // Check that the endTime is greater than the last range's end time
+      if (lastRange.end >= endTime) revert InvalidRange();
      rangeList[length] = Range(
        lastRange.end,
        endTim

This will prevent underflow on line 266 below when internally calling `derivePrice()` in the for loop above:

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L262-L274

// Code block 1 (solidity):
if (msg.value == 0) {
      revert GasFeeTooLow();
    }

SourceBridge.sol#L72-L74, rUSDY.sol#L327-L337, rUSDY.sol#L353-L364, rUSDY.sol#L276-L279, RWADynamicOracle.sol#L119-L128, RWADynamicOracle.sol#L262-L274


0xWaitress Q

[QA-1] redundant code at excessIncomeDistribution can be refactored

these 3 lines exist in both `if` and `else`
```solidity
            bool success = EUSD.transferFrom(msg.sender, address(configurator), income);
            require(success, "TF");

            try configurator.distributeRewards() {} catch {}
```

## Recommendation
```solidity
uint256 toTransfer = payAmount > income ? income : payAmount.
bool success = EUSD.transferFrom(msg.sender, address(configurator), toTransfer);
require(success, "TF");
try configurator.distributeRewards() {} catch {}
```

2.

// Code block 1 (solidity):
bool success = EUSD.transferFrom(msg.sender, address(configurator), income);
            require(success, "TF");

            try configurator.distributeRewards() {} catch {}

// Code block 2 (solidity):
uint256 toTransfer = payAmount > income ? income : payAmount.
bool success = EUSD.transferFrom(msg.sender, address(configurator), toTransfer);
require(success, "TF");
try configurator.distributeRewards() {} catch {}

bool success = EUSD.transferFrom(msg.sender, address(configurator), income);
            require(success, "TF");

            try configurator.distributeRewards() {} catch {}

uint256 toTransfer = payAmount > income ? income : payAmount.
bool success = EUSD.transferFrom(msg.sender, address(configurator), toTransfer);
require(success, "TF");
try configurator.distributeRewards() {} catch {}


K42 Analysis

### Advanced Analysis Report for [Revert-Lend](https://github.com/code-423n4/2024-03-revert-lend) by K42

#### Overview

[Revert-Lend](https://github.com/code-423n4/2024-03-revert-lend) is a sophisticated non-custodial lending protocol that allows users to borrow against their Uniswap V3 LP positions. The protocol consists of the core V3Vault contract for lending/borrowing, various yield optimization strategy contracts (AutoCompound, AutoExit, AutoRange), leverage management contracts (LeverageTransformer, FlashloanLiquidator), a custom V3Oracle for price feeds, an InterestRateModel for interest rate calculations, and utility contracts (V3Utils, Swapper).

#### Understanding the Ecosystem:

The V3Vault is the clear heart of the [Revert-Lend](https://github.com/code-423n4/2024-03-revert-lend) ecosystem. It integrates with the following key components:

1. V3Oracle: Provides USD price feeds for the LP tokens used as collateral. Uses a combination of Chainlink oracles and Uniswap V3 TWAPs.
2. InterestRateModel: Calculates borrow and supply interest rates based on utilization.
3. Automator: Abstract base contract for keeper-based automations like AutoCompound, AutoExit, AutoRange, defines common functionality like configuration of keeper roles and limits, withdrawing tokens, swap helper functions etc.
4. AutoCompound, AutoExit, AutoRange: Optionally used for yield optimization strategies on the LP tokens used as collateral.
4. LeverageTransformer, FlashloanLiquidator: Used for managing leveraged positions and liquidations respectively.
5. V3Utils: A utility contract for common Uniswap V3 operations like collecting fees, changing ranges etc.
6. Swapper: A utility contract for token swaps using 0x API or Uniswap Universal Router.

Users can deposit base assets to mint vault shares (following ERC4626) and use their Uniswap V3 LP tokens as collateral to borrow base assets. The automation contracts interact with the Vault to execute yield optimization strategies on the colla

#### Interaction Graph for [V3Oracle](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Oracle.sol):

#### Interaction Graph for [InterestRateModel](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/InterestRateModel.sol):


0x4non Q

# QA

## Low

### L01 Unsafe downcasting

On [PrivatePool.sol#L230-L231](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L230-L231) there are two unsafe downcasting from `uint256` to `uint128`;
```solidity
        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);
```

I think is possible under certain cirscunstances that `weightSum > uint128.max` or `netInputAmount - feeAmount - protocolFeeAmount > uint128.max`

Consider using OpenZeppelin’s SafeCast library to prevent unexpected overflows/underflows when casting from other types

### L02 Its possible to reenter to `PrivatePool.change` function

Its factible to reenter to the `change` function if you send more ether than you supposed to, the line tha makes possible the reentrancy is on [PrivatePool.sol#L436](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L436)

I think its more practical to avoid native ETH in favour of always using WETH, this will make all contracts less complex.

If you want to test it out you could reuse [this test](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/test/EthRouter/Change.t.sol#L25-L57) and add a `receive() external payable` to reenter or do any action that you want before the protocol take you nft in give you and change it for another.

### L03 Missing `address(0)` checks

- `privatePoolMetadata` can be set to `address(0)` on [Factory.sol#L129-L131](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L129-L131)
- `privatePoolImplementation` can be set to `address(0)` on [Factory.sol#L135-L137](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L135-L137)

### L04 Missing bound check can set variables to extremes that make

// Code block 1 (solidity):
virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);

PrivatePool.sol#L230-L231, PrivatePool.sol#L436, Change.t.sol#L25-L57, Factory.sol#L129-L131, Factory.sol#L135-L137


dimulski G

## Summary

### Gas Optimizations
| |Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
| [G‑01] | `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables (`-=` too) | 4 |  452 |
| [G‑02] | Functions guaranteed to revert when called by normal users can be marked `payable` | 14 |  294 |
| [G‑03] | Setting the constructor to `payable` | 4 |  - |

Total: 22 instances over 3 issues with **746 gas** saved.

## Gas Optimizations

### [G‑01] | `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables (`-=` too)

Using the addition operator instead of plus-equals saves **[113 gas](https://gist.github.com/IllIllI000/cbbfb267425b898e5be734d4008d4fe8)**. Subtructions act the same way.

*There are 4 instances of this issue:*

```solidity
File: contracts\SafEth\SafEth.sol
72: underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * derivatives[i].balance()) / 10 ** 18;

95: totalStakeValueEth += derivativeReceivedEthValue;

172: localTotalWeight += weights[i];

192: localTotalWeight += weights[i];

```

### [G‑02]  Functions guaranteed to revert when called by normal users can be marked `payable`
If a function modifier such as `onlyOwner` is used, the function will revert if a normal user tries to pay the function. Marking the function as `payable` will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are
`CALLVALUE`(2),`DUP1`(3),`ISZERO`(3),`PUSH2`(3),`JUMPI`(10),`PUSH1`(3),`DUP1`(3),`REVERT`(0),`JUMPDEST`(1),`POP`(2), which costs an average of about **21 gas per call** to the function, in addition to the extra deployment cost.

*There are 14 instances of this issue:*

```solidity
File: contracts\SafEth\SafEth.sol
138: function rebalanceToWeights() external onlyOwner {}

165: function adjustWeight(uint256 _derivativeIndex,uint256weight)    external onlyOwner {}

182: function addDerivative(addre

File: contracts\SafEth\SafEth.sol
138: function rebalanceToWeights() external onlyOwner {}

165: function adjustWeight(uint256 _derivativeIndex,uint256weight)    external onlyOwner {}

182: function addDerivative(address _contractAddress,uint256 _weight) external onlyOwner {}

202: function setMaxSlippage(uint _derivativeIndex,uint _slippage) external onlyOwner {}

214: function setMinAmount(uint256 _minAmount) external onlyOwner {}

223: function setMaxAmount(uint256 _maxAmount) external onlyOwner {}

232: function setPauseStaking(bool _pause) external onlyOwner {}

241: function setPauseUnstaking(bool _pause) external onlyOwner {}

// Code block 1 (solidity):
File: contracts\SafEth\SafEth.sol
72: underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * derivatives[i].balance()) / 10 ** 18;

95: totalStakeValueEth += derivativeReceivedEthValue;

172: localTotalWeight += weights[i];

192: localTotalWeight += weights[i];


codeislight G

1. reconstructing CashManager mapping variables

from

```
  // Struct representing all redemption requests in an epoch
  struct RedemptionRequests {
    // Total CASH burned in the epoch
    uint256 totalBurned;
    // Mapping from address to amount of CASH address burned
    mapping(address => uint256) addressToBurnAmt;
  }

  // Mapping from epoch to redemption info struct for that epoch
  mapping(uint256 => RedemptionRequests) public redemptionInfoPerEpoch;

  // Mapping used for getting the exchange rate during a given epoch
  mapping(uint256 => uint256) public epochToExchangeRate;

  // Nested mapping containing mint requests for an epoch
  // { <epoch> : {<user> : <collateralAmount> }
  mapping(uint256 => mapping(address => uint256)) public mintRequestsPerEpoch;
```
to:
```
  // Struct representing all requests in an epoch
    struct Request {
        uint128 mintAmt;
        uint128 burnAmt;
    }
    // Struct representing all information in an epoch
    struct Info {
        uint128 exchangeRate;
        uint128 totalBurned;
    }

    mapping(uint256 => mapping(address => Request)) public requestsPerEpoch;

    mapping(uint256 => Info) public infoPerEpoch;
```

2. CashManager requestManager needs to check if there is any fees for feesInCollateral before transferring.

```
    function requestMint(uint256 collateralAmountIn)
	…
        uint256 feesInCollateral = _getMintFees(collateralAmountIn);
        ...
        if (feesInCollateral > 0)
            collateral.safeTransferFrom(
                msg.sender,
                feeRecipient,
                feesInCollateral
            );
```
3. improve on CashManager.completeRedemptions(), we have a reptetivite loops which can be merged into 1. in essence, there is _check and _process.
Best approach would be to include the code within the check in the process to save on extra unnecessary iterations.
```
    function completeRedemptions(
        address[] calldata redeemers,
        address[] calldata refundee

// Code block 1 (unknown):
// Struct representing all redemption requests in an epoch
  struct RedemptionRequests {
    // Total CASH burned in the epoch
    uint256 totalBurned;
    // Mapping from address to amount of CASH address burned
    mapping(address => uint256) addressToBurnAmt;
  }

  // Mapping from epoch to redemption info struct for that epoch
  mapping(uint256 => RedemptionRequests) public redemptionInfoPerEpoch;

  // Mapping used for getting the exchange rate during a given epoch
  mapping(uint256 => uint256) public epochToExchangeRate;

  // Nested mapping containing mint requests for an epoch
  // { <epoch> : {<user> : <collateralAmount> }
  mapping(uint256 => mapping(address => uint256)) public mintRequestsPerEpoch;

// Code block 2 (unknown):
// Struct representing all requests in an epoch
    struct Request {
        uint128 mintAmt;
        uint128 burnAmt;
    }
    // Struct representing all information in an epoch
    struct Info {
        uint128 exchangeRate;
        uint128 totalBurned;
    }

    mapping(uint256 => mapping(address => Request)) public requestsPerEpoch;

    mapping(uint256 => Info) public infoPerEpoch;

// Code block 3 (unknown):
function requestMint(uint256 collateralAmountIn)
	…
        uint256 feesInCollateral = _getMintFees(collateralAmountIn);
        ...
        if (feesInCollateral > 0)
            collateral.safeTransferFrom(
                msg.sender,
                feeRecipient,
                feesInCollateral
            );


ALTERNATE report

# Overview

## About C4

Code4rena (C4) is an open organization consisting of security researchers, auditors, developers, and individuals with domain expertise in smart contracts.

A C4 audit is an event in which community participants, referred to as Wardens, review, audit, or analyze smart contract logic in exchange for a bounty provided by sponsoring projects.

During the audit outlined in this document, C4 conducted an analysis of the AI Arena smart contract system written in Solidity. The audit took place between February 9—February 21 2024.

Following the C4 audit, 3 wardens ([d3e4](https://code4rena.com/@d3e4), [fnanni](https://code4rena.com/@fnanni), and [niser93](https://code4rena.com/@niser93)) reviewed the mitigations for all identified issues; the [mitigation review report](#mitigation-review) is appended below the audit report.

## Wardens

299 Wardens contributed reports to AI Arena:

  1. [fnanni](https://code4rena.com/@fnanni)
  2. [d3e4](https://code4rena.com/@d3e4)
  3. [niser93](https://code4rena.com/@niser93)
  4. [haxatron](https://code4rena.com/@haxatron)
  5. [BARW](https://code4rena.com/@BARW) ([BenRai](https://code4rena.com/@BenRai) and [albertwh1te](https://code4rena.com/@albertwh1te))
  6. [juancito](https://code4rena.com/@juancito)
  7. [DanielArmstrong](https://code4rena.com/@DanielArmstrong)
  8. [0xDetermination](https://code4rena.com/@0xDetermination)
  9. [merlinboii](https://code4rena.com/@merlinboii)
  10. [0x11singh99](https://code4rena.com/@0x11singh99)
  11. [MrPotatoMagic](https://code4rena.com/@MrPotatoMagic)
  12. [klau5](https://code4rena.com/@klau5)
  13. [MidgarAudits](https://code4rena.com/@MidgarAudits) ([vangrim](https://code4rena.com/@vangrim) and [EVDoc](https://code4rena.com/@EVDoc))
  14. [Timenov](https://code4rena.com/@Timenov)
  15. [lil\_eth](https://code4rena.com/@lil_eth)
  16. [btk](https://code4rena.com/@btk)
  17. [givn](https://code4rena.com/@givn)
  18. [rspadi](https://code4rena.com/@rspadi)
  19. [josep

    function addMinter(address newMinterAddress) external {
        require(msg.sender == _ownerAddress);
        _setupRole(MINTER_ROLE, newMinterAddress);
    }

    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

* [WARNING]
    * ====
    * This function should only be called from the constructor when setting
    * up the initial roles for the system.
    *
    * Using this function in any other way is effectively circumventing the admin
    * system imposed by {AccessControl}.
    * ====
    *
    * NOTE: This function is deprecated in favor of {_grantRole}.
    */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    * Roles can be granted and revoked dynamically via the {grantRole} and
    * {revokeRole} functions. Each role has an associated admin role, and only
    * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
    *
    * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
    * that only accounts with this role will be able to grant or revoke other
    * roles. More complex role relationships can be created by using
    * {_setRoleAdmin}.
    *
    * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
    * grant and revoke this role. Extra precautions should be taken to secure
    * accounts that have been granted it.
    */


Awesome Q

# 1. Use mixedCase

It is recommended to use the mixedCase naming convention for improving the readability and consistency of source code.

This convention involves combining words with no spaces, with the first letter of each word capitalized except for the first word.

For more information, see the Solidity style guide at the following link:

- [Naming Convention For Local and State Variable Names](https://docs.soliditylang.org/en/v0.5.3/style-guide.html#local-and-state-variable-names)

Affected lines of code:

- [TroveManager.sol#L86](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L86)

- [TroveManager.sol#L116](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L116)

- [TroveManager.sol#L119-L120](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/TroveManager.sol#L119-L120)

# 2. Unspecific Compiler Version Pragma

It is generally not recommended to use floating pragmas (i.e. pragmas that do not specify a specific compiler version) in contracts that are not intended to be used as libraries.

This is because using floating pragmas in application contracts can pose a security risk.

For example, a known vulnerable compiler version may be selected by mistake, or security tools might revert to an older compiler version that produces a different EVM compilation than the one intended to be deployed on the blockchain.

To avoid these potential issues, consider specifying a specific compiler version in your pragmas.

So instead of using a floating pragma like `pragma solidity ^0.8.0;`, it is better to use a concrete compiler version like `pragma solidity 0.8.17;`.

More information can be found in the following links:

- [Consensys Audit of 1inch](https://consensys.net/diligence/audits/2020/12/1inch-liquidity-protocol/#unspecific-compiler-version-pragma)
- [Solidity docs](https://docs.soliditylang.org/en/latest/layout-of-sou

import {contract1, contract2} from "filename.sol";

File: Ethos-Core/contracts/TroveManager.sol
Line  936:    * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove.

...

Line 1155:    * this indicates that rewards have occured since the snapshot was made, and the user therefore has


c3phas G

# Codebase Optimization Report

## Auditor's Disclaimer

While we try our best to maintain readability in the provided code snippets, some functions have been truncated to highlight the affected portions.

It's important to note that during the implementation of these suggested changes, developers must exercise caution to avoid introducing vulnerabilities. Although the optimizations have been tested prior to these recommendations, it is the responsibility of the developers to conduct thorough testing again.

Code reviews and additional testing are strongly advised to minimize any potential risks associated with the refactoring process.

## Table of contents

- [Codebase Optimization Report](#codebase-optimization-report)
  - [Auditor's Disclaimer](#auditors-disclaimer)
  - [Table of contents](#table-of-contents)
  - [Codebase impressions](#codebase-impressions)
  - [Note on Gas estimates.](#note-on-gas-estimates)
  - [Pack structs by putting data types that can fit together next to each other](#pack-structs-by-putting-data-types-that-can-fit-together-next-to-each-other)
    - [We can pack start and end together by reducing their size to `uint48` since they are just timestamps(save 1 SLOT: 2K Gas)](#we-can-pack-start-and-end-together-by-reducing-their-size-to-uint48-since-they-are-just-timestampssave-1-slot-2k-gas)
    - [The function should first verify if msg.value \> 0 before performing any other operation(Save 7146  Gas)](#the-function-should-first-verify-if-msgvalue--0-before-performing-any-other-operationsave-7146--gas)
  - [Optimize the function `_mintIfThresholdMet()`](#optimize-the-function-_mintifthresholdmet)
    - [Only read state if we are going to execute the entire logic(Saves ~2000 gas for the state read)](#only-read-state-if-we-are-going-to-execute-the-entire-logicsaves-2000-gas-for-the-state-read)
  - [Optimize check order: avoid making any state reads/writes if we have to validate some function parameters](#optimize-check-order-avoid-making-any-st

File: /contracts/rwaOracles/RWADynamicOracle.sol
295:  struct Range {
296:    uint256 start;
297:    uint256 end;
298:    uint256 dailyInterestRate;
299:    uint256 prevRangeClosePrice;
300:  }

https://github.com/code-423n4/2023-09-ondo/blob/02ebedeeb85b708345a4deb53a2b543ecae160d0/contracts/bridge/SourceBridge.sol#L61-L74

### The function should first verify if msg.value > 0 before performing any other operation(Save 7146  Gas)

**In case of a revert on `msg.value == 0` we save 7146 gas on average from the protocol gas tests(to simulate the sad path ie `msg.value == 0` see the test modification)**

|        | Min    | Average | Median   | Max   |
| ------ | --- | ------- | ----- | ----- |
| Before | 9815    | 9815   | 9815 | 9815 |
| After  | 2669    | 2669   | 2669 | 2669 |


SAQ G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] | Avoid contract existence checks by using low level calls | 13 - more than | - |
| [G-02] | Internal functions only called once can be inlined to save gas | 6 | - |
| [G-03] | keccak256() should only need to be called on a specific string literal once | 1 | - |
| [G-04] | Optimize Names to save Gas | 5 | - |
| [G-05] | USE A MORE RECENT VERSION OF SOLIDITY | All files - 39 | - |
| [G-06] | Using PRIVATE rather than PUBLIC FOR Constants/Immutable, Saves Gas | 6 | - |
| [G-07] | Require()/Revert() string longer than 32 bytes cost extra gas | 10 | - |
| [G-08] | SPLITTING  REQUIRE() STATEMENTS THAT USE  && SAVES GAS | 4 | - |
| [G-09] | Setting the constructor to payable | 4 | - |
| [G-10] | Before transfer of  some functions, we should check some variables for possible gas save | 2 | - |
| [G-11] | Require() or revert() statements that check input arguments should be at the top of the function | 7 | - |
| [G-12] | Bytes constants are more efficient than String constants | 6 | - |
| [G-13] | Don't compare boolean expressions to boolean literals | 1 | - |
| [G-14] | State varaibles only set in the Constructor should be declared Immutable | 2 | - |
| [G-15] | Using storage instead of memory for structs/arrays saves gas | 5 | - |
| [G-16] | Duplicated require()/if() checks should be refactored to a modifier or function  | 1 | - |
| [G-17] | Use constants instead of type(uintx).max | 5 | - |
| [G-18] | A modifier used only once and not being inherited should be inlined to save gas | 3 | - |
| [G-19] | abi.encode() is less efficient than  abi.encodepacked() | 1 | - |
| [G-20] | Do not calculate constant | 1 | - |
| [G-21] | Structs can be packed into fewer storage slots by editing time variables | 1 | - |
| [G-22] | require() Should Be Used Instead Of assert() | 1 | - |
| [G-23] | Use assembly to emit events | 4 | - |
| [G-24] | Use hardcode address instead address(this) | 4 | - |
| [G-25] |

file: /contracts/contracts/ActivePool.sol

60        address _authorityAddress = address(AuthNoOwner(cdpManagerAddress).authority());

186            ICollSurplusPool(_account).increaseTotalSurplusCollShares(_shares);

272        uint256 oldRate = collateral.getPooledEthByShares(DECIMAL_PRECISION);

278            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == FLASH_SUCCESS_VALUE,

303            collateral.getPooledEthByShares(DECIMAL_PRECISION) == oldRate,

337        return collateral.balanceOf(address(this));

391        ICdpManagerData(cdpManagerAddress).syncGlobalAccountingAndGracePeriod(); // Accrue State First

418        ICdpManagerData(cdpManagerAddress).syncGlobalAccountingAndGracePeriod(); // Accrue State First

file: /contracts/contracts/BorrowerOperations.sol

364        vars.collShares = cdpManager.getCdpCollShares(_cdpId);

367        uint256 _cdpStEthBalance = collateral.getPooledEthByShares(vars.collShares);

372        vars.oldICR = EbtcMath._computeCR(_cdpStEthBalance, vars.debt, vars.price);

465        uint256 _netCollAsShares = collateral.getSharesByPooledEth(vars.netStEthBalance);

466        uint256 _liquidatorRewardShares = collateral.getSharesByPooledEth(LIQUIDATOR_REWARD);


Kaysoft Q

## [LC-01] AVOID FLOATING PRAGMA
Lock the prama version to ensure the contracts are deployed with the same version that they were tested with.
Consider changing
```
pragma solidity ^0.8.19;
```
to
```
pragma solidity 0.8.19;
```
files: all files
see: https://swcregistry.io/docs/SWC-103

## [LC-02] USE THE SAFECAST LIBRARY FOR CASTING VALUES TO AVOID OVERFLOW/UNDERFLOW
File: https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230L231
```
virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);
```

## [NC-01] Lines too long
File: https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L58-L60
The Solidity style guide recommends maximum of 120 characters per line of code. Use a formatter to ensure code are formatted to follow this guidelines.

```
event Initialize(address indexed baseToken, address indexed nft, uint128 virtualBaseTokenReserves, uint128 virtualNftReserves, uint56 changeFee, uint16 feeRate, bytes32 merkleRoot, bool useStolenNftOracle, bool payRoyalties);
    event Buy(uint256[] tokenIds, uint256[] tokenWeights, uint256 inputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount);
    event Sell(uint256[] tokenIds, uint256[] tokenWeights, uint256 outputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount);
```

// Code block 1 (unknown):
pragma solidity ^0.8.19;

// Code block 2 (unknown):
pragma solidity 0.8.19;

// Code block 3 (unknown):
virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);

// Code block 4 (unknown):
event Initialize(address indexed baseToken, address indexed nft, uint128 virtualBaseTokenReserves, uint128 virtualNftReserves, uint56 changeFee, uint16 feeRate, bytes32 merkleRoot, bool useStolenNftOracle, bool payRoyalties);
    event Buy(uint256[] tokenIds, uint256[] tokenWeights, uint256 inputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount);
    event Sell(uint256[] tokenIds, uint256[] tokenWeights, uint256 outputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount);


PetarTolev G

## Gas Optimization Issues

| ID     | Finding                                                                              |
| ------ | ------------------------------------------------------------------------------------ |
| [G-01] | Redundant `attributeProbabilities` Initialization in the `AiArenaHelper` Constructor |
| [G-02] | Utilize Cached Variable in Require Statement Checks                                  |
| [G-03] | Use `_spendAllowance` in `Neuron.burnFrom`                                           |
| [G-04] | Omit Redundant Allowance Check in `Neuron.claim`                                     |
| [G-05] | Cache Array Length in Loops                                                          |
| [G-06] | Reuse Cached Variables                                                               |
| [G-07] | Avoid Modifying Storage Variables with Zero                                          |

### [G-01] Redundant `attributeProbabilities` Initialization in the `AiArenaHelper` Constructor

GitHub: [Link](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L49), [Link](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L137)

The deployment cost is unnecessarily increased due to the `probabilities` being set twice in the `attributeProbabilities` mapping within the constructor: initially through the [addAttributeProbabilities](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L137) function and then within a [for loop](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L49). Therefore, it's advised to eliminate the `addAttributeProbabilities` call from the constructor.

Additionally, incorporate the `require(probabilities.length == 6, "Invalid number of attribute arrays");` statement in the constructor for validat

### [G-04] Omit Redundant Allowance Check in `Neuron.claim`

The [allowance check](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L139-L142) is already performed within `ERC20.transferFrom` -> `ERC20._spendAllowance`, making it unnecessary.

[ERC20.\_spendAllowance](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b438cb695a1ac520cee6678610b161b1d5df4d9c/contracts/token/ERC20/ERC20.sol#L337)

### [G-05] Cache Array Length in Loops

In [FighterFarm.redeemMintPass](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L233), caching `mintpassIdsToBurn.length` will decrease gas costs.

#### Recommendation

AiArenaHelper.sol#L49, AiArenaHelper.sol#L137, Neuron.sol#L139-L142, ERC20.sol#L337, FighterFarm.sol#L233

ERC20.sol, Neuron.sol, AiArenaHelper.sol, FighterFarm.sol


oberon G

### REFACTOR SIMILAR REQUIRE()/REVERT()
---
```javascript
function oracleType() private view {
    require(
      fTokenToOracleType[fToken] == OracleType.CHAINLINK,
      "OracleType must be Chainlink"
    );
}
255: oracleType();
280: oracleType();
```

[OndoPriceOracleV2:255_280](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L255)

### USE MULTIPLE REQUIRE() FOR &&
---
```javascript
    require(answeredInRound >= roundId);
    require((updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),
      "Chainlink oracle price is stale"
    );
```

[OndoPriceOracleV2:293](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L293)
[CTokenCash:46](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CTokenCash.sol#L46)
[CTokenModified:46](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenModified.sol#L46)

### USE FUNCTIONS INSTEAD OF MODIFIERS
---
```javascript
function updateEpoch() private view {
    transitionEpoch();
}
```

[CashManager:557](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L557)
[CashManager:883](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L883)
[CashFactory:151](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashFactory.sol#L151)
[CashKYCSenderFactory:162](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderFactory.sol#L162)
[CashKYCSenderReceiverFactory:162](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderReceiverFactory.sol#L162)

### X += Y COSTS MORE THAN X = X + Y FOR STATE VARIABLES
---
```javascript
    currentEpoch = currentEpoch + epochDifference;
```

[CashManager.sol:582_630_649_681](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L582)

// Code block 1 (javascript):
function oracleType() private view {
    require(
      fTokenToOracleType[fToken] == OracleType.CHAINLINK,
      "OracleType must be Chainlink"
    );
}
255: oracleType();
280: oracleType();

// Code block 2 (javascript):
require(answeredInRound >= roundId);
    require((updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),
      "Chainlink oracle price is stale"
    );

// Code block 3 (javascript):
function updateEpoch() private view {
    transitionEpoch();
}

// Code block 4 (javascript):
currentEpoch = currentEpoch + epochDifference;

OndoPriceOracleV2.sol#L255, OndoPriceOracleV2.sol#L293, CTokenCash.sol#L46, CTokenModified.sol#L46, CashManager.sol#L557, CashManager.sol#L883, CashFactory.sol#L151, CashKYCSenderFactory.sol#L162, CashKYCSenderReceiverFactory.sol#L162, CashManager.sol#L582

CashManager.sol, CTokenCash.sol, CTokenModified.sol, CashKYCSenderFactory.sol, CashFactory.sol, OndoPriceOracleV2.sol, CashKYCSenderReceiverFactory.sol


lukris02 G

# Gas Optimizations Report for Biconomy - Smart Contract Wallet contest
## Overview
During the audit, 2 gas issues were found.

Total savings ~455.

№ | Title | Instance Count | Saved
--- | --- | --- | ---
G-1 | Use unchecked blocks for incrementing | 10 | 350
G-2 | Use unchecked blocks for subtractions where underflow is impossible | 3 | 105

## Gas Optimizations Findings(2)
### G-1. Use unchecked blocks for incrementing
##### Description
In Solidity 0.8+, there’s a default overflow and underflow check on unsigned integers. In the loops, "i" (or other variable) will not overflow because the loop will run out of gas before that.
##### Instances
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L216
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L502)
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L124
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L100
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L107
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L112
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L114
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L128
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L134
- https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.

for (uint256 i; i < n;) {
 // ...
 unchecked { ++i; }
}

SmartAccount.sol#L216, SmartAccount.sol#L502, ModuleManager.sol#L124, EntryPoint.sol#L100, EntryPoint.sol#L107, EntryPoint.sol#L112, EntryPoint.sol#L114, EntryPoint.sol#L128, EntryPoint.sol#L134


MLmochi Q

By MLmochi
email wooddustsniffer@gmail.com
discord MLmochi#0400
code4rena contest: https://github.com/code-423n4/2023-08-dopex/tree/main

Overview
- LOW 01- Conflicting reservesIndex mappings
- LOW 02- Incorrect reserveTokens element popped

---
# LOW 01- Conflicting reservesIndex mappings
## Description

In the Core-contract: `contract/core/RdpxV2Core.sol` the function:`addAssetTotokenReserves(address _asset, string memory _assetSymbol)`
allows the **Admin** to add new Assets to the reserve. It checks that the reserveAssets's **address** is not already present, **however** it does not check if the input `string memory _assetSymbol` is already present.

Repo Link: https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L240C36-L240C36

```
  function addAssetTotokenReserves(
    address _asset,
    string memory _assetSymbol
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(_asset != address(0), "RdpxV2Core: asset cannot be 0 address");

    for (uint256 i = 1; i < reserveAsset.length; i++) {
      require(
        reserveAsset[i].tokenAddress != _asset,
        "RdpxV2Core: asset already exists"
      );
    }

    ReserveAsset memory asset = ReserveAsset({
      tokenAddress: _asset,
      tokenBalance: 0,
      tokenSymbol: _assetSymbol
    });
    reserveAsset.push(asset);
    reserveTokens.push(_assetSymbol);

    reservesIndex[_assetSymbol] = reserveAsset.length - 1;

    emit LogAssetAddedTotokenReserves(_asset, _assetSymbol);
  }
```

Adding a new reserveAsset with an existing `_assetSymbol` will render the entire previous existing `reseveAsset` inaccessible, which can result a loss of user funds. Due to the dependency on `reservesIndex` to extract a `reserveAsset` in this contract essential functions such as :

- `_purchaseOptions(...)`
- `_stake(...)`
- `_transfer(...)`
- `_bondWithDelegate(...)`
- `settle(...)
- etc ..
### Assessed type
**admin privilege**
## Burden of proof
Add

  function settle(
    uint256[] memory optionIds
  )
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    returns (uint256 amountOfWeth, uint256 rdpxAmount)
  {
    _whenNotPaused();
    (amountOfWeth, rdpxAmount) = IPerpetualAtlanticVault(
      addresses.perpetualAtlanticVault
    ).settle(optionIds);
    for (uint256 i = 0; i < optionIds.length; i++) {
      optionsOwned[optionIds[i]] = false;
    }

    reserveAsset[reservesIndex["WETH"]].tokenBalance += amountOfWeth;
    reserveAsset[reservesIndex["RDPX"]].tokenBalance -= rdpxAmount;

    emit LogSettle(optionIds);
  }

// Code block 1 (unknown):
function addAssetTotokenReserves(
    address _asset,
    string memory _assetSymbol
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(_asset != address(0), "RdpxV2Core: asset cannot be 0 address");

    for (uint256 i = 1; i < reserveAsset.length; i++) {
      require(
        reserveAsset[i].tokenAddress != _asset,
        "RdpxV2Core: asset already exists"
      );
    }

    ReserveAsset memory asset = ReserveAsset({
      tokenAddress: _asset,
      tokenBalance: 0,
      tokenSymbol: _assetSymbol
    });
    reserveAsset.push(asset);
    reserveTokens.push(_assetSymbol);

    reservesIndex[_assetSymbol] = reserveAsset.length - 1;

    emit LogAssetAddedTotokenReserves(_asset, _assetSymbol);
  }


bloxploit G

# Gas Optimisation Report

## Require,Asset and Revert string size should be small

### Require

```bash
./paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol:49:        require(!Address.isContract(paymasterId), "Paymaster Id can not be smart contract address");

```

Occurance:[VerifyingSingletonPaymaster.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol)

### Revert

```bash
./paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol:42:        revert("Deposit must be for a paymasterId. Use depositFor");
./paymasters/BasePaymaster.sol:52:        revert("must override");
```

Occurance:[VerifyingSingletonPaymaster.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol),[BasePaymaster.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol)

## Empty block should be removed or made to revert something.

```bash
./SmartAccount.sol:550:    receive() external payable {}
./aa-4337/core/EntryPoint.sol:119:                try aggregator.validateSignatures(ops, opa.signature) {}
./aa-4337/core/EntryPoint.sol:458:                    try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {}
./SmartAccountNoAuth.sol:540:    receive() external payable {}
```

Occurance:[SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol),[Entrypoint.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol),[SmartAccountNoAuth.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol)

// Code block 1 (bash):
./paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol:49:        require(!Address.isContract(paymasterId), "Paymaster Id can not be smart contract address");

// Code block 2 (bash):
./paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol:42:        revert("Deposit must be for a paymasterId. Use depositFor");
./paymasters/BasePaymaster.sol:52:        revert("must override");

// Code block 3 (bash):
./SmartAccount.sol:550:    receive() external payable {}
./aa-4337/core/EntryPoint.sol:119:                try aggregator.validateSignatures(ops, opa.signature) {}
./aa-4337/core/EntryPoint.sol:458:                    try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {}
./SmartAccountNoAuth.sol:540:    receive() external payable {}


Kaysoft G

## [G-01] USE {unchecked ++i}/{unchecked i++} for ++i/i++ when operation is not possible to overflow.

Files:
- [Ethos-Core/contracts/CollateralConfig.sol#L56](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/CollateralConfig.sol#L56)

- [Ethos-Core/contracts/StabilityPool.sol#L351](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L351)

- [Ethos-Core/contracts/StabilityPool.sol#L397](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L397)

- [Ethos-Core/contracts/StabilityPool.sol#L640](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L640)

- [Ethos-Core/contracts/StabilityPool.sol#L810](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L810)

- [Ethos-Core/contracts/StabilityPool.sol#L831](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L831)

- [Ethos-Core/contracts/StabilityPool.sol#L859](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L859)

## [G-02] require()/revert() strings that are longer that 32 bytes cost extra gas

Every extra bytes more than the original 32 incurs extra MSTORE which is 3 gas.
see: https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc#consider-having-short-revert-strings

Files:
- [Ethos-Vault/contracts/ReaperVaultV2.sol#L150](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Vault/contracts/ReaperVaultV2.sol#L150)

```
require(!emergencyShutdown, "Cannot add strategy during emergency shutdown");
```

## [G-03]  x = x + y cost less gas than x +=y

// Code block 1 (unknown):
require(!emergencyShutdown, "Cannot add strategy during emergency shutdown");

CollateralConfig.sol#L56, StabilityPool.sol#L351, StabilityPool.sol#L397, StabilityPool.sol#L640, StabilityPool.sol#L810, StabilityPool.sol#L831, StabilityPool.sol#L859, ReaperVaultV2.sol#L150


Polaris_tow G

## SETTING THE CONSTRUCTOR TO PAYABLE
 Saves ~13 gas per instance
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L90
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L53
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L143
```
    constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
        factory = payable(_factory);
        royaltyRegistry = _royaltyRegistry;
        stolenNftOracle = _stolenNftOracle;
    }
```
## FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE
If a function modifier or require such as onlyOwner/onlyX is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2) which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L129
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L135
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L141
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L148
`    function withdraw(address token, uint256 amount) public onlyOwner {`

## CAN MAKE THE VARIABLE OUTSIDE THE LOOP TO SAVE GAS
Make it outside and only use it inside.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L106-L111
```

        for (uint256 i = 0; i < buys.length; i++) {
            if (buys[i].isPublicPool) {
                // execute the buy against a public pool
                uint256 inputAmount = Pair(buys[i].pool).nftBuy{value: buys[i].baseTokenAmount}(
                    buys[i].tokenIds, buys[i].baseTokenAmount, 0
                );

// Code block 1 (unknown):
constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
        factory = payable(_factory);
        royaltyRegistry = _royaltyRegistry;
        stolenNftOracle = _stolenNftOracle;
    }

EthRouter.sol#L90, Factory.sol#L53, PrivatePool.sol#L143, Factory.sol#L129, Factory.sol#L135, Factory.sol#L141, Factory.sol#L148, EthRouter.sol#L106-L111


Kaysoft Q

## [NC-01] Use named imports

Files: All files.

Consider using named imports like below

```jsx
import {CTokenCash} from "./CTokenCash.sol";

```

instead of

```jsx
import "./CTokenCash.sol";

```

## [NC-02] Include the parameters in NatSpec comments
- [KYCRegistryClientInitializable.sol#L58](https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/cash/kyc/KYCRegistryClientInitializable.sol#L58)

```jsx
/**
   * @dev Internal function to future-proof parent linearization. Matches OZ
   *      upgradeable suggestions
   */
  function __KYCRegistryClientInitializable_init_unchained(
    address _kycRegistry,
    uint256 _kycRequirementGroup
  ) internal onlyInitializing {
    _setKYCRegistry(_kycRegistry);
    _setKYCRequirementGroup(_kycRequirementGroup);
  }

```

## [L-01] Use latest Solidity stable version 0.8.17

Files: All files
Latest stable versions of Soildity compilers have bugfixes and security improvement.

see: https://swcregistry.io/docs/SWC-102
#### Recommended Mitigation Steps
Consider using latest Solidity version.

## [L-02] Avoid floating pragma
Files: All files

See: https://swcregistry.io/docs/SWC-103

#### Recommended Mitigation Steps
Consider using fixed pragma version like below

```jsx
pragma solidity 0.8.17;

```

## [L-03] Solidity compiler optimization can be problematic

```
const config: HardhatUserConfig = {
  solidity: {
    compilers: [
      {
        version: "0.8.16",
        settings: {
          optimizer: {
            enabled: true,
            runs: 100,
          },
        },
      },
```

### Description

Protocol has enabled optional compiler optimizations in Solidity. There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.

Therefore, it is unclear how well they are being tested and

// Code block 1 (jsx):
import {CTokenCash} from "./CTokenCash.sol";

// Code block 2 (jsx):
import "./CTokenCash.sol";

// Code block 3 (jsx):
/**
   * @dev Internal function to future-proof parent linearization. Matches OZ
   *      upgradeable suggestions
   */
  function __KYCRegistryClientInitializable_init_unchained(
    address _kycRegistry,
    uint256 _kycRequirementGroup
  ) internal onlyInitializing {
    _setKYCRegistry(_kycRegistry);
    _setKYCRequirementGroup(_kycRequirementGroup);
  }

// Code block 4 (jsx):
pragma solidity 0.8.17;

// Code block 5 (unknown):
const config: HardhatUserConfig = {
  solidity: {
    compilers: [
      {
        version: "0.8.16",
        settings: {
          optimizer: {
            enabled: true,
            runs: 100,
          },
        },
      },


0xpanicError G

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol

# Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-1](#GAS-1) | Intialise non-zero variables at the time of declaration | 1 |
| [GAS-2](#GAS-2) | Cache array length in memory when called in loops | 5 |
| [GAS-3](#GAS-3) | If value is read only once, load directly from storage | 2 |
| [GAS-4](#GAS-4) | No need to loop to add or adjust totalWeight | 2 |
### [GAS-1] Intialise non-zero variables at the time of declaration
If a variable will hold a non-zero value then it should be set to a non-zero value at time of declaration.

`preDepositPrice` is declared and later set to either `10 ** 18` or `(10 ** 18 * underlyingValue) / totalSupply`. Instead it can be set to `10**18` and
if total supply is not zero, set to `(10 ** 18 * underlyingValue) / totalSuppl`.

*SSTORE requires 20000 gas from zero to non-zero but only 2900 from non-zero to non-zero*

*Instances (1)*:
```solidity
File: contracts/SafEth/SafEth.sol

78:   uint256 preDepositPrice; // Price of safETH in regards to ETH

79:   if (totalSupply == 0)

80:      preDepositPrice = 10 ** 18; // initializes with a price of 1

81:   else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
```

### [GAS-2] Cache array length in memory when called in loops
Whenever looping through an array, cache the array length in memory to save gas.

In the given loops, `derivativesCount` is called from storage for each iteration. Instead set `derivativesCount` in memory and call that
in loops to save gas inside each function.

*SLOAD requires 100 gas while MLOAD only requires 3 gas*

*Instances (5)*:
```solidity
File: contracts/SafEth/SafEth.sol

71:   for (uint i = 0; i < derivativeCount; i++)

84:   for (uint i = 0; i < derivativeCount; i++)

113:  for (uint256 i = 0; i < derivativeCount; i++)

140:  for (uint i = 0; i < derivativeCount; i++)

147:  for (uint i = 0; i < derivativeCount; i++)
```

### [GAS-3] If value is read only

// Code block 1 (solidity):
File: contracts/SafEth/SafEth.sol

78:   uint256 preDepositPrice; // Price of safETH in regards to ETH

79:   if (totalSupply == 0)

80:      preDepositPrice = 10 ** 18; // initializes with a price of 1

81:   else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

// Code block 2 (solidity):
File: contracts/SafEth/SafEth.sol

71:   for (uint i = 0; i < derivativeCount; i++)

84:   for (uint i = 0; i < derivativeCount; i++)

113:  for (uint256 i = 0; i < derivativeCount; i++)

140:  for (uint i = 0; i < derivativeCount; i++)

147:  for (uint i = 0; i < derivativeCount; i++)

File: contracts/SafEth/SafEth.sol

78:   uint256 preDepositPrice; // Price of safETH in regards to ETH

79:   if (totalSupply == 0)

80:      preDepositPrice = 10 ** 18; // initializes with a price of 1

81:   else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

File: contracts/SafEth/SafEth.sol

71:   for (uint i = 0; i < derivativeCount; i++)

84:   for (uint i = 0; i < derivativeCount; i++)

113:  for (uint256 i = 0; i < derivativeCount; i++)

140:  for (uint i = 0; i < derivativeCount; i++)

147:  for (uint i = 0; i < derivativeCount; i++)


pauliax G

* Maybe this could be extracted to a constant because this data does not change:
```solidity
  bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
```

* I think ```PaymasterHelpers``` does not actually use ```ECDSA``` library:
```solidity
  library PaymasterHelpers {
    using ECDSA for bytes32;
```

// Code block 1 (solidity):
bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));

// Code block 2 (solidity):
library PaymasterHelpers {
    using ECDSA for bytes32;

bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));

library PaymasterHelpers {
    using ECDSA for bytes32;


Aymen0909 G

# Gas Optimizations

## Summary

|               | Issue         | Instances     |
| :-------------: |:-------------|:-------------:|
| 1  | Variables inside struct should be packed to save gas  | 2  |
| 2  | `storage` variable should be cached into `memory` variables instead of re-reading them  |  6 |
| 3  | Use `unchecked` blocks to save gas  |  5 |
| 4  | `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state variables  |  9 |

## Findings

### 1- Variables inside `struct` should be packed to save gas :

As the solidity EVM works with 32 bytes, variables less than 32 bytes should be packed inside a struct so that they can be stored in the same slot, each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.

There are 2 instance of this issue:

File: interfaces/vault/IVaultRegistry.sol [Line 7-22](https://github.com/code-423n4/2023-01-popcorn/blob/main/src/interfaces/vault/IVaultRegistry.sol#L7-L22)

```solidity
struct Config {
    bool allowed;
    uint256 decimals;
    uint256 MCR;
    uint256 CCR;
}
```

As the `MCR` and `CCR` variables values can't really overflow **2^96** and the `decimals` value (usually less than 18) can't overflow **2^8**, so their values should be packed in order to save gas, the struct should be modified as follow :

```solidity
struct Config {
    bool allowed;
    uint8 decimals;
    uint96 MCR;
    uint96 CCR;
}
```

File: ReaperVaultV2.sol [Line 25-33](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L25-L33)

```solidity
struct StrategyParams {
    uint256 activation; // Activation block.timestamp
    uint256 feeBPS; // Performance fee taken from profit, in BPS
    uint256 allocBPS; // Allocation in BPS of vault's total assets
    uint256 allocated; // Amount of capital allocated to this strategy
    uint256 gains; // Total returns that Strategy has realized for Vault
    uint256 lo

// Code block 1 (solidity):
struct Config {
    bool allowed;
    uint256 decimals;
    uint256 MCR;
    uint256 CCR;
}

// Code block 2 (solidity):
struct Config {
    bool allowed;
    uint8 decimals;
    uint96 MCR;
    uint96 CCR;
}

struct Config {
    bool allowed;
    uint256 decimals;
    uint256 MCR;
    uint256 CCR;
}

struct Config {
    bool allowed;
    uint8 decimals;
    uint96 MCR;
    uint96 CCR;
}


HHK G

### GAS1: `reserveTokens` is not needed

#### Technical Details

In the core contract, the storage array [`reserveTokens`](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L70) is not needed as the token symbols are being stored in the `reserveAsset` array.

#### Recommendation

Remove `reserveTokens` and update the function [`removeAssetFromtokenReserves()`](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L270) to use the `reserveAsset` array.

### GAS2: If `minAmount` is set then no need to compute `minOut`.

#### Technical Details

In the function [`_curveSwap()`](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L515) of the core contract. `minOut` is always computed although it won't be use if `minAmount` was set in the params.

#### Recommendation

Add an if statement to not compute `minOut` if `minAmount > 0`.

### GAS3: Simplify `_calculateAmounts()` ratio

#### Technical Details

In the function [`_calculateAmounts()`](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L598) of the core contract, the `amount1` is calculated by recomputing the rdpx value and then finding the ratio of the bond amount.

But because the ratio of ETH and RDPX is a constant 75/25, we could just take 25% of the amount (rdpx part) and then apply the delegate fee.

#### Recommendation

Remove the oracle call and replace the `amount1` calculation with:
```solidity
// amount required for delegatee
amount1 = _amount * RDPX_RATIO_PERCENTAGE / (100 * DEFAULT_PRECISION)
// account for delegate fee
amount1 = (amount1 * (100e8 - _delegateFee)) / 1e10;
```

### GAS4: `_whenNotPaused()` not needed in `mint()`

#### Technical Details

In the [`mint()`](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd

// Code block 1 (solidity):
// amount required for delegatee
amount1 = _amount * RDPX_RATIO_PERCENTAGE / (100 * DEFAULT_PRECISION)
// account for delegate fee
amount1 = (amount1 * (100e8 - _delegateFee)) / 1e10;

RdpxV2Core.sol#L70, RdpxV2Core.sol#L270, RdpxV2Core.sol#L515, RdpxV2Core.sol#L598


Fon G

Function innerHandleOp is external but requires it be called from the contract entry point contract
```solidity
    function innerHandleOp(bytes calldata callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) {
        uint256 preGas = gasleft();
        require(msg.sender == address(this), "AA92 internal call only");
```
consider changing the function to an internal function

// Code block 1 (solidity):
function innerHandleOp(bytes calldata callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) {
        uint256 preGas = gasleft();
        require(msg.sender == address(this), "AA92 internal call only");


0xanmol G

## Unnecessary use of multiple mapping for the same key

In `LRTDepositConfig.sol` there are three mapping with the same key of token address.

### Code Line

https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTConfig.sol#L15C4-L17C78

```js

// @audit use struct instead
    mapping(address token => bool isSupported) public isSupportedAsset;
    mapping(address token => uint256 amount) public depositLimitByAsset;
    mapping(address token => address strategy) public override assetStrategy;

```

 Instead of using multiple storage to store data corresponding to the same key, it can use a struct to store all data and update this struct when necessary. This way only one storage slot will be used saving a massive amount of gas, also it will be a lot easier to query the data this way.

### Example

```js

struct Asset {
   bool isSupportedAsset;
   uint256 depositLimit;
   address strategy;
}

mapping(address token => Asset asset) public asset;

```

// Code block 1 (js):
// @audit use struct instead
    mapping(address token => bool isSupported) public isSupportedAsset;
    mapping(address token => uint256 amount) public depositLimitByAsset;
    mapping(address token => address strategy) public override assetStrategy;

// Code block 2 (js):
struct Asset {
   bool isSupportedAsset;
   uint256 depositLimit;
   address strategy;
}

mapping(address token => Asset asset) public asset;


hunter_w3b Q

## [L-01] Missing Checks Allow Collateral Balance to be Drained via Flash Loans

The ActivePool contract allows flash loans to be taken out of the collateral token (stETH) held by the contract. However, there are no checks to prevent multiple flash loans from being taken out simultaneously that could drain the entire collateral balance.

The maxFlashLoan() function returns the full collateral balance without accounting for any outstanding loans. And the flashLoan() function does not track the amount loaned out or decrement it from the available balance.

```solidity
File: contracts/contracts/ActivePool.sol

    function maxFlashLoan(address token) public view override returns (uint256) {
        if (token != address(collateral)) {
            return 0;
        }

        if (flashLoansPaused) {
            return 0;
        }

        return collateral.balanceOf(address(this));
    }

```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/ActivePool.sol#L328-L339

This means if two or more flash loans are taken at the same time for the full balance amount, they could both succeed even though together they exceed the actual available balance. When the loans are repaid, there may be an insufficient balance to return one or both of the principal+fee amounts.

This is a security issue as an attacker could theoretically empty out the contract's collateral reserves by taking out multiple flash loans in quick succession before repayments are processed.

To remedy this, the contract should track the amount of loans outstanding and deduct that from the max loan amount reported. Individual loan origination should also fail if it would exceed the updated remaining balance amount.

## [L-02] Lack of Validation Allows Unauthorized Accounts to Take Out Flash Loans

The flashLoan() function in the ActivePool contract does not validate or restrict the flash loan receiver address in any way. Any external account is able to be passed as the receiver

File: contracts/contracts/ActivePool.sol

    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external override returns (bool) {
        require(amount > 0, "ActivePool: 0 Amount");
        uint256 fee = flashFee(token, amount); // NOTE: Check for `token` is implicit in the requires above // also checks for paused
        require(amount <= maxFlashLoan(token), "ActivePool: Too much");

        uint256 amountWithFee = amount + fee;
        uint256 oldRate = collateral.getPooledEthByShares(DECIMAL_PRECISION);

        collateral.transfer(address(receiver), amount);

        // Callback
        require(
            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == FLASH_SUCCESS_VALUE,
            "ActivePool: IERC3156: Callback failed"
        );

        // Transfer of (principal + Fee) from flashloan receiver
        collateral.transferFrom(address(receiver), address(this), amountWithFee);

        // Send earned fee to designated recipient
        collateral.transfer(feeRecipientAddress, fee);

        // Check new balance
        // NOTE: Invariant Check, technically breaks CEI but I think we must use it
        // NOTE: This means any balance > systemCollShares is stuck, this is also present in LUSD as is

        // NOTE: This check effectively prevents running 2 FL at the same time
        //  You technically could, but you'd be having to repay any amount below systemCollShares to get Fl2 to not revert
        require(
            collateral.balanceOf(address(this)) >= collateral.getPooledEthByShares(systemCollShares),
            "ActivePool: Must repay Balance"
        );
        require(
            collateral.sharesOf(address(this)) >= systemCollShares,
            "ActivePool: Must repay Share"
        );
        require(
            collateral.getPooledEthByShares(DECIMAL_PRECISION) == oldRate,
            "ActivePool: Should keep same collateral share

// Code block 1 (solidity):
File: contracts/contracts/ActivePool.sol

    function maxFlashLoan(address token) public view override returns (uint256) {
        if (token != address(collateral)) {
            return 0;
        }

        if (flashLoansPaused) {
            return 0;
        }

        return collateral.balanceOf(address(this));
    }


eyexploit G

# Finding 1 :  check `borrower == liquidator` before getting KYC status to save the gas

https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenCash.sol#L1013

Above check is performed before checking the KYC status for both borrower and liquidator. Here KYC status is fetching through an external call to the KYC registry contract which cost high amount of gas and if borrower and liquidator are the same then there is unnecessary wastage of gas.

We might think vice versa, where borrower and liquidator are different, but unauthorize. But again it just perform equal to check, which is much cheaper than the external call in above case.

*Recommendation* : Make sure above check execute before the
https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenCash.sol#L997-L998

# Finding 2 : `transferTokens` checking twice for KYC status for the same user

Tokens which are getting transferred via `transfer` function have same `spender` and `src`, which means in the `transferTokens` function it will check the KYC status twice for the same address. Its make an external call to the registry contract to get the KYC status for passed address, and doing it twice waste the gas.

**File: contracts/lending/tokens/cCash/CTokenCash.sol**

```
  function transferTokens(
    address spender,
    address src,
    address dst,
    uint tokens
  ) internal returns (uint) {
    /* Revert if KYC not valid */
    require(_getKYCStatus(spender), "Spender not KYC'd");
    require(_getKYCStatus(src), "Source not KYC'd");
    ...
}
```
**File: contracts/lending/tokens/cCash/CTokenCash.sol**
```
  function transfer(
    address dst,
    uint256 amount
  ) external override nonReentrant returns (bool) {
    return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR;
  }
```

*Recommendation*
consider modifiy `transferTokens` function to below

```

// Code block 1 (unknown):
function transferTokens(
    address spender,
    address src,
    address dst,
    uint tokens
  ) internal returns (uint) {
    /* Revert if KYC not valid */
    require(_getKYCStatus(spender), "Spender not KYC'd");
    require(_getKYCStatus(src), "Source not KYC'd");
    ...
}

// Code block 2 (unknown):
function transfer(
    address dst,
    uint256 amount
  ) external override nonReentrant returns (bool) {
    return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR;
  }


cryptonue Q

# [L] in Factory, create private pool doesn't check required variable already initialize in Constructor (or initialize function)

The following variable doesn't initialize up until the admin/owner manually set via dedicated function. This resulting when create private pool potential to failure since there is also no check in `create()` function if the variables already setted.

- PrivatePoolMetadata
- PrivatePoolImplementation

```js
File: Factory.sol
129:     function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {
130:         privatePoolMetadata = _privatePoolMetadata;
131:     }
132:
133:     /// @notice Sets the private pool implementation contract that newly deployed proxies point to.
134:     /// @param _privatePoolImplementation The private pool implementation contract.
135:     function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {
136:         privatePoolImplementation = _privatePoolImplementation;
137:     }
```

# [L] Solmate’s SafeTransferLib doesn’t check whether the ERC20 contract exists

Caviar use Solmate’s SafeTransferLib when transfering ERC20 token which contains a common issue.

Solmate’s SafeTransferLib, which is often used to interact with non-compliant/unsafe ERC20 tokens, does not check whether the ERC20 contract exists. The following code will not revert in case the token doesn’t exist (yet).

This is stated in the Solmate library:

https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol#L9

# [L] Two step transfer ownership

Caviar use solmate's Owned library (`import {Owned} from "solmate/auth/Owned.sol";`) which doesn't have a two step transfer ownership.

Recommend considering implementing a two step process where the owner or admin nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.

# [L] Un

File: Factory.sol
141:     function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
142:         protocolFeeRate = _protocolFeeRate;
143:     }

// Code block 1 (js):
File: Factory.sol
129:     function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {
130:         privatePoolMetadata = _privatePoolMetadata;
131:     }
132:
133:     /// @notice Sets the private pool implementation contract that newly deployed proxies point to.
134:     /// @param _privatePoolImplementation The private pool implementation contract.
135:     function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {
136:         privatePoolImplementation = _privatePoolImplementation;
137:     }


DavidGiladi Q

### Low Issues
|Title|Issue|Instances|
|-|:-|:-:|
|[L-1] Burn functions must be protected with a modifier | [Burn functions must be protected with a modifier](#burn-functions-must-be-protected-with-a-modifier) | 4 |
|[L-2] Divide before multiply | [Divide before multiply](#divide-before-multiply) | 2 |
|[L-3] ERC20 Approve Call is Not Safe | [ERC20 Approve Call is Not Safe](#erc20-approve-call-is-not-safe) | 3 |
|[L-4] Fee/Rate Caps Missing in Smart Contracts | [Fee/Rate Caps Missing in Smart Contracts](#feerate-caps-missing-in-smart-contracts) | 2 |
|[L-5] Calls inside a loop | [Calls inside a loop](#calls-inside-a-loop) | 21 |
|[L-6] Reentrancy vulnerabilities | [Reentrancy vulnerabilities](#reentrancy-vulnerabilities-2) | 14 |
|[L-7] Arrays can grow without a way to shrink them | [Arrays can grow without a way to shrink them](#arrays-can-grow-without-a-way-to-shrink-them) | 1 |
|[L-8] Unsafe Cast Unsigned to Signed | [Unsafe Cast Unsigned to Signed](#unsafe-cast-unsigned-to-signed) | 3 |
|[L-9] Zero-value transfers may revert | [Zero-value transfers may revert](#zero-value-transfers-may-revert) | 5 |

Total: 9 issues

### Non-Critical Issues
|Title|Issue|Instances|
|-|:-|:-:|
|[N-1] Do not calculate constants | [Do not calculate constants](#do-not-calculate-constants) | 7 |
|[N-2] Cyclomatic complexity | [Cyclomatic complexity](#cyclomatic-complexity) | 1 |
|[N-3] Dead-code | [Dead-code](#dead-code) | 1 |
|[N-4] Else block not required | [Else block not required](#else-block-not-required) | 7 |
|[N-5] Hardcoded addresses in contract | [Hardcoded addresses in contract](#hardcoded-addresses-in-contract) | 3 |
|[N-6] Interfaces Should Be Defined in Separate Files From Their Usage | [Interfaces Should Be Defined in Separate Files From Their Usage](#interfaces-should-be-defined-in-separate-files-from-their-usage) | 1 |
|[N-7] Token contract should have a blacklist function or modifier | [Token contract should have a blacklist function or modifier](#token-contract-s

Line: 188          function _burn(address _target, uint256 _amount) internal

Line: 79          function burn(uint256 _wusdaAmount) external override returns (uint256 _usdaAmount)


hals Q

# Findings Summary

| ID              | Title                                                                       | Severity     |
| --------------- | --------------------------------------------------------------------------- | ------------ |
| [L-01](#l-01)   | `isOtherEarningsClaimable(address user)`: division by zero is not prevented | Low          |
| [L-02](#l-02)   | Different pools might use different `esLBRBoost` contract                   | Low          |
| [L-03](#l-03)   | `ProtocolRewardsPool` contract: any user can free mint 3 esLBR tokens       | Low          |
| [L-04](#l-04)   | LBR & esLBR tokens addresses might be changed in different contracts        | Low          |
| [NC-01](#nc-01) | Boolean is compared to a boolean                                            | Non Critical |
| [NC-02](#nc-02) | `_checkHealth` function visibility must be public                           | Non Critical |
| [NC-03](#nc-03) | Wrong error message                                                         | Non Critical |

# Low

## [L-01] `isOtherEarningsClaimable(address user)`: division by zero is not prevented

## Details

In `EUSDMiningIncentives.sol`/`isOtherEarningsClaimable(address user)` function : the result of stakedOf(user) could be zero if he doesn't have any borrowed amount in any pool.

## Impact

This will cause run-time error when invoking `isOtherEarningsClaimable(address user)` or `getReward()` functions with an address with zero borrowed amounts.

## Proof of Concept

```solidity
File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 188-190:
   function isOtherEarningsClaimable(address user) public view returns (bool) {
        return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
    }
```

```solidity
File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 193:
    require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");
```

```solidity
File: 2023

// Code block 1 (solidity):
File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 188-190:
   function isOtherEarningsClaimable(address user) public view returns (bool) {
        return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
    }

// Code block 2 (solidity):
File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 193:
    require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");

File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 188-190:
   function isOtherEarningsClaimable(address user) public view returns (bool) {
        return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
    }

File: 2023-06-lybra/contracts/lybra/miner/EUSDMiningIncentives.sol
Line 193:
    require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");