SaharDevep G

# c4udit Report

### Summery
[G001] Don't Initialize Variables with Default Value
[G002] Cache Array Length Outside of Loop
[G003] Use != 0 instead of > 0 for Unsigned Integer Comparison
[G004] Use immutable for OpenZeppelin AccessControl's Roles Declarations
[G005] Long Revert Strings
[G006] Use Shift Right/Left instead of Division/Multiplication if possible
[G007] TIGHT VARIABLE PACKING
[G008] MULTIPLE ACCESSES OF A MAPPING/ARRAY SHOULD USE A LOCAL VARIABLE CACHE
[G009] STATE VARIABLES ONLY SET IN THE CONSTRUCTOR SHOULD BE DECLARED IMMUTABLE
[G010] USING PRIVATE RATHER THAN PUBLIC FOR CONSTANTS, SAVES GAS
[G011] USE CUSTOM ERRORS RATHER THAN REVERT()/REQUIRE() STRINGS TO SAVE GAS
[G012] ABI.ENCODE() IS LESS EFFICIENT THAN ABI.ENCODEPACKED()
[G013] INLINE THE MODIFIER THAT IS USED ONCE
[G014] NOT USING THE NAMED RETURN VARIABLES WHEN A FUNCTION RETURNS, WASTES DEPLOYMENT GAS

## Issues found

### [G001] Don't Initialize Variables with Default Value

#### Impact
Issue Information: (https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md#g001---dont-initialize-variables-with-default-value)

#### Findings:
```
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::234 => uint256 payment = 0;
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::310 => uint256 i = 0;
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::468 => for (uint i = 0; i < dest.length;) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::74 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::78 => uint256 collected = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::80 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::99 => uint256 totalOps = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::100 => for (uint256 i = 0; i < opasLen; i

scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::234 => uint256 payment = 0;
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::310 => uint256 i = 0;
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::468 => for (uint i = 0; i < dest.length;) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::74 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::78 => uint256 collected = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::80 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::99 => uint256 totalOps = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::100 => for (uint256 i = 0; i < opasLen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::106 => uint256 opIndex = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::107 => for (uint256 a = 0; a < opasLen; a++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::112 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::126 => uint256 collected = 0;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::128 => for (uint256 a = 0; a < opasLen; a++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::134 => for (uint256 i = 0; i < opslen; i++) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::313 => uint256 missingAccountFunds = 0;
scw-contracts\contracts\smart-contract-wallet\base\ModuleManager.sol::119 => uint256 moduleCount = 0;

scw-contracts\contracts\smart-contract-wallet\BaseSmartAccount.sol::64 => if (userOp.initCode.length == 0) {
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::199 => //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::201 => //console.log("init %s", 21000 + msg.data.length * 8);
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::324 => // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::325 => require(uint256(s) + 32 <= signatures.length, "BSA022");
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::327 => // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::333 => require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::467 => require(dest.length == func.length, "wrong array lengths");
scw-contracts\contracts\smart-contract-wallet\SmartAccount.sol::468 => for (uint i = 0; i < dest.length;) {
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::70 => uint256 opslen = ops.length;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::98 => uint256 opasLen = opsPerAggregator.length;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::101 => totalOps += opsPerAggregator[i].userOps.length;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::111 => uint256 opslen = ops.length;
scw-contracts\contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol::132 => uint256 opslen = ops.length;
scw-contracts\contracts\smart-contract-wallet\aa-


hunter_w3b G

## Summary

### Gas Optimizations

|               | Issue                                                                                | Instances | Total Gas Saved |
| ------------- | :----------------------------------------------------------------------------------------------------------- | :-------: | :-------------: |
| [G‑01] | Avoid `contract existence` checks by using solidity version 0.8.10 or later                                  |    27     |      2700       |
| [G‑02] | Expressions for `constant` values such as a call to `keccak256()`, should use immutable rather than constant |     2     |        -        |
| [G‑03] | Use solidity version `0.8.19` to gain some gas boost                                                         |    11     |       968       |
| [G‑04] |  `<X> += <Y>` costes more gas than`<X> = <X> + <Y>` for state variables                                      |     7     |       791       |
| [G‑05] | Not using the named return variables when a function `returns` , wastes deployment gas                       |    51     |                 |
| [G‑06] | Can make the `variable` outside the loop to save gas                                                         |     9     |        -        |
| [G‑07] | Using `calldata` instead of `memory` for read-only arguments in external functions save gas                  |    37     |      4440       |
| [G‑08] | Use `assembly` to check for `address(0)`                                                                     |     1     |        -        |
| [G‑09] | Before some functions, we should `check` some variables for possible gas save                                |     7     |        -        |
| [G‑10] | Using `bools` for storage incurs overhead                                                                    |     4     |      68400      |
| [G‑11] | The result of function `calls` should b

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

/// @audit     updateInterest()
274        IPool(params_.pool).updateInterest();

/// @audit    bucketInfo()
282        ) = IPool(params_.pool).bucketInfo(params_.fromIndex);

/// @audit    moveQuoteToken()
310        ) = IPool(params_.pool).moveQuoteToken(

/// @audit    collateralAddress()
420       = IPool(pool_).collateralAddress();

/// @audit  quoteTokenAddress()
421       = IPool(pool_).quoteTokenAddress();

/// @audit  deployedPools()
423       = erc20PoolFactory.deployedPools(subsetHash_, collateralAddress, quoteAddress);

/// @audit  deployedPools()
424      = erc721PoolFactory.deployedPools(subsetHash_, collateralAddress, quoteAddress);

File: /ajna-core/src/RewardsManager.sol

/// @audit      currentBurnEpoch()
150         = IPool(ajnaPool).currentBurnEpoch();

/// @audit      bucketExchangeRate()
180         = uint128(IPool(ajnaPool).bucketExchangeRate(toIndex));

/// @audit      ownerOf()
213        if (IERC721(address(positionManager)).ownerOf(tokenId_) != msg.sender)

/// @audit      currentBurnEpoch()
219         = IPool(ajnaPool).currentBurnEpoch();

/// @audit      bucketExchangeRate()
241         = uint128(IPool(ajnaPool).bucketExchangeRate(bucketId));

/// @audit      transferFrom()
250        IERC721(address(positionManager)).transferFrom(msg.sender, address(this), tokenId_);

/// @audit      currentBurnEpoch()
283            IPool(ajnaPool).currentBurnEpoch(),

/// @audit      transferFrom()
302        IERC721(address(positionManager)).transferFrom(address(this), msg.sender, tokenId_);

/// @audit      currentBurnEpoch()
570        if (validateEpoch_ && epochToClaim_ > IPool(ajnaPool_).currentBurnEpoch())

/// @audit      burnInfo()
645        ) = IPool(pool_).burnInfo(currentBurnEventEpoch_);

/// @audit      burnInfo()
651        ) = IPool(pool_).burnInfo(lastBurnEventEpoch_);

/// @audit      currentBurnEpoch()
676         = IPool(pool_).currentBurnEpoch();

/// @audit      bucketExchangeRate()
752         = IPool(pool_).bucketExchangeRate(bucketIndex_);

/// @audit      bucketExchangeRate()
779          = IPool(pool_).bucketExchangeRate(bucketIndex_);

/// @audit      bucketInfo()
792                (, , , uint256 bucketDeposit, ) = IPool(pool_).bucketInfo(bucketIndex_);

/// @audit      balanceOf()
814           = IERC20(ajnaToken).balanceOf(address(this));

/// @audit      safeTransfer()
819            IERC20(ajnaToken).safeTransfer(msg.sender, rewardsEarned_);


LewisBroadhurst G

## Gas Optimisations

### [G-01]

If statements are made throughout the code base to perform important checks. Instead of using if statements, we can make use of the `require` function to perform the same checks. Modest gas saving that adds up over time.

```
// Example

- if (initialized) revert AlreadyInitialized();

+ require(!initialized, AlreadyInitialized());
```

It will also make the code more readable for the indented code.

Infractions found:
1. PrivatePool.sol
   - ln 170: if (initialized) revert AlreadyInitialized();
   - ln 173: if (_feeRate > 5_000) revert FeeRateTooHigh();
   - ln 226: if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();
   - ln 263: if (msg.value < netInputAmount) revert InvalidEthAmount();
   - ln 398: if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();
   - ln 414: if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight();
   - ln 565: if (newFeeRate > 5_000) revert FeeRateTooHigh();
   - ln 630: if (!availableForFlashLoan(token, tokenId)) revert NotAvailableForFlashLoan();
   - ln 636: if (baseToken == address(0) && msg.value < fee) revert InvalidEthAmount();
   - ln 646: if (!success) revert FlashLoanFailed();
   - ln 683: if (!MerkleProofLib.verifyMultiProof(proof.proof, merkleRoot, leafs, proof.flags)...
   - ln 793: if (royaltyFee > salePrice) revert InvalidRoyaltyFee();

2. EthRouter.sol
   - ln 101: if (block.timestamp > deadline && deadline != 0)...
   - ln 154: if (block.timestamp > deadline && deadline != 0)...
   - ln 203: if (address(this).balance < minOutputAmount)...
   - ln 234: if (price > maxPrice || price < minPrice)...
   - ln 256: if (block.timestamp > deadline && deadline != 0)...
   - ln 314: if (royaltyFee > salePrice) revert InvalidRoyaltyFee();

// Code block 1 (unknown):
// Example

- if (initialized) revert AlreadyInitialized();

+ require(!initialized, AlreadyInitialized());


MalfurionWhitehat Q

# 1. Use `block.chainid` on `SmartAccount.getChainId`

The use of inline assembly is unnecessary here:

```diff
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..928c16a 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -138,12 +138,7 @@ contract SmartAccount is

     /// @dev Returns the chain id used by this contract.
     function getChainId() public view returns (uint256) {
-        uint256 id;
-        // solhint-disable-next-line no-inline-assembly
-        assembly {
-            id := chainid()
-        }
-        return id;
+        return block.chainid;
     }

     //@review getNonce specific to EntryPoint requirements

```

# 2. Do not use `tx.origin` for testing purposes on `SmartAccount._validateSignature`. Instead, create a mock implementation that overrides this method and bypasses any necessary signature validation.

Mixing testing and deployment code will unnecessarily introduce dead code on production contracts and will make hinder maintainability.

```diff
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..78e960b 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -508,7 +508,7 @@ contract SmartAccount is
         bytes32 hash = userOpHash.toEthSignedMessageHash();
         //ignore signature mismatch of from==ZERO_ADDRESS (for eth_callUserOp validation purposes)
         // solhint-disable-next-line avoid-tx-origin
-        require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");
+        require(owner == hash.recover(userOp.signature), "account: wrong signature");
         return 0;
     }

```

# 3. Emit events on important state variable changing operations: `VerifyingSingletonPaymaster.setSigner`

// Code block 1 (diff):
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..928c16a 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -138,12 +138,7 @@ contract SmartAccount is

     /// @dev Returns the chain id used by this contract.
     function getChainId() public view returns (uint256) {
-        uint256 id;
-        // solhint-disable-next-line no-inline-assembly
-        assembly {
-            id := chainid()
-        }
-        return id;
+        return block.chainid;
     }

     //@review getNonce specific to EntryPoint requirements

// Code block 2 (diff):
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..78e960b 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -508,7 +508,7 @@ contract SmartAccount is
         bytes32 hash = userOpHash.toEthSignedMessageHash();
         //ignore signature mismatch of from==ZERO_ADDRESS (for eth_callUserOp validation purposes)
         // solhint-disable-next-line avoid-tx-origin
-        require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");
+        require(owner == hash.recover(userOp.signature), "account: wrong signature");
         return 0;
     }

diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..928c16a 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -138,12 +138,7 @@ contract SmartAccount is

     /// @dev Returns the chain id used by this contract.
     function getChainId() public view returns (uint256) {
-        uint256 id;
-        // solhint-disable-next-line no-inline-assembly
-        assembly {
-            id := chainid()
-        }
-        return id;
+        return block.chainid;
     }

     //@review getNonce specific to EntryPoint requirements

diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..78e960b 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -508,7 +508,7 @@ contract SmartAccount is
         bytes32 hash = userOpHash.toEthSignedMessageHash();
         //ignore signature mismatch of from==ZERO_ADDRESS (for eth_callUserOp validation purposes)
         // solhint-disable-next-line avoid-tx-origin
-        require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");
+        require(owner == hash.recover(userOp.signature), "account: wrong signature");
         return 0;
     }


Daniel526 Q

A. Inconsistent State Update in pickWinner Function
[#L118-L132](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L118-L132)
- The `pickWinner` function is designed to select winners for the current round based on the provided list of token IDs (winners array). However, it lacks proper validation to ensure that the winners array is not empty. As a result, if the function is called with an empty array of `winners`, it proceeds to update the `isSelectionComplete` flag to `true` for the current round, indicating that `winners` have been selected. This behavior occurs regardless of whether any `winners` have actually been chosen, leading to an inconsistent state in the contract.
## Impact:
The impact of this vulnerability is that the contract state becomes inconsistent, as it indicates that winners have been selected for the current round even though no winners have actually been chosen. This inconsistency could confuse users and disrupt the normal operation of the merging pool.
## Mitigation:
```solidity
function pickWinner(uint256[] calldata winners) external {
    require(isAdmin[msg.sender]);
    require(winners.length == winnersPerPeriod, "Incorrect number of winners");
    require(!isSelectionComplete[roundId], "Winners are already selected");
    require(winners.length > 0, "Winners array cannot be empty"); // Validation to ensure winners array is not empty

    isSelectionComplete[roundId] = true;
    roundId += 1;
}

```

// Code block 1 (solidity):
function pickWinner(uint256[] calldata winners) external {
    require(isAdmin[msg.sender]);
    require(winners.length == winnersPerPeriod, "Incorrect number of winners");
    require(!isSelectionComplete[roundId], "Winners are already selected");
    require(winners.length > 0, "Winners array cannot be empty"); // Validation to ensure winners array is not empty

    isSelectionComplete[roundId] = true;
    roundId += 1;
}


cryptostellar5 G

### G-01 ++I or I++ SHOULD BE UNCHECKED{++I} or UNCHECKED{I++} WHEN IT IS NOT POSSIBLE FOR THEM TO OVERFLOW, AS IS THE CASE WHEN USED IN FOR- AND WHILE-LOOPS

*Number of Instances Identified: 5*

The `unchecked` keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves **30-40 gas [per loop](https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc#the-increment-in-for-loop-post-condition-can-be-made-unchecked)**.

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

```
100: for (uint256 i = 0; i < opasLen; i++)
107: for (uint256 a = 0; a < opasLen; a++)
112: for (uint256 i = 0; i < opslen; i++)
128: for (uint256 a = 0; a < opasLen; a++)
134: for (uint256 i = 0; i < opslen; i++)
```

### G-02 SPLITTING REQUIRE() STATEMENTS THAT USE && SAVES GAS

*Number of Instances Identified: 3*

See [this issue](https://github.com/code-423n4/2022-01-xdefi-findings/issues/128) which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by **3 gas**

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

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

### G-03 X += Y COSTS MORE GAS THAN X = X + Y FOR STATE VARIABLES

*Number of Instances Identified: 28*

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

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

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

// Code block 1 (unknown):
100: for (uint256 i = 0; i < opasLen; i++)
107: for (uint256 a = 0; a < opasLen; a++)
112: for (uint256 i = 0; i < opslen; i++)
128: for (uint256 a = 0; a < opasLen; a++)
134: for (uint256 i = 0; i < opslen; i++)

// Code block 2 (unknown):
34: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
49: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
68: require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");


hunter_w3b G

# Gas Optimization

# Summary

| Number | Optimization Details                                                                | Context |
| :----: | :---------------------------------------------------------------------------------- | :-----: |
| [G-01] | FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE    |   14    |
| [G-02] | PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD     |    8    |
| [G-03] | SETTING THE CONSTRUCTOR TO PAYABLE                                                  |    4    |
| [G-04] | NOT USING THE NAMED RETURN VARIABLES WHEN A FUNCTION RETURNS, WASTES DEPLOYMENT GAS |    1    |
| [G-05] | CAN MAKE THE VARIABLE OUTSIDE THE LOOP TO SAVE GAS                                  |    2    |
| [G-06] | The result of function calls should be cached rather than re-calling the function   |    2    |
| [G-07] | Duplicated require() checks should be refactored to a modifier or function          |    1    |
| [G-08] | With assembly, .call (bool success) transfer can be done gas-optimized              |    5    |

## [G-01] FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE

### Summary

The onlyOwner modifier makes a function revert if not called by the address registered as the owner

### Details

There are **14** instances of this issue.

### Github Permalinks

```solidity
File: /SafEth/derivatives/WstEth.sol

48     function setMaxSlippage(uint256 _slippage) external onlyOwner {

56    function withdraw(uint256 _amount) external onlyOwner {
```

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

```solidity
File: /SafEth/derivatives/SfrxEth.sol

51    function setMaxSlippage(uint256 _slippage) external onlyOwner {

60    function withdraw(uint256 _amount) external onlyOwner {
```

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

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

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

48     function setMaxSlippage(uint256 _slippage) external onlyOwner {

56    function withdraw(uint256 _amount) external onlyOwner {

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

51    function setMaxSlippage(uint256 _slippage) external onlyOwner {

60    function withdraw(uint256 _amount) external onlyOwner {

File: /SafEth/derivatives/WstEth.sol

48     function setMaxSlippage(uint256 _slippage) external onlyOwner {

56    function withdraw(uint256 _amount) external onlyOwner {

File: /SafEth/derivatives/SfrxEth.sol

51    function setMaxSlippage(uint256 _slippage) external onlyOwner {

60    function withdraw(uint256 _amount) external onlyOwner {


7ashraf Q

# Quality Assurance Report

## Summary
The QA report flags important issues and offers fixes for better project performance and security. It advises against accidentally adding new fighter types and stresses the importance of checking for existing stakeholders. It also notes potential security risks like unbounded loops and missing reentrancy locks. Recommendations include avoiding hardcoded values and simplifying complex functions for clearer code. Following these suggestions will enhance the project's overall quality and security.

| Issue Number | Issue Title                                                                                | Number of Instances |
|--------------|--------------------------------------------------------------------------------------------------|---------------------|
| L-01         | More Fighter types can be added by mistake                                                      | 1                   |
| L-02         | Check if staker already exists before adding                                                    | 1                   |
| L-03         | Possible dos for unbounded loop                                                                 | 1                   |
| L-04         | Require `amount` to be greater than zero                                                        | 1                   |
| L-05         | Add re-entrance lock                                                                            | 2                   |
| N-01         | Empty string is passed to the function, avoid hardcoded empty strings                            | 1                   |
| N-02         | Save String as a constant instead of hardcoding it in the function                               | 3                   |
| N-03         | Function should be allowed to be called only once                                                | 1                   |
| N-04         | Avoid on-chain computation

    function incrementGeneration(uint8 fighterType) external returns (uint8) {
        require(msg.sender == _ownerAddress);
        generation[fighterType] += 1;
        maxRerollsAllowed[fighterType] += 1;
        return generation[fighterType];
    }

    function addStaker(address newStaker) external {
        require(msg.sender == _ownerAddress);
        hasStakerRole[newStaker] = true;
    }


0xWaitress Q

### [L-1] _withdraw in USDA does not follow the check effect interaction patterns:

it's better to _burn the _susdAmount first before calling sUSD.transfer which is an external call. If sUSD becomes a token that has beforeTokenTransfer then it has the potential to be subject to re-entrency.

```solidity
  function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }
```
### [L-2] _withdraw in USDA call sUSD.transfer before calling _burn is not following CEI pattern.

```solidity
  function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }
```
This adds risk on potential re-entrency if sUSD.transfer allows pre-transfer hooks

### [N-1] changeCvxRewardFee in AMPHClaimer does not have a cap.

cvxRewardFee if mis-configured to beyond 100% would break many calculation like claimable due to overflow. Same with changeCrvRewardFee.

```solidity
  function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {
    cvxRewardFee = _newFee;

    emit ChangedCvxRewardFee(_newFee);
  }
```

### [N-2] _propose in GovernorCharlie could use a struct to represent 4 arrays which are expected to be the same size, to remove length checks

```

// Code block 1 (solidity):
function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }

// Code block 2 (solidity):
function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }

// Code block 3 (solidity):
function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {
    cvxRewardFee = _newFee;

    emit ChangedCvxRewardFee(_newFee);
  }

function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }

function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
    if (reserveAmount == 0) revert USDA_EmptyReserve();
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
    // Account for the susd withdrawn
    reserveAmount -= _susdAmount;
    sUSD.transfer(_target, _susdAmount);
    _burn(_msgSender(), _susdAmount);

    emit Withdraw(_target, _susdAmount);
  }

function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {
    cvxRewardFee = _newFee;

    emit ChangedCvxRewardFee(_newFee);
  }


Ignite Q

# [Low-01] - Unsigned Integer Overflow

## Impact

This function calculates the total number of fighters to mint by summing up the quantities of two types of fighters specified in the `numToMint` array at line 207.

```solidity=191
function claimFighters(
    uint8[2] calldata numToMint,
    bytes calldata signature,
    string[] calldata modelHashes,
    string[] calldata modelTypes
)
    external
{
    bytes32 msgHash = bytes32(keccak256(abi.encode(
        msg.sender,
        numToMint[0],
        numToMint[1],
        nftsClaimed[msg.sender][0],
        nftsClaimed[msg.sender][1]
    )));
    require(Verification.verify(msgHash, signature, _delegatedAddress));
    uint16 totalToMint = uint16(numToMint[0] + numToMint[1]);
    require(modelHashes.length == totalToMint && modelTypes.length == totalToMint);
    nftsClaimed[msg.sender][0] += numToMint[0];
    nftsClaimed[msg.sender][1] += numToMint[1];
    for (uint16 i = 0; i < totalToMint; i++) {
        _createNewFighter(
            msg.sender,
            uint256(keccak256(abi.encode(msg.sender, fighters.length))),
            modelHashes[i],
            modelTypes[i],
            i < numToMint[0] ? 0 : 1,
            0,
            [uint256(100), uint256(100)]
        );
    }
}
```
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L191-L222

However, if `numToMint[0]` and `numToMint[1]` are summed up to a value greater than 255, this function will revert due to overflow.

```solidity=207
uint16 totalToMint = uint16(numToMint[0] + numToMint[1]);
```
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L207

This line will sum up `numToMint[0]` and `numToMint[1]` before converting to `uint16`. Since both `numToMint[0]` and `numToMint[1]` are of type uint8, and the maximum value for a uint8 is 255, an overflow will occur if their sum exceeds this value.

## Proof of Concept

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

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

This line will sum up `numToMint[0]` and `numToMint[1]` before converting to `uint16`. Since both `numToMint[0]` and `numToMint[1]` are of type uint8, and the maximum value for a uint8 is 255, an overflow will occur if their sum exceeds this value.

## Proof of Concept

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

## Tools Used

Manual Review

## Recommended Mitigation Steps

Convert `numToMint` to uint16 type before summing it up.

For example:

// Code block 1 (unknown):
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L191-L222

However, if `numToMint[0]` and `numToMint[1]` are summed up to a value greater than 255, this function will revert due to overflow.


0xepley G

# 1.Use StETHPerToken() function in ethPerDerivative() function in WstEth.sol

## Summary
wstEth contract have another function `stEthPerToken()` that returns the price for exactly 1 ether instead of manually passing the amount in the `getStEthByWstEth(_amount)`
## Vulnerability Details
Here we can see that both the functions have same inner implementation but for the `stEthPerToken()` one ether is hardcoded so we dont need to pass it manually as in `getStEthByWstEth(_amount)`
https://github.com/lidofinance/lido-dao/blob/df95e563445821988baf9869fde64d86c36be55f/contracts/0.6.12/WstETH.sol#L107
https://github.com/lidofinance/lido-dao/blob/df95e563445821988baf9869fde64d86c36be55f/contracts/0.6.12/WstETH.sol#L99

## Impact
When argument is passed in function its gas is determined using its hex form, one non zero byte costs `16 gas` and non zero bytes cost '4 gas'.
Here gas can be saved by just using the function that donot need the argument to be passed in it.
So in total saves arount `400 gas` in total

## Proof of Concept
```solidity
pragma solidity ^0.8.0;

contract test{
    uint amount = 10;
    function test1(uint _amount) public returns(uint)
    {
        return amount;
    }
    function test2() public returns(uint)
    {
        return amount;
    }
}
```
Deployed the above simple code in remix, and passed the argument as 10**18 and on calling first one cosumes `400` less gas than second.

## Tools Used
Manual Review and Remix
## Recommended Mitigation Steps
Use the `stEthPerToken()` function described here
https://github.com/lidofinance/lido-dao/blob/df95e563445821988baf9869fde64d86c36be55f/contracts/0.6.12/WstETH.sol#L107

## 2. Redundant check that does nothing
## Details
Following multiplication and division does nothing as we multiply and divide by same number,
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L215
It can be removed and some gas can be saved as this functio

// Code block 1 (solidity):
pragma solidity ^0.8.0;

contract test{
    uint amount = 10;
    function test1(uint _amount) public returns(uint)
    {
        return amount;
    }
    function test2() public returns(uint)
    {
        return amount;
    }
}


CodeFoxInc G

# Gas Optimization
## Gas-01 Not necessary to reinitiate the default value in struct

In the function `addStrategy()`. It is not necessary to initiate the value as 0. So we can remove this part to save gas.

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

```diff
-	strategies[_strategy] = StrategyParams({
-	     activation: block.timestamp,
-            feeBPS: _feeBPS,
-            allocBPS: _allocBPS,
-            allocated: 0,
-            gains: 0,
-            losses: 0,
-            lastReport: block.timestamp
-       });
+       strategies[_strategy].activation = block.timestamp;
+       strategies[_strategy].feeBPS = _feeBPS;
+       strategies[_strategy].allocBPS = _allocBPS;
+       strategies[_strategy].lastReport = block.timestamp;
```

### Proof of Concept

Before:

```
····································|······························|·············|·············|·············|···············|··············
|  ReaperVaultERC4626               ·  addStrategy                 ·     170982  ·     205182  ·     182382  ·            3  ·          -  │
····································|······························|·············|·············|·············|···············|··············
```

After:

```
····································|······························|·············|·············|·············|···············|··············
|  ReaperVaultERC4626               ·  addStrategy                 ·     164169  ·     198369  ·     175569  ·            3  ·          -  │
····································|······························|·············|·············|·············|···············|··············
```

### Recommendation

As a result it generally save about 7,000 gas. So I recommend that you change

// Code block 1 (diff):
-	strategies[_strategy] = StrategyParams({
-	     activation: block.timestamp,
-            feeBPS: _feeBPS,
-            allocBPS: _allocBPS,
-            allocated: 0,
-            gains: 0,
-            losses: 0,
-            lastReport: block.timestamp
-       });
+       strategies[_strategy].activation = block.timestamp;
+       strategies[_strategy].feeBPS = _feeBPS;
+       strategies[_strategy].allocBPS = _allocBPS;
+       strategies[_strategy].lastReport = block.timestamp;

// Code block 2 (unknown):
····································|······························|·············|·············|·············|···············|··············
|  ReaperVaultERC4626               ·  addStrategy                 ·     170982  ·     205182  ·     182382  ·            3  ·          -  │
····································|······························|·············|·············|·············|···············|··············

// Code block 3 (unknown):
····································|······························|·············|·············|·············|···············|··············
|  ReaperVaultERC4626               ·  addStrategy                 ·     164169  ·     198369  ·     175569  ·            3  ·          -  │
····································|······························|·············|·············|·············|···············|··············


SAQ G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 4 | - |
| [G-02] | Should use arguments instead of state variable | 7 | - |
| [G-03] | State variable can be packed into fewer storage slots | 1 | - |
| [G-04] | Duplicated require()/if() checks should be refactored to a modifier or function | 6 | - |
| [G-05] | Use nested if and, avoid multiple check combinations | 6 | - |
| [G-06] | Use assembly to write address storage values | 4 | - |
| [G-07] | Do not calculate  constant | 7 | - |
| [G-08] | Use hardcode address instead of address(this) | 9 | - |
| [G-09] | Non efficient zero initialization | 3 | - |
| [G-10] | Variable names that consist of all capital letters should be reserved for constant/immutable variables | 1 | - |
| [G-11] | array[index] += amount is cheaper than array[index] = array[index] + amount (or related variants) | 5 | - |
| [G-12] | Use uint256(1)/uint256(2) instead for true and false boolean states | 4 | - |
| [G-13] | Using Bools For Storage Incurs Overhead | 2 | - |
| [G-14] | bytes.concat() can be used in place of abi.encodePacked | 3 | - |
| [G-15] | Use assembly for math (add, sub, mul, div) | 7 | - |
| [G-16] | Refactor event to avoid emitting empty data | 4 | - |

## Gas Optimizations

## [G-1] Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use theright tool for the task at hand. There is a difference between constant variables and immutable variables, and they shouldeach be used in their appropriate contexts.constants should be used for literal values written into the code, and immutablevariables should be used for expressions, or values calculated in, or passed into the constructor.

```solidity
file: /contracts/governanc

file: /contracts/governance/GovernorCharlie.sol

19      bytes32 public constant DOMAIN_TYPEHASH =
20          keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

23      bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');

File: /contracts/utils/UFragments.sol

88     keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');

89     bytes32 public constant PERMIT_TYPEHASH =
90     keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');


0xAgro Q

# QA Report
## Finding Summary
||Issue|Instances|
|-|-|-|
|[NC-01]|Long Lines (> 120 Characters)|85|
|[NC-02]|Contracts Missing `@title` NatSpec Tag|25|
|[NC-03]|`TODO` Left In Production Code|11|
|[NC-04]|Order of Functions Not Compliant With Solidity Docs|8|
|[NC-05]|Contract Layout Voids Solidity Docs|7|
|[NC-06]|Commented Code Left In Production Code|6|
|[NC-07]|Order of Modifiers Not Compliant With Solidity Docs|5|
|[NC-08]|Debug Left in Production Code|5|
|[NC-09]|NatSpec Comments Use `//` Instead of `///`|3|
|[NC-10]|Dead Comments|2|
|[NC-11]|Inconsistent NatSpec Function Headers|2|
|[NC-12]|Underscore Notation Not Used / Not Used Consistently|1|
|[NC-13]|Power of Ten Literal > `10e3` Not In Scientific Notation|1|
|[NC-14]|Non-Assembly Function Available (`address().code.length`)|1|
|[NC-15]|Non-Assembly Function Available (`chainid()`)|1|
|[NC-16]|Whitespace In `mapping`|1|
|[NC-17]|`validate` Misspelled as `valiate`|1|
|[NC-18]|`// Emit events here..`|1|
|[NC-19]|Duplicate Comment|1|

### [NC-01] Long Lines (> 120 Characters)

Lines with greater length than 120 characters are used. The [Solidity Style Guide](https://docs.soliditylang.org/en/v0.8.17/style-guide.html#maximum-line-lengthhttps://docs.soliditylang.org/en/v0.8.17/style-guide.html#maximum-line-length) suggests that all lines should be 120 characters or less in width.

#### Findings:

*scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol*
 Links: [20](https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol#L20), [24](https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol#L24).
```solidity
20:     *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
24:    function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, address aggregator, uint2

20:     *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
24:    function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, address aggregator, uint256 missingAccountFunds)

39:     * subclass should return a nonce value that is used both by _validateAndUpdateNonce, and by the external provider (to read the current nonce)
45:     * subclass should return a nonce value that is used both by _validateAndUpdateNonce, and by the external provider (to read the current nonce)
57:     * subclass doesn't need to override this method. Instead, it should override the specific internal validation methods.
60:    function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, address aggregator, uint256 missingAccountFunds)


j4ld1na Q

|     | Issues                                                                                | Instances |
| :-- | :---------------------------------------------------------------------------------------------------------------------- | --------: |
| 01  | Return values of `approve()` not checked.                                                                               |         2 |
| 02  | `SafeMath` is generally not needed starting with `Solidity 0.8`, since the compiler now has built in overflow checking. |         1 |
| 03  | Don't use `catch`-block empty.                                                                                |         8 |

## 01 - Return values of `approve()` not checked.

_Not all `IERC20` implementations `revert()` when there's a failure in `approve()`. The function signature has a `boolean` return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything_

There are 2 instances for this issue:

-   [contracts/lybra/configuration/LybraConfigurator.sol#L302](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L302)

```ts
EUSD.approve(address(curvePool), balance);
```

-   [contracts/lybra/pools/LybraWstETHVault.sol#L35](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWstETHVault.sol#L35)

```ts
lido.approve(address(collateralAsset), msg.value);
```

Recommended Mitigation Steps:

-   It is recommend to use safeApprove().
-   Consider using safeIncreaseAllowance instead of approve function. (Approve race condition)

## 02 - `SafeMath` is generally not needed starting with `Solidity 0.8`, since the compiler now has built in overflow checking.

_All 3(ver cuantos?) in-scope contracts use the SafMath library for simple addition, subtraction, multiplication and division._

Recommended Mitigation Steps:

-   It is recommend to use safeApprove().
-   Consider using safeIncreaseAllowance instead of approve function. (Approve race condition)

## 02 - `SafeMath` is generally not needed starting with `Solidity 0.8`, since the compiler now has built in overflow checking.

_All 3(ver cuantos?) in-scope contracts use the SafMath library for simple addition, subtraction, multiplication and division._

_This causes an unnecessary overhead since beginning from Solidity version 0.8.0 arithemtic operations revert by default on overflow / underflow._

_By looking into the implementation of the SafeMath library you can also see that it is just a wrapper around the basic arithmatic operations and does not add any checks (at least for the functions used in the in-scope contracts)._

[Docs-Changes of the Semantics](https://docs.soliditylang.org/en/v0.8.9/080-breaking-changes.html#silent-changes-of-the-semantics)

There an instance:

-   [contracts/lybra/token/EUSD.sol](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/EUSD.sol)

Recommended Mitigation Steps:

-   Remove `SafeMath` library.

## 03 - Don't use `catch`-block empty.

_Usually empty try-catch is a bad idea because you are silently swallowing an error condition and then continuing execution._

_Exceptions should only be thrown if there is truly an exception - something happening beyond the norm. An empty catch block basically says "something bad is happening, but I just don't care"._

_If you don't want to handle the exception, let it propagate upwards until it reaches some code that can handle it._

There are 8 instances:

-   [contracts/lybra/miner/EUSDMiningIncentives.sol#L216](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L216)

// Code block 1 (ts):
EUSD.approve(address(curvePool), balance);

// Code block 2 (ts):
lido.approve(address(collateralAsset), msg.value);

LybraConfigurator.sol#L302, LybraWstETHVault.sol#L35, EUSD.sol, EUSDMiningIncentives.sol#L216

EUSDMiningIncentives.sol, LybraConfigurator.sol, LybraWstETHVault.sol, EUSD.sol


Rolezn Q

## Summary<a name="Summary">

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#LOW‑1) | Avoid using `tx.origin` | 3 |
| [LOW‑2](#LOW‑2) | Use of `ecrecover` is susceptible to signature malleability | 2 |
| [LOW‑3](#LOW‑3) | Init functions are susceptible to front-running | 1 |
| [LOW‑4](#LOW‑4) | Low Level Calls With Solidity Version 0.8.14 Can Result In Optimiser Bug | 6 |
| [LOW‑5](#LOW‑5) | Missing parameter validation in `constructor` | 1 |
| [LOW‑6](#LOW‑6) | Missing Contract-existence Checks Before Low-level Calls | 10 |
| [LOW‑7](#LOW‑7) | Contracts are not using their OZ Upgradeable counterparts | 9 |
| [LOW‑8](#LOW‑8) | Remove unused code | 5 |
| [LOW‑9](#LOW‑9) | `require()` should be used instead of `assert()` | 1 |
| [LOW‑10](#LOW‑10) | Unused `receive()` Function Will Lock Ether In Contract  | 1 |
| [LOW‑11](#LOW‑11) | Missing Non Reentrancy Modifiers | 6

Total: 45 contexts over 11 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#NC‑1) | Critical Changes Should Use Two-step Procedure | 6 |
| [NC‑2](#NC‑2) | Function creates dirty bits | 7 |
| [NC‑3](#NC‑3) | Function writing that does not comply with the Solidity Style Guide  | 37 |
| [NC‑4](#NC‑4) | Use `delete` to Clear Variables | 4 |
| [NC‑5](#NC‑5) | No need to initialize uints to zero | 11 |
| [NC‑6](#NC‑6) | Lines are too long | 3 |
| [NC‑7](#NC‑7) | Missing event for critical parameter change | 4 |
| [NC‑8](#NC‑8) | Implementation contract may not be initialized | 6 |
| [NC‑9](#NC‑9) | Non-usage of specific imports | 43 |
| [NC‑10](#NC‑10) | Use a more recent version of Solidity | 37 |
| [NC‑11](#NC‑11) | Open TODOs | 1 |
| [NC‑12](#NC‑12) | Us

257: address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;

281: address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;


0xepley Analysis

Upon thorough examination of the presented smart contracts constituting the decentralized finance (DeFi) project, I discerned an intricately designed system that adeptly manages assets, incorporates oracle functionalities for price feeds, and implements a secure access control mechanism. The adoption of OpenZeppelin libraries is commendable, underscoring the commitment to industry best practices and substantially contributing to the overall reliability and security of the system. Nevertheless, it is imperative to underscore the necessity of a comprehensive security audit tailored specifically to the dynamic landscape of DeFi. This is crucial given the considerable financial implications and potential risks associated with decentralized financial transactions. Furthermore, the optimization of gas usage warrants careful consideration to augment the overall efficiency of contract execution. This becomes especially pertinent in decentralized environments where transaction costs wield significant influence.

- The smart contracts exhibit a sophisticated architecture tailored to the demands of the DeFi landscape, encompassing robust asset management, seamless integration of oracle functionalities for accurate price feeds, and a well-structured secure access control system.
- The decision to leverage OpenZeppelin libraries not only underscores a commitment to industry standards but also substantially bolsters the reliability and security parameters of the entire system.
- It is crucial to emphasize the imperative need for a specialized security audit specifically designed for the unique challenges posed by DeFi projects. This consideration is paramount, given the inherent financial nature of the transactions and the potential risks involved.
- Gas optimization strategies should be thoroughly explored to fine-tune the efficiency of contract execution. This strategic imperative gains heightened significance within decentralized environments, where transaction costs wield con

   import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
   ```

2. **Security Considerations:**
   - Conducting a meticulous review of access control mechanisms, leveraging OpenZeppelin's AccessControlUpgradeable for fine-grained control over contract access.
   - Recommending a specialized DeFi security audit to further enhance overall robustness and safeguard against potential vulnerabilities.

   ```solidity
   import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
   ```

3. **Asset Management and Strategies:**
   - Scrutinizing asset management processes, especially the logic behind depositing assets into strategies and interacting with the Eigen Strategy Manager.
   - Ensuring alignment with the project's overarching objectives and optimal utilization of deployed strategies.

   ```solidity
   function depositAssetIntoStrategy(address asset) external onlySupportedAsset(asset) onlyLRTManager {
       // Implementation...
   }
   ```

4. **Oracle Integration:**
   - Thoroughly examining the integration of oracles, with a specific focus on the ChainlinkPriceOracle.
   - Verifying the use of Chainlink's AggregatorInterface for fetching asset prices and maintaining accurate oracle data.

   ```solidity
   function updatePriceFeedFor(address asset, address priceFeed) external onlyLRTManager onlySupportedAsset(asset) {
       // Implementation...
   }
   ```

5. **Gas Optimization:**
   - Assessing gas optimization strategies to ensure efficient contract execution and a seamless user experience.
   - Providing recommendations for optimizing functions, promoting cost-effectiveness while maintaining optimal performance.

   ```solidity
   function getAssetPrice(address asset) external view onlySupportedAsset(asset) returns (uint256) {
       return AggregatorInterface(assetPriceFeed[asset]).latestAnswer();
   }
   ```
6. **Contract Initialization and Upgrade

2. **Utilization of OpenZeppelin Libraries:**
   - Leveraging OpenZeppelin libraries, such as ERC20Upgradeable and AccessControlUpgradeable, demonstrates a commitment to industry best practices and significantly enhances the security of the codebase.


jeffy G

### unchecked arithmetics

in which cases where arithmetic operations can’t overflow (mostly incrementing loop counters after comparing them with array lengths in loops), we can mark then unchecked to save gas

there were 5 found instances of this optimizations:

```jsx
868:for (uint192 _i; _i < enabledTokens.length; ++_i)
896:for (uint192 _i; _i < enabledTokens.length; ++_i)
259:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)
313:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)
382:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)
```

[[868](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L868),[896](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L896),[259](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L259),[313](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L313),[382](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L382C1-L382C64)]

### struct optimization

in [Proposal struct](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/utils/GovernanceStructs.sol#L4C8-L4C16) we can declare the following variables right after each others

- proposer
- canceled
- executed
- executed

doing so will cause the struct to use one less storage slot in the EVM (saving 20000 gas unit), it will also make future reads/writes to that slot cost less (2100 for a cold read vs 100 for a hot read, and 20000/2900 for a cold write vs 800 (TODO : change this) for a hot write), so this optimization will cause most subs

[[868](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L868),[896](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L896),[259](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L259),[313](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L313),[382](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L382C1-L382C64)]

### struct optimization

in [Proposal struct](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/utils/GovernanceStructs.sol#L4C8-L4C16) we can declare the following variables right after each others

- proposer
- canceled
- executed
- executed

doing so will cause the struct to use one less storage slot in the EVM (saving 20000 gas unit), it will also make future reads/writes to that slot cost less (2100 for a cold read vs 100 for a hot read, and 20000/2900 for a cold write vs 800 (TODO : change this) for a hot write), so this optimization will cause most subsequent writes to he same slot of be considered hot

## indexed events

marking event value-variables (such as int, uint, address ..) as indexed causes them to be stored on the stack instead of memory, thus saving gas

instances:

### caching variables on the stack

- array.length

mostly array lengths in for loops, instead of reading them using `array.length` every time, one could declare a `uint` variable to the stack, and then set it to the array length.

 the following instances were found:

// Code block 1 (jsx):
868:for (uint192 _i; _i < enabledTokens.length; ++_i)
896:for (uint192 _i; _i < enabledTokens.length; ++_i)
259:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)
313:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)
382:for (uint256 _i = 0; _i < _proposal.targets.length; _i++)

VaultController.sol#L868, VaultController.sol#L896, GovernorCharlie.sol#L259, GovernorCharlie.sol#L313, GovernorCharlie.sol#L382-L1, GovernanceStructs.sol#L4-L8


falconhoof Analysis

# LOW

## L-01 typo in Pools::removeLiquidity()

Link: https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/Pools.sol#L170-L200

There is a typo in `removeLiquidity()` which allows the `reserve1` token (tokenB) to drop below the `DUST` value which can have negative consequences for the stability of the protocol and spot prices in Pools.
The highlighted require statement checks whether reserve0 is below DUST twice without checking reserve 1 when it should check both.

```
	function removeLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToRemove, uint256 minReclaimedA, uint256 minReclaimedB, uint256 totalLiquidity ) external nonReentrant returns (uint256 reclaimedA, uint256 reclaimedB)
		{
        // SOME CODE

>>>     require((reserves.reserve0 >= PoolUtils.DUST) && (reserves.reserve0 >= PoolUtils.DUST), "Insufficient reserves after liquidity removal");

        // SOME CODE
		}
```

### POC
Add test to Pools.t.sol and run:
```
	function test_Reserve1BelowDUST() public
    	{
		vm.startPrank(address(collateralAndLiquidity));
		IERC20 tokenA = new TestERC20("newToken", 18);
        IERC20 tokenB = new TestERC20("newToken", 18);
		vm.stopPrank();

		vm.prank(address(dao));
		poolsConfig.whitelistPool( pools,   tokenA, tokenB);

		uint256 transferAmountA = 100_000 ether;
		uint256 transferAmountB = 100 ether;

		vm.startPrank(address(collateralAndLiquidity));
    	tokenA.transfer(alice, transferAmountA);
    	tokenB.transfer(alice, transferAmountB);
		vm.stopPrank();

		// assert funds transferred correctly
		assertEq(transferAmountA, tokenA.balanceOf(alice));
		assertEq(transferAmountB, tokenB.balanceOf(alice));

		vm.startPrank(alice); // depositing tokens are
		tokenA.approve(address(collateralAndLiquidity),type(uint256).max);
		tokenB.approve(address(collateralAndLiquidity),type(uint256).max);
		vm.stopPrank();

		uint256 aliceDeposit_TokenA = 100_000 ether;
		uint256 aliceDeposit_TokenB = 10 ether;

		// ali

	function test_Reserve1BelowDUST() public
    	{
		vm.startPrank(address(collateralAndLiquidity));
		IERC20 tokenA = new TestERC20("newToken", 18);
        IERC20 tokenB = new TestERC20("newToken", 18);
		vm.stopPrank();

		vm.prank(address(dao));
		poolsConfig.whitelistPool( pools,   tokenA, tokenB);

		uint256 transferAmountA = 100_000 ether;
		uint256 transferAmountB = 100 ether;

		vm.startPrank(address(collateralAndLiquidity));
    	tokenA.transfer(alice, transferAmountA);
    	tokenB.transfer(alice, transferAmountB);
		vm.stopPrank();

		// assert funds transferred correctly
		assertEq(transferAmountA, tokenA.balanceOf(alice));
		assertEq(transferAmountB, tokenB.balanceOf(alice));

		vm.startPrank(alice); // depositing tokens are
		tokenA.approve(address(collateralAndLiquidity),type(uint256).max);
		tokenB.approve(address(collateralAndLiquidity),type(uint256).max);
		vm.stopPrank();

		uint256 aliceDeposit_TokenA = 100_000 ether;
		uint256 aliceDeposit_TokenB = 10 ether;

		// alice is the initial depositor
        vm.prank(alice);
		collateralAndLiquidity.depositLiquidityAndIncreaseShare( tokenA, tokenB, aliceDeposit_TokenA, aliceDeposit_TokenB, 0, block.timestamp, false );
		uint256 aliceLiquidity = collateralAndLiquidity.userShareForPool(alice, PoolUtils._poolID(tokenA, tokenB));

		vm.warp( block.timestamp + 1 hours);

		vm.prank(alice);
		collateralAndLiquidity.withdrawLiquidityAndClaim( tokenA, tokenB, aliceLiquidity - 100, 0, 0, block.timestamp );
		aliceLiquidity = collateralAndLiquidity.userShareForPool(alice, PoolUtils._poolID(tokenA, tokenB));
		(uint256 poolA, uint256 poolB) = pools.getPoolReserves(tokenA, tokenB);
		assert(poolA == 100);
		assert(poolB < 100);
        }

// Code block 1 (unknown):
function removeLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToRemove, uint256 minReclaimedA, uint256 minReclaimedB, uint256 totalLiquidity ) external nonReentrant returns (uint256 reclaimedA, uint256 reclaimedB)
		{
        // SOME CODE

>>>     require((reserves.reserve0 >= PoolUtils.DUST) && (reserves.reserve0 >= PoolUtils.DUST), "Insufficient reserves after liquidity removal");

        // SOME CODE
		}


Awesome Q

# 1. Improve the Readability by following modularity principles

To make your Solidity code more modern and readable, it is recommended to update your import statements to only include the specific contracts or objects that you need.

This practice, known as modular programming, helps to keep the code cleaner and more organized.

To avoid unnecessarily polluting the source code with unused contracts or objects, it is recommended to use specific imports with curly braces. An example of this syntax is shown below:

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

By using specific imports in this way, you can ensure that your code follows the principles of modularity and is as clear and maintainable as possible.

Affected lines of code:

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

# 2. Use the `delete` operator to clear variables, instead of assigning a value of zero.

To clear variables, use the `delete` operator rather than assigning to zero. This conveys the intention more clearly and is more idiomatic.

For example on [line 52](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L52) you can refactor it as such:

```solidity
Line 52:    delete modules[module];
```

Affected line of code:

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

# 3. Open TODOs

Open TODOs can point to architecture or programming issues that still need to be resolved. Consider resolving them before deploying.

Affected lines of code:

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

# 4. inadequate NatSpec

Contracts can use the Ethereum Natural Language Specificat

// Code block 1 (solidity):
import {contract1, contract2} from "filename.sol";

// Code block 2 (solidity):
Line 52:    delete modules[module];

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

Line 52:    delete modules[module];


Wander Q

## 1. Reduce repetition of code
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L170-L173
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L190-L193
The mentioned code fragments are repeated, instead put it in a method. (Don't repeat the code, Write Code Once)

###### Recommended Solution
1 . Create a new function:
```
function recalculateTotalWeight() private {
        uint256 localTotalWeight = 0;
        for (uint256 i; i < derivativeCount; ++i)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
}
```
2 . Call the function wherever you need, for example:
```
function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = IDerivative(_contractAddress);
        weights[derivativeCount] = _weight;
        derivativeCount++;

        recalculateTotalWeight(); // <<--------- Call the function ---------------

        emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
    }
```

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

        recalculateTotalWeight(); // <<--------- Call the function -------------

        emit WeightChange(_derivativeIndex, _weight);
    }
```

## 2. There is no need to recalculate the value of `totalWeight` in `addDerivative`
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L190-L193
The mentioned code lines can be replaced with a single line of code.
`addDerivative` function can be changed as follows:
```
function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = IDerivative(_contractAddress);

// Code block 1 (unknown):
function recalculateTotalWeight() private {
        uint256 localTotalWeight = 0;
        for (uint256 i; i < derivativeCount; ++i)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
}

// Code block 2 (unknown):
function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = IDerivative(_contractAddress);
        weights[derivativeCount] = _weight;
        derivativeCount++;

        recalculateTotalWeight(); // <<--------- Call the function ---------------

        emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
    }

// Code block 3 (unknown):
function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;

        recalculateTotalWeight(); // <<--------- Call the function -------------

        emit WeightChange(_derivativeIndex, _weight);
    }


lsaudit Q

# [1] Percentage of rewards sent to the initial team is hardcoded in the contract, thus it's not possible to change it in the future

[File: DAO.sol](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/DAO.sol#L340)
```
		// Send 10% of the rewards to the initial team
		uint256 amountToSendToTeam = claimedSALT / 10;
```

The percentage of rewards which will be sent to the initial team is set to 10% and hard-coded into the contract. It's not possible to change it in the future. Consider implementing a way of changing this value in the future and do not hard-code it in the protocol.

# [2] Mixing tabs and white-space in code-formatting

Multiple of contracts does not obey the best coding standard in the context of tabulations and white-spaces. Tabs and white-spaces are mixed.
This issue occurs basically in every contract. For the QA report readability, we've marked down only two examples of this issue. Nonetheless, this issue affects every contract in scope.

[File: InitialDistribution](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/InitialDistribution.sol#L52-L53)
```
    	require( msg.sender == address(bootstrapBallot), "InitialDistribution.distributionApproved can only be called from the BootstrapBallot contract" );  // @audit: 4 whitespaces + tab
		require( salt.balanceOf(address(this)) == 100 * MILLION_ETHER, "SALT has already been sent from the contract" ); // // @audit: 2 tabs
```

[File: USDS.sol](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/USDS.sol#L22-L25)
```
	constructor()
	ERC20( "USDS", "USDS" )
		{ // @audit: 2 tabs
        }  // @audit: 8 whitespaces

```

[File: CollateralAndLiquidity.sol](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/CollateralAndLiquidity.sol#L30-L36)
```
	using SafeERC20 for IERC20;  // @audit: 1 tab
	using SafeERC20 for I

// Code block 1 (unknown):
// Send 10% of the rewards to the initial team
		uint256 amountToSendToTeam = claimedSALT / 10;

// Code block 2 (unknown):
require( msg.sender == address(bootstrapBallot), "InitialDistribution.distributionApproved can only be called from the BootstrapBallot contract" );  // @audit: 4 whitespaces + tab
		require( salt.balanceOf(address(this)) == 100 * MILLION_ETHER, "SALT has already been sent from the contract" ); // // @audit: 2 tabs

// Code block 3 (unknown):
constructor()
	ERC20( "USDS", "USDS" )
		{ // @audit: 2 tabs
        }  // @audit: 8 whitespaces

DAO.sol#L340, InitialDistribution.sol#L52-L53, USDS.sol#L22-L25, CollateralAndLiquidity.sol#L30-L36

USDS.sol, CollateralAndLiquidity.sol, InitialDistribution.sol, DAO.sol


Topmark Q

###  Report 1:
#### Missing Validation check for PoolIds in PoolStats contract.
When a pool Id is invalid it returns INVALID_POOL_ID which represents type(uint64).max, however this return value was not checked at L88-L91 in the updateArbitrageIndicies() function. This would allow invalid pool during Arbitrage and could escalate to serious problems, The return value should be checked as adjusted below.
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolStats.sol#L89-L91
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolStats.sol#L71
```solidity
function _poolIndex( IERC20 tokenA, IERC20 tokenB, bytes32[] memory poolIDs ) internal pure returns (uint64 index)
	{
	bytes32 poolID = PoolUtils._poolID( tokenA, tokenB );

	for( uint256 i = 0; i < poolIDs.length; i++ )
		{
		if (poolID == poolIDs[i])
			return uint64(i);
		}

>>>	return INVALID_POOL_ID;
	}
```
```solidity
function updateArbitrageIndicies() public
	{
	bytes32[] memory poolIDs = poolsConfig.whitelistedPools();

	for( uint256 i = 0; i < poolIDs.length; i++ )
	{
	bytes32 poolID = poolIDs[i];
	(IERC20 arbToken2, IERC20 arbToken3) = poolsConfig.underlyingTokenPair(poolID);

	// The middle two tokens can never be WETH in a valid arbitrage path as the path is WETH->arbToken2->arbToken3->WETH.
	if ( (arbToken2 != _weth) && (arbToken3 != _weth) )
				{
>>>	uint64 poolIndex1 = _poolIndex( _weth, arbToken2, poolIDs );
>>>	uint64 poolIndex2 = _poolIndex( arbToken2, arbToken3, poolIDs );
>>>	uint64 poolIndex3 = _poolIndex( arbToken3, _weth, poolIDs );
+++     require ( poolIndex1 != INVALID_POOL_ID && poolIndex2 != INVALID_POOL_ID && poolIndex3 != INVALID_POOL_ID , "INVALID_POOL_ID" );
				...
		}
```
### Report 2:
#### Unused Variable
Possible Incomplete implementation in the performUpkeep(...) function of the RewardsEmitter.sol contracr. As noted in the code provided below, uint256 sum was declared and was continuously assigned a value of amountToAddForPool at every loop cycle, but `su

// Code block 1 (solidity):
function _poolIndex( IERC20 tokenA, IERC20 tokenB, bytes32[] memory poolIDs ) internal pure returns (uint64 index)
	{
	bytes32 poolID = PoolUtils._poolID( tokenA, tokenB );

	for( uint256 i = 0; i < poolIDs.length; i++ )
		{
		if (poolID == poolIDs[i])
			return uint64(i);
		}

>>>	return INVALID_POOL_ID;
	}

// Code block 2 (solidity):
function updateArbitrageIndicies() public
	{
	bytes32[] memory poolIDs = poolsConfig.whitelistedPools();

	for( uint256 i = 0; i < poolIDs.length; i++ )
	{
	bytes32 poolID = poolIDs[i];
	(IERC20 arbToken2, IERC20 arbToken3) = poolsConfig.underlyingTokenPair(poolID);

	// The middle two tokens can never be WETH in a valid arbitrage path as the path is WETH->arbToken2->arbToken3->WETH.
	if ( (arbToken2 != _weth) && (arbToken3 != _weth) )
				{
>>>	uint64 poolIndex1 = _poolIndex( _weth, arbToken2, poolIDs );
>>>	uint64 poolIndex2 = _poolIndex( arbToken2, arbToken3, poolIDs );
>>>	uint64 poolIndex3 = _poolIndex( arbToken3, _weth, poolIDs );
+++     require ( poolIndex1 != INVALID_POOL_ID && poolIndex2 != INVALID_POOL_ID && poolIndex3 != INVALID_POOL_ID , "INVALID_POOL_ID" );
				...
		}

function _poolIndex( IERC20 tokenA, IERC20 tokenB, bytes32[] memory poolIDs ) internal pure returns (uint64 index)
	{
	bytes32 poolID = PoolUtils._poolID( tokenA, tokenB );

	for( uint256 i = 0; i < poolIDs.length; i++ )
		{
		if (poolID == poolIDs[i])
			return uint64(i);
		}

>>>	return INVALID_POOL_ID;
	}

function updateArbitrageIndicies() public
	{
	bytes32[] memory poolIDs = poolsConfig.whitelistedPools();

	for( uint256 i = 0; i < poolIDs.length; i++ )
	{
	bytes32 poolID = poolIDs[i];
	(IERC20 arbToken2, IERC20 arbToken3) = poolsConfig.underlyingTokenPair(poolID);

	// The middle two tokens can never be WETH in a valid arbitrage path as the path is WETH->arbToken2->arbToken3->WETH.
	if ( (arbToken2 != _weth) && (arbToken3 != _weth) )
				{
>>>	uint64 poolIndex1 = _poolIndex( _weth, arbToken2, poolIDs );
>>>	uint64 poolIndex2 = _poolIndex( arbToken2, arbToken3, poolIDs );
>>>	uint64 poolIndex3 = _poolIndex( arbToken3, _weth, poolIDs );
+++     require ( poolIndex1 != INVALID_POOL_ID && poolIndex2 != INVALID_POOL_ID && poolIndex3 != INVALID_POOL_ID , "INVALID_POOL_ID" );
				...
		}


koo G

From the implementation of EUSD.transferFrom.
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/EUSD.sol#L226
```solidity
    function transferFrom(address from, address to, uint256 amount) public returns (bool) {
        address spender = _msgSender();
        if (!configurator.mintVault(spender)) {
            _spendAllowance(from, spender, amount);
        }
        _transfer(from, to, amount);
        return true;
    }
```
We can see it will always return true or revert the transaction. Thus We don't need to check the return value of EUSD.transferFrom.

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/EUSDMiningIncentives.sol#L215

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraStETHVault.sol#L70

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraStETHVault.sol#L85

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/PeUSDMainnetStableVision.sol#L82

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/PeUSDMainnetStableVision.sol#L133

// Code block 1 (solidity):
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
        address spender = _msgSender();
        if (!configurator.mintVault(spender)) {
            _spendAllowance(from, spender, amount);
        }
        _transfer(from, to, amount);
        return true;
    }

EUSD.sol#L226, EUSDMiningIncentives.sol#L215, LybraStETHVault.sol#L70, LybraStETHVault.sol#L85, PeUSDMainnetStableVision.sol#L82, PeUSDMainnetStableVision.sol#L133

EUSDMiningIncentives.sol, LybraStETHVault.sol, EUSD.sol, PeUSDMainnetStableVision.sol


Diana G

## G-01 Increments can be unchecked

In Solidity 0.8+, there’s a default overflow check on unsigned integers. It’s possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline

Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of under- or overflow leading to widespread use of libraries that introduce additional checks.

Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, thus making the use of these libraries unnecessary.

To obtain the previous behaviour, an unchecked block can be used. It saves **30-40 gas** per loop.

_There are 9 instances of this issue_

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

```
File: contracts/cash/CashManager.sol

750: for (uint256 i = 0; i < size; ++i) {
786: for (uint256 i = 0; i < size; ++i) {
933: for (uint256 i = 0; i < size; ++i) {
961: for (uint256 i = 0; i < exCallData.length; ++i) {
```

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

```
File: contracts/cash/factory/CashFactory.sol

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

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

```
File: contracts/cash/factory/CashKYCSenderFactory.sol

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

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

```
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

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

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

```
File: contracts/cash/kyc/KYCRegistry.sol

163: for (uint256 i = 0; i < length; i++) {
180: for (uint256 i = 0; i < length; i++) {
```

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

## G-02 x += y costs more gas than x = x + y for state var

// Code block 1 (unknown):
File: contracts/cash/CashManager.sol

750: for (uint256 i = 0; i < size; ++i) {
786: for (uint256 i = 0; i < size; ++i) {
933: for (uint256 i = 0; i < size; ++i) {
961: for (uint256 i = 0; i < exCallData.length; ++i) {

// Code block 2 (unknown):
File: contracts/cash/factory/CashFactory.sol

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

// Code block 3 (unknown):
File: contracts/cash/factory/CashKYCSenderFactory.sol

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

// Code block 4 (unknown):
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

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

// Code block 5 (unknown):
File: contracts/cash/kyc/KYCRegistry.sol

163: for (uint256 i = 0; i < length; i++) {
180: for (uint256 i = 0; i < length; i++) {

CashManager.sol, CashFactory.sol, CashKYCSenderFactory.sol, CashKYCSenderReceiverFactory.sol, KYCRegistry.sol

CashManager.sol, CashKYCSenderFactory.sol, CashFactory.sol, KYCRegistry.sol, CashKYCSenderReceiverFactory.sol


Sathish9098 Q

## LOW FINDINGS

|  Low Count | Issues | Instances |
| -------- | --------| -------- |
| [L-1]  | Lack of address(0) check when assigning address to state variables  | 4  |
| [L-2]  | A single point of failure | 8  |
| [L-3]  | Front running attacks by the onlyOwner | 8  |
| [L-4]  | Prevent division by 0 | 5  |
| [L-5]  | Sanity/Threshold/Limit Checks | 10  |
| [L-6]  | Consider using OpenZeppelin’s SafeCast library to prevent unexpected errors  | 3  |
| [L-7]  | Function may run out of gas | 7  |
| [L-8]  | Gas griefing/theft is possible on unsafe external call | 1  |
| [L-9]  | Loss of precision due to rounding | 11  |
| [L-10]  | Use latest @openzeppelin/merkle-tree version  | 1  |
| [L-11]  | abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256() | 7  |

# NON CRITICAL FINDINGS

| NC Count | Issues | Instances |
| -------- | --------| -------- |
| [NC-1]   | Add a timelock to critical functions | 8  |
| [NC-2]   | No same value control  | 8  |
| [NC-3]   | Critical changes should use two-step procedure | 8  |
| [NC-4]   | Events that mark critical parameter changes should contain both the old and the new value | 8 |
| [NC-5]   | NATSPEC COMMENTS SHOULD BE INCREASED IN CONTRACTS | -  |
| [NC-6]   | NOT USING THE NAMED RETURN VARIABLES ANYWHERE IN THE FUNCTION IS CONFUSING | 11 |
| [NC-7]   | Mark visibility of initialize(…) functions as external | 1  |
| [NC-8]   | Contract layout and order of functions | 8  |
| [NC-9]   | Pragma float | -  |
| [NC-10]   | immutable should be uppercase  | 3  |
| [NC-11]   | Public functions not called by contract can declare as external  | 11  |
| [NC-12]   | Include return parameters in NatSpec comments | 6  |
| [NC-13]   | Use of bytes.concat() instead of abi.encodePacked() | 7  |
| [NC-14]   | For functions, follow Solidity standard naming conventions (internal function style rule) | 1 |
| [NC-15]   | Typos | 1  |
| [NC-16]   | NatSpec is incomplete | 3  |
| [N

FILE: 2023-04-caviar/src/EthRouter.sol

   constructor(address _royaltyRegistry) {
        royaltyRegistry = _royaltyRegistry;
    }

FILE: 2023-04-caviar/src/PrivatePool.sol

143: constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:        factory = payable(_factory);
145:        royaltyRegistry = _royaltyRegistry;
146:        stolenNftOracle = _stolenNftOracle;
147:    }


Pechenite G

### [G‑01] Use calldata instead of memory for arrays
**Summary**
You can indeed use `calldata` instead of `memory` for the `_pools` array argument, because the function is marked as `external`.
Using `calldata` instead of `memory` can reduce the gas cost of this function, particularly for larger arrays.

**There is 1 instance of this issue:**
```solidity
File: contracts/lybra/miner/EUSDMiningIncentives.sol

    function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }

```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L93-L98

**Potential Gas Savings**
When array is passed as an argument of a function, the EVM charges gas for each element of the array.
Here's the approximate cost:
- 200 gas per word for memory
- 3 gas per word for calldata

### [G‑02] Use internal function inside in the functions instead of modifiers to minimize the deployment cost.
**Summary**
When you use the modifier the whole code of the modifier is repeated every time. To minimize the deployment cost of your contract instead you can create an internal function with the code of the modifier and call it from the other functions who need it.

**There are 8 instances of this issue:**
```solidity
File: contracts/lybra/configuration/LybraConfigurator.sol

   modifier onlyRole(bytes32 role) {
        GovernanceTimelock.checkOnlyRole(role, msg.sender);
        _;
    }
   // https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L85-L88
```

```solidity
File: contracts/lybra/configuration/LybraConfigurator.sol

    modifier checkRole(bytes32 role) {
        GovernanceTimelock.checkRole(role, msg.sender);
        _;
    }
    // https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L

// Code block 1 (solidity):
File: contracts/lybra/miner/EUSDMiningIncentives.sol

    function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }

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

   modifier onlyRole(bytes32 role) {
        GovernanceTimelock.checkOnlyRole(role, msg.sender);
        _;
    }
   // https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L85-L88

File: contracts/lybra/miner/EUSDMiningIncentives.sol

    function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }

File: contracts/lybra/configuration/LybraConfigurator.sol

   modifier onlyRole(bytes32 role) {
        GovernanceTimelock.checkOnlyRole(role, msg.sender);
        _;
    }
   // https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L85-L88


Aamir Q

# Summary

| Severity                 | Issue                                                                                | Instance |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| [[L-0](#low-0)]          | `iconsTypes` length should also be checked in `FighterFarm::redeemMintPass(...)`                                                              | 1        |
| [[L-1](#low-1)]          | `customAttributes` are not checked for valid ranges when sent using `FighterFarm::mintFromMergingPool(...)`                                   | 1        |
| [[L-2](#low-2)]          | No check for new Game Item is added in `GameItems::createGameItem(...)`                                                                       | 1        |
| [[L-3](#low-3)]          | Add checks for duplicate addresses passed as winners in `MergingPool::pickWinner()`                                                           | 1        |
| [[L-4](#low-4)]          | Add Array parameter length checks in `MergingPool::claimRewards(...)`                                                                         | 1        |
| [[L-5](#low-5)]          | `Neuron::mint(...)` contract can't mint tokens to the absolute value of `MAX_SUPPLY`                                                          | 1        |
| [[L-6](#low-6)]          | `globalStakedAmount` is not updated after the end of each round showing incorrect info.                                                       | 1        |
| [[L-7](#low-7)]          | Holder of the tokenId will not be to claim rewards in `RankedBattle::claimNRN(...)` if the NFT is transferred to another address              | 1        |
| [[L-8](#low-8)]          | `RankedBattle::_getStakingFactor()` works in a biased way for small stakers.

File: 2024-02-ai-arena/src/FighterFarm.sol

243        require(
244            mintpassIdsToBurn.length == mintPassDnas.length &&
245            mintPassDnas.length == fighterTypes.length &&
246            fighterTypes.length == modelHashes.length &&
248            modelHashes.length == modelTypes.length
249        );

File: 2024-02-ai-arena/src/FighterFarm.sol

    function _createFighterBase(
        uint256 dna,
        uint8 fighterType
    )
        private
        view
        returns (uint256, uint256, uint256)
    {
@>        uint256 element = dna % numElements[generation[fighterType]];
@>        uint256 weight = dna % 31 + 65;
@>        uint256 newDna = fighterType == 0 ? dna : uint256(fighterType);
        return (element, weight, newDna);
    }


Dinesh11G G

### 1st BUG
Don't Initialize Variables with Default Value

#### Impact
Issue Information:
Uninitialized variables are assigned with the types default value.

Explicitly initializing a variable with it's default value costs unnecessary gas.

Example
🤦 Bad:

uint256 x = 0;
bool y = false;
🚀 Good:

uint256 x;
bool y;

#### Findings:
```
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::72 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::78 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::101 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::107 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::113 => for (uint256 i = 0; i < numCollaterals; i++) {
```
#### Tools used
Manual VS Code review

### 2nd BUG
Cache Array Length Outside of Loop

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

Example
🤦 Bad:

for (uint256 i = 0; i < array.length; i++) {
    // invariant: array's length is not changed
}
🚀 Good:

uint256 len = array.length
for (uint256 i = 0; i < len; i++) {
    // invariant: array's length is not changed
}

#### Findings:
```
2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::106 => uint256 numCollaterals = collaterals.length;
2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::107 => require(numCollaterals == _erc4626vaults.length, "Vaults array length must match number of collaterals");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::52 => require(_collaterals.length != 0, "At least one collateral required");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::53 => require(_MCRs.length == _collaterals.length, "Array lengths must match");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::54 => require(_CCRs.l

2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::106 => uint256 numCollaterals = collaterals.length;
2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::107 => require(numCollaterals == _erc4626vaults.length, "Vaults array length must match number of collaterals");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::52 => require(_collaterals.length != 0, "At least one collateral required");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::53 => require(_MCRs.length == _collaterals.length, "Array lengths must match");
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::54 => require(_CCRs.length == _collaterals.length, "Array lenghts must match");
2023-02-ethos/Ethos-Core/contracts/Dependencies/Address.sol::152 => if (returndata.length > 0) {
2023-02-ethos/Ethos-Core/contracts/Dependencies/SafeERC20.sol::70 => if (returndata.length > 0) { // Return data is optional
2023-02-ethos/Ethos-Core/contracts/Dependencies/UsingTellor.sol::359 => for (uint256 _i = 0; _i < _b.length; _i++) {
2023-02-ethos/Ethos-Core/contracts/HintHelpers.sol::156 => Submitting numTrials = k * sqrt(length), with k = 15 makes it very, very likely that the ouput address will
2023-02-ethos/Ethos-Core/contracts/HintHelpers.sol::157 => be <= sqrt(length) positions away from the correct insert position.
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::205 => amounts = new uint[](assets.length);
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::227 => uint[] memory amounts = new uint[](collaterals.length);
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::239 => uint numCollaterals = assets.length;
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::105 => uint256 numCollaterals = collaterals.length;
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::107 => require(_priceAggregatorAddresses.length == numCollaterals, "Array lengths must match");
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::108 => require(_tellorQueryIds.length == numCollaterals, "Array lengths

// Code block 1 (unknown):
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::72 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::78 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::101 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::107 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::113 => for (uint256 i = 0; i < numCollaterals; i++) {


nicobevi Q

# [QA] - OpenZeppelin's library used is too old. An upgrade is recommended.

The OZ's contracts library used on `contracts/cash` is the version `v4.4.1`. However, the newest version available is `v4.7.0`. The recommendation is to upgrade the library and install it through forge as an external library instead of copy the files to an internal folder.

1. Install the library through forge command line:
```sh
  forge install Openzeppelin/openzeppelin-contracts --no-commit
  forge install Openzeppelin/openzeppelin-contracts-upgradeable --no-commit
```

2. Remove the files from `/contracts/cash/external/openzeppelin`

3. Add this line to your `remappings.txt` file:
```txt
  @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
  @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
```

4. Change all the references to the installed version. (Fix the breaking changes that could popup with this change)
```diff
- import "contracts/cash/external/openzeppelin/contracts/...";
+ import "@openzeppelin/contracts/...";

- import "contracts/cash/external/openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol";
+ import "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol";
```

# [QA] - Function params shadowing storage variables
If an input param has the same name than a storage variable, this one will be shadowed by that input variable. The recommendation is to change the names so these are unique.

## Where
* https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/Proxy.sol#L25 (`_admin` is shadowing a storage variable)

# [QA] - Different pragma versions across the project
It is recommended to use the same and fixed pragma version across the entire project. Currently we can find different versions on different contracts.

For example:
* `/contracts/lending/tokens/cCash/CTokenCash.sol` is using `^0.8.10;`
* `/contracts/cash/Proxy.so

// Code block 1 (sh):
forge install Openzeppelin/openzeppelin-contracts --no-commit
  forge install Openzeppelin/openzeppelin-contracts-upgradeable --no-commit

// Code block 2 (txt):
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
  @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/

// Code block 3 (diff):
- import "contracts/cash/external/openzeppelin/contracts/...";
+ import "@openzeppelin/contracts/...";

- import "contracts/cash/external/openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol";
+ import "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol";


GT_Blockchain Q

--- PrivatePool.sol ---

A. Fee structure is inconsistent for buys/changes. In `buy()` and `sell()` function protocolFee is based on a percentage of the input/output amount but in `change()` the fee is based on the pool fee not the input amount. It should be based on the input/output amount for consistency

B. Several places use the `ERC20.decimals()` function. `decimals()` is not a required part of the ERC20 standard, and not every token uses it. If the baseToken does not support decimals then the pool will be unusable since baseToken cannot be changed once initialized. Proper procedure is to wrap the decimals call in a "try-catch" block where decimals is default set at 18 if the call fails.
Lines 733 & 744,
```
try ERC20(baseToken).decimals() returns (uint8 decimals) {
     exponent = 36 - decimals;
   } catch {
     // handle the exception here, e.g. set a default value
     exponent = 18;
```
https://eips.ethereum.org/EIPS/eip-20

C. Functions that take in multiple arrays as parameters do not verify that the arrays are of equal length. This can result in unexpected outcomes and transaction failures.

D. `FlashFee` is a flat amount, meaning the cost to flash loan an NFT at floor price is the same as one worth 5x the floor price. `FlashFee` should instead be based on the weigh of the NFT being flashloaned, with more expensive NFTs charging a higher fee.

E. All external facing functions should have `nonReentrant` tagged to prevent reentrancy vulnerabilities.

// Code block 1 (unknown):
try ERC20(baseToken).decimals() returns (uint8 decimals) {
     exponent = 36 - decimals;
   } catch {
     // handle the exception here, e.g. set a default value
     exponent = 18;