Lucyan99 Q

### [GAS-1]  Duplicate execution of the same code

You shouldn't call `addAttributeProbabilities(0, probabilities)` in the constructor on line 45, because you add the attribute probabilities manually on line 49:

*Instances (1)*:

```solidity
File: src/AiArenaHelper.sol.sol

41:    constructor(uint8[][] memory probabilities) {
42:        _ownerAddress = msg.sender;
43:
44:        // Initialize the probabilities for each attribute
45:        addAttributeProbabilities(0, probabilities);
46:
47:        uint256 attributesLength = attributes.length;
48:        for (uint8 i = 0; i < attributesLength; i++) {
49:            attributeProbabilities[0][attributes[i]] = probabilities[i];
50:            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
51:        }
52:    }

```
```solidity

131:    function addAttributeProbabilities(uint256 generation, uint8[][] memory probabilities) public {
132:        require(msg.sender == _ownerAddress);
133:        require(probabilities.length == 6, "Invalid number of attribute arrays");
134:
135:        uint256 attributesLength = attributes.length;
136:        for (uint8 i = 0; i < attributesLength; i++) {
137:            attributeProbabilities[generation][attributes[i]] = probabilities[i];
138:        }
139:    }

```

[Link to code](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol)

### [NC-1]  The owner of the contract can be different from the deployer.

As mention in the natspec, the `ownerAddress` parameter you send to the `constructor` function should be the address of the contract deployer. In order to make sure you don't send a wrong address when you deploy the contract, you can simply use `msg.sender` instead of the `ownerAddress` parameter.

*Instances (5)*:

```solidity
File: src/FighterFarm.sol

101:    /// @param ownerAddress Address of contract deployer.
102:    /// @param delegatedAddress Address of delegated signer for messages.
103:    /// @param treasuryAddress_ Community t

// Code block 1 (solidity):
File: src/AiArenaHelper.sol.sol

41:    constructor(uint8[][] memory probabilities) {
42:        _ownerAddress = msg.sender;
43:
44:        // Initialize the probabilities for each attribute
45:        addAttributeProbabilities(0, probabilities);
46:
47:        uint256 attributesLength = attributes.length;
48:        for (uint8 i = 0; i < attributesLength; i++) {
49:            attributeProbabilities[0][attributes[i]] = probabilities[i];
50:            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
51:        }
52:    }

// Code block 2 (solidity):
131:    function addAttributeProbabilities(uint256 generation, uint8[][] memory probabilities) public {
132:        require(msg.sender == _ownerAddress);
133:        require(probabilities.length == 6, "Invalid number of attribute arrays");
134:
135:        uint256 attributesLength = attributes.length;
136:        for (uint8 i = 0; i < attributesLength; i++) {
137:            attributeProbabilities[generation][attributes[i]] = probabilities[i];
138:        }
139:    }

File: src/AiArenaHelper.sol.sol

41:    constructor(uint8[][] memory probabilities) {
42:        _ownerAddress = msg.sender;
43:
44:        // Initialize the probabilities for each attribute
45:        addAttributeProbabilities(0, probabilities);
46:
47:        uint256 attributesLength = attributes.length;
48:        for (uint8 i = 0; i < attributesLength; i++) {
49:            attributeProbabilities[0][attributes[i]] = probabilities[i];
50:            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
51:        }
52:    }

131:    function addAttributeProbabilities(uint256 generation, uint8[][] memory probabilities) public {
132:        require(msg.sender == _ownerAddress);
133:        require(probabilities.length == 6, "Invalid number of attribute arrays");
134:
135:        uint256 attributesLength = attributes.length;
136:        for (uint8 i = 0; i < attributesLength; i++) {
137:            attributeProbabilities[generation][attributes[i]] = probabilities[i];
138:        }
139:    }


dontonka Q

Some reported issues here are just added as a comment, but most have been applied the suggested corrections already, you can see all the final diffs. Take note that the code compile and all test pass with those too.

```diff
diff --git a/Ethos-Core/contracts/ActivePool.sol b/Ethos-Core/contracts/ActivePool.sol
index 771da52..4e58ef4 100644
--- a/Ethos-Core/contracts/ActivePool.sol
+++ b/Ethos-Core/contracts/ActivePool.sol
@@ -83,6 +83,7 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
         onlyOwner
     {
         require(!addressesSet, "Can call setAddresses only once");
+        require(_treasuryAddress != address(0), "Treasury cannot be 0 address"); //@audit (QA) moved beginning of function so its cleaner.

         checkContract(_collateralConfigAddress);
         checkContract(_borrowerOperationsAddress);
@@ -90,7 +91,6 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
         checkContract(_stabilityPoolAddress);
         checkContract(_defaultPoolAddress);
         checkContract(_collSurplusPoolAddress);
-        require(_treasuryAddress != address(0), "Treasury cannot be 0 address");
         checkContract(_lqtyStakingAddress);

         collateralConfigAddress = _collateralConfigAddress;
@@ -124,19 +124,20 @@ contract ActivePool is Ownable, CheckContract, IActivePool {

     function setYieldingPercentage(address _collateral, uint256 _bps) external onlyOwner {
         _requireValidCollateralAddress(_collateral);
-        require(_bps <= 10_000, "Invalid BPS value");
+        require(_bps <= 10_000, "Invalid BPS value"); //@audit (QA) no lower limit verification. A value of 0 seems to be problematic it would turn off the yield.
         yieldingPercentage[_collateral] = _bps;
         emit YieldingPercentageUpdated(_collateral, _bps);
     }

     function setYieldingPercentageDrift(uint256 _driftBps) external onlyOwner {
-        require(_driftBps <= 500, "Exceeds max allowed value of 500 BPS");
+        require(_drif

```diff
diff --git a/Ethos-Core/contracts/BorrowerOperations.sol b/Ethos-Core/contracts/BorrowerOperations.sol
index 6ee5c75..ecc605b 100644
--- a/Ethos-Core/contracts/BorrowerOperations.sol
+++ b/Ethos-Core/contracts/BorrowerOperations.sol
@@ -194,7 +194,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

         // ICR is based on the composite debt, i.e. the requested LUSD amount + LUSD borrowing fee + LUSD gas comp.
         vars.compositeDebt = _getCompositeDebt(vars.netDebt);
-        assert(vars.compositeDebt > 0);
+        assert(vars.compositeDebt > 0); //@audit (QA) seems redundant, _requireAtLeastMinNetDebt already confirm this indirectly. What would be more useful is probably vars.compositeDebt > vars.netDebt. I would probably just remove this assert tbh to save gas, its dead code in the end.

         vars.ICR = LiquityMath._computeCR(_collAmount, vars.compositeDebt, vars.price, vars.collDecimals);
         vars.NICR = LiquityMath._computeNominalCR(_collAmount, vars.compositeDebt, vars.collDecimals);

```diff
diff --git a/Ethos-Core/contracts/LQTY/CommunityIssuance.sol b/Ethos-Core/contracts/LQTY/CommunityIssuance.sol
index c79adfe..39f7ac8 100644
--- a/Ethos-Core/contracts/LQTY/CommunityIssuance.sol
+++ b/Ethos-Core/contracts/LQTY/CommunityIssuance.sol
@@ -2,17 +2,18 @@

 pragma solidity 0.6.11;

 import "../Dependencies/IERC20.sol";
 import "../Interfaces/ICommunityIssuance.sol";
 import "../Dependencies/BaseMath.sol";
 import "../Dependencies/LiquityMath.sol";
 import "../Dependencies/Ownable.sol";
 import "../Dependencies/CheckContract.sol";
 import "../Dependencies/SafeMath.sol";
+import "../Dependencies/SafeERC20.sol";

 contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMath {
     using SafeMath for uint;
+    using SafeERC20 for IERC20; //@audit (QA) added the safe as other contracts, and updated the corresponding lines

     // --- Data ---

@@ -83,6 +84,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa
     // @dev issues a set amount of Oath to the stability pool
     function issueOath() external override returns (uint issuance) {
         _requireCallerIsStabilityPool();
+
         if (lastIssuanceTimestamp < lastDistributionTime) {
             uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;
             uint256 timePassed = endTimestamp.sub(lastIssuanceTimestamp);
@@ -100,7 +102,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa
     */
     function fund(uint amount) external onlyOwner {
         require(amount != 0, "cannot fund 0");
-        OathToken.transferFrom(msg.sender, address(this), amount);
+        OathToken.safeTransferFrom(msg.sender, address(this), amount);

         // roll any unissued OATH into new distribution
         if (lastIssuanceTimestamp < lastDistributionTime) {
@@ -109,7 +111,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa


kaden G

#### Pack Config into single storage slot

##### Severity: Gas Optimization

##### Context:

- https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/CollateralConfig.sol#L27

##### Description

The current `Config` struct in `CollateralConfig` requires 4 storage slots.

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

The following will pack all the elements into a single storage slot where initializing `collateralConfig`s will only require 1 SSTORE instead of 4, similarly retrieving the config for a collateral will only require 1 SLOAD instead of 4.

#### Redundant negation

##### Severity: Gas Optimization

##### Context:

- https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L128

##### Description

The following line decrements `repayment` by `-roi`.

```
// ReaperBaseStrategyV4.sol:L128
repayment -= uint256(-roi);
```

This double negation is redundant, and instead should be replaced by a simple increment.

```
repayment += uint256(roi);
```

### Static Analysis Findings

#### Mark storage variables as `immutable` if they never change after contract initialization.

State variables can be declared as constant or immutable. In both cases, the variables cannot be modified after the contract has been constructed. For constant variables, the value has to be fixed at compile-time, while for immutable, it can still be assigned at construction time.

The compiler does not reserve a storage slot for these variables, and every occurrence is inlined by the respective value.

Compared to regular state variables, the gas costs of constant and immutable variables are much lower. For a constant variable, the expression assigned to it is copied to all the places where it is accessed and also re-evaluated each time. This allows for local optimizations. Immutable variables are evaluated o

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

// Code block 2 (unknown):
// ReaperBaseStrategyV4.sol:L128
repayment -= uint256(-roi);

// Code block 3 (unknown):
repayment += uint256(roi);


Bason Q

## Low and Non-Critical Issues Summary
| Number |Issues Details |
|:--:|:-------|
|[NC-01]| Use stable pragma statement
|[NC-02]| Add more indexed fields to Events for better traceability
|[L-01]| Missing events for important parameter changes
|[L-02]| Possible to pass address 0 in functions that pass ownership
|[L-03]| Empty receive() function in EthRouter.sol
|[L-04]| Function sellQuote has misleading NatSpec
|[L-05]| flashFee function has two input variables which are not used

## |[NC-01]| Use stable pragma statement

Using a floating pragma statement `^0.8.19` is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode.

## |[NC-02]| Add more indexed fields to events for better traceability

It is recommended events to have more than one indexed parameters if possible to make searching event logs faster.

Factory.sol:
Add tokenIds to be indexed in the Create event definition
BEFORE:
```
event Create(address indexed privatePool, uint256[] tokenIds, uint256 baseTokenAmount);
```
AFTER:
```
event Create(address indexed privatePool, indexed uint256[] tokenIds, uint256 baseTokenAmount);
```

## [L-01] MISSING EVENT FOR IMPORTANT PARAMETER CHANGE

Emitting events allows monitoring activities with off-chain monitoring tools.
I would suggest creating custom events and emitting them on critical parameter changes. I am giving an example for a few files.

```solidity
Factory.sol
Define custom events:
event PrivatePoolMetaDataSet(indexed address privatePoolMetadata)
event PrivatePoolImplementationSet(indexed address privatePoolImplementation)
event ProtocolFeeRateSet();

function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {
        privatePoolMetadata = _privatePoolMetadata;
        emit PrivatePoolMetadataSet(privatePoolMetadata)
    }

function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {
        privatePoolImple

// Code block 1 (unknown):
event Create(address indexed privatePool, uint256[] tokenIds, uint256 baseTokenAmount);

// Code block 2 (unknown):
event Create(address indexed privatePool, indexed uint256[] tokenIds, uint256 baseTokenAmount);


Musaka G

# Table of findings
|          |                                               |
|----------|-----------------------------------------------|
| **G-01** | Make a modifier to check for collateral ratio |

# Findings

**[G-01]** Make a modifier to check for collateral ratio
Collateral ratio is checked in a couple of function is bolt [`LybraPeUSDVaultBase`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol) and [`LybraEUSDVaultBase`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol), to lower the total gas, you can make it as a modifier
**Example:**
```jsx
modifier checkColl(address who, uint coll) {
   uint256 providerCollateralRatio = (depositedAsset[who] * assetPrice * 100) / borrowed[who];
   require(providerCollateralRatio >= coll, "Inseficient collateral ration");
  }
```
You can use it in [`rigidRedemption`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L157-L168) as:
```jsx
 function rigidRedemption(address provider, uint256 peusdAmount) external virtual checkColl(provider,100e18){}
```

// Code block 1 (jsx):
modifier checkColl(address who, uint coll) {
   uint256 providerCollateralRatio = (depositedAsset[who] * assetPrice * 100) / borrowed[who];
   require(providerCollateralRatio >= coll, "Inseficient collateral ration");
  }

// Code block 2 (jsx):
function rigidRedemption(address provider, uint256 peusdAmount) external virtual checkColl(provider,100e18){}


jokopoppo Q

| Number | Issues  | Instances |
| ------ | ------- | --------- |
| [N-01] | Whitelist array can exceed maximumWhitelistedPools state | 1         |

### [N-01] -  Whitelist array can exceed maximumWhitelistedPools state

#### Line References

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/PoolsConfig.sol#L77-L91

#### Description

The `changeMaximumWhitelistedPools()` function can be used to modify the `maximumWhitelistedPools` state, which sets the boundary for the length of the whitelist array.

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/PoolsConfig.sol#L77-L91
```solidity=77
function changeMaximumWhitelistedPools(bool increase) external onlyOwner
    {
    if (increase)
        {
        if (maximumWhitelistedPools < 100)
            maximumWhitelistedPools += 10;
        }
    else
        {
        if (maximumWhitelistedPools > 20)
            maximumWhitelistedPools -= 10;
        }

    emit MaximumWhitelistedPoolsChanged(maximumWhitelistedPools);
    }
```

However, there is no validation check on the whitelist array length when changing the `maximumWhitelistedPools` state, resulting in a potential conflict between the `maximumWhitelistedPools` state and the whitelist array.

#### Recommended Mitigation Steps
Adding a validation check on the whitelist array length before modifying the `maximumWhitelistedPools` state, for example.

```diff=77
function changeMaximumWhitelistedPools(bool increase) external onlyOwner
    {
    if (increase)
        {
        if (maximumWhitelistedPools < 100)
            maximumWhitelistedPools += 10;
        }
    else
        {
        if (maximumWhitelistedPools > 20)
            maximumWhitelistedPools -= 10;
++          require( _whitelist.length() <= maximumWhitelistedPools, "Maximum number of whitelisted pools already reached" );
        }

    emit MaximumWhitelistedPoolsChanged(maximumWhitelistedPools);
    }
``

// Code block 1 (unknown):
However, there is no validation check on the whitelist array length when changing the `maximumWhitelistedPools` state, resulting in a potential conflict between the `maximumWhitelistedPools` state and the whitelist array.

#### Recommended Mitigation Steps
Adding a validation check on the whitelist array length before modifying the `maximumWhitelistedPools` state, for example.


MSaptarshi Q

# [L-01] Unsafe Casting
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Oracle.sol#L342C24-L342C30
There are also other instances where it is casted from uint to int

## Recomendation
Use OZ safeCasting while casting

# [L-02] Natspec comment might not be giving out the exact intended behavior in`AutoCompound::execute()`

The comment->
`Can only be called only from configured operator account, or vault via transform`
```
 if (!operators[msg.sender] && !vaults[msg.sender]) {
            revert Unauthorized();
        }
```
## Recommendation
Edit the comments telling the intended behavior
# [NC -01] Code structure not suggested
Refer to the solidity docs for the code structure

// Code block 1 (unknown):
if (!operators[msg.sender] && !vaults[msg.sender]) {
            revert Unauthorized();
        }


Bloqarl Q

## QA Report

# [L-01] Use Ownable2StepUpgradeable instead of OwnableUpgradeable contract
In four contracts the `transferOwnership` is used in the `initialize()` function in order to use to change Ownership. It is, however, imported from `OwnableUpgradeable`. There is another Openzeppelin Ownable contract (Ownable2StepUpgradeable.sol) that also has `transferOwnership()` function.

There are four instances in contracts `SafEth.sol`, `Reth.sol`, `SfrxEth.sol` and `WstEth.sol`

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L18
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L19
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L13
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L12

# [L-02] Use `_safeTransferOwnership` instead of `_transferOwnership`
Use `_safeTransferOwnership` function instead of `_transferOwnership`. It is more secure due to 2-stage ownership transfer.

There are four instances in contracts `SafEth.sol`, `Reth.sol`, `SfrxEth.sol` and `WstEth.sol`

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L34
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L37
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L43
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L53

# [L-03] Avoid magic numbers
In order to make the code more readable for future changes, reviews or other usages, it is always convenient to not use

address public constant UNISWAP_ROUTER =
        0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
address public constant UNI_V3_FACTORY =
        0x1F98431c8aD98523631AE4a59f267346ea31F984;

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

SafEth.sol#L18, Reth.sol#L19, SfrxEth.sol#L13, WstEth.sol#L12, WstEth.sol#L34, SfrxEth.sol#L37, Reth.sol#L43, SafEth.sol#L53

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


codeslide Q

## Summary

### Low Risk Issues
There are 73 instances over 8 issues.
|      ID       | Description                                                                             | Count |
| :-----------: | :-------------------------------------------------------------------------------------- | ----: |
| [L-01](#l-01) | Setters should have input validation                                                    |     5 |
| [L-02](#l-02) | Missing checks for `address(0x0)` when updating `address` state variables               |     3 |
| [L-03](#l-03) | Some tokens may revert when zero value transfers are made                               |     9 |
| [L-04](#l-04) | External calls in an un-bounded `for`-loop may result in a DOS                          |     4 |
| [L-05](#l-05) | Contracts use infinite approvals with no means to revoke                                |     9 |
| [L-06](#l-06) | Functions calling contracts/addresses with transfer hooks are missing reentrancy guards |     9 |
| [L-07](#l-07) | Large approvals may not work with some `ERC20` tokens                                   |    12 |
| [L-08](#l-08) | Loss of precision                                                                       |    22 |

### Non-Critical Issues
There are 17 instances over 3 issues.
|       ID        | Description                                                                | Count |
| :-------------: | :------------------------------------------------------------------------- | ----: |
| [NC-01](#nc-01) | Missing event for critical parameter change                                |     1 |
| [NC-02](#nc-02) | Consider bounding input array length                                       |     2 |
| [NC-03](#nc-03) | Not using the named return variables anywhere in the function is confusing |    14 |

## Details

### Low Risk Iss

File: src/ExchangeConfig.sol

/// @audit state variables: dao, upkeep, initialDistribution, airdrop, teamVestingWallet, daoVestingWallet
48: 	function setContracts( IDAO _dao, IUpkeep _upkeep, IInitialDistribution _initialDistribution, IAirdrop _airdrop, VestingWallet _teamVestingWallet, VestingWallet _daoVestingWallet ) external onlyOwner
49: 		{

File: src/pools/Pools.sol

/// @audit state variables: dao, collateralAndLiquidity
60: 	function setContracts( IDAO _dao, ICollateralAndLiquidity _collateralAndLiquidity ) external onlyOwner
61: 		{


0x6020c0 Q

[N-01] In `SafEthStorage.sol` line 17. Misspelling in the comment should be `bool public pauseUnstaking; // true if unstaking is paused`

[N-02] In `Reth.sol` lines 197-201. To be consistent with naming convention in `SafEth.sol` change to
```
     uint256 rethBalanceBefore = rocketTokenRETH.balanceOf(address(this));
     rocketDepositPool.deposit{value: msg.value}();
     uint256 rethBalanceAfter = rocketTokenRETH.balanceOf(address(this));
     require(rethBalanceAfter > rethBalanceBefore, "No rETH was minted");
     uint256 rethMinted = rethBalanceAfter - rethBalanceBefore;
```

[L-01] In `SafEth.sol` line 158. Misleading function description. This function does not add new derivative to the index fund, it changes the weight of existing one. Fix: change `@notice` to `Adjust weight of derivative at index`

// Code block 1 (unknown):
uint256 rethBalanceBefore = rocketTokenRETH.balanceOf(address(this));
     rocketDepositPool.deposit{value: msg.value}();
     uint256 rethBalanceAfter = rocketTokenRETH.balanceOf(address(this));
     require(rethBalanceAfter > rethBalanceBefore, "No rETH was minted");
     uint256 rethMinted = rethBalanceAfter - rethBalanceBefore;


Arz Q

## Issue 1 - getExchangeRatio() function does not exist

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

In LybraRETHVault.sol getExchangeRatio() function is used when calculating the asset price.

However the Rocket pool contract(0xae78736Cd615f374D3085123A210448E74Fc6393) doesnt have a function that is called getExchangeRatio(). It only has a function that is called getExchangeRate(). If getExchangeRatio() would get called then the call would revert.

## Recommended Mitigation Steps
Use getExchangeRate()
```solidity
function getAssetPrice() public override returns (uint256) {
     return (_etherPrice() * IRETH(address(collateralAsset)).getExchangeRate()) / 1e18;
}

```

## Issue 2 - A user is allowed to burn 0 EUSD in the EUSD Vault
Contract: LybraEUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L140

The burn() function in LybraEUSDVaultBase.sol doesnt check if the amount that the user is burning is 0.
Above the function is written a requirement: `amount` Must be higher than 0 however this isnt checked in the fuction

## Recommended Mitigation Steps
Check if the amount that the user is burning is 0

```solidity

function burn(address onBehalfOf, uint256 amount) external {
        require(amount > 0, "ZERO_BURN");
        require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
        _repay(msg.sender, onBehalfOf, amount);
    }
```

// Code block 1 (solidity):
function getAssetPrice() public override returns (uint256) {
     return (_etherPrice() * IRETH(address(collateralAsset)).getExchangeRate()) / 1e18;
}

// Code block 2 (solidity):
function burn(address onBehalfOf, uint256 amount) external {
        require(amount > 0, "ZERO_BURN");
        require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
        _repay(msg.sender, onBehalfOf, amount);
    }

function getAssetPrice() public override returns (uint256) {
     return (_etherPrice() * IRETH(address(collateralAsset)).getExchangeRate()) / 1e18;
}

function burn(address onBehalfOf, uint256 amount) external {
        require(amount > 0, "ZERO_BURN");
        require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
        _repay(msg.sender, onBehalfOf, amount);
    }


cyberinn G

## [G-1] Use elementary types or a user-defined type instead of a struct that has only one member

### Impact

Use elementary types or a user-defined type instead of a struct that has only one member.

### Findings

Total:1

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

```solidity
174:    struct Deposit {
175:        uint initialValue;
176:    }
```

## [G-2] Splitting require() statements that use `&&` saves gas

### Impact

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

### Findings

Total:3

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

```solidity
352:    require(
353:        !stabilityPools[_recipient] &&
354:        !troveManagers[_recipient] &&
355:        !borrowerOperations[_recipient],
356:            "LUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps"
357:   );
```

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

```solidity
347:    require(
348:        _recipient != address(0) &&
349:        _recipient != address(this),
350:            "LUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address"
351:    );
```
[Ethos-Core/contracts/BorrowerOperations.sol#L653-L654](https://github.com/code-423n4/2023-02-ethos/blob/main//Ethos-Core/contracts/BorrowerOperations.sol#L653-L654)

```solidity
653:    require(_maxFeePercentage >= BORROWING_FEE_FLOOR &&

// Code block 1 (solidity):
174:    struct Deposit {
175:        uint initialValue;
176:    }

// Code block 2 (solidity):
352:    require(
353:        !stabilityPools[_recipient] &&
354:        !troveManagers[_recipient] &&
355:        !borrowerOperations[_recipient],
356:            "LUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps"
357:   );

// Code block 3 (solidity):
347:    require(
348:        _recipient != address(0) &&
349:        _recipient != address(this),
350:            "LUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address"
351:    );

174:    struct Deposit {
175:        uint initialValue;
176:    }

352:    require(
353:        !stabilityPools[_recipient] &&
354:        !troveManagers[_recipient] &&
355:        !borrowerOperations[_recipient],
356:            "LUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps"
357:   );

347:    require(
348:        _recipient != address(0) &&
349:        _recipient != address(this),
350:            "LUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address"
351:    );

StabilityPool.sol#L174-L176, LUSDToken.sol#L352-L357, LUSDToken.sol#L347-L351, BorrowerOperations.sol#L653-L654


orion Q

NodeDelegator.depositAssetIntoStrategy does not check if strategy address (lrtConfig.assetStrategy(asset)) is not address(0), since user when calling this function will transfer his funds via

```
IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);
```

which transfers from NodeDelegator to strategy on the given token address

```
    function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares) {
        token.transferFrom(msg.sender, address(strategy), amount);
```

Since the function does not check if strategy address (lrtConfig.assetStrategy(asset)) is not address(0), if `lrtConfig.assetStrategy(asset);` returns address(0) then NodeDelegator assets of `token` will be sent to address(0) (if token does not follows OZ pattern) and will be lost

// Code block 1 (unknown):
IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

// Code block 2 (unknown):
function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares) {
        token.transferFrom(msg.sender, address(strategy), amount);


Madalad G

# Gas Report

## `abi.encodePacked` is more gas efficient than `abi.encode`

`abi.encode` pads all elementary types to 32 bytes, whereas `abi.encodePacked` will only use the minimal required memory to encode the data. See [here](https://docs.soliditylang.org/en/v0.8.11/abi-spec.html?highlight=encodepacked#non-standard-packed-mode) for more info.

Instances: 2
- [Ethos-Core/contracts/LUSDToken.sol#L284](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L284)
- [Ethos-Core/contracts/LUSDToken.sol#L305](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L305)

<br>

## Functions guaranteed to revert when called by normal users can be marked `payable`

If a function modifier such as `onlyOwner` is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.

The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost (2400 per instance).

Instances: 14
- [Ethos-Core/contracts/CollateralConfig.sol#L47](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L47)
- [Ethos-Core/contracts/CollateralConfig.sol#L86](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L86)
- [Ethos-Core/contracts/BorrowerOperations.sol#L110](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L110)
- [Ethos-Core/contracts/ActivePool.sol#L71](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L71)
- [Ethos-Core/contracts/ActivePool.sol#L125](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Cor

assembly {
 if iszero(_addr) {
  mstore(0x00, "zero address")
  revert(0x00, 0x20)
 }
}

function solidityHash(uint256 a, uint256 b) public view {
	//unoptimized
	keccak256(abi.encodePacked(a, b));
}

LUSDToken.sol#L284, LUSDToken.sol#L305, CollateralConfig.sol#L47, CollateralConfig.sol#L86, BorrowerOperations.sol#L110, ActivePool.sol#L71

LUSDToken.sol, BorrowerOperations.sol, CollateralConfig.sol, ActivePool.sol


Proxy G

### Gas Optimizations List

| Number | Optimization Details                                                               | Instances |
| :----: | :--------------------------------------------------------------------------------- | :-------: |
| [G-01] | Mark functions as payable (with discretion)                                        |    28     |
| [G-02] | Use assembly to check for address(0)                                               |    21     |
| [G-03] | Right shift or Left shift instead of dividing or multiplying by powers of two      |     2     |
| [G-04] | Use `calldata` instead of `memory` for function arguments that do not get mutated. |    12     |
| [G-05] | Use assembly to write storage values                                               |    19     |
| [G-06] | Use assembly when getting a contract's balance of ETH.                             |     8     |
| [G-07] | Pack structs                                                                       |     2     |
| [G-08] | Use assembly to hash instead of Solidity                                           |     2     |
| [G-09] | Use assembly for math (add, sub, mul, div)                                         |    24     |

Total 9 issues

## [G-01] Mark functions as payable (with discretion)

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

### Lines

- [PrivatePool.sol:157](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L157)
- [PrivatePool.sol:301](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L301)
- [PrivatePool.sol:514](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L514)
- [PrivatePool.sol:538](htt

function assemblyOwnerNotZero(address _addr) public pure {
    assembly {
        if iszero(_addr) {
            mstore(0x00, "zero address")
            revert(0x00, 0x20)
        }
    }
}

contract ExampleContract {
    address owner = 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84;

    function assemblyUpdateOwner(address newOwner) public {
        assembly {
            sstore(owner.slot, newOwner)
        }
    }
}


rbserver Q

## [01] USERS ARE UNABLE TO CALL `KYCRegistry.addKYCAddressViaSignature` FUNCTION WITH VALID SIGNATURES, WHICH HAVE `v` BEING 0 OR 1, THAT ARE SIGNED BY USING `web3.eth.sign`
The following `KYCRegistry.addKYCAddressViaSignature` function checks if the `v` input is 27 or 28; if not, calling it will revert. However, a valid signature's `v` can be 0 or 1 when it is signed by using `web3.eth.sign`, which can be confirmed by the notes and examples in https://github.com/web3/web3.js/blob/0.20.7/DOCUMENTATION.md#web3ethsign and https://web3js.readthedocs.io/en/v1.8.1/web3-eth.html#sign. Because of the current `v` requirement in the `KYCRegistry.addKYCAddressViaSignature` function, users are unable to use these valid signatures, which have `v` being 0 or 1, that are signed by using `web3.eth.sign`. This limits the protocol's usability and degrades the user experience.

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L79-L112
```solidity
  function addKYCAddressViaSignature(
    uint256 kycRequirementGroup,
    address user,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");
    ...
  }
```

As a mitigation, https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L87 can be updated to the following code.
```solidity
    if (v <= 1) {
        v += 27;
    }
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");
```

## [02] USER WORKFLOW IN DOCUMENTATION DOES NOT MATCH `CashManager.requestMint` FUNCTION CODE
The documentation in https://github.com/code-423n4/2023-01-ondo#cashmanager says that "to mint Cash tokens, a user must send USDC to the contract and call requestMint." However, as shown by the following `CashManager.requestMint` function, USDC are actually transferred from `msg.sender`, who is the user, to the `feeRecipient` and `assetRecipient` addresses directly. If the d

// Code block 1 (solidity):
function addKYCAddressViaSignature(
    uint256 kycRequirementGroup,
    address user,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");
    ...
  }

// Code block 2 (solidity):
if (v <= 1) {
        v += 27;
    }
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");

function addKYCAddressViaSignature(
    uint256 kycRequirementGroup,
    address user,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");
    ...
  }

if (v <= 1) {
        v += 27;
    }
    require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");


lsaudit G

# [G-01] Claiming airdrops in `Airdrop.sol` can be fully rewritten and optimized

`Airdrop.sol` utilizes mapping `claimed` to check if user has already claimed some token:

[File: Airdrop.sol](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/Airdrop.sol#L29)
```
	// Those users who have already claimed
	mapping(address=>bool) public claimed;
```

Now, in function `claimAirdrop()`, we're checking this mapping (and update it):

[File: Airdrop.sol](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/Airdrop.sol#L74)
```
function claimAirdrop() external nonReentrant
    	{
    	require( claimingAllowed, "Claiming is not allowed yet" );
    	require( isAuthorized(msg.sender), "Wallet is not authorized for airdrop" );
    	require( ! claimed[msg.sender], "Wallet already claimed the airdrop" );

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

    	claimed[msg.sender] = true;
    	}
```

This is, however - redundant, since we already have a list of users who can claim airdrop in `_authorizedUsers`.
When claiming is allowed, no other users can be added to `_authorizedUsers` (function `authorizeWallet()` does not allow to add new wallets when claiming is allowed).
This constitutes a very simple and elegant solution. Whenever we call `claimAirdrop()` - we can just remove user from `_authorizedUsers`.

Please notice, that we already check if user is on `_authorizedUsers`: (line 77) `require( isAuthorized(msg.sender), "Wallet is not authorized for airdrop" );`.
If after claiming an airdrop, we would remove it from `_authorizedUsers`, he won't be able to call `claimAirdrop()` again.
Moreover, since `_authorizedUsers` is Open Zeppelin's enumerable set, removing element from that set costs O(1).

To sum up

// Code block 1 (unknown):
// Those users who have already claimed
	mapping(address=>bool) public claimed;

// Code block 2 (unknown):
function claimAirdrop() external nonReentrant
    	{
    	require( claimingAllowed, "Claiming is not allowed yet" );
    	require( isAuthorized(msg.sender), "Wallet is not authorized for airdrop" );
    	require( ! claimed[msg.sender], "Wallet already claimed the airdrop" );

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

    	claimed[msg.sender] = true;
    	}


Sathish9098 G

## GAS OPTIMIZATIONS

Gas usage is determined by EVM opcodes and Remix sample tests, improving the overall user experience and reducing transaction fees for users

##

| Gas Count| Issues| Instances| Gas Saved|
|-------|-----|--------|--------|
| [G-1] | Using bools for storage incurs overhead  | 3 | 60000 |
| [G-2] | USING CALLDATA INSTEAD OF MEMORY FOR READ-ONLY ARGUMENTS IN EXTERNAL FUNCTIONS SAVES GAS | 20 | 2400 |
| [G-3] | Structs can be packed into fewer storage slots  | 1 | 2000 |
| [G-4] | <x> += <y>/<x> -= <y> costs more gas than <x> = <x> + <y>/<x> = <x> - <y> for state variables| 14 |182 |
| [G-5] | It Costs More Gas To Initialize Variables To Zero Than To Let The Default Of Zero Be Applied| 22 |1062|
| [G-6] | Checking Non-Zero Amount Values Before Transferring to Minimize unnecessary Execution Costs|2 | - |
| [G-7] | SUPERFLUOUS EVENT FIELDS  | 2 | - |
| [G-8] | For events use 3 indexed rule to save gas  | 5 | - |
| [G-9] | Use nested if and, avoid multiple check combinations | 6 | 54 |
| [G-10] | Unnecessary look up in if condition  | 10 | - |
| [G-11] | Use assembly to check for address(0) |1| 6 |
| [G-12] | Shorthand way to write if / else statement can reduce the deployment cost  | 2 | - |
| [G-13] | internal functions not called by the contract should be removed to save deployment gas | 2 | - |
| [G-14] | private functions only called once can be inlined to save gas  | 1 | 50 |
| [G-15] | Use solidity version 0.8.19 to gain some gas boost  | - | - |
| [G-16] | Shorten the array rather than copying to a new one  | 4 | - |
| [G-17] | abi.encode() is less efficient than abi.encodepacked()  | 9 | - |
| [G-18] | Duplicated require()/revert()/IF Checks Should Be Refactored To A Modifier Or Function   | 2 | - |
| [G-19] | Public Functions To External | 1 | - |
| [G-20] | Do not calculate constant variables | 7 | - |
| [G-21] | Convert the .call recursive calls to batch operations to save large volume of gas | - | - |
| [G-22] | EnumerableSet.UintSet can

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

FILE: 2023-05-ajna/ajna-core/src/RewardsManager.sol

70: mapping(uint256 => mapping(uint256 => bool)) public override isEpochClaimed;


0xepley Analysis

# 🛠 Revert Lend
**A lending protocol specifically designed for liquidity providers on Uniswap v3.**

## Conceptual overview of the project:

The Revert Lend project is a sophisticated DeFi ecosystem designed to optimize and automate liquidity management on Uniswap V3, provide innovative borrowing and lending solutions, and enable strategic leverage adjustments, all while integrating advanced features like auto-compounding, range adjustments, and flash loan-based liquidations. At its core, the project facilitates more efficient capital utilization and enhanced earning potential for liquidity providers and borrowers alike.

Users begin their interaction with the Revert Lend project by providing liquidity to Uniswap V3 pools, potentially leveraging the platform's automated features like `AutoCompound` to reinvest earned fees and `AutoRange` to dynamically adjust their positions within the most lucrative price ranges, maximizing their return on investment. These automated strategies not only save time but also optimize the position's performance in response to market movements.

Borrowers, on the other hand, can take advantage of the platform's borrowing facilities, which are directly tied to the liquidity positions, allowing for leverage adjustments. This is where the `LeverageTransformer` comes into play, enabling users to either increase their leverage through `leverageUp` operations or decrease it via `leverageDown`, with all actions executed in a manner that aims to preserve capital efficiency and minimize slippage.

Furthermore, the Revert Lend project incorporates a unique mechanism for managing underperforming positions through `FlashloanLiquidator`, utilizing flash loans for immediate liquidation while ensuring that liquidators can execute these operations profitably and efficiently. This not only maintains the health of the lending pool but also provides an additional layer of security and stability for the platform.

The user journey through the Revert Lend

function setValues(
    uint256 baseRatePerYearX96,
    uint256 multiplierPerYearX96,
    uint256 jumpMultiplierPerYearX96,
    uint256 _kinkX96
) public onlyOwner {
    ...
}

mapping(address => bool) public operators;

function setOperator(address _operator, bool _active) public onlyOwner {
    ...
}


fs0c Q

## [QA-01] `msg.value` in loop

The change function in EthRouter is using `msg.value` in loop. Which means user has to send msg.value once and if there are ETHs in the contract they would be used instead.

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

            //...
            PrivatePool(_change.pool).change{value: msg.value}(
                _change.inputTokenIds,
                _change.inputTokenWeights,
                _change.inputProof,
                _change.stolenNftProofs,
                _change.outputTokenIds,
                _change.outputTokenWeights,
                _change.outputProof
            );

            // transfer NFTs to caller
            for (uint256 j = 0; j < changes[i].outputTokenIds.length; j++) {
                ERC721(_change.nft).safeTransferFrom(
                    address(this),
                    msg.sender,
                    _change.outputTokenIds[j]
                );
            }
}
```

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

            //...
            PrivatePool(_change.pool).change{value: msg.value}(
                _change.inputTokenIds,
                _change.inputTokenWeights,
                _change.inputProof,
                _change.stolenNftProofs,
                _change.outputTokenIds,
                _change.outputTokenWeights,
                _change.outputProof
            );

            // transfer NFTs to caller
            for (uint256 j = 0; j < changes[i].outputTokenIds.length; j++) {
                ERC721(_change.nft).safeTransferFrom(
                    address(this),
                    msg.sender,
                    _change.outputTokenIds[j]
                );
            }
}


0x6980 G

# Report

## Gas Optimizations

| |Issue|Instances|Gas Saved|
|-|:-|:-:|-|
| [G-01](#G-01) | Use `assembly` to check for `address(0)` | 8 | 48
| [G-02](#G-02) | `++i`/`i++` should be `unchecked{++i}`/`unchecked{i++}` when it is not possible for them to overflow, as is the case when used in `for`- and `while`-loops | 8 | 480
| [G-03](#G-03) | `array[index] += amount` is cheaper than `array[index] = array[index] + amount` (or related variants) | 2 | -
| [G-04](#G-04) | The result of function calls should be cached rather than re-calling the function | 3 | 63
| [G-05](#G-05) | State variables should be cached in stack variables rather than re-reading them from storage | 3 | -
| [G-06](#G-06) | Help The Optimizer By Saving A Storage Variable's Reference Instead Of Repeatedly Fetching It | 2 | -
| [G-07](#G-07) | Pre-increments and pre-decrements are cheaper than post-increments and post-decrements | 1 | -
| [G-08](#G-08) | Remove or replace unused state variables | 1 | -
| [G-09](#G-09) | `require()` or `revert()` statements that check input arguments should be at the top of the function | 2 | -
| [G-10](#G-10) | Shorten the array rather than copying to a new one | 4 | -
| [G-11](#G-11) | State variables which are not modified within functions should be set as `constant` or `immutable` for values set at deployment | 2 | 40,000
| [G-12](#G-12) | Unnecessary look up in `if` condition | 1 | -
| [G-13](#G-13) | Use hardcoded address instead `address(this)` | 3 | -
| [G-14](#G-14) | Use upcoming OpenZeppelin contracts | 1 | -

## Gas Optimizations

### [G-01] Use `assembly` to check for `address(0)`
*Saves 6 gas per instance*

*Instances (8)*:

see instances

```solidity
File: contracts/usdy/rUSDY.sol

490:     require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");

491:     require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

519:     require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");

520:     req

File: contracts/usdy/rUSDY.sol

490:     require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");

491:     require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

519:     require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");

520:     require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

547:     require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");

579:     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

642:     if (from != address(0)) {

649:     if (to != address(0)) {

File: contracts/bridge/DestinationBridge.sol

134:     for (uint256 i = 0; i < thresholds.length; ++i) {

160:       for (uint256 i = 0; i < t.approvers.length; ++i) {

264:     for (uint256 i = 0; i < amounts.length; ++i) {


jeffy G

## [G-01] use if + revert instead of require/assert to save gas
Consider using custom errors instead of revert strings. Can save gas when the revert condition has been met during runtime.
Here are some examples of where this optimization could be used:
```solidity
File: contracts/bridge/SourceBridge.sol
168:       require(success, "Call Failed");

File: contracts/rwaOracles/RWADynamicOracle.sol
405:     require(y == 0 || (z = x * y) / y == x);

File: contracts/usdy/rUSDYFactory.sol
100:     assert(rUSDYProxyAdmin.owner() == guardian);
134:       require(success, "Call Failed");
155:     require(msg.sender == guardian, "rUSDYFactory: You are not the Guardian");

File: contracts/usdy/rUSDY.sol
307:     require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");
358:     require(
      currentAllowance >= _subtractedValue,
      "DECREASED_ALLOWANCE_BELOW_ZERO"
    );
435:     require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
450:     require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
490:     require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
491:     require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");
519:     require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
520:     require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");
525:     require(
      _sharesAmount <= currentSenderShares,
      "TRANSFER_AMOUNT_EXCEEDS_BALANCE"
    );
547:     require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");
579:     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
584:     require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");
634:       require(!_isBlocked(msg.sender), "rUSDY: 'sender' address blocked");
635:       require(!_isSanctioned(msg.sender), "rUSDY: 'sender' address sanctioned");
636:       require(
        _isAllowed(msg.sender),
        "rUSDY: 'sender' address not on allowlist"
      );
644:       require(!_isBlocked(from), "rUSDY: 'from' add

File: contracts/bridge/SourceBridge.sol
168:       require(success, "Call Failed");

File: contracts/rwaOracles/RWADynamicOracle.sol
405:     require(y == 0 || (z = x * y) / y == x);

File: contracts/usdy/rUSDYFactory.sol
100:     assert(rUSDYProxyAdmin.owner() == guardian);
134:       require(success, "Call Failed");
155:     require(msg.sender == guardian, "rUSDYFactory: You are not the Guardian");

File: contracts/usdy/rUSDY.sol
307:     require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");
358:     require(
      currentAllowance >= _subtractedValue,
      "DECREASED_ALLOWANCE_BELOW_ZERO"
    );
435:     require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
450:     require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
490:     require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
491:     require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");
519:     require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
520:     require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");
525:     require(
      _sharesAmount <= currentSenderShares,
      "TRANSFER_AMOUNT_EXCEEDS_BALANCE"
    );
547:     require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");
579:     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
584:     require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");
634:       require(!_isBlocked(msg.sender), "rUSDY: 'sender' address blocked");
635:       require(!_isSanctioned(msg.sender), "rUSDY: 'sender' address sanctioned");
636:       require(
        _isAllowed(msg.sender),
        "rUSDY: 'sender' address not on allowlist"
      );
644:       require(!_isBlocked(from), "rUSDY: 'from' address blocked");
645:       require(!_isSanctioned(from), "rUSDY: 'from' address sanctioned");
646:       require(_isAllowed(from), "rUSDY: 'from' address not on allowlist");
651:       require(!_isBlocked(to), "rUSDY: 'to' address blocked");
652:       require(!_is

File: contracts/bridge/DestinationBridge.sol
134: for (uint256 i = 0; i < thresholds.length; ++i) {
160: for (uint256 i = 0; i < t.approvers.length; ++i) {
264: for (uint256 i = 0; i < amounts.length; ++i) {

File: contracts/bridge/SourceBridge.sol
79: bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);
164: for (uint256 i = 0; i < exCallData.length; ++i) {

File: contracts/rwaOracles/RWADynamicOracle.sol
77: for (uint256 i = 0; i < length; ++i) {
113: for (uint256 i = 0; i < length; ++i) {
129: for (uint256 i = 0; i < length + 1; ++i) {

File: contracts/usdy/rUSDYFactory.sol
130: for (uint256 i = 0; i < exCallData.length; ++i) {


MiniGlome G

## Gas Optimizations
| |Issue|Instances|
|-|:-|:-:|
| [GAS-01] | Structs can be packed into fewer storage slots | 2 |
| [GAS-02] | Functions guaranteed to revert when called by normal users can be marked `payable` | 11 |
| [GAS-03] | Setting the `constructor` to `payable` | 3 |
| [GAS-04] | Usage of uint/int smaller than 32 bytes | 9 |
| [GAS-05] | `<x> += <y>` Costs More Gas Than `<x> = <x> + <y>` For State Variables | 4 |

### [GAS-01] Structs can be packed into fewer storage slots
Structs can be packed in slot of 32 bytes like state variables to reduce storage usage.
See more: https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html

*Instances (2)*:
```solidity
File: src/EthRouter.sol
48:    struct Buy {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
        bool isPublicPool;
    }

58:    struct Sell {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bool isPublicPool;
        bytes32[][] publicPoolProofs;
    }

```
Can be packed like this:
```solidity
    struct Buy {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
    }

    struct Sell {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bytes32[][] publicPoolProofs;
    }

```

### [GAS-02] Functions guaranteed to revert when called by normal users can be marked `payable`
If a function modifier or require such as onl

// Code block 1 (solidity):
File: src/EthRouter.sol
48:    struct Buy {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
        bool isPublicPool;
    }

58:    struct Sell {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bool isPublicPool;
        bytes32[][] publicPoolProofs;
    }

// Code block 2 (solidity):
struct Buy {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
    }

    struct Sell {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bytes32[][] publicPoolProofs;
    }

File: src/EthRouter.sol
48:    struct Buy {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
        bool isPublicPool;
    }

58:    struct Sell {
        address payable pool;
        address nft;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bool isPublicPool;
        bytes32[][] publicPoolProofs;
    }

struct Buy {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        uint256 baseTokenAmount;
    }

    struct Sell {
        address payable pool;
        address nft;
        bool isPublicPool;
        uint256[] tokenIds;
        uint256[] tokenWeights;
        PrivatePool.MerkleMultiProof proof;
        IStolenNftOracle.Message[] stolenNftProofs;
        bytes32[][] publicPoolProofs;
    }


0xCiphky Q

# NRN total supply can never equal the max

# Relevant GitHub Links:

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L155
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L43

# Vulnerability details

The Neuron token is used for various functions within the platform, including staking, governance, and rewards. The contract sets a maximum supply of NRN tokens during contract creation.

```solidity
/// @notice Maximum supply of NRN tokens.
uint256 public constant MAX_SUPPLY = 10**18 * 10**9;
```

This cap is enforced within the [mint](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L155)  function to prevent the issuance of tokens beyond the predetermined maximum supply. However, the validation logic used to enforce this cap requiring the sum of the current total supply and the amount to be minted to be less than the maximum supply, effectively prohibits the total supply from ever achieving the maximum threshold.

```solidity
function mint(address to, uint256 amount) public virtual {
        require(totalSupply() + amount < MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have minter role to mint");
        _mint(to, amount);
    }
```

## **Impact:**

This discrepancy means the platform cannot fully utilize the token supply as intended, creating a deviation from the protocol documentation.

## **Tools Used:**

- Manual analysis

## **Recommendation:**

Adjust the mint function to allow minting up to the maximum supply by changing the condition to totalSupply() + amount <= MAX_SUPPLY.

```solidity
function mint(address to, uint256 amount) public virtual {
        require(totalSupply() + amount <= MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have mi

// Code block 1 (solidity):
/// @notice Maximum supply of NRN tokens.
uint256 public constant MAX_SUPPLY = 10**18 * 10**9;

// Code block 2 (solidity):
function mint(address to, uint256 amount) public virtual {
        require(totalSupply() + amount < MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have minter role to mint");
        _mint(to, amount);
    }

/// @notice Maximum supply of NRN tokens.
uint256 public constant MAX_SUPPLY = 10**18 * 10**9;

function mint(address to, uint256 amount) public virtual {
        require(totalSupply() + amount < MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have minter role to mint");
        _mint(to, amount);
    }


Inspex G

## Gas Issues

| Number | Issues  | Instances |
| ------ | ------- | --------- |
| [G-01] |  Calculate strike and timetoExpiry only if putOptionsRequired value is true | 1         |

### [G-01] Calculate strike and timetoExpiry only if putOptionsRequired value is true

#### Line References

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1189-L1198

#### Description
The `strike` and `timeToExpiry` are local variables in the `calculateBondCost()` function. These variables are only used when `putOptionsRequired` is `true`. So if the `putOptionsRequired` is `false` this function call would be wasting gas to compute the value of `strike` and `timeToExpiry`.

**RdpxV2Core.sol**

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1156-L1199

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

  if (_rdpxBondId == 0) {
    uint256 bondDiscount = (bondDiscountFactor *
      Math.sqrt(IRdpxReserve(addresses.rdpxReserve).rdpxReserve()) *
      1e2) / (Math.sqrt(1e18)); // 1e8 precision

    _validate(bondDiscount < 100e8, 14);

    rdpxRequired =
      ((RDPX_RATIO_PERCENTAGE - (bondDiscount / 2)) *
        _amount *
        DEFAULT_PRECISION) /
      (DEFAULT_PRECISION * rdpxPrice * 1e2);

    wethRequired =
      ((ETH_RATIO_PERCENTAGE - (bondDiscount / 2)) * _amount) /
      (DEFAULT_PRECISION * 1e2);
  } else {
    // if rdpx decaying bonds are being used there is no discount
    rdpxRequired =
      (RDPX_RATIO_PERCENTAGE * _amount * DEFAULT_PRECISION) /
      (DEFAULT_PRECISION * rdpxPrice * 1e2);

    wethRequired =
      (ETH_RATIO_PERCENTAGE * _amount) /
      (DEFAULT_PRECISION * 1e2);
  }

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

  uint256 timeToE

#### Recommended Mitigation Steps
We suggest computing the `strike` and `timeToExpiry` only if `putOptionsRequired` value is `true`. For example:

**RdpxV2Core.sol**

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1189-L1198


Shubham Q

## Low Risk Issues

| |Issue|Instances|
|-|:-|:-:|
| [L-01](#L-01) | Update codes to avoid Compile Errors | 8 |
| [L-02](#L-02) | In the constructor, there is no return of incorrect address identification | 8 |

*Total 2 issues.*

## Non-Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | Lines too long | 4 |
| [NC-2](#NC-2) | Events are missing `indexed` field | 13 |
| [NC-3](#NC-3) | Missing NatSpec | 1 |
| [NC-4](#NC-4) | Spell check | 1 |
| [NC-5](#NC-5) | Floating pragma | 5 |
| [NC-6](#NC-6) | Assembly Codes Specific – Should Have Comments | 1 |
| [NC-7](#NC-7) | Pragma version^0.8.19 version too recent to be trusted | 5 |

*Total 7 issues.*

## [L-01] Update codes to avoid Compile Errors

*There are 8 instances of this issue.*

```solidity
warning[2519]: Warning: This declaration shadows an existing declaration.
  --> test/EthRouter/Change.t.sol:36:9:
   |
36 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2519]: Warning: This declaration shadows an existing declaration.
  --> test/EthRouter/Change.t.sol:70:9:
   |
70 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2519]: Warning: This declaration shadows an existing declaration.
   --> test/EthRouter/Change.t.sol:109:9:
    |
109 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2519]: Warning: This declaration shadows an existing declaration.
  --> test/EthRouter/Change.t.sol:36:9:
   |
36 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2519]: Warning: This declaration shadows an existing declaration.
  --> test/EthRouter/Change.t.sol:70:9:
   |
70 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2519]: Warning: This declaration shadows an existing declaration.
   --> test/EthRouter/Change.t.sol:109:9:
    |
109 |         EthRouter.Change[] memory changes = new EthRouter.Change[](1);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The shadowed declaration is here:
 --> test/EthRouter/Change.t.sol:9:5:
  |
9 |     EthRouter.Change[] public changes;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning[2072]: Warning: Unused local variable.
  --> test/Factory/Nft.t.sol:39:9:
   |
39 |         string memory tokenURI = factory.tokenURI(tokenId);
   |         ^^^^^^^^^^^^^^^^^^^^^^

warning[2072]: Warning: Unused local variable.
   --> test/PrivatePool/Quotes.t.sol:115:37:
    |
115 |         (uint256 returnedFeeAmount, uint256 protocolFeeAmount) = privatePool.changeFeeQuote(inputAmount);
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^

File: main/src/EthRouter.sol

L:90      constructor(address _royaltyRegistry) {
L:91         royaltyRegistry = _royaltyRegistry;
L:92      }


0xnev Q

### Issues Template
| Letter | Name | Description |
|:--:|:-------:|:-------:|
| L  | Low risk | Potential risk |
| NC |  Non-critical | Non risky findings |
| R  | Refactor | Code changes |
| O | Ordinary | Commonly found issues |

| Total Found Issues | 19 |
|:--:|:--:|

### Non-Critical Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [N-01] | NatSpec comments should be increased in contracts | All |
| [N-02] | Add `return` parameters in NatSpec comments | All |
| [N-03] | Unnecessary return statements in functions that do not return anything | 4 |
| [N-04] | Unnecessary return statements in functions that have already define named return variable(s) | 4 |
| [N-05] | Solidity compiler optimizations can be problematic| All |
| [N-06] | For mordern and more readable code, update import usages | 109 |
| [N-07] | Omission of important parameters in events emitted | 51 |
| [N-08] | Add timelock to critical functions | 5 |
| [N-09] | Missing checks in `set` and `update` functions | 5 |
| [N-10] | Interchangeable usage of `uint` and `uint256` | 6 |
| [N-11] | Constant values such as a call to `keccak256()`, should use `immutable` rather than constant | 8 |

| Total Non-Critical Issues | 11 |
|:--:|:--:|

### Refactor Issues Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [R-01] | Use `revert` with a descriptive string instead of using `return` | 6 |
| [R-02] | Use `require` instead of `assert` | 12 |
| [R-03] | Use `delete` instead of zero assignment to clear storage variables  | 4 |
| [R-04] | Use Time units directly  | 1 |
| [R-05] | Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18) | 2 |
| [R-06] | Inconsistent declaration of `constant` and `immutable` state variables | 16 |
| [R-07] | Number values can be refactored to use _ | 1 |

| Total Refactor Issues | 7 |
|:--:|:--:|

### Ordinary Issues Template
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [O-01] | Commented out code | 7 |
| [O-02] | Comply

4 results - 1 file

/TroveManager.sol
1076:    function applyPendingRewards(address _borrower, address _collateral) external override {
1077:        _requireCallerIsBorrowerOperationsOrRedemptionHelper();
1078:        return _applyPendingRewards(activePool, defaultPool, _borrower, _collateral);
1079:    }

1111:    function updateTroveRewardSnapshots(address _borrower, address _collateral) external override {
1112:        _requireCallerIsBorrowerOperations();
1113:       return _updateTroveRewardSnapshots(_borrower, _collateral);
1114:    }

1183:    function removeStake(address _borrower, address _collateral) external override {
1184:        _requireCallerIsBorrowerOperationsOrRedemptionHelper();
1185:        return _removeStake(_borrower, _collateral);
1186:    }

1273:    function closeTrove(address _borrower, address _collateral, uint256 _closedStatusNum) external override {
1274:        _requireCallerIsBorrowerOperationsOrRedemptionHelper();
1275:        return _closeTrove(_borrower, _collateral, Status(_closedStatusNum));
1276:    }

4 results - 2 files

/TroveManager.sol
357:    function _liquidateRecoveryMode(
358:        IActivePool _activePool,
359:        IDefaultPool _defaultPool,
360:        address _collateral,
361:        address _borrower,
362:        uint _ICR,
363:        uint _LUSDInStabPool,
364:        uint _TCR,
365:        uint _price,
366:        uint256 _MCR
367:    )
368:        internal
369:        returns (LiquidationValues memory singleLiquidation)

432:            LiquidationValues memory zeroVals;
433:            return zeroVals;
434:        }
435:
436:        return singleLiquidation;
437:    }

1321:    function _addTroveOwnerToArray(address _borrower, address _collateral) internal returns (uint128 index) {
1329:        index = uint128(TroveOwners[_collateral].length.sub(1));
1330:        Troves[_borrower][_collateral].arrayIndex = index;
1331:
1332:        return index;
1333:    }

/StabilityPool.sol
504:    function _computeRewardsPerUnitStaked(
505:        address _collateral,
506:        uint _collToAdd,
507:        uint _debtToOffset,
508:        uint _totalLUSDDeposits
509:    )
510:        internal
511:        returns (uint collGainPerUnitStaked, uint LUSDLossPerUnitStaked)

543:        return (collGainPerUnitStaked, LUSDLossPerUnitStaked);


0xnev G

### Gas Optimizations
| |Issue|Instances|Total Gas Saved|
|:-:|:-|:-:|:-:|
| [G-01] |  `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables (same for -=) | 4 |  452 |
| [G-02] | Use nested `if` statements to avoid multiple check combinations using `&&` | 9 |  54 |
| [G-03] | Structs can be packed in to fewer storage slots | 2 |  40000 |
| [G-04] | Public functions not called by contract can be declared external instead | 6 |  - |
| [G-05] | Functions guaranteed to revert when called by normal users can be marked payable | 10 | 210 |
| [G-06] | Refactor `PrivatePool.availableForFlashLoan()` | 1 |  2236 |
| [G-07] | `PrivatePool.flashFee()` function not required since it simply returns `changeFee` | 1 | 88510 |
| [G-08] | `PrivatePool.flashFeeToken()` function not required  | 1 | 46871 |

| Total Found Issues | 8 |
|:--:|:--:|
| Total Gas Savings | Estimated >= 178333 |

### [G-01] `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables (same for -=)
[PrivatePool.sol#L230-L231](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230-L231)
[PrivatePool.sol#L323-L324](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L323-L324)
```solidity
4 result - 1 file

/PrivatePool.sol
230:        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);

231:        virtualNftReserves -= uint128(weightSum);

323:        virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);

324:        virtualNftReserves += uint128(weightSum);
```
Using the addition operator instead of plus-equals saves 113 gas. This applies to `-=` too.

### [G-02] Use nested `if` statements to avoid multiple check combinations using `&&`
[EthRouter.sol#L101](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L101)
[EthRouter.sol#L154](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L154)
[EthRouter.sol#L228](https://github.co

9 results - 2 files

/EthRouter.sol
101:        if (block.timestamp > deadline && deadline != 0)

154:        if (block.timestamp > deadline && deadline != 0)

228:        if (block.timestamp > deadline && deadline != 0)

256:        if (block.timestamp > deadline && deadline != 0)

// Code block 1 (solidity):
4 result - 1 file

/PrivatePool.sol
230:        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);

231:        virtualNftReserves -= uint128(weightSum);

323:        virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);

324:        virtualNftReserves += uint128(weightSum);

PrivatePool.sol#L230-L231, PrivatePool.sol#L323-L324, EthRouter.sol#L101, EthRouter.sol#L154


inmarelibero Q

\==============================================================================================================
\# 1
\==============================================================================================================

# Abstract
At line #194 the wrong `index` is included in the event.

```
File: contracts/SafEth/SafEth.sol
182:     function addDerivative(
183:         address _contractAddress,
184:         uint256 _weight
185:     ) external onlyOwner {
186:         derivatives[derivativeCount] = IDerivative(_contractAddress);
187:         weights[derivativeCount] = _weight;
188:         derivativeCount++;
189:
190:         uint256 localTotalWeight = 0;
191:         for (uint256 i = 0; i < derivativeCount; i++)
192:             localTotalWeight += weights[i];
193:         totalWeight = localTotalWeight;
194:         emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
195:     }

```

For instance, when adding the first `derivative`, the third parameter `index` to be passed to the `DerivativeAdded` event should be 0.

```
event DerivativeAdded(
    address indexed contractAddress,
    uint weight,
    uint index
);
```

When `188: derivativeCount++; ` is called before `194: emit DerivativeAdded(_contractAddress, _weight, derivativeCount);`, the count is incremented and then `index` will be 1 instead of 0.

# Patch

Increment `derivativeCount` after emitting event.

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

\==============================================================================================================
\# 2
\==============================================================================================================

# Abstract

Avoid duplicated fragment of code when updating `totalWeight`

Same logic is repeated here https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L170 and here https://github.com/code-423n4/2023-03-asymmetry/blob/main/c

// Code block 1 (unknown):
File: contracts/SafEth/SafEth.sol
182:     function addDerivative(
183:         address _contractAddress,
184:         uint256 _weight
185:     ) external onlyOwner {
186:         derivatives[derivativeCount] = IDerivative(_contractAddress);
187:         weights[derivativeCount] = _weight;
188:         derivativeCount++;
189:
190:         uint256 localTotalWeight = 0;
191:         for (uint256 i = 0; i < derivativeCount; i++)
192:             localTotalWeight += weights[i];
193:         totalWeight = localTotalWeight;
194:         emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
195:     }

// Code block 2 (unknown):
event DerivativeAdded(
    address indexed contractAddress,
    uint weight,
    uint index
);

// Code block 3 (unknown):
emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
derivativeCount++;


PNS G

[G-1] Don’t compare boolean expressions to boolean literals
====================
`if (<x> == true) => if (<x>), if (<x> == false) => if (!<x>).`

```solidity
File: contracts/SafEth/SafEth.sol
64:         require(pauseStaking == false, "staking is paused");
```

```solidity
File: contracts/SafEth/SafEth.sol
109:         require(pauseUnstaking == false, "unstaking is paused");
```

[G-2] `++i/i++` should be `unchecked{++i}/unchecked{i++}` when it is not possible for them to overflow, as is the case when used in `for` loops
===
[The increment in for loop post condition can be made unchecked](https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc#the-increment-in-for-loop-post-condition-can-be-made-unchecked)
```solidity
File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)
```
```solidity
File: contracts/SafEth/SafEth.sol
84:         for (uint i = 0; i < derivativeCount; i++) {
```
```solidity
File: contracts/SafEth/SafEth.sol
113:         for (uint256 i = 0; i < derivativeCount; i++) {
```
```solidity
File: contracts/SafEth/SafEth.sol
140:         for (uint i = 0; i < derivativeCount; i++) {
```
```solidity
File: contracts/SafEth/SafEth.sol
147:         for (uint i = 0; i < derivativeCount; i++) {
```
```solidity
File: contracts/SafEth/SafEth.sol
171:         for (uint256 i = 0; i < derivativeCount; i++)
```
```solidity
File: contracts/SafEth/SafEth.sol
191:         for (uint256 i = 0; i < derivativeCount; i++)
```
[G-3] Multiple accesses of a mapping should use a local variable cache
===
`derivatives` mapping
---
```solidity
File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)
72:             underlyingValue +=
73:                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74:                     derivatives[i].balance()) /
75:                 10 ** 18;
```
[G-4] State variables should be cached in local variables
===
`derivativeCount` variable in `stake()` function
--

// Code block 1 (solidity):
File: contracts/SafEth/SafEth.sol
64:         require(pauseStaking == false, "staking is paused");

// Code block 2 (solidity):
File: contracts/SafEth/SafEth.sol
109:         require(pauseUnstaking == false, "unstaking is paused");

// Code block 3 (solidity):
File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)

// Code block 4 (solidity):
File: contracts/SafEth/SafEth.sol
84:         for (uint i = 0; i < derivativeCount; i++) {

// Code block 5 (solidity):
File: contracts/SafEth/SafEth.sol
113:         for (uint256 i = 0; i < derivativeCount; i++) {

// Code block 6 (solidity):
File: contracts/SafEth/SafEth.sol
140:         for (uint i = 0; i < derivativeCount; i++) {

// Code block 7 (solidity):
File: contracts/SafEth/SafEth.sol
147:         for (uint i = 0; i < derivativeCount; i++) {

// Code block 8 (solidity):
File: contracts/SafEth/SafEth.sol
171:         for (uint256 i = 0; i < derivativeCount; i++)

// Code block 9 (solidity):
File: contracts/SafEth/SafEth.sol
191:         for (uint256 i = 0; i < derivativeCount; i++)

// Code block 10 (solidity):
File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)
72:             underlyingValue +=
73:                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74:                     derivatives[i].balance()) /
75:                 10 ** 18;

File: contracts/SafEth/SafEth.sol
64:         require(pauseStaking == false, "staking is paused");

File: contracts/SafEth/SafEth.sol
109:         require(pauseUnstaking == false, "unstaking is paused");

File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)

File: contracts/SafEth/SafEth.sol
84:         for (uint i = 0; i < derivativeCount; i++) {

File: contracts/SafEth/SafEth.sol
113:         for (uint256 i = 0; i < derivativeCount; i++) {

File: contracts/SafEth/SafEth.sol
140:         for (uint i = 0; i < derivativeCount; i++) {

File: contracts/SafEth/SafEth.sol
147:         for (uint i = 0; i < derivativeCount; i++) {

File: contracts/SafEth/SafEth.sol
171:         for (uint256 i = 0; i < derivativeCount; i++)

File: contracts/SafEth/SafEth.sol
191:         for (uint256 i = 0; i < derivativeCount; i++)

File: contracts/SafEth/SafEth.sol
71:         for (uint i = 0; i < derivativeCount; i++)
72:             underlyingValue +=
73:                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74:                     derivatives[i].balance()) /
75:                 10 ** 18;


Bough G

# [G-01]  ++i costs less gas than i++, especially when it’s used in for-loops (--i/i-- too)
##description
++i costs less gas compared to i++ or i += 1 (same for --i vs i-- or i -= 1).
it Saves 5 gas per loop
## Codes
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L108
```   for(uint256 i = 0; i < numCollaterals; i++) { ```
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L608
```for (vars.i = 0; vars.i < _n && vars.user != firstUser; vars.i++) {```
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L690
```for (vars.i = 0; vars.i < _n; vars.i++) ```
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L808
```for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {```

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

```for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {```

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

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

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

```i++``

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/PriceFeed.sol#L113
``` for (uint256 i = 0; i < numCollaterals; i++) {```

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/RedemptionHelper.sol#L154
```for (uint256 i = 0; i < numCollaterals; i++) {```

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

```while (vars.currentTroveuser != address(0) && vars.remainingLUSD > 0 && _maxIterations-- > 0)```
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultERC4626.sol#L273
```        if (x % y != 0) q++;```

# [G‑02] Structs can be packed into fewer storage slots.

https://github.com/code-

// Code block 1 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L608

// Code block 2 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L690

// Code block 3 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L808

// Code block 4 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L882

// Code block 5 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L56

// Code block 6 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/HintHelpers.sol#L191

// Code block 7 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/RedemptionHelper.sol#L154

// Code block 8 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/HintHelpers.sol#L120

// Code block 9 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultERC4626.sol#L273

ActivePool.sol#L108, TroveManager.sol#L608, TroveManager.sol#L690, TroveManager.sol#L808, TroveManager.sol#L882, CollateralConfig.sol#L56, HintHelpers.sol#L191, PriceFeed.sol#L113, RedemptionHelper.sol#L154, HintHelpers.sol#L120, ReaperVaultERC4626.sol#L273

TroveManager.sol, PriceFeed.sol, ActivePool.sol, ReaperVaultERC4626.sol, RedemptionHelper.sol, CollateralConfig.sol, HintHelpers.sol


Blank_Space G

#[G-01] The `transferOwnership` function does not make the owner an admin

The `transferOwnership` function updates the owner of the contract, but does not give the owner admin functionality, even though the owner is only address that can call `adjustAdminAccess`. This means that there is no reason for an owner to not be an admin, thus the address would have to make a second function call to `adjustAdminAccess` at a gas cost. If there was an incentive for an owner to not be an admin, the current implementation does not stop an owner from making themselves an admin.

```diff
function transferOwnership(address newOwnerAddress) external {
        require(msg.sender == _ownerAddress);
++ isAdmin[newOwnerAddress] = true;
        _ownerAddress = newOwnerAddress;
    }
```

#[G-02] The `attributeProbabilities` mapping gets overridden in the constructor

The constructor calls `addAttributeProbabilities` which “Initializes the probabilities for each attribute” via a for loop in `addAttributeProbabilities`,  but then overwrites those probabilities and adds them as part of the for loop in the constructor itself. It is, thus not necessary to call `addAttributeProbabilities` in the constructor, thus saving gas.

Location: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L45

Location: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L85-L88

#[G-03] The `defaultAttributeDivisor` can be passed as a parameter in the constructor

The `defaultAttributeDivisor` is only used once in the constructor to populate the initial `attributeToDnaDivisor` .It is not necessary to store an array in storage for a single use during the construction of the contract. Consider passing the array as a constructor parameter.

Location: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L50

// Code block 1 (diff):
function transferOwnership(address newOwnerAddress) external {
        require(msg.sender == _ownerAddress);
++ isAdmin[newOwnerAddress] = true;
        _ownerAddress = newOwnerAddress;
    }


SovaSlava Q

Q1 - Different token name.
Factory emit event with wrong token name, when deploy new token contract.
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDYFactory.sol#L105
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L195

Q2 - Event has wrong amount of transfered shares.
Function wrap() mint shares with amount - _USDYAmount * BPS_DENOMINATOR. But emit event TransferShares with value of _USDYAmount. Without multiplying by BPS_DENOMINATOR. Correct code:
```
 emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR);
```
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L439

Q3 - Function dont round derived price to the 8th decimal.
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L282C18-L282C18

Q4 - User cant see own token balance, when oracle now has pause mode enabled.
Function balanceOf() call oracle.getPrice(). Function getPrice() has modifier whenNotPaused.
It is normal that in pause mode the user cannot move their tokens, but viewing the balance should be available. For example, you can display the last price in pause modeю
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L227

Q5 - Approver can't revoke his vote
There is not opportunity revoke vote for tx in DestinationBridge.sol
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L197

Q6 - Insufficient verification of threshold amount in setThresholds().
Function dont checks, that chainToThresholds[srcChain][i - 1].amount is equal to amounts[i]. If these values are equal, should be revert error - ThresholdsNotInAscendingOrder().
https://github.com/code-423n4/2023-09-ondo/blob/47d34

// Code block 1 (unknown):
emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR);

rUSDYFactory.sol#L105, rUSDY.sol#L195, rUSDY.sol#L439, RWADynamicOracle.sol#L282-L18, rUSDY.sol#L227, DestinationBridge.sol#L197

rUSDYFactory.sol, rUSDY.sol, RWADynamicOracle.sol, DestinationBridge.sol


alexzoid Q

## Summary

### Low Issues

Total of **1 issue**:

|ID|Issue|
|:--:|:---|
| [L-01] | Missing Zero Address Check for `_authorityAddress` in `EBTCToken` Constructor |

---

## Low Issues
### [L-01] - Missing Zero Address Check for `_authorityAddress` in `EBTCToken` Constructor

- https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/EBTCToken.sol#L66
- https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/Dependencies/AuthNoOwner.sol#L55-L63

The lack of a zero address check for the `_authorityAddress` parameter in the `EBTCToken` constructor was identified as a vulnerability through a formal verification contest using the `authNoOwnerInitializedAndAddressSetInConstructor()` rule. This rule is designed to ensure that `_authorityInitialized` is `true` only when `_authority` is set to a non-zero address in `AuthNoOwner` contract.

```
invariant authNoOwnerInitializedAndAddressSetInConstructor() ghostAuthorityInitialized == (ghostAuthority != 0)
    filtered { f -> f.selector == 0 } {
    preserved {
        require(false);
    }
}
```
In the initial `EBTCToken` contract, this rule was violated, suggesting that `_authority` could be set to the zero address while `_authorityInitialized` was `true`. This issue was not found in other contracts such as `ActivePool.sol` and `CollSurplusPool.sol`.

An additional point of concern is the `setAuthority()` function in the `AuthNoOwner` contract. This function is intended to set a new `_authority` address. However, if `_authority` is initially set to the zero address due to the absence of a zero address check in the constructor, it would be impossible to change the `_authority` later, as the `setAuthority()` function requires the current `_authority` to authorize the change. This limitation poses a significant risk to the contract's flexibility and security.

***Recommendation***: It is advised to implement a zero address check for `_authorityAddress` in the `EBTCToken`

require(_authorityAddress != address(0), "EBTCToken: zero authority!");
_initializeAuthority(_authorityAddress);

// Code block 1 (unknown):
invariant authNoOwnerInitializedAndAddressSetInConstructor() ghostAuthorityInitialized == (ghostAuthority != 0)
    filtered { f -> f.selector == 0 } {
    preserved {
        require(false);
    }
}


DeFiHackLabs Q

## [L-01] Lack of access control

fighterCreatedEmitter() is public, allowing anyone to arbitrarily modify the data storing the new Fighter NFT.

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterOps.sol#L53
```
   /// @notice Emits a FighterCreated event.
    function fighterCreatedEmitter(
        uint256 id,
        uint256 weight,
        uint256 element,
        uint8 generation
    )
        public
    {
        emit FighterCreated(id, weight, element, generation);
    }
```

## [L-02]Potential Voltage loss
There are some senarios:
1. Users can replenish Voltage every day, but _replenishVoltage is executed within spendVoltage().
 The spendVoltage() function takes the voltageSpent parameter as input.
Users who only want to replenish Voltage but lack of awareness or accidental input of voltageSpent will lose Voltage.

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/VoltageManager.sol#L110
```
ownerVoltage[spender] -= voltageSpent;
```
It is recommended to separate replenishVoltage into a standalone function.

2. The useVoltageBattery() only checks if the user's voltage is less than 100. Users with non-zero voltage will lose their remaining Voltage.

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/VoltageManager.sol#L94
```
require(ownerVoltage[msg.sender] < 100);
```

## [L-03] URI returns false data.

The owner can add admin access for a user through adjustAdminAccess().
Multiple users may have admin access.
setTokenURI() and createGameItem() are called by a user with admin access.
After createGameItem(), other users with admin access can freely modify the tokenURI using setTokenURI.

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/GameItems.sol#L194
```
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
        require(isAdmin[msg.sender]);
        _tokenURIs[tokenId] = _tokenURI;
    }
```
The value of the NFTs depends on their metadata which includes critical information such a

// Code block 1 (unknown):
/// @notice Emits a FighterCreated event.
    function fighterCreatedEmitter(
        uint256 id,
        uint256 weight,
        uint256 element,
        uint8 generation
    )
        public
    {
        emit FighterCreated(id, weight, element, generation);
    }

// Code block 2 (unknown):
ownerVoltage[spender] -= voltageSpent;

// Code block 3 (unknown):
require(ownerVoltage[msg.sender] < 100);

// Code block 4 (unknown):
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
        require(isAdmin[msg.sender]);
        _tokenURIs[tokenId] = _tokenURI;
    }

FighterOps.sol#L53, VoltageManager.sol#L110, VoltageManager.sol#L94, GameItems.sol#L194