martin G

# Ondo Finance

## Gas Optimizations Report

### G-01 Splitting `require()` statements that use `&&` saves gas

_There are **3** instances of this issue:_

Instead of using `&&` on single require check using two require checks can save gas

```solidity
292: require(
      (answeredInRound >= roundId) &&
        (updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),
      "Chainlink oracle price is stale"
    );
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol

```solidity
45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCash.sol

```solidity
45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenModified.sol

### G-02 Replace `x <= y` with `x < y + 1`, and `x >= y` with `x > y – 1`

In the EVM, there is no opcode for >= or <=. When using greater than or equal, two operations are performed: > and =. Using strict comparison operators hence saves gas

_There are **3** instances of this issue:_

```solidity
92: require(block.timestamp <= deadline, "KYCRegistry: signature expired");
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol

```solidity
417: borrowRateMantissa <= borrowRateMaxMantissa,
```

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

```solidity
416: require(
      borrowRateMantissa <= borrowRateMaxMantissa,
      "borrow rate is absurdly high"
    );
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenModified.sol

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

// Code block 2 (solidity):
45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );

// Code block 3 (solidity):
45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );

// Code block 4 (solidity):
92: require(block.timestamp <= deadline, "KYCRegistry: signature expired");

// Code block 5 (solidity):
417: borrowRateMantissa <= borrowRateMaxMantissa,

// Code block 6 (solidity):
416: require(
      borrowRateMantissa <= borrowRateMaxMantissa,
      "borrow rate is absurdly high"
    );

292: require(
      (answeredInRound >= roundId) &&
        (updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),
      "Chainlink oracle price is stale"
    );

45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );

45: require(
      accrualBlockNumber == 0 && borrowIndex == 0,
      "market may only be initialized once"
    );

92: require(block.timestamp <= deadline, "KYCRegistry: signature expired");

417: borrowRateMantissa <= borrowRateMaxMantissa,

416: require(
      borrowRateMantissa <= borrowRateMaxMantissa,
      "borrow rate is absurdly high"
    );

OndoPriceOracleV2.sol, CCash.sol, CTokenModified.sol, KYCRegistry.sol, CTokenCash.sol

CTokenCash.sol, CTokenModified.sol, KYCRegistry.sol, OndoPriceOracleV2.sol, CCash.sol


Rickard G

# STATE VARIABLES CAN BE PACKED INTO FEWER STORAGE SLOTS
BaseSmartAccount.sol
```
struct Transaction {
        	address to;
-      	uint256 value;
        	bytes data;
        	Enum.Operation operation;
+      	uint256 value;
        	uint256 targetTxGas;
    	}

```
```
struct FeeRefund {
+      	address gasToken;
        	uint256 baseGas;
        	uint256 gasPrice; //gasPrice or tokenGasPrice
        	uint256 tokenGasPriceFactor;
-       	address gasToken;
        	address payable refundReceiver;
    	}

```
EntryPoint.sol
```
    	struct MemoryUserOp {
        		address sender;
+		address paymaster;
       	 	uint256 nonce;
       		uint256 callGasLimit;
       		uint256 verificationGasLimit;
       		uint256 preVerificationGas;
-		address paymaster;
        		uint256 maxFeePerGas;
       		uint256 maxPriorityFeePerGas;
    		}
```
```
    		struct DepositInfo {
-		uint112 deposit;
-		bool staked;
-		uint112 stake;
        		uint32 unstakeDelaySec;
        		uint64 withdrawTime;
+		uint112 stake;
+		uint112 deposit;
+		bool staked;
    		}
```
# BYTES CONSTANTS ARE MORE EFFICIENT THAN STRING CONSTANTS
If data can fit into 32 bytes, then you should use bytes32 datatype rather than bytes or strings as it is cheaper in solidity.
[SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L36)
```
    string public constant VERSION = "1.0.2"; // using AA 0.3.0
```
[SmartAccountFactory.sol](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L11)
```
    string public constant VERSION = "1.0.2";
```
DefaultCallbackHandler.sol [L12](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L12), [L13](https://github.com/code-423n4/20

// Code block 1 (unknown):
struct Transaction {
        	address to;
-      	uint256 value;
        	bytes data;
        	Enum.Operation operation;
+      	uint256 value;
        	uint256 targetTxGas;
    	}

// Code block 2 (unknown):
struct FeeRefund {
+      	address gasToken;
        	uint256 baseGas;
        	uint256 gasPrice; //gasPrice or tokenGasPrice
        	uint256 tokenGasPriceFactor;
-       	address gasToken;
        	address payable refundReceiver;
    	}

// Code block 3 (unknown):
struct MemoryUserOp {
        		address sender;
+		address paymaster;
       	 	uint256 nonce;
       		uint256 callGasLimit;
       		uint256 verificationGasLimit;
       		uint256 preVerificationGas;
-		address paymaster;
        		uint256 maxFeePerGas;
       		uint256 maxPriorityFeePerGas;
    		}

// Code block 4 (unknown):
struct DepositInfo {
-		uint112 deposit;
-		bool staked;
-		uint112 stake;
        		uint32 unstakeDelaySec;
        		uint64 withdrawTime;
+		uint112 stake;
+		uint112 deposit;
+		bool staked;
    		}

// Code block 5 (unknown):
string public constant VERSION = "1.0.2"; // using AA 0.3.0

// Code block 6 (unknown):
string public constant VERSION = "1.0.2";


Rolezn Q

## Summary<a name="Summary">

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#LOW‑1) | `decimals()` not part of ERC20 standard | 1 |
| [LOW‑2](#LOW‑2) | Event is missing parameters | 2 |
| [LOW‑3](#LOW‑3) | Function can risk gas exhaustion on large receipt calls due to multiple mandatory loops | 2 |
| [LOW‑4](#LOW‑4) | Possible rounding issue | 2 |
| [LOW‑5](#LOW‑5) | Contracts are not using their OZ Upgradeable counterparts | 6 |
| [LOW‑6](#LOW‑6) | `require()` should be used instead of `assert()` | 20 |
| [LOW‑7](#LOW‑7) | Inability to remove specific array values | 2 |
| [LOW‑8](#LOW‑8) | Upgrade OpenZeppelin Contract Dependency | 3 |

Total: 38 contexts over 8 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#NC‑1) | Add a timelock to critical functions | 15 |
| [NC‑2](#NC‑2) | Avoid Floating Pragmas: The Version Should Be Locked | 3 |
| [NC‑3](#NC‑3) | Variable Names That Consist Of All Capital Letters Should Be Reserved For Const/immutable Variables | 2 |
| [NC‑4](#NC‑4) | Constants Should Be Defined Rather Than Using Magic Numbers | 4 |
| [NC‑5](#NC‑5) | Constants in comparisons should appear on the left side | 12 |
| [NC‑6](#NC‑6) | Critical Changes Should Use Two-step Procedure | 15 |
| [NC‑7](#NC‑7) | Duplicated `require()`/`revert()` Checks Should Be Refactored To A Modifier Or Function | 6 |
| [NC‑8](#NC‑8) | Duplicate imports | 2 |
| [NC‑9](#NC‑9) | `block.timestamp` is already used when emitting events, no need to input timestamp | 1 |
| [NC‑10](#NC‑10) | Function writing that does not comply with the Solidity Style Guide  | 11 |
| [NC‑11](#NC‑11) | Large or complicated code bases should implement fuzzing tests | 1 |
| [NC‑12](#NC‑12) | Use `d

63: uint256 decimals = IERC20(collateral).decimals()

function pullCollateralFromBorrowerOperationsOrDefaultPool(address _collateral, uint _amount) external override {
        ...
        emit ActivePoolCollateralBalanceUpdated(_collateral, collAmount[_collateral]);
        ...
    }


OpeOginni G

## 1. IF’s/require() statements that check input arguments should be at the top of the function

***Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gas in a function that may ultimately revert in the unhappy case.***

```
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol

192:        require(assetAmount <= depositedAsset[onBehalfOf], "total of collateral can be liquidated at most");
```

This require statement only checks the values from the parameters of the Function `superLiquidation(address provider, address onBehalfOf, uint256 assetAmount)`, but is put below some other function calls that might waste gas if this Require statement ends up failing the whole function. Therefore it the require statement at line 192 should be put at the top of the function BEFORE any internal function calls.

This also happens in the following files:
```
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol

130:        require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
        require(PeUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
```

// Code block 1 (unknown):
This require statement only checks the values from the parameters of the Function `superLiquidation(address provider, address onBehalfOf, uint256 assetAmount)`, but is put below some other function calls that might waste gas if this Require statement ends up failing the whole function. Therefore it the require statement at line 192 should be put at the top of the function BEFORE any internal function calls.

This also happens in the following files:


cyberinn G

# Gas Optimizations Report

## G-01 Splitting `require()` statements that use `&&` saves gas

### Impact

When using `&&` in a `require()` statement, the entire statement will be evaluated before the transaction reverts. If the first condition is false, the second condition will not be evaluated. This means that if the first condition is false, the second condition will not be evaluated, which is a waste of gas. Splitting the `require()` statement into two separate statements will save gas.

### Findings``

[scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L68](https://github.com/code-423n4/2023-01-biconomy/tree/main//scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L68)

```solidity
68:    require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");
```

[scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L34](https://github.com/code-423n4/2023-01-biconomy/tree/main//scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L34)

```solidity
34:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
```

[scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L49](https://github.com/code-423n4/2023-01-biconomy/tree/main//scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L49)

```solidity
49:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
```

## G-02 Use named returns for local variables where it is possible.

### Impact

It is not necessary to have both a named return and a return statement.

### Findings

[scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L486](https://github.com/code-423n4/2023-01-biconomy/tree/main//scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L486)

```solidity
486:    uint256 maxFeePerGas = mUserOp.maxFeePerGas;
```

[scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L47](https://github

// Code block 1 (solidity):
68:    require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");

// Code block 2 (solidity):
34:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

// Code block 3 (solidity):
49:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

// Code block 4 (solidity):
486:    uint256 maxFeePerGas = mUserOp.maxFeePerGas;

68:    require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");

34:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

49:    require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

486:    uint256 maxFeePerGas = mUserOp.maxFeePerGas;

ModuleManager.sol#L68, ModuleManager.sol#L34, ModuleManager.sol#L49, EntryPoint.sol#L486


adam idarrha Q

Title: depositors might not be able to deposit even if deposit limit is not reached

## Lines of code:

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/oracles/ChainlinkPriceOracle.sol#L37-L39

## Vulnerability details:
### Details:

the protocol has a deposit limit per asset for all depositor calculated in the following way:
total assets in LRTDepositPool + total assets in nodeDelegators + amountDepositedInEigenLayer:

```solidity=47
function getTotalAssetDeposits(address asset) public view override returns (uint256 totalAssetDeposit) {
        (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer) =
            getAssetDistributionData(asset);
        return (assetLyingInDepositPool + assetLyingInNDCs + assetStakedInEigenLayer);
    }
```

the amount Deposited in EigenLayer is calculated with the function `userUnderlyingView` which returns how much asset the user has in the pool, based on how many shares he has. which takes into account amount deposited + any rewards accrued from the strategy.

`NodeDelegator`

```solidity=121
function getAssetBalance(address asset) external view override returns (uint256) {
        address strategy = lrtConfig.assetStrategy(asset);
        return IStrategy(strategy).userUnderlyingView(address(this));
    }
```

`StrategyBase.sol` out of scope:

```solidity
/**
     * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
     * this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
     */
    function userUnderlyingView(address user) external view virtual returns (uint256) {
        return sharesToUnderlyingView(shares(user));
    }
```

so the amount deposited in eigenlayer returns how much the user deposited + any rewards or slashing.

which makes it that the protocol think that he reached the deposit limit without it actually reaching it.

```solidity=56

// Code block 1 (unknown):
the amount Deposited in EigenLayer is calculated with the function `userUnderlyingView` which returns how much asset the user has in the pool, based on how many shares he has. which takes into account amount deposited + any rewards accrued from the strategy.

`NodeDelegator`

// Code block 2 (unknown):
`StrategyBase.sol` out of scope:

// Code block 3 (unknown):
so the amount deposited in eigenlayer returns how much the user deposited + any rewards or slashing.

which makes it that the protocol think that he reached the deposit limit without it actually reaching it.


0xSmartContract Q

## Summary
### Low Risk Issues List
| Number |Issues Details|Context|
|:--:|:-------|:--:|
|[L-01]| Prevent division by 0| 1 |
|[L-02]|  Use of EIP 4337, which is likely to change, not recommended for general use or application | 1 |
|[L-03]| Consider using OpenZeppelin's SafeCast library to prevent unexpected overflows when casting from uint256| 1 |
|[L-04]| Gas griefing/theft is possible on unsafe external call| 8 |
|[L-05]| Front running attacks by the `onlyOwner` | 1 |
|[L-06]| A single point of failure  | 14 |
|[L-07]| Loss of precision due to rounding  | 1 |
|[L-08]| No Storage Gap for ` BaseSmartAccount ` and `ModuleManager `| 2 |
|[L-09]| Missing Event for critical parameters init and change | 1 |
|[L-10]| Use `2StepSetOwner ` instead of ` setOwner `| 1 |
|[L-11]| init() function can be called by anybody| 1 |
|[L-12]| The minimum transaction value of 21,000 gas may change in the future| 1 |

Total 12 issues

### Non-Critical Issues List
| Number |Issues Details|Context|
|:--:|:-------|:--:|
| [N-01]|Insufficient coverage|1|
| [N-02] |Unused function parameter and local variable |2|
| [N-03] |Initial value check is missing in Set Functions| 3 |
| [N-04] |NatSpec comments should be increased in contracts |All Contracts|
| [N-05] |`Function writing` that does not comply with the `Solidity Style Guide` |All Contracts|
| [N-06] |Add a timelock to critical functions| 1|
| [N-07] |For modern and more readable code; update import usages| 116 |
| [N-08] |Include return parameters in NatSpec comments | All Contracts |
| [N-09] |Long lines are not suitable for the ‘Solidity Style Guide’| 9 |
| [N-10] |Need Fuzzing test| 23  |
| [N-11] |Test environment comments and codes should not be in the main version| 1 |
| [N-12] |Use of bytes.concat() instead of abi.encodePacked()| 5 |
| [N-13] |For functions, follow Solidity standard naming conventions (internal function style rule)| 13 |
| [N-14] |Omissions in Events| 1 |
| [N-15] |Open TODOs | 1 |
| [N-16] |Mark visibility of

2 results - 1 file

contracts/smart-contract-wallet/SmartAccount.sol:
  264:             payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);
  288:             payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);

contracts/smart-contract-wallet/SmartAccount.sol:
  246
  247:     function handlePayment(
  248:         uint256 gasUsed,
  249:         uint256 baseGas,
  250:         uint256 gasPrice,
  251:         uint256 tokenGasPriceFactor,
  252:         address gasToken,
  253:         address payable refundReceiver
  254:     ) private nonReentrant returns (uint256 payment) {
  255:         // uint256 startGas = gasleft();
  256:         // solhint-disable-next-line avoid-tx-origin
  257:         address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
  258:         if (gasToken == address(0)) {
  259:             // For ETH we will only adjust the gas price to not be higher than the actual used gas price
  260:             payment = (gasUsed + baseGas) * (gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
  261:             (bool success,) = receiver.call{value: payment}("");
  262:             require(success, "BSA011");
  263:         } else {
  264:             payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);
  265:             require(transferToken(gasToken, receiver, payment), "BSA012");
  266:         }
  267:         // uint256 requiredGas = startGas - gasleft();
  268:         //console.log("hp %s", requiredGas);
  269:     }
  270:
  271:     function handlePaymentRevert(
  272:         uint256 gasUsed,
  273:         uint256 baseGas,
  274:         uint256 gasPrice,
  275:         uint256 tokenGasPriceFactor,
  276:         address gasToken,
  277:         address payable refundReceiver
  278:     ) external returns (uint256 payment) {
  279:         uint256 startGas = gasleft();
  280:         // solhint-disable-next-line avoid-tx-origin
  281:         address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
  282:         if (gasToken == address(0)) {
  283:             // For ETH we will only adjust the gas price to not be higher than the actual used gas price


AkshaySrivastav Q

- `KYCRegistryClientConstructable` contains double import statements.
    ```solidity
    import "contracts/cash/kyc/KYCRegistryClient.sol";
    import "contracts/cash/kyc/KYCRegistryClient.sol";
    ```
- In CashManager contract more input validations should be added for privileged access restricted functions.
Like:
    - `setMintExchangeRateDeltaLimit` - `_exchangeRateDeltaLimit` must be less than `BPS_DENOMINATOR`.
    - `setRedeemLimit` - `_redeemLimit` must not be less than `currentRedeemAmount`.

- Ownable - no getter function present for `pendingOwnerAddr` variable.
    ```solidity
    address private pendingOwnerAddr;
    ```
- `OndoPriceOracle.getUnderlyingPrice` should validate that `cTokenAddress != 0`. The the developer comments mentions a check but it is not implemented.
    ```solidity
      /**
       * @notice Retrieve the price of the provided fToken
       *         contract's underlying asset
       * @param fToken fToken contract address
       * @dev This function first attempts to check if the price has been set directly
              in contract storage. If not set, we check if there is a corresponding cToken
       *      set within `fTokenToCToken` and piggy back on an external price oracle.
       */
      function getUnderlyingPrice(address fToken) external view override returns (uint256) {
        if (fTokenToUnderlyingPrice[fToken] != 0) {
          return fTokenToUnderlyingPrice[fToken];
        } else {
          address cTokenAddress = fTokenToCToken[fToken];   // @audit-issue - should validate cTokenAddress != 0,
          return cTokenOracle.getUnderlyingPrice(cTokenAddress);
        }
      }
    ```
- During research and discussion it was mentioned by Ondo team that interactions with Compound's CEth contract could be possible in the future. However the current state of Ondo contracts are not compatible with CEth interactions. Eg: - the `OndoPriceOracleV2` contract calls `underlying()` and `decimals()` functions of Compound's CT

// Code block 1 (solidity):
import "contracts/cash/kyc/KYCRegistryClient.sol";
    import "contracts/cash/kyc/KYCRegistryClient.sol";

// Code block 2 (solidity):
address private pendingOwnerAddr;

// Code block 3 (solidity):
/**
       * @notice Retrieve the price of the provided fToken
       *         contract's underlying asset
       * @param fToken fToken contract address
       * @dev This function first attempts to check if the price has been set directly
              in contract storage. If not set, we check if there is a corresponding cToken
       *      set within `fTokenToCToken` and piggy back on an external price oracle.
       */
      function getUnderlyingPrice(address fToken) external view override returns (uint256) {
        if (fTokenToUnderlyingPrice[fToken] != 0) {
          return fTokenToUnderlyingPrice[fToken];
        } else {
          address cTokenAddress = fTokenToCToken[fToken];   // @audit-issue - should validate cTokenAddress != 0,
          return cTokenOracle.getUnderlyingPrice(cTokenAddress);
        }
      }

import "contracts/cash/kyc/KYCRegistryClient.sol";
    import "contracts/cash/kyc/KYCRegistryClient.sol";

address private pendingOwnerAddr;

/**
       * @notice Retrieve the price of the provided fToken
       *         contract's underlying asset
       * @param fToken fToken contract address
       * @dev This function first attempts to check if the price has been set directly
              in contract storage. If not set, we check if there is a corresponding cToken
       *      set within `fTokenToCToken` and piggy back on an external price oracle.
       */
      function getUnderlyingPrice(address fToken) external view override returns (uint256) {
        if (fTokenToUnderlyingPrice[fToken] != 0) {
          return fTokenToUnderlyingPrice[fToken];
        } else {
          address cTokenAddress = fTokenToCToken[fToken];   // @audit-issue - should validate cTokenAddress != 0,
          return cTokenOracle.getUnderlyingPrice(cTokenAddress);
        }
      }


Aymen0909 Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1 | `delegateBySig` doesn't revert when `signer` is `address(0)` | Low | 1 |
| 2 | Front-runable `initialize` function | Low | 1 |
| 3 | Mistake in the `feeBPS` checks revert string | NC | 2 |
| 4 | Remove old code comment lines | NC | 1 |

## Findings

### 1- `permit` doesn't revert when `recoveredAddress` is `address(0)` :

### Risk : Low

The function `permit` from the LUSDToken contract is used to allow the users to delegate by signatures and it uses the `ecrecover` function to get the `recoveredAddress` address, this function can in some cases return `address(0)` but the `permit` function does not check the `recoveredAddress` address and thus it will not revert in case of `recoveredAddress == address(0)`.

### Proof of Concept

the issue occurs in the `permit` function below :

File: LUSDToken.sol [Line 262-294](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L262-L294)

```solidity
function permit
(
    address owner,
    address spender,
    uint amount,
    uint deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
)
    external
    override
{
    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
    // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
        revert('LUSD: Invalid s value');
    }
    require(deadline >= now, 'LUSD: expired deadline');
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01',
                        dom

function permit
(
    address owner,
    address spender,
    uint amount,
    uint deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
)
    external
    override
{
    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
    // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
        revert('LUSD: Invalid s value');
    }
    require(deadline >= now, 'LUSD: expired deadline');
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01',
                        domainSeparator(), keccak256(abi.encode(
                        _PERMIT_TYPEHASH, owner, spender, amount,
                        _nonces[owner]++, deadline))));
    address recoveredAddress = ecrecover(digest, v, r, s);
    require(recoveredAddress == owner, 'LUSD: invalid signature');
    _approve(owner, spender, amount);
}

function initialize(
    address _vault,
    address[] memory _strategists,
    address[] memory _multisigRoles,
    IAToken _gWant
) public initializer


Jerry0x Q

Confirm the derivative address to avoid incorrectly setting weight.
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L165-L175

    function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight,
        address _checkDerivative
    ) external onlyOwner {
        require(_checkDerivative == address(derivatives[_derivativeIndex]), "wrong derivative");
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            if (weight == 0) continue;
            uint256 ethAmount = (minAmount * weight) / totalWeight;
            require(ethAmount > 0, "weight too low");
        }
        emit WeightChange(_derivativeIndex, _weight);
    }
```
Adding and removing functions can reduce gas costs and minimize unknown risks.
```
    function removeDerivative(
        uint256 _derivativeIndex,
        address _checkDerivative
    ) external onlyOwner {
        require(_checkDerivative != address(0), "invalid address");
        require(_checkDerivative == address(derivatives[_derivativeIndex]), "wrong derivative");
        adjustWeight(_derivativeIndex, 0,_checkDerivative);
        rebalanceToWeights();
        --derivativeCount;
        derivatives[_derivativeIndex] = derivatives[derivativeCount];
        weights[_derivativeIndex] = weights[derivativeCount];
    }
```
Lower gas, reduced risk, easier to understand.
```
struct Derivative {
    IDerivative derivative;
    uint96 weight;
}
mapping(uint256 => Derivative) public derivatives;
```

totalEth and totalEthAmount are not always equal,  Whether returning or depositing, we should handle it.
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L83-L96
        uint256 totalEth =

// Code block 1 (unknown):
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L165-L175

    function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight,
        address _checkDerivative
    ) external onlyOwner {
        require(_checkDerivative == address(derivatives[_derivativeIndex]), "wrong derivative");
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            if (weight == 0) continue;
            uint256 ethAmount = (minAmount * weight) / totalWeight;
            require(ethAmount > 0, "weight too low");
        }
        emit WeightChange(_derivativeIndex, _weight);
    }

// Code block 2 (unknown):
function removeDerivative(
        uint256 _derivativeIndex,
        address _checkDerivative
    ) external onlyOwner {
        require(_checkDerivative != address(0), "invalid address");
        require(_checkDerivative == address(derivatives[_derivativeIndex]), "wrong derivative");
        adjustWeight(_derivativeIndex, 0,_checkDerivative);
        rebalanceToWeights();
        --derivativeCount;
        derivatives[_derivativeIndex] = derivatives[derivativeCount];
        weights[_derivativeIndex] = weights[derivativeCount];
    }

// Code block 3 (unknown):
struct Derivative {
    IDerivative derivative;
    uint96 weight;
}
mapping(uint256 => Derivative) public derivatives;


shark G

## 1. Splitting `require` statements that use `&&` saves gas

Instead of using the `&&` operator to check multiple conditions, use multiple `require()` statements. This will save approximately 3 gas per `&&`.

Here is an example of this issue:

File: `ModuleManager.sol` [Line 34](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L34)

```solidity
        require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
```

In the example above, the `&&` could be split up:

```solidity
        require(module != address(0), "BSA101");
        require(module != SENTINEL_MODULES, "BSA101");
```

Here are 2 other instances of this issue:

- File: `ModuleManager.sol` [Line 49](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L49)
- File: `ModuleManager.sol` [Line 68](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L68)

## 2. Strict equalities cost less gas than non-strict

Strict equalities (`<`, `>`) cost less gas than non-strict (`<=`, `>=`). This is because strict equality uses fewer opcodes.

For instance:

File: `EntryPoint.sol` [Line 213](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L213)

```solidity
            require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");
```

The `require` statement above could use `>` instead of `>=` to save gas:

```solidity
            require(paymasterAndData.length > 19, "AA93 invalid paymasterAndData");
```

Here are some more instances of this issue:

- File: `StakeManager.sol` [Line 41](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L41)
- File: `StakeManager.sol` [Line 62](https://github.com/code-423n4/2023-

// Code block 1 (solidity):
require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

// Code block 2 (solidity):
require(module != address(0), "BSA101");
        require(module != SENTINEL_MODULES, "BSA101");

// Code block 3 (solidity):
require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");

// Code block 4 (solidity):
require(paymasterAndData.length > 19, "AA93 invalid paymasterAndData");

require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

require(module != address(0), "BSA101");
        require(module != SENTINEL_MODULES, "BSA101");

require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");

require(paymasterAndData.length > 19, "AA93 invalid paymasterAndData");

ModuleManager.sol#L34, ModuleManager.sol#L49, ModuleManager.sol#L68, EntryPoint.sol#L213, StakeManager.sol#L41


MohammedRizwan G

## Summary

### Gas Optimizations
| |Issue|Instances| |
|-|:-|:-:|:-:|
| [G‑01] | `_rmul()` can gas optimized by removing extra lines of code | 1 |
| [G‑02] |  Use assembly to emit events | 1 |
| [G‑03] |  Catch arguments as local variables in `simulateRange()` | 1 |
| [G‑04] |  Catch `value` in `roundUpTo8()` | 1 |
| [G‑05] |  save gas by using 10e27 instead 10 ** 27 | 1 |
| [G‑06] |  `_rpow()` and `_rmul()` can be imported instead of hardcoding it | 1 |
| [G‑07] |  Short the string message in `onlyGuardian` to save 256 bit storage | 1 |

### [G‑01]  `_rmul()` can gas optimized by removing extra lines of code
`_rmul()` has been used in `derivePrice()`. With current implementation it has added extra bytes resulting in more deployment cost. This can be further gas optimized per recommendation.

There is [1 instance](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L400-L406) of this issue:

```Solidity
File: contracts/rwaOracles/RWADynamicOracle.sol

  function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    z = _mul(x, y) / ONE;
  }

  function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    require(y == 0 || (z = x * y) / y == x);
  }
```

### Recommended Mitigation steps

```diff

  function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
-    z = _mul(x, y) / ONE;
+    z = x * y;
+    require(y == 0 || z / y == x);
+       z = z / ONE;
  }

-  function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
-    require(y == 0 || (z = x * y) / y == x);
-  }
```

### [G‑02]  Use assembly to emit events
Assembly can be used to emit events efficiently by utilizing scratch space and the free memory pointer. This will allow us to potentially avoid memory expansion costs.
Note: In order to do this optimization safely, we will need to cache and restore the free memor

### [G‑02]  Use assembly to emit events
Assembly can be used to emit events efficiently by utilizing scratch space and the free memory pointer. This will allow us to potentially avoid memory expansion costs.
Note: In order to do this optimization safely, we will need to cache and restore the free memory pointer.

There are 2 instances of this issue:

// Code block 1 (Solidity):
File: contracts/rwaOracles/RWADynamicOracle.sol

  function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    z = _mul(x, y) / ONE;
  }

  function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    require(y == 0 || (z = x * y) / y == x);
  }

// Code block 2 (diff):
function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
-    z = _mul(x, y) / ONE;
+    z = x * y;
+    require(y == 0 || z / y == x);
+       z = z / ONE;
  }

-  function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
-    require(y == 0 || (z = x * y) / y == x);
-  }


IceBear Q

## [L-01] Hardcoding EXPECTED_SIGNER reduces future scalability.
https://github.com/code-423n4/2024-01-salty/blob/main/src/SigningTools.sol#L7
```
address constant public EXPECTED_SIGNER = 0x1234519DCA2ef23207E1CA7fd70b96f281893bAa;
```
## [L-02] No mechanism for blacklisting.

Once a user has been granted permission, there is no mechanism for removal in the absence of a blacklist.
https://github.com/code-423n4/2024-01-salty/blob/main/src/AccessManager.sol#L65
```
// Grant access to the sender for the given geoVersion.
// Requires the accompanying correct message signature from the offchain verifier.
function grantAccess(bytes calldata signature) external
    {
        require( _verifyAccess(msg.sender, signature), "Incorrect AccessManager.grantAccess signatory" );

        _walletsWithAccess[geoVersion][msg.sender] = true;

        emit AccessGranted( msg.sender, geoVersion );
    }
```

## [L-03] Excess tokens approval will be give to  the staking contract.
This issue is covered in the [bot race report](https://github.com/code-423n4/2024-01-salty/blob/main/bot-report.md#l-16) but lacks a more detailed explanation.

saltBalance is the  salt balance of the Airdrop contract.
saltAmountForEachUser is calculated as follows:

https://github.com/code-423n4/2024-01-salty/blob/main/src/launch/Airdrop.sol#L64
```
saltAmountForEachUser = saltBalance / numberAuthorized();
```
Afterward, approve saltBalance to the staking contract.
https://github.com/code-423n4/2024-01-salty/blob/main/src/launch/Airdrop.sol#L67
```
salt.approve( address(staking), saltBalance );
```
claimAirdrop() only sends out saltAmountForEachUser.

https://github.com/code-423n4/2024-01-salty/blob/main/src/launch/Airdrop.sol#L81-L82
```
// Have the Airdrop contract stake a specified amount of SALT and then transfer it to the user
		staking.stakeSALT( saltAmountForEachUser );
		staking.transferStakedSaltFromAirdropToUser( msg.sender, saltAmountForEachUser );
```
Consider the following scenarios:
If saltBalan

// Code block 1 (unknown):
address constant public EXPECTED_SIGNER = 0x1234519DCA2ef23207E1CA7fd70b96f281893bAa;

// Code block 2 (unknown):
// Grant access to the sender for the given geoVersion.
// Requires the accompanying correct message signature from the offchain verifier.
function grantAccess(bytes calldata signature) external
    {
        require( _verifyAccess(msg.sender, signature), "Incorrect AccessManager.grantAccess signatory" );

        _walletsWithAccess[geoVersion][msg.sender] = true;

        emit AccessGranted( msg.sender, geoVersion );
    }

// Code block 3 (unknown):
saltAmountForEachUser = saltBalance / numberAuthorized();

// Code block 4 (unknown):
salt.approve( address(staking), saltBalance );

// Code block 5 (unknown):
// Have the Airdrop contract stake a specified amount of SALT and then transfer it to the user
		staking.stakeSALT( saltAmountForEachUser );
		staking.transferStakedSaltFromAirdropToUser( msg.sender, saltAmountForEachUser );

SigningTools.sol#L7, AccessManager.sol#L65, Airdrop.sol#L64, Airdrop.sol#L67, Airdrop.sol#L81-L82


KKat7531 Q

# Findings Summary

| ID     | Title                                                                              | Severity      |
| ------ | ---------------------------------------------------------------------------------- | ------------- |
| [L-01] | Missing zero address & pausable checks in `EUSD.sol`                               | Low      |
| [NC-01] | Use a safe pragma statement                                                       | Non-critical   |
| [NC-02] |  Import  OZ `SafeCast` library                                                    | Non-critical      |

# Findings

# [L-01] Missing zero address & pausable checks in `EUSD.sol`

Natspec comment mentioned checks that are not actually done, as you can see from the example below, there are no checks implemented to validate that recipient is not zero address or that the contract is not paused

 ```solidity
     * Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the caller must have at least `_sharesAmount` shares.
     * - the contract must not be paused.
     *
     * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
     */
    function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
        address owner = _msgSender();
        _transferShares(owner, _recipient, _sharesAmount);
        emit TransferShares(owner, _recipient, _sharesAmount);
        uint256 tokensAmount = getMintedEUSDByShares(_sharesAmount);
        emit Transfer(owner, _recipient, tokensAmount);
        return tokensAmount;
    }
```

# [NC-01] Use a safe pragma statement

Always use stable pragma statement to lock the compiler version and to have deterministic compilation to bytecode. Finally consider upgrading the version to a newer one to use bugfixes and optimizations in the compiler. This is recommended to be changed in all contracts in the scope.

# [NC-02] Import  OZ `SafeCast` library

Function `clock` returns a `SafeCast.toUint48(blo

    function clock() public override view returns (uint48){
        return SafeCast.toUint48(block.number);

// Code block 1 (solidity):
* Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the caller must have at least `_sharesAmount` shares.
     * - the contract must not be paused.
     *
     * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
     */
    function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
        address owner = _msgSender();
        _transferShares(owner, _recipient, _sharesAmount);
        emit TransferShares(owner, _recipient, _sharesAmount);
        uint256 tokensAmount = getMintedEUSDByShares(_sharesAmount);
        emit Transfer(owner, _recipient, tokensAmount);
        return tokensAmount;
    }


d3tonator Q

# [L-01] Outdated Compiler 0.8.13 Used by the Contracts

#### Description:

During the analysis it was observed that the contracts code written in Solidity uses an outdated compiler version 0.8.13. The use of an outdated compiler version 0.8.13 poses several security risks that can compromise the security and stability of the smart contract. The following are the key findings of our audit:

**Code Generation:** Fix data corruption that affected ABI-encoding of calldata values represented by tuples: structs at any nesting level; argument lists of external functions, events and errors; return value lists of external functions. The 32 leading bytes of the first dynamically-encoded value in the tuple would get zeroed when the last component contained a statically-encoded array.

Apart from these, there are several minor bug fixes and improvements.

- Assembler: Avoid duplicating subassembly bytecode where possible.
- Code Generator: Avoid including references to the deployed label of referenced functions if they are called right away.
- ContractLevelChecker: Properly distinguish the case of missing base constructor arguments from having an unimplemented base function.
- SMTChecker: Fix internal error caused by unhandled `z3` expressions that come from the solver when bitwise operators are used.
- SMTChecker: Fix internal error when using the custom NatSpec annotation to abstract free functions.
- TypeChecker: Also allow external library functions in `using for`.

#### Steps to reproduce:

Navigate to the following contracts and directory:

`https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/interfaces/IDerivative.sol#L2`

````

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
````

`https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts](https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts)/*`

#### Mitigation

The minimum required version must be [0.8.19](https://github.com/ethere

99        _mint(msg.sender, mintAmount);

// Code block 1 (unknown):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

99        _mint(msg.sender, mintAmount);


sayan G

## 1.   X = X + Y IS CHEAPER THAN X += Y
File: [EntryPoint.sol](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)
line:[81](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L81)

```
            collected += _executeUserOp(i, ops[i], opInfos[i]);
```

File: [EntryPoint.sol](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)
line:[101](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L101)
```
            totalOps += opsPerAggregator[i].userOps.length;
```
File: [EntryPoint.sol](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)
line:[135](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L135)
```
           collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);
```
File:[VerifyingSingletonPaymaster.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol)
line:[51](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L51)
```
        paymasterIdBalances[paymasterId] += msg.value;
```
File:[Math.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol)
line:[210](https://github.

// Code block 1 (unknown):
collected += _executeUserOp(i, ops[i], opInfos[i]);

// Code block 2 (unknown):
totalOps += opsPerAggregator[i].userOps.length;

// Code block 3 (unknown):
collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

// Code block 4 (unknown):
paymasterIdBalances[paymasterId] += msg.value;

EntryPoint.sol, EntryPoint.sol#L81, EntryPoint.sol#L101, EntryPoint.sol#L135, VerifyingSingletonPaymaster.sol, VerifyingSingletonPaymaster.sol#L51, Math.sol


0xnev G

### Gas Optimization Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [G-01] | Usage of `uint` smaller than 32 bytes (256 bits) incurs overhead | 7 |
| [G-02] | Splitting `require()` statements that uses `&&` saves gas | 5 |
| [G-03] | internal/private functions only called once can be inlined to save gas | 47 |
| [G-04] | Multiple accesses of a mapping/array should use a local variable cache | 59 |
| [G-05] | Use nested `if` statements to avoid multiple check combinations using `&&`| 11 |
| [G-06] | Avoid caching global variables | 2 |
| [G-07] | Check amount before execution of `transfer` functions for possible gas savings | 4 |
| [G-08] | `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables | 6 |
| [G-09] | `<x> -= <y>` costs more gas than `<x> = <x> - <y>` for state variables | 11 |
| [G-10] | Use a more recent version of solidity | - |
| [G-11] | Do not initialize variables with default value | 4 |
| [G-12] | Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate | 8 |
| [G-13] | Unnecessary `return` statements in functions that do not return anything | 4 |
| [G-14] |Unnecessary `return` statements in functions that have already define named return variable(s) | 4 |

| Total Gas-Optimization Issues | 14 |
|:--:|:--:|

### [G-01] Usage of `uint` smaller than 32 bytes (256 bits) incurs overhead
Context:
```solidity
7 results - 3 files

/TroveManager.sol
  82: uint128 arrayIndex;
1321: uint128 index // dont need to downcast from uint256 to uint128

/StabilityPool.sol
 247: uint128 _epoch, uint128 _scale
 248: uint128 _epoch, uint128 _scale
 249: uint128 _currentEpoch
 250: uint128 _currentScale

 /ReaperStrategyGranarySupplyOnly.sol
  35: uint16 private constant LENDER_REFERRAL_CODE_NONE
```
Additionally, if `index` is declared using `uint256`, the downcasting to `uint128`in line 1329 is not required
```solidity
1321:   function _addTroveOwnerToArray(address _borrower, address _c

1321:   function _addTroveOwnerToArray(address _borrower, address _collateral) internal returns (uint256 index) {
1325:        // Push the Troveowner to the array
1326:        TroveOwners[_collateral].push(_borrower);
1327:
1328:        // Record the index of the new Troveowner on their Trove struct
1329:        index = TroveOwners[_collateral].length.sub(1);
1330:        Troves[_borrower][_collateral].arrayIndex = index;
1331:
1332:        return index;
1333:    }

// Code block 1 (solidity):
7 results - 3 files

/TroveManager.sol
  82: uint128 arrayIndex;
1321: uint128 index // dont need to downcast from uint256 to uint128

/StabilityPool.sol
 247: uint128 _epoch, uint128 _scale
 248: uint128 _epoch, uint128 _scale
 249: uint128 _currentEpoch
 250: uint128 _currentScale

 /ReaperStrategyGranarySupplyOnly.sol
  35: uint16 private constant LENDER_REFERRAL_CODE_NONE


Pechenite Q

# QA Report - Kelp DAO Audit Contest | 10 Nov 2023 - 15 Nov 2023

---

# Executive summary

### Overview

|               |                                                                          |
| :-----------  | :----------------------------------------------------------------------- |
| Project Name  | Kelp DAO                                                                 |
| Repository    | https://github.com/code-423n4/2023-11-kelp                               |
| Website       | [Link](https://linktr.ee/kelpdao)                                        |
| Twitter       | [Link](https://twitter.com/KelpDAO)                                      |
| Methods       | Manual Review                                                            |
| Total SLOC    | 504 over 9 contracts                                                     |

### Findings Summary

| ID                | Title |  Severity                  |
| ----------------- | ----- |  ------------------------- |
| [L‑1](#l1-do-not-allow-the-modification-of-the-asset-for-particular-eigenlayer-strategy-after-amount-of-the-specific-asset-has-already-been-deposited-into-the-eigenlayer-strategy)     | Do not allow the modification of the `asset` for particular `EigenLayer Strategy` after `amount` of the specific `asset` has already been deposited into the `EigenLayer Strategy` | _Low_ |
| [L‑2](#l2-a-malicious-lrt-manager-can-disrupt-the-deposit-process-for-a-particular-asset-by-setting-depositlimitbyassetasset-to-a-value-smaller-than-the-actual-total-supply-of-this-asset-within-contracts-consequently-the-depositing-function-will-consistently-revert-due-to-underflow)     | A malicious `LRT Manager` can disrupt the deposit process for a particular asset by setting `depositLimitByAsset[asset]` to a value smaller than the `actual total supply of this asset within contracts`. Consequently, the depositing function will consistently revert due to underflow | _Low_ |
| [L‑3](#l3-ownership-can-

function addNodeDelegatorContractToQueue(
    address[] calldata nodeDelegatorContracts
) external onlyLRTAdmin {
    uint256 length = nodeDelegatorContracts.length;
    if (nodeDelegatorQueue.length + length > maxNodeDelegatorCount) {
        revert MaximumNodeDelegatorCountReached();
    }

    for (uint256 i; i < length; ) {
        UtilLib.checkNonZeroAddress(nodeDelegatorContracts[i]);
        nodeDelegatorQueue.push(nodeDelegatorContracts[i]);
        emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
        unchecked {
            ++i;
        }
    }
}

// Deploy LRTDepositPool contract
// ...

// Repeatedly add NodeDelegator contract addresses to the queue
for (uint256 i = 0; i < 1000; i++) {
    lrtDepositPool.addNodeDelegatorContractToQueue([nodeDelegatorAddress]);
}


arialblack14 Q

# QA REPORT

### Summary of low risk issues
| Number | Issue details | Instances |
|---|---|:---:|
| [L-1](#L1) | Lock pragmas to specific compiler version. | 4
| [L-2](#L2) | It is not checked if `derivativeCount > 0` which can cause a Panic Error if something happens during deployment. | 1
| [L-3](#L3) | No input validation for the `derivativeIndex` of `adjustWeight` function. | 1

*Total: 3 issues.*

### Summary of non-critical issues
| Number | Issue details | Instances |
|---|---|:---:|
| [NC-1](#NC1) |Missing timelock for critical changes. | 14
| [NC-2](#NC2) |For modern and more readable code, update import usages. | 31
| [NC-3](#NC3) |Add parameter to emitted event. | 1
| [NC-4](#NC4) |Use of `bytes.concat()` instead of `abi.encodePacked()`. | 6

*Total: 4 issues.*

---
### Low Risk Issues

### [L-1] Lock pragmas to specific compiler version.

##### Description
Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile locally.

##### Recommendation
Ethereum Smart Contract Best Practices - Lock pragmas to specific compiler version. [solidity-specific/locking-pragmas](https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/locking-pragmas/)

##### *Instances (4):*
File: [2023-03-asymmetry/contracts/SafEth/SafEth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L2 )
```solidity
2: pragma solidity ^0.8.13;
```
File: [2023-03-asymmetry/contracts/SafEth/derivatives/Reth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L2 )
```solidity
2: pragma solidity ^0.8.13;
```
File: [2023-03-asymmetry/contracts/SafEth/derivatives/SfrxEth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives

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

// Code block 2 (solidity):
2: pragma solidity ^0.8.13;

2: pragma solidity ^0.8.13;

2: pragma solidity ^0.8.13;


0xLeveler Q

## Title
`nodeDelegator` and `LRTDepositPoolContract` do not have any mechanism to remove node delegators from the queue when needed

## Proof of Concept
contract has a mechanism to add node delegators, but none to validate if they're valid contracts and remove them if they're added by mistake.

```solidity
function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {
        uint256 length = nodeDelegatorContracts.length;
        if (nodeDelegatorQueue.length + length > maxNodeDelegatorCount) {
            revert MaximumNodeDelegatorCountReached();
        }

        for (uint256 i; i < length;) {
            UtilLib.checkNonZeroAddress(nodeDelegatorContracts[i]);
            nodeDelegatorQueue.push(nodeDelegatorContracts[i]);
            emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
            unchecked {
                ++i;
            }
        }
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Adding a mechanism to remove malicious or wrongly added node delegators.

// Code block 1 (solidity):
function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {
        uint256 length = nodeDelegatorContracts.length;
        if (nodeDelegatorQueue.length + length > maxNodeDelegatorCount) {
            revert MaximumNodeDelegatorCountReached();
        }

        for (uint256 i; i < length;) {
            UtilLib.checkNonZeroAddress(nodeDelegatorContracts[i]);
            nodeDelegatorQueue.push(nodeDelegatorContracts[i]);
            emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
            unchecked {
                ++i;
            }
        }
    }


0xdeadbeef0x Q

## `executeBatch` will revert if single transaction fails

If an owner or EntryPoint (according to `_requireFromEntryPointOrOwner`)  calls `executeBatch` with one transaction out of a batch that will fail, all other transactions in the batch will fail.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L469
```
function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
--------
        for (uint i = 0; i < dest.length;) {
            _call(dest[i], 0, func[i]);
--------
        }
    }

    function _call(address target, uint256 value, bytes memory data) internal {
        (bool success, bytes memory result) = target.call{value : value}(data);
        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }
```

## Recommendation

Do not revert if call did not succeed, instead emit a log or store the target and data in storage to be later troubleshooted by owner.

----

## `execute` and `executeBatch` have an onlyOwner modifier

`_requireFromEntryPointOrOwner()` hints that `execute` and `executeBatch` are supposed to be called by an `EntryPoint`.
The current implementation of the function has an `onlyOwner` modifier and therefore cannot be called by the `EntryPoint`.
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L461
```
function execute(address dest, uint value, bytes calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
------
    }

    function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
------
        }
```

## Recommendation

If the functions are supposed to be called by the EntryPoint
- Remove the `onlyOwner` mod

// Code block 1 (unknown):
function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
--------
        for (uint i = 0; i < dest.length;) {
            _call(dest[i], 0, func[i]);
--------
        }
    }

    function _call(address target, uint256 value, bytes memory data) internal {
        (bool success, bytes memory result) = target.call{value : value}(data);
        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }

// Code block 2 (unknown):
function execute(address dest, uint value, bytes calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
------
    }

    function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
        _requireFromEntryPointOrOwner();
------
        }


Ignite G

# Code4rena - Assymetic Gas

## Gas Optimizations
|  | Issue | Instances |
|---|---|---|
| Gas-1 | Emitting events with variables from memory rather than state variables can help reduce gas costs | 4 |
| Gas-2 | Declare local variables as part of the function instead of inside the function  | 1 |

### [Gas-1] Emitting events with variables from memory rather than state variables can help reduce gas costs
Using memory variable instead of state variable in the emit statement may be slightly more gas-efficient because it does not require the Solidity compiler to generate an additional `SLOAD` instruction to read the state variable from storage.

Instances(4):

```solidity
        emit ChangeMinAmount(minAmount);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L216

```solidity
        emit ChangeMaxAmount(maxAmount);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L225

```solidity
        emit StakingPaused(pauseStaking);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L234

```solidity
        emit UnstakingPaused(pauseUnstaking);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L243

### [Gas-2] Declare local variables as part of the function instead of inside the function
Declare local variables as part of the function instead of inside the function to save gas.

Instances(1):

```
    function deposit() external payable onlyOwner returns (uint256) {
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L73

// Code block 1 (solidity):
emit ChangeMinAmount(minAmount);

// Code block 2 (solidity):
emit ChangeMaxAmount(maxAmount);

// Code block 3 (solidity):
emit StakingPaused(pauseStaking);

// Code block 4 (solidity):
emit UnstakingPaused(pauseUnstaking);

// Code block 5 (unknown):
function deposit() external payable onlyOwner returns (uint256) {

emit ChangeMinAmount(minAmount);

emit ChangeMaxAmount(maxAmount);

emit StakingPaused(pauseStaking);

emit UnstakingPaused(pauseUnstaking);

SafEth.sol#L216, SafEth.sol#L225, SafEth.sol#L234, SafEth.sol#L243, WstEth.sol#L73


JCN G

# Summary
A majority of the optimizations were benchmarked via the protocol's tests, i.e. using the following config for `ajna-core`: `solc version 0.8.14`, `optimizer on`, `500 runs` and the following config for `ajna-grants`: `solc version 0.8.16`, `optimizer on`, `1000000 runs`. Optimizations that were not benchmarked are explained via EVM gas costs and opcodes.

Below are the overall average gas savings for the following tested functions, with all the optimizations applied (not including [issue 11](#refactor-event-to-avoid-emitting-data-that-is-already-present-in-transaction-data), [issue 12](#refactor-event-to-avoid-emitting-empty-data), [issue 13](#hash-proposal-values-offchain), and [issue 14](#sort-array-offchain-to-check-duplicates-in-on-instead-of-on2)):
| Function |    Before   |    After   | Avg Gas Savings |
| ------ | -------- | -------- | ------- |
| GrantFund.claimDelegateReward |  65340  |  42268  | 23072 |
| GrantFund.executeExtraordinary |  95823  |  95682  | 141 |
| GrantFund.executeStandard |  47894  |  47520  | 374 |
| GrantFund.fundTreasury |  65872  |  65788  | 84 |
| GrantFund.fundingVote |  409345  |  396776  | 12569 |
| GrantFund.proposeExtraordinary |  86505  |  86451  | 54 |
| GrantFund.proposeStandard |  82900  |  82820  | 80 |
| GrantFund.screeningVote |  399146  |  390626  | 8520 |
| GrantFund.startNewDistributionPeriod |  75597  |  75139  | 458 |
| GrantFund.updateSlate |  318329  |  310231  | 8098 |
| GrantFund.voteExtraordinary |  31424  |  30811  | 613 |
| PositionManager.burn |  10451  |  8524  | 1927 |
| PositionManager.memorializePositions |  1134444  |  1133268  | 1176 |
| PositionManager.mint |  98876  |  97653  | 1223 |
| PositionManager.permit |  54387  |  32554  | 21833 |
| RewardsManager.claimRewards |  393064  |  381560  | 11504 |
| RewardsManager.moveStakedLiquidity |  2112272  |  2081035  | 31237 |

**Total gas saved across all listed functions: 122963**

*Notes*:

- The `Avg`, `Med`, and `# of calls`

File: ajna-core/src/RewardsManager.sol
135:    function moveStakedLiquidity(
136:        uint256 tokenId_,
137:        uint256[] memory fromBuckets_,
138:        uint256[] memory toBuckets_,
139:        uint256 expiry_
140:    ) external nonReentrant override {

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol#L519-L521

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol#L612-L618

*Gas Savings for `GrantFund.fundingVote`, obtained via protocol's tests: Avg 1589 gas*

|        |    Max   |
| ------ | -------- |
| Before |   409345 |
| After  |   407756 |


Diana G

---------------------------

**Gas Optimization**

----------

| S No. | Issue | Instances | Gas Savings (from provided tests) |
|-----|-----|-----|-----|
| [G-01] | x += y costs more gas than x = x + y for state variables | 4 | 452
| [G-02] | Don't compare boolean expressions to boolean literals | 2 | 110

-------------

## G-01 x += y costs more gas than x = x + y for state variables

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

_There are 4 instances of this issue_

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

```
File: SafEth/SafEth.sol

72-75: underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
95: totalStakeValueEth += derivativeReceivedEthValue;
172: localTotalWeight += weights[i];
192: localTotalWeight += weights[i];
```

-------

## G-02 Don't compare boolean expressions to boolean literals

Comparing to a constant (`true` or `false`) is a bit more expensive than directly checking the returned boolean value. I suggest using `if(!directValue)` instead of `if(directValue == false)`

`if (<x> == true)` => `if (<x>)`, `if (<x> == false)` => `if (!<x>)`

Gas Saved: **55**

_There are 2 instances of this issue_

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

```
File: SafEth/SafEth.sol

64: require(pauseStaking == false, "staking is paused");
109: require(pauseUnstaking == false, "unstaking is paused");
```

------------

// Code block 1 (unknown):
File: SafEth/SafEth.sol

72-75: underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
95: totalStakeValueEth += derivativeReceivedEthValue;
172: localTotalWeight += weights[i];
192: localTotalWeight += weights[i];

// Code block 2 (unknown):
File: SafEth/SafEth.sol

64: require(pauseStaking == false, "staking is paused");
109: require(pauseUnstaking == false, "unstaking is paused");


nadin Q

# QA Report
## Summary
Total 04 Low and 09 Non-Critical
### Low Risk Issues
[L-01] getAssetPrice() SHOULD BE TURNED INTO A VIEW FUNCTION
[L-02] REFACTORING TO SUPPORT UINT256 AMOUNT
[L-03] ROUNDING ERROR IN `purchaseOtherEarnings()` MIGHT RESULT IN `biddingFee`
[L-04] MAY LEAD TO DOS IF `curvePool.exchange_underlying` REVERT

## [L-01] getAssetPrice() SHOULD BE TURNED INTO A VIEW FUNCTION
`getAssetPrice()` is used to get the price of the asset, it is used in most of the important functions, should be set to view function to avoid arbitrary price exploitation like reentrancy attack.
```
File: LybraRETHVault
46:    function getAssetPrice() public override returns (uint256) {
File: LybraWbETHVault.sol
34:    function getAssetPrice() public override returns (uint256) { // @audit change to view function
File: LybraWstETHVault.sol
48:    function getAssetPrice() public override returns (uint256) {
File: LybraEUSDVaultBase.sol
327:    function getAssetPrice() public virtual returns (uint256);
File: LybraPeUSDVaultBase.sol
269:    function getAssetPrice() public virtual returns (uint256);
```
[LybraRETHVault.sol#L46](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraRETHVault.sol#L46)
[LybraWstETHVault.sol#L48](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraWstETHVault.sol#L48)
[LybraWbETHVault.sol#L34](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraWbETHVault.sol#L34)
[LybraEUSDVaultBase.sol#L327](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L327)
[LybraPeUSDVaultBase.sol#L269](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L269)

## [L-02] REFACTORING TO SUPPORT UINT256 AMOUNT
- Alth

The scope of this PR:

Remove type(uint64).max limit for amount from OFTV2Core, ProxyOFTV2 and ProxyOFTWithFee
Introduce OFTV2Aptos, ProxyOFTV2Aptos, ProxyOFTWithFeeAptos that allow no more than 6 shared decimals and have type(uint64).max limit for amount
Replace abi.encodePacked with abi.encode in OFTV2Core and derived contracts
Using abi.encodePacked makes encoding inconsistent with previous versions and decoding ugly. Additionally, abi.encodePacked will be removed in a future version of Solidity (see Remove abi.encodePacked ethereum/solidity#11593)

// Code block 1 (unknown):
File: LybraRETHVault
46:    function getAssetPrice() public override returns (uint256) {
File: LybraWbETHVault.sol
34:    function getAssetPrice() public override returns (uint256) { // @audit change to view function
File: LybraWstETHVault.sol
48:    function getAssetPrice() public override returns (uint256) {
File: LybraEUSDVaultBase.sol
327:    function getAssetPrice() public virtual returns (uint256);
File: LybraPeUSDVaultBase.sol
269:    function getAssetPrice() public virtual returns (uint256);

LybraRETHVault.sol#L46, LybraWstETHVault.sol#L48, LybraWbETHVault.sol#L34, LybraEUSDVaultBase.sol#L327, LybraPeUSDVaultBase.sol#L269

LybraEUSDVaultBase.sol, LybraWstETHVault.sol, LybraRETHVault.sol, LybraWbETHVault.sol, LybraPeUSDVaultBase.sol


0xStalin Q

## [L-01] Number of approvers can be set to be greather than the existing approvers in the DestinatinationBridge contract

When [setting the tresholds](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L255-L279) in the DestinationBridge, there is no checks to validate if the number of approvers that are been set to the ranges surpasses the existing maximum number of appprovers in the contract.
Setting the number of required approvers greather than the total number of approvers will cause that tx never reaches the approved status until new approvers are added to the DestinationBridge contract.

```solidity
function setThresholds(
  string calldata srcChain,
  uint256[] calldata amounts,
  uint256[] calldata numOfApprovers
) external onlyOwner {
  if (amounts.length != numOfApprovers.length) {
    revert ArrayLengthMismatch();
  }
  delete chainToThresholds[srcChain];
  for (uint256 i = 0; i < amounts.length; ++i) {
    if (i == 0) {
      chainToThresholds[srcChain].push(
        Threshold(amounts[i], numOfApprovers[i])
      );
    } else {
      if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
        revert ThresholdsNotInAscendingOrder();
      }
      //@audit-issue => Not validating if numOfApprovers[i] is greather than the existing number of approvers
      chainToThresholds[srcChain].push(
        Threshold(amounts[i], numOfApprovers[i])
      );
    }
  }
  emit ThresholdSet(srcChain, amounts, numOfApprovers);
}

```

**Fix:**
- Keep track of the total numbers of approvers, and when setting thresholds validate if the number of required approvers is greather than the total approvers, if so, either revert the tx or set the number of required approvers as the total number of approvers, or emit an event to notify off-chain that will be required to add more approvers to the DestinationBridge

## [L-02] When attaching thresholds, if the amount of tokens to be minted exceeds all the thresholds, the tx will be

function _attachThreshold(
  uint256 amount,
  bytes32 txnHash,
  string memory srcChain
) internal {
  Threshold[] memory thresholds = chainToThresholds[srcChain];
  for (uint256 i = 0; i < thresholds.length; ++i) {
    Threshold memory t = thresholds[i];
    if (amount <= t.amount) {
      txnToThresholdSet[txnHash] = TxnThreshold(
        t.numberOfApprovalsNeeded,
        new address[](0)
      );
      break;
    }
  }
  //@audit-info => If the amount exceeded all the thresholds, the numberOfApprovalsNeeded won't be updated, thus, the tx will be reverted!s
  if (txnToThresholdSet[txnHash].numberOfApprovalsNeeded == 0) {
    revert NoThresholdMatch();
  }
}

// Code block 1 (solidity):
function setThresholds(
  string calldata srcChain,
  uint256[] calldata amounts,
  uint256[] calldata numOfApprovers
) external onlyOwner {
  if (amounts.length != numOfApprovers.length) {
    revert ArrayLengthMismatch();
  }
  delete chainToThresholds[srcChain];
  for (uint256 i = 0; i < amounts.length; ++i) {
    if (i == 0) {
      chainToThresholds[srcChain].push(
        Threshold(amounts[i], numOfApprovers[i])
      );
    } else {
      if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
        revert ThresholdsNotInAscendingOrder();
      }
      //@audit-issue => Not validating if numOfApprovers[i] is greather than the existing number of approvers
      chainToThresholds[srcChain].push(
        Threshold(amounts[i], numOfApprovers[i])
      );
    }
  }
  emit ThresholdSet(srcChain, amounts, numOfApprovers);
}


Raihan G

# GAS OPTIMIZATION

# SUMMARY

|        | issue                                                                               | instance |
| ------ | ----------------------------------------------------------------------------------- | -------- |
| [G‑01] | Gas Optimization: Declare Variables Outside the Loop                                | 3        |
| [G‑02] | Gas Optimization: Using Assembly for Efficient Validation of `msg.sender`           | 31       |
| [G‑03] | Gas Optimization: Use Assembly for Writing Address Storage Values                   | 8        |
| [G‑04] | Do not calculate constants                                                          | 1        |
| [G‑05] | Gas Optimization: Change Public State Variable Visibility to Private                | 36       |
| [G‑06] | State variables can be packed to use fewer storage slots                            | 10       |
| [G‑07] | Gas Optimization Issue - Use Constants Instead of type(uintx).max                   | 15       |
| [G‑08] | Gas Optimization: Inlining Internal Functions                                       | 8        |
| [G‑09] | Use assembly to perform efficient back-to-back calls                                | 7        |
| [G‑10] | Gas Optimization: Using Modifiers Instead of Functions                              | 2        |
| [G‑11] | Using delete instead of setting info struct to 0 saves gas                          | 2        |
| [G‑12] | Cache external calls outside of loop to avoid re-calling function on each iteration | 1        |
| [G‑13] | Access mappings directly rather than using accessor functions                       | 2        |
| [G‑14] | Pre-increment and pre-decrement are cheaper than `+1`, `-1`                         | 9        |
| [G‑15] | Using constants instead of enum can save gas                                        | 1        |
| [G‑16] | Consider using OZ EnumerateSet in place of nested mappings                          | 5        |
| [G‑17] | Instead of cou

contract MyContract {
    function sum(uint256[] memory values) public pure returns (uint256) {
        for (uint256 i = 0; i < values.length; i++) {
            uint256 total; // This declaration is inside the loop and not initialized
            total += values[i];
        }

        return total; // This line will result in a compilation error; total is not visible here
    }
}

contract MyContract {
    function sum(uint256[] memory values) public pure returns (uint256) {
        uint256 total; // Declare the variable outside the loop without initializing

        for (uint256 i = 0; i < values.length; i++) {
            total += values[i];
        }

        return total;
    }
}


TS Q

#title
Lack of verification if NFT destination address handles ERC721

#Description
The ``unstake`` of the ``RewardsManager`` contract does not verify if the NFT recipient is capable of receiving ERC721. The caller is responsible to confirm that the recipient is capable of receiving ERC721 or else they may be permanently lost.

Affected code (https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L250):
```
    function unstake(
        uint256 tokenId_
    ) external override {
        ...omitted for brevity...
        IERC721(address(positionManager)).transferFrom(address(this), msg.sender, tokenId_);
    }
```

It is important to note that the ``unstake``  has to be done by an address that previously staked an NFT. However, it has to be taken into consideration that the address might be a proxy, which originally had a implementation that handled IERC721, but later that implementation changed to another that does not handle IERC721. In this case, the NFT would be lost after calling the ``unstake`` function.

#Recommendation
Use safeTransferFrom instead transferFrom

// Code block 1 (unknown):
function unstake(
        uint256 tokenId_
    ) external override {
        ...omitted for brevity...
        IERC721(address(positionManager)).transferFrom(address(this), msg.sender, tokenId_);
    }


0xWaitress Q

In the function PrivatePool::buy, tokenIds are looped twice, in the second loop the important task is to transfer royalty fee to the recipient, but the result of _getRoyalty can actually be skipped since it was called in the earlier loop

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L271-L283

```solidity
        if (payRoyalties) {
            for (uint256 i = 0; i < tokenIds.length; i++) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); @> audit

                // transfer the royalty fee to the recipient if it's greater than 0
                if (royaltyFee > 0 && recipient != address(0)) {
                    if (baseToken != address(0)) {
                        ERC20(baseToken).safeTransfer(recipient, royaltyFee);
                    } else {
                        recipient.safeTransferETH(royaltyFee);
                    }
                }
```
save the needed amount in an arrays in the first loop

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L238-L249

```solidity
+++ uint256[] memory royaltyFees;
+++ address[] memory recipients;
for (uint256 i = 0; i < tokenIds.length; i++) {
            // transfer the NFT to the caller
            ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);

            if (payRoyalties) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);
+++             royaltyFees[i] = royaltyFee;
+++             recipients[i] = recipient;
                // add the royalty fee to the total royalty fee amount
                royaltyFeeAmount += royaltyFee;
            }
        }

```

// Code block 1 (solidity):
if (payRoyalties) {
            for (uint256 i = 0; i < tokenIds.length; i++) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); @> audit

                // transfer the royalty fee to the recipient if it's greater than 0
                if (royaltyFee > 0 && recipient != address(0)) {
                    if (baseToken != address(0)) {
                        ERC20(baseToken).safeTransfer(recipient, royaltyFee);
                    } else {
                        recipient.safeTransferETH(royaltyFee);
                    }
                }

// Code block 2 (solidity):
+++ uint256[] memory royaltyFees;
+++ address[] memory recipients;
for (uint256 i = 0; i < tokenIds.length; i++) {
            // transfer the NFT to the caller
            ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);

            if (payRoyalties) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);
+++             royaltyFees[i] = royaltyFee;
+++             recipients[i] = recipient;
                // add the royalty fee to the total royalty fee amount
                royaltyFeeAmount += royaltyFee;
            }
        }

if (payRoyalties) {
            for (uint256 i = 0; i < tokenIds.length; i++) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); @> audit

                // transfer the royalty fee to the recipient if it's greater than 0
                if (royaltyFee > 0 && recipient != address(0)) {
                    if (baseToken != address(0)) {
                        ERC20(baseToken).safeTransfer(recipient, royaltyFee);
                    } else {
                        recipient.safeTransferETH(royaltyFee);
                    }
                }

+++ uint256[] memory royaltyFees;
+++ address[] memory recipients;
for (uint256 i = 0; i < tokenIds.length; i++) {
            // transfer the NFT to the caller
            ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);

            if (payRoyalties) {
                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);
+++             royaltyFees[i] = royaltyFee;
+++             recipients[i] = recipient;
                // add the royalty fee to the total royalty fee amount
                royaltyFeeAmount += royaltyFee;
            }
        }


BioMatriX Analysis

### [H-1] Unauthorized Access in InitialDistribution.sol (External Function Accessibility)

**Description:**
The `distributionApproved` function is external and can be called by anyone. It's crucial to ensure that the `BootstrapBallot` contract itself has the proper access control to prevent unauthorized execution.

**Impact:**
High

**Proof of Concept:**
```
`require( msg.sender == address(bootstrapBallot), "InitialDistribution.distributionApproved can only be called from the BootstrapBallot contract" );`
```

**Recommended Mitigation:**
Implement strict access control mechanisms to ensure that only authorized contracts or addresses can call sensitive functions.

---

### [H-2] Unauthorized Access in Airdrop.sol (Contract Access Control)

**Description:**
It's critical to ensure that referenced contracts (`initialDistribution().bootstrapBallot()` and `initialDistribution()`) have proper access controls to prevent unauthorized access and potential exploitation.

**Impact:**
High

**Proof of Concept:**
```
require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Only the BootstrapBallot can call Airdrop.authorizeWallet" );
require( ! claimingAllowed, "Cannot authorize after claiming is allowed" );
```

**Recommended Mitigation:**
Review and reinforce the access control mechanisms for all referenced contracts to secure the protocol against unauthorized access.

---

### [H-3] Single Point of Failure in ExchangeConfig.sol (Owner Access Control)

**Description:**
Providing all access to the owner for different parameters can lead to a single point of failure. It's crucial to ensure that the owner is a trusted entity and has proper access control mechanisms in place.

**Impact:**
High

**Proof of Concept:**
Function `setContracts` is externally accessible and modifiable only by the owner.

**Recommended Mitigation:**
Consider implementing multi-signature or decentralized governance mechanisms to mitigate the risks associated wi

// Code block 1 (unknown):
`require( msg.sender == address(bootstrapBallot), "InitialDistribution.distributionApproved can only be called from the BootstrapBallot contract" );`

// Code block 2 (unknown):
require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Only the BootstrapBallot can call Airdrop.authorizeWallet" );
require( ! claimingAllowed, "Cannot authorize after claiming is allowed" );


JP_Courses Q

0. QA/LOW: Lack of address(0) checks, relying solely on trusting that the privileged/admin access users will not accidentally pass zero addresses as function parameters.

1. QA: lack of input validation for `_amount`, possible to pass zero value. Possible to send zero rUSDY tokens to another user.

https://github.com/code-423n4/2023-09-ondo/blob/3362e1252f3a54943e2517460e5a7988388bc821/contracts/usdy/rUSDY.sol#L246

Recommendation:
```solidity
  function transfer(address _recipient, uint256 _amount) public returns (bool) {
 ++ require(_amount != 0);
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }
```

2. QA: seems approve() not internally called, so could use external modifier instead

https://github.com/code-423n4/2023-09-ondo/blob/3362e1252f3a54943e2517460e5a7988388bc821/contracts/usdy/rUSDY.sol#L276

3. QA: unless planning to call this function internally, `public` should be changed to `external` visibility modifier

https://github.com/code-423n4/2023-09-ondo/blob/3362e1252f3a54943e2517460e5a7988388bc821/contracts/usdy/rUSDY.sol#L330

4. LOW: recommend to use safetransferFrom() instead. fail without error message/return value nor revert?

https://github.com/code-423n4/2023-09-ondo/blob/3362e1252f3a54943e2517460e5a7988388bc821/contracts/usdy/rUSDY.sol#L437

// Code block 1 (solidity):
function transfer(address _recipient, uint256 _amount) public returns (bool) {
 ++ require(_amount != 0);
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

rUSDY.sol#L246, rUSDY.sol#L276, rUSDY.sol#L330, rUSDY.sol#L437


chrisdior4 Q

# QA report

## Low Risk
| L-R    |Issue|Instances|
|:------:|:----|:-------:|
| [L‑01] | Use the latest version of OpenZeppelin | 1 |
| [L‑02] | Unused/empty receive()/fallback() function | 1 |
| [L‑03] | Avoid the use of tx.origin | 1 |

Total: 3 instances over 3 issues

## Non-critical

| N-C   |Issue|Instances|
|:------:|:----|:-------:|
| [N‑01] | Event is missing indexed fields | 2 |
| [N‑02] | Commented out code | 2 |
| [N‑03] | Use the latest Solidity version | 1 |
| [N‑04] | Constants should be defined rather than using magic numbers | 2 |

Total: 7 instances over 4 issues

## Low Risk

# [L-01] Use the latest version of OpenZeppelin

To prevent any issues in the future (e.g. using solely hardhat to compile and deploy the contracts), upgrade the used OZ packages within the package.json to the latest versions.

```solidity
"@openzeppelin/contracts": "^4.7.3",
```

### Recommended Mitigation Steps:

Consider using the latest OZ packages (v4.8.0) within package.json.

# [L-02] Unused/empty receive()/fallback() function
Sometimes Ether might be mistakenly sent to the contract. It's better to prevent this from happening.
smartAccount.sol
If the intention is for the Ether to be used, the function should call another function, otherwise it should revert
If a method does not have an external call then it is impossible to reenter, so you can skip this modifier in such methods

File: SmartAccount.sol

```solidity
    receive() external payable {}
```

Lines of code:

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

# [L-03] Avoid the use of tx.origin

The tx.origin environment variable has been found to influence a control flow decision. Note that using tx.origin as a security control might cause a situation where a user inadvertently authorizes a smart contract to perform an action on their behalf. It is

// Code block 1 (solidity):
"@openzeppelin/contracts": "^4.7.3",

// Code block 2 (solidity):
receive() external payable {}

"@openzeppelin/contracts": "^4.7.3",

receive() external payable {}


abiih G

# Report

---

## Gas Optimizations

---

|     | Issue                                                                                |
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | [Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead](#usage-of-uintsints-smaller-than-32-bytes-256-bits-incurs-overhead)                            |
| 2   | [Use external instead of public where possible](#use-external-instead-of-public-where-possible)                                                                       |
| 3   | [Functions Guaranteed To Revert When Called By Normal Users Can Be Marked Payable](#functions-guaranteed-to-revert-when-called-by-normal-users-can-be-marked-payable) |
| 4   | [ Use calldata instead of memory for function arguments that do not get mutated](#use-calldata-instead-of-memory-for-function-arguments-that-do-not-get-mutated)      |

---

1. ### Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

   When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher.
   This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

   - https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L51
   - https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L88
   - https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L91

```diff
-     uint16 public protocolFeeRate;
+     uint16 public protocolFeeRate;

```

2. ### Use external instead of public where possible
   Functions with the public visibility modifier are costlier than external. Default to using the external modifie

2. ### Use external instead of public where possible
   Functions with the public visibility modifier are costlier than external. Default to using the external modifier until you need to expose it to other functions within your contract.

- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L157
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L301
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L385
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L459
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L484
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L602

// Code block 1 (diff):
-     uint16 public protocolFeeRate;
+     uint16 public protocolFeeRate;

Factory.sol#L51, PrivatePool.sol#L88, PrivatePool.sol#L91, PrivatePool.sol#L157, PrivatePool.sol#L211, PrivatePool.sol#L301, PrivatePool.sol#L385, PrivatePool.sol#L459, PrivatePool.sol#L484, PrivatePool.sol#L602


report

---
sponsor: "Dopex"
slug: "2023-08-dopex"
date: "2023-12-29"
title: "Dopex "
findings: "https://github.com/code-423n4/2023-08-dopex-findings/issues"
contest: 278
---

# Overview

## About C4

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

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

During the audit outlined in this document, C4 conducted an analysis of the Dopex smart contract system written in Solidity. The audit took place between August 21—September 6 2023.

## Wardens

203 Wardens contributed reports to Dopex:

  1. [said](https://code4rena.com/@said)
  2. [peakbolt](https://code4rena.com/@peakbolt)
  3. [Toshii](https://code4rena.com/@Toshii)
  4. [\_\_141345\_\_](https://code4rena.com/@__141345__)
  5. [LokiThe5th](https://code4rena.com/@LokiThe5th)
  6. [Evo](https://code4rena.com/@Evo)
  7. [deadrxsezzz](https://code4rena.com/@deadrxsezzz)
  8. [volodya](https://code4rena.com/@volodya)
  9. [rvierdiiev](https://code4rena.com/@rvierdiiev)
  10. [0xnev](https://code4rena.com/@0xnev)
  11. [pep7siup](https://code4rena.com/@pep7siup)
  12. [juancito](https://code4rena.com/@juancito)
  13. [kutugu](https://code4rena.com/@kutugu)
  14. [QiuhaoLi](https://code4rena.com/@QiuhaoLi)
  15. [Yanchuan](https://code4rena.com/@Yanchuan)
  16. [catellatech](https://code4rena.com/@catellatech)
  17. [glcanvas](https://code4rena.com/@glcanvas)
  18. [nirlin](https://code4rena.com/@nirlin)
  19. [T1MOH](https://code4rena.com/@T1MOH)
  20. [c3phas](https://code4rena.com/@c3phas)
  21. [RED-LOTUS-REACH](https://code4rena.com/@RED-LOTUS-REACH) ([BlockChomper](https://code4rena.com/@BlockChomper), [DedOhWale](https://code4rena.com/@DedOhWale), [reentrant](https://code4rena.com/@reentrant), and [escrow](https://code4rena.com/@escrow))

function bond(
  uint256 _amount,
  uint256 rdpxBondId,
  address _to
) public returns (uint256 receiptTokenAmount) {
  _whenNotPaused();
  // Validate amount
  _validate(_amount > 0, 4);

  // Compute the bond cost
  (uint256 rdpxRequired, uint256 wethRequired) = calculateBondCost(
    _amount,
    rdpxBondId
  );
  ...
}

function calculateBondCost(
  uint256 _amount,
  uint256 _rdpxBondId
) public view returns (uint256 rdpxRequired, uint256 wethRequired) {
  uint256 rdpxPrice = getRdpxPrice();

  ...

  uint256 strike = IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
    .roundUp(rdpxPrice - (rdpxPrice / 4)); // 25% below the current price

  uint256 timeToExpiry = IPerpetualAtlanticVault(
    addresses.perpetualAtlanticVault
  ).nextFundingPaymentTimestamp() - block.timestamp;
  if (putOptionsRequired) {
    wethRequired += IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
      .calculatePremium(strike, rdpxRequired, timeToExpiry, 0);
  }
}


Rickard G

## [G-01] Remove the `initializer` modifier
If we can just ensure that the `initialize()` function can only be called from within the constructor, we shouldn’t need to worry about it getting called again.
````solidity
src/LRTConfig.sol
41:    function initialize(
42:        address admin,
43:        address stETH,
44:        address rETH,
45:        address cbETH,
46:        address rsETH_
47:    )
48:        external
49:        initializer
50:    {
````
[https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTConfig.sol#L41-L50](https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTConfig.sol#L41-L50)
````solidity
src/LRTDepositPool.sol
31:    function initialize(address lrtConfigAddr) external initializer {
````
[https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTDepositPool.sol#L31](https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTDepositPool.sol#L31)
````solidity
src/LRTOracle.sol
29:    function initialize(address lrtConfigAddr) external initializer {
````
[https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTOracle.sol#L29](https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTOracle.sol#L29)
````solidity
src/NodeDelegator.sol
26:    function initialize(address lrtConfigAddr) external initializer {
````
[https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/NodeDelegator.sol#L26](https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/NodeDelegator.sol#L26)
````solidity
src/RSETH.sol
32:    function initialize(address admin, address lrtConfigAddr) external initializer {
````
[https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/RSETH.sol#L32](https://github.com/code-423n4/2023-11-kelp/blob/c5

// Code block 1 (solidity):
src/LRTConfig.sol
41:    function initialize(
42:        address admin,
43:        address stETH,
44:        address rETH,
45:        address cbETH,
46:        address rsETH_
47:    )
48:        external
49:        initializer
50:    {

// Code block 2 (solidity):
src/LRTDepositPool.sol
31:    function initialize(address lrtConfigAddr) external initializer {

// Code block 3 (solidity):
src/LRTOracle.sol
29:    function initialize(address lrtConfigAddr) external initializer {

// Code block 4 (solidity):
src/NodeDelegator.sol
26:    function initialize(address lrtConfigAddr) external initializer {

// Code block 5 (solidity):
src/RSETH.sol
32:    function initialize(address admin, address lrtConfigAddr) external initializer {

src/LRTConfig.sol
41:    function initialize(
42:        address admin,
43:        address stETH,
44:        address rETH,
45:        address cbETH,
46:        address rsETH_
47:    )
48:        external
49:        initializer
50:    {

src/LRTDepositPool.sol
31:    function initialize(address lrtConfigAddr) external initializer {

src/LRTOracle.sol
29:    function initialize(address lrtConfigAddr) external initializer {

src/NodeDelegator.sol
26:    function initialize(address lrtConfigAddr) external initializer {

src/RSETH.sol
32:    function initialize(address admin, address lrtConfigAddr) external initializer {

LRTConfig.sol#L41-L50, LRTDepositPool.sol#L31, LRTOracle.sol#L29, NodeDelegator.sol#L26, RSETH.sol#L32

LRTOracle.sol, RSETH.sol, NodeDelegator.sol, LRTConfig.sol, LRTDepositPool.sol