Rolezn Q

## QA Summary<a name="QA Summary">

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#low1-ierc20-approve-is-deprecated) | IERC20 `approve()` Is Deprecated | 3 |
| [LOW‑2](#low2-condition-will-not-revert-when-blocktimestamp-is--to-the-compared-variable) | Condition will not revert when `block.timestamp` is `==` to the compared variable | 2 |
| [LOW‑3](#low3-consider-the-case-where-totalsupply-is-0) | Consider the case where `totalsupply` is 0 | 6 |
| [LOW‑4](#low4-constant-decimal-values) | Constant decimal values | 13 |
| [LOW‑5](#low5-function-can-risk-gas-exhaustion-on-large-receipt-calls-due-to-multiple-mandatory-loops) | Function can risk gas exhaustion on large receipt calls due to multiple mandatory loops | 1 |
| [LOW‑6](#low6-minting-tokens-to-the-zero-address-should-be-avoided) | Minting tokens to the zero address should be avoided | 5 |
| [LOW‑7](#low7-the-contract-should-approve0-first) | The Contract Should `approve(0)` First | 1 |
| [LOW‑8](#low8-contracts-are-not-using-their-oz-upgradeable-counterparts) | Contracts are not using their OZ Upgradeable counterparts | 37 |
| [LOW‑9](#low9-no-storage-gap-for-upgradeable-contracts) | No Storage Gap For Upgradeable Contracts | 1 |
| [LOW‑10](#low10-use-safecast-to-safely-downcast-variables) | Use SafeCast to safely downcast variables | 5 |

Total: 74 contexts over 10 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#nc1-consider-adding-a-denylist) | Consider adding a deny-list | 3 |
| [NC‑2](#nc2-consistent-usage-of-require-vs-custom-error) | Consistent usage of require vs custom error | 4 |
| [NC‑3](#nc3-constant-redefined-elsewhere) | Constant redefined elsewhere | 1 |
| [NC‑4](#nc4-blocktimestamp-is-already-used-when-emitting-events-no-need-to-input-timestamp) | `block.timestamp` is already used when emitting events, no need to input timestamp | 1 |
| [NC‑5](#nc5-mint-ev

File: Vault.sol

212: CRV.approve(address(_amphClaimer), _takenCRV);

File: Vault.sol

213: CVX.approve(address(_amphClaimer), _takenCVX);


chrisdior4 G

## Gas Optimization

| G-O    |Issue|
|:------:|:----|
| [G‑01] | Remove public visibility from constant variables | 2 |
| [G‑02] | Cache Array Length Outside of Loop | 6 |
| [G‑03] | x = x - y costs less gas than x -= y, same for addition | 11 |
| [G‑04] | USE REQUIRE INSTEAD OF ASSERT| 19 |
| [G‑05] | Consider using custom errors instead of require statements with string error | 31 |
| [G‑06] | REQUIRE()/REVERT() STRINGS LONGER THAN 32 BYTES COST EXTRA GAS | 2 |
| [G‑07] | THERE’S NO NEED TO SET DEFAULT VALUES FOR VARIABLES | 1 |
| [G‑08] | USE CALLDATA INSTEAD OF MEMORY | 1 |

Total: 8 issues

### [G-01] Remove public visibility from constant variables

Constant variables are custom to the contract and won't need to be read on-chain - anyone can just see their values from the source code and, if needed, hardcode them into other contracts. Removing the public visibility will optimise deployment cost since no automatically generated getters will exist in the bytecode of the contract.

Files: `CashFactory.sol`

```solidity
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);
```

Lines of code:

- https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/cash/factory/CashFactory.sol#L44
- https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/cash/factory/CashFactory.sol#L45
- https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/cash/factory/CashFactory.sol#L46

### [G-02]  Cache Array Length Outside of Loop

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.

File: `CashFactory.sol`

```solidity
for (uint256 i = 0; i < exCallData.length; ++i) {
```

Lines of code:

// Code block 1 (solidity):
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

// Code block 2 (solidity):
for (uint256 i = 0; i < exCallData.length; ++i) {

bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

for (uint256 i = 0; i < exCallData.length; ++i) {


SAQ G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] |  Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate| 2 | - |
| [G-02] | Using storage instead of memory for structs/arrays saves gas | 6 | - |
| [G-03] | internal functions only called once can be inlined to save gas | 18 | - |
| [G-04] | Use more recent version of solidity  | 2 | - |
| [G-05] | <X> += <Y> Costs more GAS than <X> = <X> + <Y> for State variables or ( -= ) | 2 | - |
| [G-06] | Public fuction to External | 2 | - |
| [G-07] | Using fixed bytes is cheaper than using String | 8 | - |
| [G-08] | State variables should be cached in stack variables rather than re-reading them from storage | 8 | - |
| [G-09] | Can make the variable outside the loop to save gas | 7 | - |
| [G-10] | Using calldata instead of memory for read-only argument in external function saves gas | 29 | - |
| [G-11] | Use assembly to check for address(0) | 1 | - |
| [G-12] | Before some functions, we should check some variables for possible gas save  | 1 | - |
| [G-13] | Use bitmaps to save gas | 1 | - |
| [G-14] | Store using Struct over multiple mappings | 1 | - |
| [G-15] | Duplicated require()/if() checks should be refactored to a modifier or function | 3 | - |
| [G-16] | Using ERC721A instead of ERC720 for more gas-efficient | 1 | - |
| [G-17] | Use nested if and, avoid multiple check combinations | 6 | - |
| [G-18] | Use constants instead of type(uintx).max | 1 | - |
| [G-19] | Use assembly to write address storage values | 34 | - |
| [G-20] | abi.encode() is less efficient than abi.encodepacked() | 7 | - |
| [G-21] | Do not calculate  constant | 5 | - |
| [G-22] | Structs can be packed into fewer storage slots by editing time variables | 1 | - |
| [G-23] | Using delete statement can save gas | 3 | - |
| [G-24] | >= costs less gas than > | 8 | - |
| [G-25] | Minimize external calls in every contruct file | All file | - |
| [G-26] | Sort S

file: /ajna-grants/src/grants/base/StandardFunding.sol

82     mapping(uint256 => uint256[]) internal _topTenProposals;
83
84    /**
85    * @notice Mapping of a hash of a proposal slate to a list of funded proposals.
86     * @dev slate hash => proposalId[]
87   */
88    mapping(bytes32 => uint256[]) internal _fundedPropo/ajna-grants/src/grants/interfaces/IStandardFunding.salSlates;

file: /ajna-core/src/PositionManager.sol

367     Position memory position = positions[params_.tokenId][index]

454     Position memory position = positions[tokenId_][index_];

469     uint256[] memory indexes = positionIndexes[tokenId_].values();

422     BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];

442     BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];


Im_th3AK Q

# Risk Rating
- Low
# Description
- missing zero address validation.
LRTConfig.setRSETH(address).rsETH_ (src/LRTConfig.sol#144) lacks a zero-check on :
- rsETH = rsETH_ (src/LRTConfig.sol#146)
# Impact
Link:- https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L144-L146
```
 function setRSETH(address rsETH_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        UtilLib.checkNonZeroAddress(rsETH_);
        rsETH = rsETH_;
    }
```
It lacks a zero-check on the rsETH_ parameter, which is an address type. This means that the function setRSETH could potentially accept the zero address (0x0000000000000000000000000000000000000000) as a valid input and assign it to the rsETH state variable. This could have serious consequences, such as:

- The zero address could be used to bypass some access control or validation logic that relies on the rsETH variable.
- The zero address could be used to lock funds or tokens that are sent to the rsETH variable, since the zero address is not owned by anyone and cannot be controlled.
- The zero address could be used to cause unexpected behavior or errors in other functions that interact with the rsETH variable.
# Tools
- Manual
# Recommendations
```
function setRSETH(address rsETH_) external onlyRole(DEFAULT_ADMIN_ROLE) {
    // Check that rsETH_ is not the zero address
    require(!iszero(rsETH_), "rsETH_ cannot be the zero address");
    UtilLib.checkNonZeroAddress(rsETH_);
    rsETH = rsETH_;
}

```
This code ensures that the function setRSETH will revert if rsETH_ is the zero address, and prevents any potential security or functionality risks.

// Code block 1 (unknown):
function setRSETH(address rsETH_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        UtilLib.checkNonZeroAddress(rsETH_);
        rsETH = rsETH_;
    }

// Code block 2 (unknown):
function setRSETH(address rsETH_) external onlyRole(DEFAULT_ADMIN_ROLE) {
    // Check that rsETH_ is not the zero address
    require(!iszero(rsETH_), "rsETH_ cannot be the zero address");
    UtilLib.checkNonZeroAddress(rsETH_);
    rsETH = rsETH_;
}


Koko1912 Q

### Table Of Issues
| ID | Details of the issue |
|:---|:------|
| Non | Use a more recent pragma version |
| Low | ERC20Rewards claiming can fail if there are no tokens |
| Low | The project does not have a strategy if a token gets stuck |
| Low | Not emitting event for changes in the state|

## Non - Use a more recent pragma version

The version used in the Anjas' project is older.
```sol
pragma solidity 0.8.14;
```
I recommend updating the version to the latest which is 0.8.19.

## Low - ERC20Rewards claiming can fail if there are no tokens
```sol
The value in the claimRewards(https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L114) is not checked enough
```
I recommend setting new rewards periods, make sure that enough rewardsTokens are in the contract to cover the entire period.

## The project does not have a strategy if a token gets stuck
Most of the Farming Strategies interact with other protocols and for this reason they are subject to airdrops.

I recommend adding a ``` TokenGetsStuck``` so that  these extra tokens would not be claimable and would be lost forever.

##  Low - Not emitting event for changes in the state
In the contract ```StandardFunding.sol```. Not always the functions emit.

The function ```_updateTreasury``` needs to be emitted, because it makes change to the state variables.

// Code block 1 (sol):
pragma solidity 0.8.14;

// Code block 2 (sol):
The value in the claimRewards(https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L114) is not checked enough

pragma solidity 0.8.14;

The value in the claimRewards(https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L114) is not checked enough


Mike_Bello90 G

  **DOPEX CONTEST - 2023/08/21 - Gas Optimizations.**

1.- To save gas you can cache some math calculations in a variable, so the compiler just does the math once and then you reuse that variable in the different places where it’s needed.

For example in the function: _stake  - RdpxV2Core SC

```
function _stake(address _to, uint256 _amount) internal returns (uint256 receiptTokenAmount) {
Uint256 midAmount = _amount / 2;
        reserveAsset[reservesIndex["WETH"]].tokenBalance -= midAmount;

        IDpxEthToken(reserveAsset[reservesIndex["DPXETH"]].tokenAddress).mint(address(this), midAmount);

        // deposit into the rdpxV2ReceiptToken contract
        receiptTokenAmount = IRdpxV2ReceiptToken(addresses.rdpxV2ReceiptToken).deposit(midAmount);

        // mint receipt token bonds
        _issueBond(_to, receiptTokenAmount);
    }
```
In function: calculateBondCost - RdpxV2Core SC
```
function calculateBondCost(uint256 _amount, uint256 _rdpxBondId)
        public
        view
        returns (uint256 rdpxRequired, uint256 wethRequired)
    {

    ** Code omitted *

uint256 bondDiscountHalf = bondDiscount / 2;
Uint256 divisor1 = (DEFAULT_PRECISION * rdpxPrice * 1e2);
Uint256 divisor2 = DEFAULT_PRECISION * 1e2;

            rdpxRequired = ((RDPX_RATIO_PERCENTAGE - (bondDiscountHalf)) * _amount * DEFAULT_PRECISION)
                / divisor1;

            wethRequired = ((ETH_RATIO_PERCENTAGE - (bondDiscountHalf)) * _amount) /divisor2;
        } else {
            // if rdpx decaying bonds are being used there is no discount
            rdpxRequired = (RDPX_RATIO_PERCENTAGE * _amount * DEFAULT_PRECISION) / divisor1;

            wethRequired = (ETH_RATIO_PERCENTAGE * _amount) / divisor2;
        }

      ** Code omitted **
    }
```

In function updateFundingPaymentPointer -  PerpetualAtlanticVault
```
function updateFundingPaymentPointer() public {
        while (block.timestamp >= nextFundingPaymentTimestamp()) {
            *
	uint256 amount = (current

// Code block 1 (unknown):
function _stake(address _to, uint256 _amount) internal returns (uint256 receiptTokenAmount) {
Uint256 midAmount = _amount / 2;
        reserveAsset[reservesIndex["WETH"]].tokenBalance -= midAmount;

        IDpxEthToken(reserveAsset[reservesIndex["DPXETH"]].tokenAddress).mint(address(this), midAmount);

        // deposit into the rdpxV2ReceiptToken contract
        receiptTokenAmount = IRdpxV2ReceiptToken(addresses.rdpxV2ReceiptToken).deposit(midAmount);

        // mint receipt token bonds
        _issueBond(_to, receiptTokenAmount);
    }

// Code block 2 (unknown):
function calculateBondCost(uint256 _amount, uint256 _rdpxBondId)
        public
        view
        returns (uint256 rdpxRequired, uint256 wethRequired)
    {

    ** Code omitted *

uint256 bondDiscountHalf = bondDiscount / 2;
Uint256 divisor1 = (DEFAULT_PRECISION * rdpxPrice * 1e2);
Uint256 divisor2 = DEFAULT_PRECISION * 1e2;

            rdpxRequired = ((RDPX_RATIO_PERCENTAGE - (bondDiscountHalf)) * _amount * DEFAULT_PRECISION)
                / divisor1;

            wethRequired = ((ETH_RATIO_PERCENTAGE - (bondDiscountHalf)) * _amount) /divisor2;
        } else {
            // if rdpx decaying bonds are being used there is no discount
            rdpxRequired = (RDPX_RATIO_PERCENTAGE * _amount * DEFAULT_PRECISION) / divisor1;

            wethRequired = (ETH_RATIO_PERCENTAGE * _amount) / divisor2;
        }

      ** Code omitted **
    }


0xhacksmithh G

### [Gas-01] Function result should be cached in ```memory``` instead of calling it again and again.
```solidity
underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
```
Here function result of ```derivatives[i].balance()``` should be cached in ```memory``` instead of calling it again.
```Instances(2)```
```
File: contracts/SafEth/SafEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L72-L75
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L141-L142
```

### [Gas-02] Mathematic logics for ```adjustWeight()``` could be improved to save gas.
```solidity
 function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        emit WeightChange(_derivativeIndex, _weight);
    }
```
Here first weight set in mapping, then pointer(program pointer) itterate over whole mapping and add their corresponding weights and finally set result(```localTotalWeight```) to ```totalWeight``` state variable.

Here multiple stateVariable read and addition occure to set just one derivative's weight

Which can preform with less cost i.e
1. Whenever we change a weight
     a. first delete that much weight from ```totalWeight```
     b. Set corresponding weight with its index in ```weights``` mapping.
     b. then add new weight to ```totalWeight```

```solidity
 function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
+        totalWeight = totalWeight - weights[_derivativeIndex];
         weights[_derivativeIndex] = _weight;
+        totalWeight = totalWeight + _weight;

-

// Code block 1 (solidity):
underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

// Code block 2 (unknown):
### [Gas-02] Mathematic logics for

// Code block 3 (solidity):
function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        emit WeightChange(_derivativeIndex, _weight);
    }

// Code block 4 (unknown):
b. Set corresponding weight with its index in

underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        emit WeightChange(_derivativeIndex, _weight);
    }


codeslide Q

### Low Risk Issues List

| Number | Issues Details                     | Instances |
| :----: | :--------------------------------- | :-------: |
| [L-01] | Unspecific compiler version pragma |     4     |

Total: 1 issue

#### [L-01] Unspecific compiler version pragma

The compiler version specified for a contract should be locked to the version it has been tested the most with. Locking the pragma helps ensure that contracts do not accidentally get deployed using, for example, the latest compiler which may have higher risks of undiscovered bugs. Contracts may also be deployed by others and the pragma indicates the compiler version intended by the original authors. [locking-pragmas/](https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/locking-pragmas/)

For example:

```solidity
// bad
pragma solidity ^0.8.13;

// good
pragma solidity 0.8.13;
```

1. [SafEth.sol Line 2](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L2)
2. [Reth.sol Line 2](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L2)
3. [SfrxEth.sol Line 2](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L2)
4. [WstEth.sol Line 2](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L2)

### Non-Critical Issues List

| Number | Issues Details | Instances |
| :-: | :-- | :-: |
| [N-01] | Readability and maintainability | 1 |
| [N-02] | Function grouping and ordering | 3 |
| [N-03] | Maximum Line Length | 2 |
| [N-04] | Constants should be defined rather than using magic numbers | 1 |
| [N-05] | Control structure style | 1 |

Total: 5 issues

#### [N-01] Readability and maintainability

Some code could be made easier to read and maintain by changing the syntax.

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

// before
54:    minAmount = 5 * 10 ** 17; // initializing with .5 ETH a

File: contracts/SafEth/SafEth.sol

// before
54:    minAmount = 5 * 10 ** 17; // initializing with .5 ETH as minimum
55:    maxAmount = 200 * 10 ** 18; // initializing with 200 ETH as maximum

// after
54:    minAmount = 1 ether / 2; // initializing with .5 ETH as minimum
55:    maxAmount = 200 ether;   // initializing with 200 ETH as maximum

// Code block 1 (solidity):
// bad
pragma solidity ^0.8.13;

// good
pragma solidity 0.8.13;

SafEth.sol#L2, Reth.sol#L2, SfrxEth.sol#L2, WstEth.sol#L2

SfrxEth.sol, Reth.sol, WstEth.sol, SafEth.sol


MatricksDeCoder Q

### NC1 - Add interfaces to own folder

[IRWAOracle.sol in /rwaOracles/ folder](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/IRWAOracle.sol) given there is an /interfaces folder in the /contracts folder

**Impact**-> It affects the code  maintainability, readability, organization, auditability and cleanliness of folders and structure

**Recommendation** -> It is recommended add all interfaces into their own /interface folder for all interfaces or create separate interfaces folders within the contract folders where interface is used
e.g /rwaOracles/interfaces/IRWAOracle.sol

### NC2 - Not using named imports

Imports just get entire files e.g
```solidity
import "contracts/interfaces/IAxelarGateway.sol";
import "contracts/interfaces/IAxelarGasService.sol";
import "contracts/interfaces/IMulticall.sol";
import "contracts/interfaces/IRWALike.sol";
```
vs best and cleaner practise of using named imports
```
import {IAxelarGateway} from "contracts/interfaces/IAxelarGateway.sol";
```

[SourceBridge.sol line 3-6](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/SourceBridge.sol#L3C1-L6C44)
[DestinationBridge.sol line 18-22,25](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L18)
[rUSDY.sol lines 18-28](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L18)
[rUSDYFactory.sol lines 19-22](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDYFactory.sol#L19)
[RWADynamicOracle.sol lines 18-20](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L18)

**Impact**-> it affects the code organization, readability, interpretability, maintainability, auditability and best practises

**Recommendation** -> It is recommended to i

// Code block 1 (solidity):
import "contracts/interfaces/IAxelarGateway.sol";
import "contracts/interfaces/IAxelarGasService.sol";
import "contracts/interfaces/IMulticall.sol";
import "contracts/interfaces/IRWALike.sol";

// Code block 2 (unknown):
import {IAxelarGateway} from "contracts/interfaces/IAxelarGateway.sol";

IRWAOracle.sol, SourceBridge.sol#L3-L1, DestinationBridge.sol#L18, rUSDY.sol#L18, rUSDYFactory.sol#L19, RWADynamicOracle.sol#L18

rUSDYFactory.sol, RWADynamicOracle.sol, rUSDY.sol, SourceBridge.sol, IRWAOracle.sol, DestinationBridge.sol


chaduke Q

QA1. https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L397
There is a typo here, the comparison should be against ``type(uint128).max`` instead of ``type(uint120).max`` since the documentation says "validate all numeric values in userOp are well below 128 bit".

```
require(maxGasValues <= type(uint128).max, "AA94 gas values overflow");

```

QA2: https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L75
The modifier ``onlyOwner`` is not necessary, just like the ``deposit()`` function.

QA3: https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L99
Zero address check is necessary for ``withdrawAddress`` to avoid losing funding to the zero address.

QA4: https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol#L2
Lock all contracts to the most recent version of Solidity, 0.8.17.

QA5. https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L55-L61
The function should also indicate that it supports the interface of ``ERC777TokensRecipient`` as well.

```
function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
        return
            interfaceId == type(ERC1155TokenReceiver).interfaceId ||
            interfaceId == type(ERC721TokenReceiver).interfaceId ||
            interfaceId == type(ERC777TokensRecipient).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
```
QA6. https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529ee

// Code block 1 (unknown):
require(maxGasValues <= type(uint128).max, "AA94 gas values overflow");

// Code block 2 (unknown):
function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
        return
            interfaceId == type(ERC1155TokenReceiver).interfaceId ||
            interfaceId == type(ERC721TokenReceiver).interfaceId ||
            interfaceId == type(ERC777TokensRecipient).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

EntryPoint.sol#L397, BasePaymaster.sol#L75, BasePaymaster.sol#L99, Singleton.sol#L2, DefaultCallbackHandler.sol#L55-L61

DefaultCallbackHandler.sol, BasePaymaster.sol, Singleton.sol, EntryPoint.sol


0xStriker Q

# Summary

# Low Risk Issues
no | Issue |Instances||
|-|:-|:-:|:-:|
| [L-01] |  add more validation checks for the input data in the createGameItem() function | 1 |
| [L-02] |  the function call will fail if the maxId is more than one | 1 |
| [L-03] |  zero nrn distribution | 1 |
| [L-04] |  the function adjusts the unstake amount without reverting | 1 |
| [L-05] |  it is a best practice to use the safeErc20 library | 12 |
| [L-06] |  no zero address checks | 15 |

# Non-critical
no | Issue |Instances||
|-|:-|:-:|:-:|
| [N-01] |  The values of the attributeProbabilities mapping are redundantly set| 1 |
| [N-02] |  the function name could be changed to represent it much clearer | 1 |
| [N-03] |  comment suggestion and function implementation difference | 1 |
| [N-04] |  redundant code logic | 1 |
| [N-05] |  the resetting should be skiped if the value is already set to the same value | 3 |

## [L-01] add more validation checks for the input data in the createGameItem() function
since the function createGameItem() creates gemeItems that will be in the systme permanently it is a good practice to add some more validation checks for the input data, such as check if the finiteSupply is true, if yes,than the itemsRemaining should be more than zero, and the item price should be more than zero.
```solidity
        if(itemPrice <= 0 ){
            revert();
        }
        if (finiteSupply) {
            if(itemsRemaining <= 0 ) {
                revert();
            }
        }
```
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L208-L235

## [l-02] the function call will fail if the maxId is more than one
In the getFighterPoints() function, the array points is initialized with a length of 1, which will cause the function call to fail if maxId is greater than 1. When the loop tries to access points[i] for i > 1, it will result in an out-of-bounds array access, which will cause a runtime error and rev

// Code block 1 (solidity):
if(itemPrice <= 0 ){
            revert();
        }
        if (finiteSupply) {
            if(itemsRemaining <= 0 ) {
                revert();
            }
        }


AkshaySrivastav Q

1. The `Factory.create` function is susceptible to re-entrancy as it performs a `_safeMint` before initializing the pool.
    ```solidity
    function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(_salt)));

        // mint the nft to the caller
        _safeMint(msg.sender, uint256(uint160(address(privatePool))));

        // initialize the pool
        privatePool.initialize(...);
        // ...
    }
    ```
2. The `Factory.create` function performs plain transfer of funds instead of calling the `deposit` function. This way the `Deposit` event is not emitted.
    ```solidity
    function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool.initialize(...);

        if (_baseToken == address(0)) {
            // transfer eth into the pool if base token is ETH
            address(privatePool).safeTransferETH(baseTokenAmount);
        } else {
            // deposit the base tokens from the caller into the pool
            ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);
        }

        // deposit the nfts from the caller into the pool
        for (uint256 i = 0; i < tokenIds.length; i++) {
            ERC721(_nft).safeTransferFrom(msg.sender, address(privatePool), tokenIds[i]);
        }

        // emit create event
        emit Create(address(privatePool), tokenIds, baseTokenAmount);
    }
    ```
3. There is no fee cap on the `Factory.setProtocolFeeRate` function. A value greater then 10000 can break the fee calculations in private pool. Consider validating that the input is less than 10000.
    ```solidity
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
        protocolFeeRate = _protocolFeeRate;
    }
    ```
4. `Factory.tokenId` does not validate the input `id` parameter. Consider validating that th

// Code block 1 (solidity):
function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(_salt)));

        // mint the nft to the caller
        _safeMint(msg.sender, uint256(uint160(address(privatePool))));

        // initialize the pool
        privatePool.initialize(...);
        // ...
    }

// Code block 2 (solidity):
function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool.initialize(...);

        if (_baseToken == address(0)) {
            // transfer eth into the pool if base token is ETH
            address(privatePool).safeTransferETH(baseTokenAmount);
        } else {
            // deposit the base tokens from the caller into the pool
            ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);
        }

        // deposit the nfts from the caller into the pool
        for (uint256 i = 0; i < tokenIds.length; i++) {
            ERC721(_nft).safeTransferFrom(msg.sender, address(privatePool), tokenIds[i]);
        }

        // emit create event
        emit Create(address(privatePool), tokenIds, baseTokenAmount);
    }

// Code block 3 (solidity):
function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
        protocolFeeRate = _protocolFeeRate;
    }

function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(_salt)));

        // mint the nft to the caller
        _safeMint(msg.sender, uint256(uint160(address(privatePool))));

        // initialize the pool
        privatePool.initialize(...);
        // ...
    }

function create(
        ...
    ) public payable returns (PrivatePool privatePool) {
        // ...
        privatePool.initialize(...);

        if (_baseToken == address(0)) {
            // transfer eth into the pool if base token is ETH
            address(privatePool).safeTransferETH(baseTokenAmount);
        } else {
            // deposit the base tokens from the caller into the pool
            ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);
        }

        // deposit the nfts from the caller into the pool
        for (uint256 i = 0; i < tokenIds.length; i++) {
            ERC721(_nft).safeTransferFrom(msg.sender, address(privatePool), tokenIds[i]);
        }

        // emit create event
        emit Create(address(privatePool), tokenIds, baseTokenAmount);
    }

function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
        protocolFeeRate = _protocolFeeRate;
    }


csanuragjain Q

## Unauthorized access to Permit

Contract:
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L287

Issue:

1. The address recovery from signature is done through `ecrecover` function.
2. This function gives address(0) in case of invalid signature
3. There is no check to validate recoveredAddress!=address(0) which means you can approve any amount from address(0)

```
 address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress == owner, 'LUSD: invalid signature');
        _approve(owner, spender, amount);
```

Recommendation:
Add a check to ensure that recoveredAddress!=address(0)

```
 require(recoveredAddress!=address(0) && recoveredAddress == owner, 'LUSD: invalid signature');
```

## Duplicate strategy are allowed

Contract:
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L258

Issue:

1. Duplicate strategy can be added using `setWithdrawalQueue` function
2. This means `withdrawalQueue` could contain multiple copies of same strategy
3. Since `withdrawalQueue` is used to decide the strategy used to pull fund from, a duplicate strategy could cause using same strategy multiple times which was not intended

Recommendation:
If a strategy already exist in `withdrawalQueue` then revert

// Code block 1 (unknown):
address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress == owner, 'LUSD: invalid signature');
        _approve(owner, spender, amount);

// Code block 2 (unknown):
require(recoveredAddress!=address(0) && recoveredAddress == owner, 'LUSD: invalid signature');


shamsulhaq123 G

## Gas optimizations

## Sumary

| No|Issue|Instances|
|-|:-|:-:|
|[G-01]|Change public function visibility to external|4|
|[G-02]| Use calldata instead of memory for function parameters|3|
|[G-03]|Use hardcode address instead address(this)|6|
|[G-04]|Modifiers are redundant if used only once or not used at all.|1|
|[G-05]|Use function instead of modifiers |2|
|[G-06]|Not using the named return variable when a function returns, wastes deployment gas|1|
|[G-07]| Use constants instead of type(uintx).max|1|
|[G-08]|Non efficient zero initialization|1|
|[G-09]|Should use arguments instead of state variable |17|
|[G-10]|State Variable can be packed into fewer storage slots|4|
|[G-11]|Expressions for constant values such as a call to keccak256()|2|
|[G-12]|Pre-increments and pre-decrements are cheaper than post-increments and post-decrements|4|
|[G-13]|Use uint256(1)/uint256(2) instead for true and false boolean states|1|
|[G-14]|Loop best practice to save gas|4|
|[G-15]|+= costs more gas than = + for state variables|2|
|[G-16]|Using calldata instead of memory for read-only arguments in external functions saves gas|2|
|[G-17]|Use assembly to emit events|17|

## [G-01] Change public function visibility to external
Since the following public functions are not called from within the contract, their visibility can be made external for gas optimization.

```solidity

file: LRTDepositPool.sol

47  function getTotalAssetDeposits(address asset) public view override returns (uint256 totalAssetDeposit)

56   function getAssetCurrentLimit(address asset) public view override returns (uint256)

71     function getAssetDistributionData(address asset)
        public
        view
        override
        onlySupportedAsset(asset)
        returns (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer)

95   function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
      returns (u

file: LRTDepositPool.sol

47  function getTotalAssetDeposits(address asset) public view override returns (uint256 totalAssetDeposit)

56   function getAssetCurrentLimit(address asset) public view override returns (uint256)

71     function getAssetDistributionData(address asset)
        public
        view
        override
        onlySupportedAsset(asset)
        returns (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer)

95   function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
      returns (uint256 rsethAmountToMint)

file: LRTOracle.sol

45   function getAssetPrice(address asset) public view onlySupportedAsset(asset) returns (uint256) {
        return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset);
    }


Draiakoo Q

## Title
The following invariant can be broken:
maxNodeDelegatorCount >= nodeDelegatorQueue.length

## Explanation
The protocol should hold the previous exposed invariant, however it is possible to make it maxNodeDelegatorCount < nodeDelegatorQueue.length by setting a limit lower than the current number of nodes.

## Proof of Concept
```
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.21;

import { Test } from "forge-std/Test.sol";
import "forge-std/console.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import { LRTConfig, ILRTConfig } from "src/LRTConfig.sol";
import { RSETH } from "src/RSETH.sol";
import { LRTDepositPool } from "src/LRTDepositPool.sol";

import { LRTConstants } from "src/utils/LRTConstants.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

contract MockToken is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) { }

    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}

contract LRTOracleMock {

    LRTConfig public lrtConfig;
    mapping(address asset => uint256 price) public prices;

    constructor(address lrtConfigAddr) {
        lrtConfig = LRTConfig(lrtConfigAddr);
    }

    function setAssetPrice(address asset, uint256 amount) external {
        prices[asset] = amount;
    }

    function getAssetPrice(address asset) public view returns (uint256) {
        return prices[asset];
    }

    function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = RSETH(rsETHTokenAddress).totalSupply();

        if (rsEthSupply == 0) {
            return 1 ether;
        }

        u

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.21;

import { Test } from "forge-std/Test.sol";
import "forge-std/console.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import { LRTConfig, ILRTConfig } from "src/LRTConfig.sol";
import { RSETH } from "src/RSETH.sol";
import { LRTDepositPool } from "src/LRTDepositPool.sol";

import { LRTConstants } from "src/utils/LRTConstants.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

contract MockToken is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) { }

    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}

contract LRTOracleMock {

    LRTConfig public lrtConfig;
    mapping(address asset => uint256 price) public prices;

    constructor(address lrtConfigAddr) {
        lrtConfig = LRTConfig(lrtConfigAddr);
    }

    function setAssetPrice(address asset, uint256 amount) external {
        prices[asset] = amount;
    }

    function getAssetPrice(address asset) public view returns (uint256) {
        return prices[asset];
    }

    function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = RSETH(rsETHTokenAddress).totalSupply();

        if (rsEthSupply == 0) {
            return 1 ether;
        }

        uint256 totalETHInPool;
        address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
        uint256 supportedAssetCount = supportedAssets.length;

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            addre

Running 1 test for test/BaseTest.t.sol:AuditTests
[PASS] test_NodeDelegatorLimitCanBeSetToLessThanCurrentAmountOfThem() (gas: 546943)
Traces:
  [546943] AuditTests::test_NodeDelegatorLimitCanBeSetToLessThanCurrentAmountOfThem()
    ├─ [0] VM::startPrank(admin: [0xaA10a84CE7d9AE517a52c6d5cA153b369Af99ecF])
    │   └─ ← ()
    ├─ [38891] → new MockNodeDelegator@0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395
    │   └─ ← 194 bytes of code
    ├─ [38891] → new MockNodeDelegator@0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4
    │   └─ ← 194 bytes of code
    ├─ [38891] → new MockNodeDelegator@0xDDd9A038D57372934f1b9c52bd8621F5ED4268DF
    │   └─ ← 194 bytes of code
    ├─ [38891] → new MockNodeDelegator@0x32850cAd1e9170614704fF8BA37a25e498e1B832
    │   └─ ← 194 bytes of code
    ├─ [38891] → new MockNodeDelegator@0x1742F7d30605F18f097F356E1EeAfDB5e2FDe33a
    │   └─ ← 194 bytes of code
    ├─ [167505] TransparentUpgradeableProxy::addNodeDelegatorContractToQueue([0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395, 0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4, 0xDDd9A038D57372934f1b9c52bd8621F5ED4268DF, 0x32850cAd1e9170614704fF8BA37a25e498e1B832, 0x1742F7d30605F18f097F356E1EeAfDB5e2FDe33a])
    │   ├─ [160380] LRTDepositPool::addNodeDelegatorContractToQueue([0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395, 0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4, 0xDDd9A038D57372934f1b9c52bd8621F5ED4268DF, 0x32850cAd1e9170614704fF8BA37a25e498e1B832, 0x1742F7d30605F18f097F356E1EeAfDB5e2FDe33a]) [delegatecall]
    │   │   ├─ [9792] TransparentUpgradeableProxy::hasRole(0x0000000000000000000000000000000000000000000000000000000000000000, admin: [0xaA10a84CE7d9AE517a52c6d5cA153b369Af99ecF]) [staticcall]
    │   │   │   ├─ [2694] LRTConfig::hasRole(0x0000000000000000000000000000000000000000000000000000000000000000, admin: [0xaA10a84CE7d9AE517a52c6d5cA153b369Af99ecF]) [delegatecall]
    │   │   │   │   └─ ← true
    │   │   │   └─ ← true
    │   │   ├─ emit NodeDelegatorAddedinQueue(prospectiveNodeDelegatorContract


0xAnah G

# AI ARENA GAS OPTIMIZATIONS

## INTRODUCTION
Highlighted below are optimizations exclusively targeting state-mutating functions and view/pure functions invoked by state-mutating functions. In the discussion that follows, only runtime gas is emphasized, given its inevitable dominance over deployment gas costs throughout the protocol's lifetime.

Please be aware that some code snippets may be shortened to conserve space, and certain code snippets may include @audit tags in comments to facilitate issue explanations."

## [G-01] Same conditional in loop iterations
Some conditionals in the loop do not depend on the loop's iterations. Having to evaluate this condition for every iteration of the loop is gas consuming and redundant rather the conditional should be evaluated before the loop, the result saved to a variable and the variable be used in the loop.

### 1 Instance
1. #### `iconsType == 2`, `iconsType > 0` and `iconsType == 3` conditionals do not depend on the loop iterations
- https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol#L110-#L104

In the `AiArenaHelper.createPhysicalAttributes()` function as shown below the conditions `iconsType == 2`, `iconsType > 0` and `iconsType == 3` were checked for every iteration of the loop but these conditionals do not depend on the loop iterations. We can make the function more gas efficient by evaluating these conditionals before the loop then save the result to their respective variable then these variables be used in the loop. The diff below shows how the code could be refactored:

```solidity
file: src/AiArenaHelper.sol

83:     function createPhysicalAttributes(
84:         uint256 dna,
85:         uint8 generation,
86:         uint8 iconsType,
87:         bool dendroidBool
88:     )
89:         external
90:         view
91:         returns (FighterOps.FighterPhysicalAttributes memory)
92:     {
93:         if (dendroidBool) {
94:             return FighterOps.FighterPhysicalAttributes

file: src/AiArenaHelper.sol

83:     function createPhysicalAttributes(
84:         uint256 dna,
85:         uint8 generation,
86:         uint8 iconsType,
87:         bool dendroidBool
88:     )
89:         external
90:         view
91:         returns (FighterOps.FighterPhysicalAttributes memory)
92:     {
93:         if (dendroidBool) {
94:             return FighterOps.FighterPhysicalAttributes(99, 99, 99, 99, 99, 99);
95:         } else {
96:             uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);
97:
98:             uint256 attributesLength = attributes.length;
99:             for (uint8 i = 0; i < attributesLength; i++) {
100:                if (
101:                  i == 0 && iconsType == 2 || // Custom icons head (beta helmet)
102:                  i == 1 && iconsType > 0 || // Custom icons eyes (red diamond)
103:                  i == 4 && iconsType == 3 // Custom icons hands (bowling ball)
104:                ) {
105:                    finalAttributeProbabilityIndexes[i] = 50;
106:                } else {
107:                    uint256 rarityRank = (dna / attributeToDnaDivisor[attributes[i]]) % 100;
108:                    uint256 attributeIndex = dnaToIndex(generation, rarityRank, attributes[i]);
109:                    finalAttributeProbabilityIndexes[i] = attributeIndex;
110:                }
111:            }
112:            return FighterOps.FighterPhysicalAttributes(
113:                finalAttributeProbabilityIndexes[0],
114:                finalAttributeProbabilityIndexes[1],
115:                finalAttributeProbabilityIndexes[2],
116:                finalAttributeProbabilityIndexes[3],
117:                finalAttributeProbabilityIndexes[4],
118:                finalAttributeProbabilityIndexes[5]
119:            );
120:        }
121:    }

## [G-02] Redundant state variable getters
Getters for public state variables are automatically generated so there is no need to code
them manually and lose gas.

### Instances
1. #### Make `attributeProbabilities` mapping variable `private` or `internal` since a getter function was defined for it.
- https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol#L30

The solidity compiler would automatically create a getter function for the `attributeProbabilities` mapping since it is declared as a `public` variable but a getter function `getAttributeProbabilities()` was also declared in the contract for the same variable thereby making it two getter functions for the same variable in the contract. We could rectify this issue by making the `mapUnits` variable private or internal (if the variable is to be inherited). The diff below shows how the code could be refactored:


emmac002 G

# Report

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-1] | <X> += <Y> COSTS MORE GAS THAN <X> = <X> + <Y> FOR STATE VARIABLES | 8 |
| [GAS-2] | USING UNCHECKED BLOCKS TO SAVE GAS | 6 |
| [GAS-3] | DUPLICATED REQUIRE()/REVERT() CHECKS SHOULD BE REFACTORED TO A MODIFIER OR FUNCTION | 6 |
| [GAS-4] | SPLITTING REQUIRE() STATEMENTS THAT USE && SAVES GAS | 4 |
| [GAS-5] | PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD | 2 |
| [GAS-6] | INTERNAL FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE REMOVED TO SAVE DEPLOYMENT GAS | 1 |
| [GAS-7] | FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE | 12 |
| [GAS-8] | EXPRESSIONS FOR CONSTANT VALUES SUCH AS A CALL TO KECCAK256(), SHOULD USE IMMUTABLE RATHER THAN CONSTANT | 8 |
| [GAS-9] | USING BOOLS FOR STORAGE INCURS OVERHEAD | 3 |
| [GAS-10] | STACK VARIABLE USED AS A CHEAPER CACHE FOR A STATE VARIABLE IS ONLY USED ONCE | 1 |
| [GAS-11] | CACHE ARRAY LENGTH OUTSIDE OF LOOP | 7 |
| [GAS-12] | USE ASSEMBLY TO CHECK FOR ADDRESS(0) | 6 |
| [GAS-13] | USAGE OF UINT/INT SMALLER THAN 32 BYTES (256 BITS) INCURS OVERHEAD  | 5 |
| [GAS-14] | EMPTY BLOCKS SHOULD BE REMOVED OR EMIT SOMETHING | 2 |
| [GAS-15] | USE CONSTANTS FOR VARIABLES WHOSE VALUE IS KNOWN BEFOREHAND AND IS NEVER CHANGED | 1 |
| [GAS-16] | STATE VARIABLES SHOULD BE CACHED IN STACK VARIABLES RATHER THAN RE-READING THEM FROM STORAGE | 3 |

### [GAS-1] <X> += <Y> COSTS MORE GAS THAN <X> = <X> + <Y> FOR STATE VARIABLES

*Instances (8)*:
#### Proof of Concept
```solidity
File: Ethos-Vault/contracts/ReaperVaultV2.sol

168:                 totalAllocBPS += _allocBPS;
196:                 totalAllocBPS += _allocBPS;
391:                 totalLoss += loss;
450:                 stratParams.losses += loss;
505:                 strategy.gains += vars.gain;
520:                 strategy.allocated += vars.credit;
521:                 totalAllocated += vars.credit;
```
https://github.com/code-423n4/2023

File: Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol

133:                 toFree += profit;

// Code block 1 (solidity):
File: Ethos-Vault/contracts/ReaperVaultV2.sol

168:                 totalAllocBPS += _allocBPS;
196:                 totalAllocBPS += _allocBPS;
391:                 totalLoss += loss;
450:                 stratParams.losses += loss;
505:                 strategy.gains += vars.gain;
520:                 strategy.allocated += vars.credit;
521:                 totalAllocated += vars.credit;


ohm Q

## ACCOUNT ADDRESS WILL NOT BE ZERO

```
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    // ISSUE ----> = postRebaseTokenAmount is not getting the post rusdy tokens
    //Because _sharesAmount is not updating.

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);
```
In the above function there is no possibility of becoming the address is 0.So we can avoid the require statement .
```
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
```

// Code block 1 (unknown):
function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    // ISSUE ----> = postRebaseTokenAmount is not getting the post rusdy tokens
    //Because _sharesAmount is not updating.

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

// Code block 2 (unknown):
require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");


codeislight Q

1. unecessary check for zero amount in CashManager._checkAndUpdateRedeemLimit(uint256)
```
        if (amount == 0) {
            revert RedeemAmountCannotBeZero();
        }
```
in this case there is a check for zero amount which is followed by:
```
      if (amount > redeemLimit - currentRedeemAmount) {
            revert RedeemExceedsRateLimit();
      }
```
we can notice that the least value possible for the right side of ">" operator is 0, which always would ensure that `amount` is greater than zero. therefore, we can safely remove the revert check for zero amount.
2. check for address code size to be greater than zero, when a function has a contract address parameter.
3. using unchecked in for loops ++i
in CashManager:
772:        for (uint256 i = 0; i < size; ++i) {
814:        for (uint256 i = 0; i < size; ++i) {
973:        for (uint256 i = 0; i < size; ++i) {
999:        for (uint256 i = 0; i < exCallData.length; ++i) {
in CashFactory:
127:        for (uint256 i = 0; i < exCallData.length; ++i) {
in CashKYCSenderReceiverFactory:
137:        for (uint256 i = 0; i < exCallData.length; ++i) {
in CashKYCSenderFactory:
137:        for (uint256 i = 0; i < exCallData.length; ++i) {
in KYCRegistry:
163:        for (uint256 i = 0; i < length; i++) {
180:        for (uint256 i = 0; i < length; i++) {
and in other instances ...

// Code block 1 (unknown):
if (amount == 0) {
            revert RedeemAmountCannotBeZero();
        }

// Code block 2 (unknown):
if (amount > redeemLimit - currentRedeemAmount) {
            revert RedeemExceedsRateLimit();
      }


MohammedRizwan Q

## Summary

### Low Risk Issues
|Number|Issue|Instances| |
|-|:-|:-:|:-:|
| [L‑01] | The contracts has used outdated version of openzeppelin library | contracts |
| [L‑02] | stale or misleading comment in `rUSDYFactory.sol` | 1 |
| [L‑03] | Multicall being payable is seriously dangerous | 1 |
| [L‑04] | Calls inside loops that may address DoS | 1 |

### [L‑01]  The contracts has used outdated version of openzeppelin library
From package.json, It is confirmed that the contracts has used `"@openzeppelin/contracts": "^4.8.3",` which is an old version and has security issues. While using external libraries, Ensure it is of latest version.

### Recommended Mitigation steps
Update the openzeppelin version to v4.9.3

### [L‑02]  stale or misleading comment in `rUSDYFactory.sol`
In `rUSDYFactory.sol`, There is misleading or stale comment. When asked about it sponsor made it stale Natspec.

```
          ii) Revoke the `MINTER_ROLE`, `PAUSER_ROLE` & `DEFAULT_ADMIN_ROLE` from address(this).
```

### Recommended Mitigation steps
Remove the stale Natspec which are no longer needed in contract to prevent misleading.

### [L‑03]  `multiexcall()` being payable is seriously dangerous
In `rUSDYFactory.sol`, `multiexcall()` allows for arbitrary batched calls but it is made payable which will cause issues since `address(exCallData[i].target).call` is in a loop, it is not advisable to add payable to the existing call(). This is because msg.value may be used multiple times.

There is [1 instance](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDYFactory.sol#L126-L137) of this issue:

```Solidity
File: contracts/usdy/rUSDYFactory.sol

  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool

File: contracts/usdy/rUSDYFactory.sol

  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

// Code block 1 (unknown):
ii) Revoke the `MINTER_ROLE`, `PAUSER_ROLE` & `DEFAULT_ADMIN_ROLE` from address(this).


Topmark G

## Report 1
The need to loop through t.approvers from the code below at every new approver update would take excess gas, using mapping is a better alternative than array, adjusting it in the whole code base now might takes extra effort but considering it might be worth it as mapping is more gas efficient
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L160
```solidity
  function _approve(bytes32 txnHash) internal {
    // Check that the approver has not already approved
    TxnThreshold storage t = txnToThresholdSet[txnHash];
    if (t.approvers.length > 0) {
 ---->     for (uint256 i = 0; i < t.approvers.length; ++i) {
        if (t.approvers[i] == msg.sender) {
          revert AlreadyApproved();
        }
      }
    }
    t.approvers.push(msg.sender);
  }
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L376
```solidity
 struct TxnThreshold {
    uint256 numberOfApprovalsNeeded;
 +++ (address => bool) approvers;
 --- address[] approvers;
  }
```

// Code block 1 (solidity):
function _approve(bytes32 txnHash) internal {
    // Check that the approver has not already approved
    TxnThreshold storage t = txnToThresholdSet[txnHash];
    if (t.approvers.length > 0) {
 ---->     for (uint256 i = 0; i < t.approvers.length; ++i) {
        if (t.approvers[i] == msg.sender) {
          revert AlreadyApproved();
        }
      }
    }
    t.approvers.push(msg.sender);
  }

// Code block 2 (solidity):
struct TxnThreshold {
    uint256 numberOfApprovalsNeeded;
 +++ (address => bool) approvers;
 --- address[] approvers;
  }

function _approve(bytes32 txnHash) internal {
    // Check that the approver has not already approved
    TxnThreshold storage t = txnToThresholdSet[txnHash];
    if (t.approvers.length > 0) {
 ---->     for (uint256 i = 0; i < t.approvers.length; ++i) {
        if (t.approvers[i] == msg.sender) {
          revert AlreadyApproved();
        }
      }
    }
    t.approvers.push(msg.sender);
  }

struct TxnThreshold {
    uint256 numberOfApprovalsNeeded;
 +++ (address => bool) approvers;
 --- address[] approvers;
  }


tabriz G

## [GAS-1] Splitting require() statements that use && saves gas

```
 require(
            recoveredAddress != address(0) && recoveredAddress == _borrower,
            "BorrowerOperations: Invalid signature"
        );
```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L734C8-L737C11

```
 require(
            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION,
            "Max fee percentage must be between redemption fee floor and 100%"
        );
```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManager.sol#L762C8-L765C11

```
 require(
            closedStatus != Status.nonExistent && closedStatus != Status.active,
            "CdpManagerStorage: close non-exist or non-active CDP!"
        );
```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L261C8-L264C11

```
  require(
            cdpStatus != Status.nonExistent && cdpStatus != Status.active,
            "CdpManagerStorage: remove non-exist or non-active CDP!"
        );

```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L466C7-L470C1

```
  require(
            CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,
            "CdpManager: Only one cdp in the system"
        );
```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L653C7-L656C11

```
 require(
            _recipient != address(0) && _recipient != address(this),
            "EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
        );
        require(
            _recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
            "EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
        );
```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contrac

// Code block 1 (unknown):
require(
            recoveredAddress != address(0) && recoveredAddress == _borrower,
            "BorrowerOperations: Invalid signature"
        );

// Code block 2 (unknown):
require(
            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION,
            "Max fee percentage must be between redemption fee floor and 100%"
        );

// Code block 3 (unknown):
require(
            closedStatus != Status.nonExistent && closedStatus != Status.active,
            "CdpManagerStorage: close non-exist or non-active CDP!"
        );

// Code block 4 (unknown):
require(
            cdpStatus != Status.nonExistent && cdpStatus != Status.active,
            "CdpManagerStorage: remove non-exist or non-active CDP!"
        );

// Code block 5 (unknown):
require(
            CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,
            "CdpManager: Only one cdp in the system"
        );

// Code block 6 (unknown):
require(
            _recipient != address(0) && _recipient != address(this),
            "EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
        );
        require(
            _recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
            "EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
        );

BorrowerOperations.sol#L734-L8, CdpManager.sol#L762-L8, CdpManagerStorage.sol#L261-L8, CdpManagerStorage.sol#L466-L7, CdpManagerStorage.sol#L653-L7


Timenov G

# Biconomy gas optimization report by Timenov

## Summary
G-01 Using `> 0` costs more gas than using `!= 0`
G-02 Using `i++` costs more gas than using `++i`. Also can add `unchecked { ++i; }` in for loops where overflow is not possible
G-03 Using `x += y` costs more gas than using `x = x + y`

### [G-01] Using `> 0` costs more gas than using `!= 0`
*There are 12 instances of this issue.*

```solidity
File: contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

174: if (callData.length > 0)
178: if (result.length > 0)
212: if (paymasterAndData.length > 0)
452: if (context.length > 0)
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

```solidity
File: contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

61: require(_unstakeDelaySec > 0, "must specify unstake delay")
64: require(stake > 0, "no stake specified")
99: require(stake > 0, "No stake to withdraw")
100: require(info.withdrawTime > 0, "must call unlockStake() first")
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

```solidity
File: contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountFactory.sol

31: if (codeSize > 0)
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountFactory.sol

```solidity
File: contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccountFactory.sol

30: if (codeSize > 0)
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccountFactory.sol

```solidity
File: contracts/smart-contract-wallet/SmartAccount.sol

236: if (refundInfo.gasPrice > 0)
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

```solidity
File: contracts/smart-contract-wallet/

// Code block 1 (solidity):
File: contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

174: if (callData.length > 0)
178: if (result.length > 0)
212: if (paymasterAndData.length > 0)
452: if (context.length > 0)

// Code block 2 (solidity):
File: contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

61: require(_unstakeDelaySec > 0, "must specify unstake delay")
64: require(stake > 0, "no stake specified")
99: require(stake > 0, "No stake to withdraw")
100: require(info.withdrawTime > 0, "must call unlockStake() first")

// Code block 3 (solidity):
File: contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountFactory.sol

31: if (codeSize > 0)

// Code block 4 (solidity):
File: contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccountFactory.sol

30: if (codeSize > 0)

// Code block 5 (solidity):
File: contracts/smart-contract-wallet/SmartAccount.sol

236: if (refundInfo.gasPrice > 0)

File: contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

174: if (callData.length > 0)
178: if (result.length > 0)
212: if (paymasterAndData.length > 0)
452: if (context.length > 0)

File: contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

61: require(_unstakeDelaySec > 0, "must specify unstake delay")
64: require(stake > 0, "no stake specified")
99: require(stake > 0, "No stake to withdraw")
100: require(info.withdrawTime > 0, "must call unlockStake() first")

File: contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountFactory.sol

31: if (codeSize > 0)

File: contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccountFactory.sol

30: if (codeSize > 0)

File: contracts/smart-contract-wallet/SmartAccount.sol

236: if (refundInfo.gasPrice > 0)

EntryPoint.sol, StakeManager.sol, SimpleAccountFactory.sol, TestAggregatedAccountFactory.sol, SmartAccount.sol

TestAggregatedAccountFactory.sol, SimpleAccountFactory.sol, EntryPoint.sol, SmartAccount.sol, StakeManager.sol


Dee Q

### QA1
- There were numerous require statements in the codebase where there's no string argument specifying why the revert happens. I personally spent almost 24hrs debugging a revert test just because team doesnt add a string statement for the require function. It is advisable to add reasons why a function is reverting.

from solidity doc:
```
If you do not provide a string argument to require, it will revert with empty error data, not even including the error selector.
```
`AAMintPass.sol`
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AAMintPass.sol#L56

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AAMintPass.sol#L94

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AAMintPass.sol#L116

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AAMintPass.sol#L138

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AAMintPass.sol#L157

_...and many more examples throughout the codebase_

### QA2

- There are soo many places in the code where access control is needed, the team kept writing require statements repetitively. Repetition is prone to error which might be hard to debug.. it is advisable to use a modifier as best practise and to better improve the readibility of the code.

Repetitive usage under `FighterFarm.sol`

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L121

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L130

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L140

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L148

https://github.com/code-423n4/2024-02-ai-aren

// Code block 1 (unknown):
If you do not provide a string argument to require, it will revert with empty error data, not even including the error selector.

AAMintPass.sol#L56, AAMintPass.sol#L94, AAMintPass.sol#L116, AAMintPass.sol#L138, AAMintPass.sol#L157, FighterFarm.sol#L121, FighterFarm.sol#L130, FighterFarm.sol#L140, FighterFarm.sol#L148


banpaleo5 G

# Table of Contents
| Number | Issues Details                                                                                | Count |
| :----- | :----------------------------------------------------------------------------------------------------- | :---- |
| [G-1] | Enhance gas efficiency by constructing the `DOMAIN_TYPEHASH` within the `initialize()` function | 1 |
| [G-2] | Leverage the rules of short-circuit evaluation for optimization benefits | 1 |
| [G-3] | Recommended to use `assembly` for writing address storage values | 43 |
| [G-4] | If EIP-2771 isn't being supported, avoid using the `_msgSender()` function | 17 |
| [G-5] | Employing `unchecked` blocks can lead to gas savings when operations cannot overflow/underflow | 1 |

## [G-1] Enhance gas efficiency by constructing the `DOMAIN_TYPEHASH` within the `initialize()` function

There are 1 instances of this issue:

```solidity
File: GovernorCharlie.sol
bytes32 public constant DOMAIN_TYPEHASH =
```

## [G-2] Leverage the rules of short-circuit evaluation for optimization benefits

There are 1 instances of this issue:

```solidity
File: GovernorCharlie.sol
(amph.getPriorVotes(msg.sender, (block.number - 1)) < proposalThreshold && !isWhitelisted(msg.sender))
(_emergency && !isWhitelisted(msg.sender))
```

## [G-3] Recommended to use `assembly` for writing address storage values

There are 10 instances of this issue:

```solidity
File: CbEthEthOracle.sol
_vp = virtualPrice;
```

```solidity
File: VaultController.sol
_enabledTokens = enabledTokens;
```

```solidity
File: VaultController.sol
_i = _start; _i < _end;) {
```

```solidity
File: VaultController.sol
_oldClaimerContract = claimerContract;
```

```solidity
File: VaultController.sol
_oldBorrowingFee = initialBorrowingFee;
```

```solidity
File: VaultController.s

// Code block 1 (solidity):
File: GovernorCharlie.sol
bytes32 public constant DOMAIN_TYPEHASH =

// Code block 2 (solidity):
File: GovernorCharlie.sol
(amph.getPriorVotes(msg.sender, (block.number - 1)) < proposalThreshold && !isWhitelisted(msg.sender))
(_emergency && !isWhitelisted(msg.sender))

// Code block 3 (solidity):
File: CbEthEthOracle.sol
_vp = virtualPrice;

// Code block 4 (solidity):
File: VaultController.sol
_enabledTokens = enabledTokens;

// Code block 5 (solidity):
File: VaultController.sol
_i = _start; _i < _end;) {

// Code block 6 (solidity):
File: VaultController.sol
_oldClaimerContract = claimerContract;

// Code block 7 (solidity):
File: VaultController.sol
_oldBorrowingFee = initialBorrowingFee;

File: GovernorCharlie.sol
bytes32 public constant DOMAIN_TYPEHASH =

File: GovernorCharlie.sol
(amph.getPriorVotes(msg.sender, (block.number - 1)) < proposalThreshold && !isWhitelisted(msg.sender))
(_emergency && !isWhitelisted(msg.sender))

File: CbEthEthOracle.sol
_vp = virtualPrice;

File: VaultController.sol
_enabledTokens = enabledTokens;

File: VaultController.sol
_i = _start; _i < _end;) {

File: VaultController.sol
_oldClaimerContract = claimerContract;

File: VaultController.sol
_oldBorrowingFee = initialBorrowingFee;


halden Q

## [L-01] Add additional check for setting of exchangeRateDeltaLimit
It should be good to add additional check for what value can be settled for exchangeRateDeltaLimit. The variable is in basis points, so some biggest value for that variable can lead to unexpected behavior.
File CashManager.sol: [396](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L396)

## [L-02] Not good practice for adding hardcoded address for external contract
I recomend to be given address as input in the constructor instead of hardcoded address of external contract in the code.
File OndoPriceOracle.sol [42](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracle.sol#L42)

## [L-03] Move onlyGuardian and roles in abstract contract for Factory contract to make file smaller
The modifier onlyGuardian and roles can be moved in abstarct class for all factory contract. It will make file smaller and easy for reading. Other things that repeat can also be moved.

## [L-04] Skip using of v!=27 && V!=28 or v==27 || v=29
File KYCRegistry.sol [87](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L87)
See this for reference:
https://twitter.com/alexberegszaszi/status/1534461421454606336?s=20&t=H0Dv3ZT2bicx00hLWJk7Fg

Best approach:
```
// Whether v is 27 or 28.
                // The magic number has the 27th and 28th bytes
                // counting from the most significant byte set to 1.
                vIsValid := byte(v, 0x0101000000)
```

// Code block 1 (unknown):
// Whether v is 27 or 28.
                // The magic number has the 27th and 28th bytes
                // counting from the most significant byte set to 1.
                vIsValid := byte(v, 0x0101000000)


DeFiHackLabs G

# Report

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
|[G-1]| Setting the `constructor` to `payable` saves gas (sayan)|2|
|[G-2]| Mark functions as payable (with discretion)              |49|
|[G-3]| Use assembly for math (add, sub, mul, div)                 |2|
|[G-4]| Short Revert Strings                               |8|
|[G-5]| Use assembly to write storage values                 |13|
|[G-6]| Use multiple require() statments insted of require(expression && expression && …) |4|
|[G-7]| Optimal Comparison |8|
|[G-8]| Tightly pack storage variables |2|
|[G-9]| Instead of if(x), use assembly with iszeroiszero (x)(secoalba) |12|

**[G-1] Setting the `constructor` to `payable` saves gas (sayan)**

Saves ~13 gas per instance
```solidity
2 instances

File: Ethos-Vault/contracts/ReaperVaultV2.sol
111:     constructor(
112:         address _token,
113:         string memory _name,
114:         string memory _symbol,
115:         uint256 _tvlCap,
116:         address _treasury,
117:         address[] memory _strategists,
118:         address[] memory _multisigRoles
119:     ) ERC20(string(_name), string(_symbol)) {

File: Ethos-Vault/contracts/ReaperVaultERC4626.sol
16:     constructor(
17:         address _token,
18:         string memory _name,
19:         string memory _symbol,
20:         uint256 _tvlCap,
21:         address _treasury,
22:         address[] memory _strategists,
23:         address[] memory _multisigRoles
24:     ) ReaperVaultV2(_token, _name, _symbol, _tvlCap, _treasury, _strategists, _multisigRoles) {}

```

**[G-2] Mark functions as payable (with discretion)**

You can mark public or external functions as payable to save gas. Functions that are not payable have additional logic to check if there was a value sent with a call, however, making a function payable eliminates this check. This optimization should be carefully considered due to potentially unwanted behavior when a function does not need to accept ether.

Saves ~24 gas per insta

49 instances

File; Ethos-Core/contracts/TroveManager.sol

225:     constructor() public {

232:    function setAddresses(

299:    function getTroveOwnersCount(address _collateral) external view override returns (uint) {

303:    function getTroveFromTroveOwnersArray(address _collateral, uint _index) external view override returns (address) {

310:    function liquidate(address _borrower, address _collateral) external override {

513:    function liquidateTroves(address _collateral, uint _n) external override {

715:    function batchLiquidateTroves(address _collateral, address[] memory _troveArray) public override {

939:    function redeemCloseTrove(

962:    function updateDebtAndCollAndStakesPostRedemption(

982:    function burnLUSDAndEmitRedemptionEvent(

1016:    function redeemCollateral(

1045:    function getNominalICR(address _borrower, address _collateral) public view override returns (uint) {

1054:    function getCurrentICR(

1076:    function applyPendingRewards(address _borrower, address _collateral) external override {

1111:    function updateTroveRewardSnapshots(address _borrower, address _collateral) external override {

1123:    function getPendingCollateralReward(address _borrower, address _collateral) public view override returns (uint) {

1138:    function getPendingLUSDDebtReward(address _borrower, address _collateral) public view override returns (uint) {

1152:    function hasPendingRewards(address _borrower, address _collateral) public view override returns (bool) {

1164:    function getEntireDebtAndColl(

1183:    function removeStake(address _borrower, address _collateral) external override {

1195:    function updateStakeAndTotalStakes(address _borrower, address _collateral) external override returns (uint) {

1273:    function closeTrove(address _borrower, address _collateral, uint256 _closedStatusNum) external override {

1316:    function addTroveOwnerToArray(address _borrower, address _collateral) external override returns (uint

// Code block 1 (solidity):
2 instances

File: Ethos-Vault/contracts/ReaperVaultV2.sol
111:     constructor(
112:         address _token,
113:         string memory _name,
114:         string memory _symbol,
115:         uint256 _tvlCap,
116:         address _treasury,
117:         address[] memory _strategists,
118:         address[] memory _multisigRoles
119:     ) ERC20(string(_name), string(_symbol)) {

File: Ethos-Vault/contracts/ReaperVaultERC4626.sol
16:     constructor(
17:         address _token,
18:         string memory _name,
19:         string memory _symbol,
20:         uint256 _tvlCap,
21:         address _treasury,
22:         address[] memory _strategists,
23:         address[] memory _multisigRoles
24:     ) ReaperVaultV2(_token, _name, _symbol, _tvlCap, _treasury, _strategists, _multisigRoles) {}


lsaudit Q

# [QA-01] Lack of 2-step ownership transfer

[File: Auth.sol](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Dependencies/Auth.sol#L52)
```
 function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
```

A single step ownership change is risky due to the fact that the new owner address could be wrong.

Instead, consider using a contract like Ownable2Step, which provides a two-step ownership.

# [QA-02] `_openCdpCallback()` does not emit event

[File: LeverageMacroBase.sol](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/LeverageMacroBase.sol#L463)
```
  * Open CDP and Emit event
```

According to comment - function `_openCdpCallback()` should emit an event. However, there's no event emitted by `_openCdpCallback()`.

# [QA-03] Unused code should be removed

[File: EbtcBase.sol](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Dependencies/EbtcBase.sol#L35)
```
uint256 public constant BORROWING_FEE_FLOOR = 0; // 0.5%
```

Code-base does not use `BORROWING_FEE_FLOOR` constant. The calculation of fee floor is being performed in `_calcRedemptionRate()` in `CdpManager.sol`.

# [N-01] Typos

[File: ERC3156FlashLender.sol](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Dependencies/ERC3156FlashLender.sol#L13)
```
// Functions to modify these variables must be included in implementing contracts if desired
```

`impelemnting` should be changed to `implementing`

# [N-02] Redundant comments
Avoid inserting comments to the code-base, which does not provide any context

[File: SimplifiedDiamondLike.sol](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993

// Code block 1 (unknown):
function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }

// Code block 2 (unknown):
* Open CDP and Emit event

// Code block 3 (unknown):
uint256 public constant BORROWING_FEE_FLOOR = 0; // 0.5%

// Code block 4 (unknown):
// Functions to modify these variables must be included in implementing contracts if desired

Auth.sol#L52, LeverageMacroBase.sol#L463, EbtcBase.sol#L35, ERC3156FlashLender.sol#L13

EbtcBase.sol, Auth.sol, ERC3156FlashLender.sol, LeverageMacroBase.sol


0x11singh99 G

### Gas Optimizations List
| Number | Optimization Details | Instances |
|:--:|:-------| :-----:|
| [G-01] | Do not calculate constant variables | 3 |
| [G-02] | Use assembly for address(0) comparison |4 |
| [G-03] | Avoid updating storage when the value hasn't changed | 3 |
| [G-04] | Use custom errors rather than require() strings to save gas |__|
| [G-05] | Function calls can be cached rather than re calling | 1 |
| [G-06] | Write for loops in most gas efficient way | 3 |
| [G-07] | Reading from calldata/memory array multiple times should be cached | 2 |
| [G-08] | State variables should be cached in stack variables rather than re-reading them from storage | 16 |
| [G-09] | Using storage instead of memory for structs/arrays saves gas | 2 |
| [G-10] | Use unchecked{} whenever underflow/overflow not possible saves 130 gas each time | 10 |

Total 7 issues

## [G-01]  Do not calculate constant variables
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a
constant variable is recomputed each time that the variable is used, which wastes some gas each time of use.

*Total 3 instances - 1 files:*

### Instance#1-3 :
```solidity
File : contracts/lybra/configuration/LybraConfigurator.sol

  76:  bytes32 public constant DAO = keccak256("DAO");
  77:  bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
  78:  bytes32 public constant ADMIN = keccak256("ADMIN");
```
 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L76-L78
Make it commented and directly save hash of these strings by calculating off chain .It will save 30 gas for each time constant is used.30 Gas for calculating keccak256() each time.

## [G-02]  Use assembly for address(0) comparison
### saves 65 gas each time

*4 instances - 3 files:*

### Instance#1-2:
```solidity
File : contracts/lybra/configuration/LybraConfigurator.sol

   99:  if (address(EUSD) == address(0)) EUSD = IEUSD(_eusd);
   100

File : contracts/lybra/configuration/LybraConfigurator.sol

   99:  if (address(EUSD) == address(0)) EUSD = IEUSD(_eusd);
   100:  if (address(peUSD) == address(0)) peUSD = IEUSD(_peusd);

// Code block 1 (solidity):
File : contracts/lybra/configuration/LybraConfigurator.sol

  76:  bytes32 public constant DAO = keccak256("DAO");
  77:  bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
  78:  bytes32 public constant ADMIN = keccak256("ADMIN");


cartlex_ Q

Lost of gas when calling `InitToken` function in `LybraConfigurator.sol` contract.

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L95-L101

```
    /**
     * @notice Initializes the eUSD and peUSD address. This function can only be executed once.
     */
    function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
        if (address(EUSD) == address(0)) EUSD = IEUSD(_eusd);
        if (address(peUSD) == address(0)) peUSD = IEUSD(_peusd);
    }
```

In commentary block of this function says that it can be called once, but it possible to call it again due to `else` statment doesn't check.

To restrict calling this function again it is needed to use `!=` statement and custom error.
```
    /**
     * @notice Initializes the eUSD and peUSD address. This function can only be executed once.
     */
    function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
        if (address(EUSD) != address(0)) {
            revert AddressAlreadyInit();
        }
        if (address(peUSD) != address(0)) {
            revert AddressAlreadyInit();
        }
        peUSD = IEUSD(_peusd);
        EUSD = IEUSD(_eusd);
    }
```

// Code block 1 (unknown):
/**
     * @notice Initializes the eUSD and peUSD address. This function can only be executed once.
     */
    function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
        if (address(EUSD) == address(0)) EUSD = IEUSD(_eusd);
        if (address(peUSD) == address(0)) peUSD = IEUSD(_peusd);
    }

// Code block 2 (unknown):
/**
     * @notice Initializes the eUSD and peUSD address. This function can only be executed once.
     */
    function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
        if (address(EUSD) != address(0)) {
            revert AddressAlreadyInit();
        }
        if (address(peUSD) != address(0)) {
            revert AddressAlreadyInit();
        }
        peUSD = IEUSD(_peusd);
        EUSD = IEUSD(_eusd);
    }


kodyvim Q

## Missing Invariant check on pause functions
Pause should be callable when protocol is not in paused state already and vice versa for unpaused.
One Instance:
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L208
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L213
```solidity
/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager {
        _pause();
    }

    /// @dev Returns to normal state. Contract must be paused
    function unpause() external onlyLRTAdmin {
        _unpause();
    }
```
other instances:
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L102
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L107
https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L127
https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L132
## Recommendation
Add [whenNotPaused](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/3d4c0d5741b131c231e558d7a6213392ab3672a5/contracts/security/PausableUpgradeable.sol#L49) to `pause()` and [whenPaused](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/3d4c0d5741b131c231e558d7a6213392ab3672a5/contracts/security/PausableUpgradeable.sol#L61) modifier to `unpause()`.

## No mechanism to nonsupport assets.
With the current implementation, once an asset is supported it cannot be unsupported without an upgrade.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L80
```solidity
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);
    }
```
## Re

// Code block 1 (solidity):
/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager {
        _pause();
    }

    /// @dev Returns to normal state. Contract must be paused
    function unpause() external onlyLRTAdmin {
        _unpause();
    }

// Code block 2 (solidity):
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);
    }

/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager {
        _pause();
    }

    /// @dev Returns to normal state. Contract must be paused
    function unpause() external onlyLRTAdmin {
        _unpause();
    }

function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);
    }

LRTDepositPool.sol#L208, LRTDepositPool.sol#L213, LRTOracle.sol#L102, LRTOracle.sol#L107, NodeDelegator.sol#L127, NodeDelegator.sol#L132, PausableUpgradeable.sol#L49, PausableUpgradeable.sol#L61, LRTConfig.sol#L80

LRTOracle.sol, PausableUpgradeable.sol, NodeDelegator.sol, LRTConfig.sol, LRTDepositPool.sol


KrisApostolov G

# Gas Report

## Disclaimer

Some of the examples may contain truncated versions of the original code. There may also be `@audit` tags, which denote the actual place affected

## Table of contents

|  | Issue | Instances | Estimated savings |
| --- | --- | --- | --- |
| [G-01] | Functions guaranteed to revert, when called by normal users should be marked private to save on gas | 21 | 441 |
| [G-02] | Use unchecked blocks on variables that are guaranteed to not overflow or underflow | 1 | 126 |
| [G-03] | Unnecessary external call(s) | 2 | ^3000 |
| [G-04] | Unnecessary variable allocation | 3 | 39 |
| [G-05] | Hashes can be cached into into immutable variables | 6 | 2772 |
| [G-06] | Refactor the weight calculation | 1 | ^327 |
| [G-07] | Saving storage variable into memory saves gas when variable is called more than once | 11 | 1067 |
| [G-08] | Saving the output of a function when possible to save on gas | 2 | ^5000 |
| [G-09] | Optimize names to save gas | The whole protocol | ^500 |
| [G-10] | Emitting an event with a storage variable wastes gas | 5 | 650 |
| [G-11] | address.call can be optimized with assembly | 5 | - |

### **Estimated savings: ^14500 gas**

## [G-01] - Functions guaranteed to revert, when called by normal users should be marked private to save on gas

In the event that a function modifier or a "require" statement like `onlyOwner` is utilized, an attempt by a regular user to make a call to the function will result in the function being reverted. If the function is marked as "payable," the cost of gas for legitimate callers will decrease, as the compiler will no longer need to include checks to determine if payment was provided. This means that the additional opcodes that are typically avoided, including `CALLVALUE(2)`, `DUP1(3)`, `ISZERO(3)`, `PUSH2(3)`, `JUMPI(10)`, `PUSH1(3)`, `DUP1(3)`, `REVERT(0)`, `JUMPDEST(1)`, and `POP(2)`, which typically cost an average of around **21 gas** per call to the function, will not be included. The following

SfrxEth.sol
function withdraw(uint256 _amount) external onlyOwner {
// The redeem function returns the withdrawn amount, which is equal to the frxEthBalance variable
        IsFrxEth(SFRX_ETH_ADDRESS).redeem(
            _amount,
            address(this),
            address(this)
        );
        uint256 frxEthBalance = IERC20(FRX_ETH_ADDRESS).balanceOf(
            address(this)
        );
        IsFrxEth(FRX_ETH_ADDRESS).approve(
            FRX_ETH_CRV_POOL_ADDRESS,
            frxEthBalance
        );

        uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
            (10 ** 18 - maxSlippage)) / 10 ** 18;

        IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(
            1,
            0,
            frxEthBalance,
            minOut
        );
        // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
        require(sent, "Failed to send Ether");
    }

Sfrx.sol
function deposit() external payable onlyOwner returns (uint256) {
        IFrxETHMinter frxETHMinterContract = IFrxETHMinter(
            FRX_ETH_MINTER_ADDRESS
        );
        uint256 sfrxBalancePre = IERC20(SFRX_ETH_ADDRESS).balanceOf(
            address(this)
        );
        frxETHMinterContract.submitAndDeposit{value: msg.value}(address(this));
        uint256 sfrxBalancePost = IERC20(SFRX_ETH_ADDRESS).balanceOf(
            address(this)
        );
        return sfrxBalancePost - sfrxBalancePre;
    }


DadeKuma Q

## Summary

---

### Low Risk Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[L-01]| Lack of input validation in ERC4626 functions | 1 |
|[L-02]| Hardcoded addresses can be problematic | 1 |
|[L-03]| `LUSDToken` can mint to `StabilityPool`, `TroveManager`, and `BorrowerOperation` | 1 |
|[L-04]| `distributionPeriod` must be > 0 to avoid locked funds | 1 |
|[L-05]| Missing `transfer` value check for ERC-20 | 2 |
|[L-06]| Lack of two-step update for critical addresses | 15 |
|[L-07]| Missing events in sensitive functions | 7 |
|[L-08]| Owner can renounce ownership | 22 |

Total: 50 instances over 8 issues.

### Non Critical Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[NC-01]| Low test coverage | 1 |
|[NC-02]| Unused named `return` is confusing | 26 |
|[NC-03]| Same constant redefined elsewhere | 8 |
|[NC-04]| Use of `constant` variables instead of `immutable` | 8 |
|[NC-05]| Use `require` instead of `assert` | 21 |
|[NC-06]| `require` without any message | 18 |
|[NC-07]| Some functions don't follow the Solidity naming conventions | 1 |
|[NC-08]| Old Solidity version | 33 |
|[NC-09]| Use of floating pragma | 18 |
|[NC-10]| Missing or incomplete NatSpec | 62 |

Total: 196 instances over 10 issues.

## Low Risk Findings

---

### [L-01] Lack of input validation in ERC4626 functions

Functions `deposit, mint, withdraw, redeem` lack a validation to check if the amount exceed the max.

```solidity
// Deposit
require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");

// Mint
require(shares <= maxMint(receiver), "ERC4626: mint more than max");

// Withdraw
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");

// Redeem
require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");
```

*There is 1 instance of this issue.*

```solidity
File: Ethos-Vault/contracts/ReaperVaultERC4626.sol

110:        function deposit(uint256 assets, address receiver) external override returns (uint256 shares) {
111:            shares

File: Ethos-Vault/contracts/ReaperVaultERC4626.sol

110:        function deposit(uint256 assets, address receiver) external override returns (uint256 shares) {
111:            shares = _deposit(assets, receiver);
112:        }
154:        function mint(uint256 shares, address receiver) external override returns (uint256 assets) {
155:            assets = previewMint(shares); // previewMint rounds up so exactly "shares" should be minted and not 1 wei less
156:            _deposit(assets, receiver);
157:        }
202:        function withdraw(
203:            uint256 assets,
204:            address receiver,
205:            address owner
206:        ) external override returns (uint256 shares) {
207:            shares = previewWithdraw(assets); // previewWithdraw() rounds up so exactly "assets" are withdrawn and not 1 wei less
208:            if (msg.sender != owner) _spendAllowance(owner, msg.sender, shares);
209:            _withdraw(shares, receiver, owner);
210:        }
258:        function redeem(
259:            uint256 shares,
260:            address receiver,
261:            address owner
262:        ) external override returns (uint256 assets) {
263:            if (msg.sender != owner) _spendAllowance(owner, msg.sender, shares);
264:            assets = _withdraw(shares, receiver, owner);
265:        }

// Code block 1 (solidity):
// Deposit
require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");

// Mint
require(shares <= maxMint(receiver), "ERC4626: mint more than max");

// Withdraw
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");

// Redeem
require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");


gesha17 Q

## [L-01] Missing input validation in `_setToken()`, token addresses in mapping should be immutable.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L156

```solidity
    mapping(bytes32 tokenKey => address tokenAddress) public tokenMap;
    .
    .
    .
    /// @dev private function to set a token
    /// @param key Token key
    /// @param val Token address
    function _setToken(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (tokenMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        tokenMap[key] = val;
        emit SetToken(key, val);
    }
```

The function checks if the new address `val` is already set to that value in the mapping: `if(tokenMap[key] == val)`.
However this is not enough. Token addresses will not change, so values in the mapping should be immutable.

This error can occur if an admin accidentally sets the address for a future token to an existing one in the mapping like `LRTConstants.ST_ETH_TOKEN` instead of the actual token address, for example `LRTConstants.NEW_ETH_TOKEN`, constant names are easy to mistake for one another. This unwanted action can result in sending the wrong type of tokens to a contract.

Recommendation is when setting the token also check if the `tokenMap[key]` is already set to a non-zero address, if it is then revert.

## [L-02] Missing input validation in `_setContract()`, contract addresses in mapping should be immutable.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L172

```solidity
    mapping(bytes32 contractKey => address contractAddress) public contractMap;
    .
    .
    .
    /// @dev private function to set a contract
    /// @param key Contract key
    /// @param val Contract address
    function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (contractMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        contractMap[key]

    mapping(bytes32 contractKey => address contractAddress) public contractMap;
    .
    .
    .
    /// @dev private function to set a contract
    /// @param key Contract key
    /// @param val Contract address
    function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (contractMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        contractMap[key] = val;
        emit SetContract(key, val);
    }

// Code block 1 (solidity):
mapping(bytes32 tokenKey => address tokenAddress) public tokenMap;
    .
    .
    .
    /// @dev private function to set a token
    /// @param key Token key
    /// @param val Token address
    function _setToken(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (tokenMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        tokenMap[key] = val;
        emit SetToken(key, val);
    }