caglankaan G

# Using Prefix Operators Costs Less Gas Than Postfix Operators in Loops

### Description
In Solidity, there are two ways to increment a variable within a loop: i++ and ++i. The difference between them is that i++ is a post-increment operator, which increments the value of i after the expression has been evaluated, while ++i is a pre-increment operator, which increments the value of i before the expression has been evaluated.

When using these operators in a loop, it is more gas-efficient to use ++i instead of i++. The reason is that the i++ operator requires an additional SSTORE operation to store the updated value of i, which consumes more gas.

### Code Location

Applies for all of the for loops
```solidity
for (uint i = 0; i < derivativeCount; i++)
```

### Recommendation
It is recommended to use ++i instead of i++ to increment the value of a uint variable within a loop. This is not applicable outside of loops.

# Avoid Unnecessary Initializations Of Uint256 And Bool Variable To 0/false
This code optimization involves avoiding the unnecessary initialization of uint256 and bool variables to 0 or false. In some cases, the code block initializes a variable to 0 or false even though it is already initialized to the same value by default, which leads to unnecessary gas consumption. This code optimization can be applied to several for loops and conditional statements in different Solidity smart contracts.

### Code Example

Applies for all of the for loops
```solidity
for (uint i = 0; i < derivativeCount; i++) {
for (uint256 i = 0; i < derivativeCount; i++) {
```

### Recommendation
To optimize the code and reduce gas consumption, it is recommended to avoid initializing uint and bool variables to 0 or false when they are already initialized to the same value by default. Instead, simply declare the variable without an initial value, as in for (uint256 i; i < length; ++i) or bool isDone;. This will make the code more efficient and optimize the gas usage. The develop

// Code block 1 (solidity):
for (uint i = 0; i < derivativeCount; i++)

// Code block 2 (solidity):
for (uint i = 0; i < derivativeCount; i++) {
for (uint256 i = 0; i < derivativeCount; i++) {

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

for (uint i = 0; i < derivativeCount; i++) {
for (uint256 i = 0; i < derivativeCount; i++) {


lukris02 Q

# QA Report for Ondo Finance contest
## Overview
During the audit, 6 non-critical issues were found.
№ | Title | Risk Rating  | Instance Count
--- | --- | --- | ---
NC-1 | Order of Functions | Non-Critical | 9
NC-2 | Order of Layout | Non-Critical | 13
NC-3 | Typos | Non-Critical | 7
NC-4 | Unused named return variables | Non-Critical | 1
NC-5 | Missing and extra leading underscores | Non-Critical | 3
NC-6 | Missing NatSpec | Non-Critical | 10

## Non-Critical Risk Findings(6)
### NC-1. Order of Functions
##### Description
According to [Style Guide](https://docs.soliditylang.org/en/v0.8.16/style-guide.html#order-of-functions), ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier.
Functions should be grouped according to their visibility and ordered:
1) constructor
2) receive function (if exists)
3) fallback function (if exists)
4) external
5) public
6) internal
7) private
##### Instances
External functions should be placed before public:
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L589-L615

External functions should be placed before private:
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L798-L876
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L938-L968

##### Recommendation
Reorder functions where possible.
#
### NC-2. Order of Layout
##### Description
According to [Order of Layout](https://docs.soliditylang.org/en/v0.8.16/style-guide.html#order-of-layout), inside each contract, library or interface, use the following order:
1) Type declarations
2) State variables
3) Events
4) Modifiers
5) Functions
##### Instances
Events should be placed right after state variables:
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashFactory.sol#L143
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderFactory.sol#L153
- ht

- [```function assignRoletoKYCGroup(```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L144) => ```RoleTo```
- (L205) [```* @param addresses           Array of addresses being added as elligible```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L205) => ```eligible```
- (L236) [```* @param addresses           Array of addresses being added as elligible```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L236) => ```eligible```
- [```*      BPS_DENOMINAOR (say 9999) and `mintFee` = 1,```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L429) => ```DENOMINATOR```
- [```/// @notice Error for when caller attempts to set the KYC registry refernce```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/interfaces/IKYCRegistryClient.sol#L38) => ```reference```
- [```* @dev Event for when a price cap is set on an fToken's underlying assset```](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/IOndoPriceOracleV2.sol#L95) => ```asset```

#
### NC-4. Unused named return variables
##### Description
Both named return variable(s) and return statement are used.
##### Instances
- https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L784-L795

##### Recommendation
To improve clarity use only named return variables.
For example, change:

CashManager.sol#L589-L615, CashManager.sol#L798-L876, CashManager.sol#L938-L968, CashFactory.sol#L143, CashKYCSenderFactory.sol#L153, KYCRegistry.sol#L144, KYCRegistry.sol#L205, KYCRegistry.sol#L236, CashManager.sol#L429, IKYCRegistryClient.sol#L38, IOndoPriceOracleV2.sol#L95, CashManager.sol#L784-L795

CashManager.sol, CashKYCSenderFactory.sol, IKYCRegistryClient.sol, IOndoPriceOracleV2.sol, CashFactory.sol, KYCRegistry.sol


Topmark Q

### Report 1:
#### No KinkX96 Boundary Validation
As noted from the code provided below in the InterestRateModel contract, it can be noted that `kinkX96` is set without any form of validation like the other variables, this would allow owner to unfairly set value that could break protocol functionality, this should be corrected by setting boundaries for this state update
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/InterestRateModel.sol#L98
```solidity
 function setValues(
        uint256 baseRatePerYearX96,
        uint256 multiplierPerYearX96,
        uint256 jumpMultiplierPerYearX96,
        uint256 _kinkX96
    ) public onlyOwner {
        if (
            baseRatePerYearX96 > MAX_BASE_RATE_X96 || multiplierPerYearX96 > MAX_MULTIPLIER_X96
                || jumpMultiplierPerYearX96 > MAX_MULTIPLIER_X96
        ) {
            revert InvalidConfig();
        }

        baseRatePerSecondX96 = baseRatePerYearX96 / YEAR_SECS;
        multiplierPerSecondX96 = multiplierPerYearX96 / YEAR_SECS;
        jumpMultiplierPerSecondX96 = jumpMultiplierPerYearX96 / YEAR_SECS;
>>>        kinkX96 = _kinkX96;

        emit SetValues(baseRatePerYearX96, multiplierPerYearX96, jumpMultiplierPerYearX96, _kinkX96);
    }
```
###  Report 2:
#### Contradictory Derivation of Lent in V3Vault contract
As noted from the pointer in the code provided below, lent was first derived using totalSupply() and Math.Rounding.Up, however on the other hand in the lendInfo() function, lend amount was derived using balanceOf(account) and Math.Rounding.Down, which is contradictory to each other
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L213
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L221
```solidity
  function vaultInfo()
        external
        view
        override
        returns (
            uint256 debt,
            uint256 lent,
            uint256 balance,
            uint256 available,
            uint256 reserv

  function vaultInfo()
        external
        view
        override
        returns (
            uint256 debt,
            uint256 lent,
            uint256 balance,
            uint256 available,
            uint256 reserves,
            uint256 debtExchangeRateX96,
            uint256 lendExchangeRateX96
        )
    {
        (debtExchangeRateX96, lendExchangeRateX96) = _calculateGlobalInterest();
        (balance, available, reserves) = _getAvailableBalance(debtExchangeRateX96, lendExchangeRateX96);

        debt = _convertToAssets(debtSharesTotal, debtExchangeRateX96, Math.Rounding.Up);
>>>        lent = _convertToAssets(totalSupply(), lendExchangeRateX96, Math.Rounding.Up);
    }

    /// @notice Retrieves lending information for a specified account.
    /// @param account The address of the account for which lending info is requested.
    /// @return amount Amount of lent assets for the account
    function lendInfo(address account) external view override returns (uint256 amount) {
        (, uint256 newLendExchangeRateX96) = _calculateGlobalInterest();
>>>        amount = _convertToAssets(balanceOf(account), newLendExchangeRateX96, Math.Rounding.Down);
    }

// Code block 1 (solidity):
function setValues(
        uint256 baseRatePerYearX96,
        uint256 multiplierPerYearX96,
        uint256 jumpMultiplierPerYearX96,
        uint256 _kinkX96
    ) public onlyOwner {
        if (
            baseRatePerYearX96 > MAX_BASE_RATE_X96 || multiplierPerYearX96 > MAX_MULTIPLIER_X96
                || jumpMultiplierPerYearX96 > MAX_MULTIPLIER_X96
        ) {
            revert InvalidConfig();
        }

        baseRatePerSecondX96 = baseRatePerYearX96 / YEAR_SECS;
        multiplierPerSecondX96 = multiplierPerYearX96 / YEAR_SECS;
        jumpMultiplierPerSecondX96 = jumpMultiplierPerYearX96 / YEAR_SECS;
>>>        kinkX96 = _kinkX96;

        emit SetValues(baseRatePerYearX96, multiplierPerYearX96, jumpMultiplierPerYearX96, _kinkX96);
    }


lsaudit G

# [G-01] Redundant variable in `LRTConfigRoleChecker.sol`

[File: LRTConfigRoleChecker.sol](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/utils/LRTConfigRoleChecker.sol#L27-L33)
```
    modifier onlyLRTAdmin() {
        bytes32 DEFAULT_ADMIN_ROLE = 0x00;
        if (!IAccessControl(address(lrtConfig)).hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAdmin();
        }
        _;
    }
```

Variable `DEFAULT_ADMIN_ROLE` is used only once, thus it does not need to be declared at all:

```
    modifier onlyLRTAdmin() {
        if (!IAccessControl(address(lrtConfig)).hasRole(0x00, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAdmin();
        }
        _;
    }
```

# [G-02] Redundant variables is `LRTOracle.sol`

[File: LRTOracle.sol](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L52-L73)
```
 function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

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

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

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

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);

            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }
```

Variables `rsETHTokenAddress

// Code block 1 (unknown):
modifier onlyLRTAdmin() {
        bytes32 DEFAULT_ADMIN_ROLE = 0x00;
        if (!IAccessControl(address(lrtConfig)).hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAdmin();
        }
        _;
    }

// Code block 2 (unknown):
modifier onlyLRTAdmin() {
        if (!IAccessControl(address(lrtConfig)).hasRole(0x00, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAdmin();
        }
        _;
    }

// Code block 3 (unknown):
function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

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

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

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

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);

            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }


kinggvd G

The code below may produce timeouts even if the response did not actually timeout and returned a valid response due to ms difference, thus wasting resources. Consider adding a small buffer when _response.success == true.

```
    function _responseTimeout(uint256 _timestamp, uint256 _timeout) internal view returns (bool) {
        return block.timestamp - _timestamp > _timeout;
    }

to

    function _responseTimeout(uint256 _timestamp, uint256 _timeout, uint256 _eps) internal view returns (bool) {
        return block.timestamp - _timestamp > _timeout + _eps;
    }
```

in e.g
```
    function _chainlinkIsFrozen(ChainlinkResponse memory _response) internal view returns (bool) {
        return
            _responseTimeout(_response.timestampEthBtc, TIMEOUT_ETH_BTC_FEED) ||
            _responseTimeout(_response.timestampStEthEth, TIMEOUT_STETH_ETH_FEED);
    }

    function _fallbackIsFrozen(
        FallbackResponse memory _fallbackResponse
    ) internal view returns (bool) {
        return
            _fallbackResponse.timestamp > 0 &&
            _responseTimeout(_fallbackResponse.timestamp, fallbackCaller.fallbackTimeout());
    }
```

// Code block 1 (unknown):
function _responseTimeout(uint256 _timestamp, uint256 _timeout) internal view returns (bool) {
        return block.timestamp - _timestamp > _timeout;
    }

to

    function _responseTimeout(uint256 _timestamp, uint256 _timeout, uint256 _eps) internal view returns (bool) {
        return block.timestamp - _timestamp > _timeout + _eps;
    }

// Code block 2 (unknown):
function _chainlinkIsFrozen(ChainlinkResponse memory _response) internal view returns (bool) {
        return
            _responseTimeout(_response.timestampEthBtc, TIMEOUT_ETH_BTC_FEED) ||
            _responseTimeout(_response.timestampStEthEth, TIMEOUT_STETH_ETH_FEED);
    }

    function _fallbackIsFrozen(
        FallbackResponse memory _fallbackResponse
    ) internal view returns (bool) {
        return
            _fallbackResponse.timestamp > 0 &&
            _responseTimeout(_fallbackResponse.timestamp, fallbackCaller.fallbackTimeout());
    }


Rolezn Q

## QA Summary

### QA Issues
| |Issue|Contexts|
|-|:-|:-:|
| [QA‑1](#QA‑1) | `address wallet` in `USDS.mintTo()` can be `address(collateralAndLiquidity)` | 1 |
| [QA‑2](#QA‑2) | `ecrecover` may return empty address | 1 |
| [QA‑3](#QA‑3) | `forceApprove` should be used instead of `approve` | 4 |
| [QA‑4](#QA‑4) | The Contract Should `approve(0)` First | 8 |
| [QA‑5](#QA‑5) | `address` shouldn't be hard-coded | 1 |
| [QA‑6](#QA‑6) | Change contract's `mint` model | 1 |
| [QA‑7](#QA‑7) | Functions calling contracts/addresses with transfer hooks are missing reentrancy guards | 2 |
| [QA‑8](#QA‑8) | A function which defines named returns in it's declaration doesn't need to use return | 42 |
| [QA‑9](#QA‑9) | Use `delete` to Clear Variables | 6 |
| [QA‑10](#QA‑10) | Incorrect withdraw declaration | 2 |
| [QA‑11](#QA‑11) | Off-by-one timestamp error | 8 |
| [QA‑12](#QA‑12) | Redundant Cast | 1 |
| [QA‑13](#QA‑13) | Indexed strings should be removed | 1 |

Total: 78 contexts over 13 issues

## QA Issues

### [QA‑1] `address wallet` in `USDS.mintTo()` can be `address(collateralAndLiquidity)`

Since the only caller can be `address(collateralAndLiquidity)` according to `require( msg.sender == address(collateralAndLiquidity), "USDS.mintTo is only callable from the Collateral contract" );`
We can see that in `CollateralAndLiquidity.borrowUSDS()` calls for `usds.mintTo( msg.sender, amountBorrowed );`
As a result, we can remove `address wallet` and simply call to `_mint(address(collateralAndLiquidity), amount);`

#### Proof Of Concept

```solidity
File: USDS.sol

40: function mintTo( address wallet, uint256 amount ) external
		{
		require( msg.sender == address(collateralAndLiquidity), "USDS.mintTo is only callable f

File: USDS.sol

40: function mintTo( address wallet, uint256 amount ) external
		{
		require( msg.sender == address(collateralAndLiquidity), "USDS.mintTo is only callable from the Collateral contract" );
		require( amount > 0, "Cannot mint zero USDS" );

		_mint( wallet, amount );

		emit USDSMinted(wallet, amount);
		}

File: SigningTools.sol

26: address recoveredAddress = ecrecover(messageHash, v, r, s);


MiniGlome Q

## [LOW] Emited event is always address(0)
In `UniV3LiquidityAmo::removeLiquidity()` the event `log()` is emited with a mapping element that has just been deleted as parameter. This will always log `address(0)`.
```solidity
File UniV3LiquidityAmo.sol

L263: delete positions_mapping[pos.token_id]; //@audit delete mapping element

    // send tokens to rdpxV2Core
    _sendTokensToRdpxV2Core(tokenA, tokenB);

    emit log(positions_array.length);
    emit log(positions_mapping[pos.token_id].token_id); //@audit has just been deleted
```
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L269

## [LOW] `UniV3LiquidityAmo` will stop working in 2036
In `UniV3LiquidityAmo::swap()` the deadline parameter of the swap configuration is hardcoded to `2105300114` which is the unix timestamp of `17 September 2036`. That means that all swaps will revert after this date and the function will become unusable.
```solidity
File UniV3LiquidityAmo.sol

L289: ISwapRouter.ExactInputSingleParams memory swap_params = ISwapRouter
      .ExactInputSingleParams(
        _tokenA,
        _tokenB,
        _fee_tier,
        address(this),
        2105300114, // Expiration: a long time from now //@audit Deadline is in 13 years (2036)
        _amountAtoB,
        _amountOutMinimum,
        _sqrtPriceLimitX96
      );

    // Approval
    TransferHelper.safeApprove(_tokenA, address(univ3_router), _amountAtoB);

    uint256 amountOut = univ3_router.exactInputSingle(swap_params);
```
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L295

## [QA] Wrong revert messages
*Instances(6)*
```solidity
File UniV2LiquidityAmo.sol

L91: "reLPContract: address cannot be 0" //@audit should be "UniV2LiquidityAmo: address cannot be 0"

L114: "reLPContract: slippage tolerance must be greater than 0" //@audit same typo

L131: require(_token != address(0), "reLPContract

// Code block 1 (solidity):
File UniV3LiquidityAmo.sol

L263: delete positions_mapping[pos.token_id]; //@audit delete mapping element

    // send tokens to rdpxV2Core
    _sendTokensToRdpxV2Core(tokenA, tokenB);

    emit log(positions_array.length);
    emit log(positions_mapping[pos.token_id].token_id); //@audit has just been deleted

// Code block 2 (solidity):
File UniV3LiquidityAmo.sol

L289: ISwapRouter.ExactInputSingleParams memory swap_params = ISwapRouter
      .ExactInputSingleParams(
        _tokenA,
        _tokenB,
        _fee_tier,
        address(this),
        2105300114, // Expiration: a long time from now //@audit Deadline is in 13 years (2036)
        _amountAtoB,
        _amountOutMinimum,
        _sqrtPriceLimitX96
      );

    // Approval
    TransferHelper.safeApprove(_tokenA, address(univ3_router), _amountAtoB);

    uint256 amountOut = univ3_router.exactInputSingle(swap_params);

File UniV3LiquidityAmo.sol

L263: delete positions_mapping[pos.token_id]; //@audit delete mapping element

    // send tokens to rdpxV2Core
    _sendTokensToRdpxV2Core(tokenA, tokenB);

    emit log(positions_array.length);
    emit log(positions_mapping[pos.token_id].token_id); //@audit has just been deleted

File UniV3LiquidityAmo.sol

L289: ISwapRouter.ExactInputSingleParams memory swap_params = ISwapRouter
      .ExactInputSingleParams(
        _tokenA,
        _tokenB,
        _fee_tier,
        address(this),
        2105300114, // Expiration: a long time from now //@audit Deadline is in 13 years (2036)
        _amountAtoB,
        _amountOutMinimum,
        _sqrtPriceLimitX96
      );

    // Approval
    TransferHelper.safeApprove(_tokenA, address(univ3_router), _amountAtoB);

    uint256 amountOut = univ3_router.exactInputSingle(swap_params);


orion Q

Price is not checks when opening//closing trove

## Impact
Contract can return unintended results if collateral price is not configured yet

## Proof of Concept
The open/close trove functionalities takes the price of the collateral from the price feed contract :

```
 vars.price = priceFeed.fetchPrice(_collateral);
...

uint price = priceFeed.fetchPrice(_collateral);
```

However the prices are used direcctly with the logic with no checks that these are higher than 0, when opening the trove may allows the attacker to get the collateral higher than he is supposed to be when the collateral is not registeres on chainlink or tellor, the same when closing the trove if the price is moved to 0

## Tools Used
Manual

## Recommended Mitigation Steps :
Once the price is fetched from the priceFeed it's better to check that it's valid (higher than 0)

// Code block 1 (unknown):
vars.price = priceFeed.fetchPrice(_collateral);
...

uint price = priceFeed.fetchPrice(_collateral);


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 | 28 |
|:--:|:--:|

### Low Risk Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [L-01] | Critical address changes should use 2 step procedure | 4 |
| [L-02] | Remove unnecessary `receive() external payable`| 4 |
| [L-03] | `onlyOwner` modifier not needed in `SafEth.setMaxSlippage`| 1 |

| Total Low Risk Issues | 3 |
|:--:|:--:|

### Non-Critical Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [N-01] | Implement checks for input validation for extra safety in setters | 7 |
| [N-02] | Missing event for critical parameters init and change | 3 |
| [N-03] | Omission of important parameters in events emitted | 5 |
| [N-04] | Missing zero-address checks for `initialize` functions | 4 |
| [N-05] |Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions | 4 |
| [N-06] | Insufficient Coverage | 3 |
| [N-07] | Add timelock to critical functions | 5 |

| Total Non-Critical Issues | 7 |
|:--:|:--:|

### Refactor Issues Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [R-01] | Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions | 1 |
| [R-02] | Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18) | 23 |
| [R-03] | Use `string.concat() ` instead of `abi.encodePacked()` | 6 |
| [R-04] | String constants used in multiple places should be defined as constants | 6 |
| [R-05] | Use ternary operators to shorten if/else statements | 2 |
| [R-06] | Indent logic in for loops in code blocks {} correctly | 2 |
| [R-07] | Should not allow `_weight` parameter to be set as zero | 2 |
| [R-08] | Use of `uint` and `uint256` interchangeably | 16 |
| [R-09] | Emit

4 results - 4 files

/WstEth.sol
33:    function initialize(address _owner) external initializer {
34:        _transferOwnership(_owner);
35:        maxSlippage = (1 * 10 ** 16); // 1%
36:    }

/SfrxEth.sol
36:    function initialize(address _owner) external initializer {
37:        _transferOwnership(_owner);
38:        maxSlippage = (1 * 10 ** 16); // 1%
39:    }

/SafEth.sol
48:    function initialize(
49:        string memory _tokenName,
50:        string memory _tokenSymbol
51:    ) external initializer {
52:        ERC20Upgradeable.__ERC20_init(_tokenName, _tokenSymbol);
53:        _transferOwnership(msg.sender);
54:        minAmount = 5 * 10 ** 17; // initializing with .5 ETH as minimum
55:        maxAmount = 200 * 10 ** 18; // initializing with 200 ETH as maximum
56:    }

/Reth.sol
42:    function initialize(address _owner) external initializer {
43:        _transferOwnership(_owner);
44:        maxSlippage = (1 * 10 ** 16); // 1%
45:    }

4 results - 4 files

/WstEth.sol
97: receive() external payable {}

/SfrxEth.sol
126: receive() external payable {}

/SafEth.sol
246: receive() external payable {}

/Reth.sol
245: receive() external payable {}


SM3_SS G

# Gas Optimizations

| Number | Issue | Instances |
|--------|-------|-----------|
|[G-01]| Precompute Off-Chain When Possible  | 6 |
|[G-02]| Check Arguments Early | 3 |
|[G-03]| Don’t make variables public unless necessary | 19 |
|[G-04]| Greater or Equal Comparison Costs Less Gas than Greater Comparison | 24 |
|[G-05]| Initialize Variables With Default Value | 14 |
|[G-06]| Avoid zero transfers | 3 |
|[G-07]| Use named returns for local variables of pure functions where it is possible | 5 |
|[G-08]| Refactor functions to combine events to reduce gas cost | 1 |
|[G-09]| Use assembly in place of abi.decode to extract calldata values more efficiently | 13  |
|[G-10]| Using calldata instead of memory for read-only arguments in external functions saves gas | 10  |
|[G-11]| Using assembly to revert with an error message | 2 |
|[G-12]| Shorten arrays with inline assembly | 3 |
|[G-13]| Use hardcoded address instead of address(this) | 35  |
|[G-14]| Do not calculate Constant  | 16 |

## [G-1]   Precompute Off-Chain When Possible

Computing values in advance outside the blockchain is cheaper than doing it inside smart contracts:

<details>

```solidity
// On-chain computation
function verify(uint numA, uint numB) {
  require(numA * numB < 1000);
}

// Precomputed off-chain
function verify(uint result) {
  require(result < 1000);
}
```
</details>

Do computations like hashes, signatures outside.
Pass in precomputed values to save on-chain gas.s

```solidity
file:  blob/main/src/V3Oracle.sol

338   if (updatedAt + feedConfig.maxFeedAge < block.timestamp || answer < 0) {

```
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Oracle.sol#L338

```solidity
file: blob/main/src/V3Vault.sol

1227    if (
                    collateralValueLimitFactorX32 < type(uint32).max
                        && _convertToAssets(tokenConfigs[token0].totalDebtShares, debtExchangeRateX96, Math.Rounding.Up)
                            > lentAssets * collateralValueLimitFact

// Code block 1 (solidity):
// On-chain computation
function verify(uint numA, uint numB) {
  require(numA * numB < 1000);
}

// Precomputed off-chain
function verify(uint result) {
  require(result < 1000);
}

// Code block 2 (solidity):
file:  blob/main/src/V3Oracle.sol

338   if (updatedAt + feedConfig.maxFeedAge < block.timestamp || answer < 0) {

// On-chain computation
function verify(uint numA, uint numB) {
  require(numA * numB < 1000);
}

// Precomputed off-chain
function verify(uint result) {
  require(result < 1000);
}

file:  blob/main/src/V3Oracle.sol

338   if (updatedAt + feedConfig.maxFeedAge < block.timestamp || answer < 0) {


chaduke G

G1.  ``findLiquidatableUsers()`` resizes array ``liquidatableUsers`` into array ``resizedLiquidatableUsers`` by copying element-wise. This is a waste of gas. The resizing can be done by assembly in the original array ``liquidatableUsers`` without copying.

[https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/CollateralAndLiquidity.sol#L311-L342](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/CollateralAndLiquidity.sol#L311-L342)

Mitigation: Simply use assembly to resize array ``liquidatableUsers``

```javascript
 assembly { mstore(liquidatableUsers, count) }
 return liquidatableUsers;
```

// Code block 1 (javascript):
assembly { mstore(liquidatableUsers, count) }
 return liquidatableUsers;


hunter_w3b G

# Gas Optimization

# Summary

| Number | Optimization Details | Context |
|:--:|:-------| :-----:|
| [G-01] | internal functions only called once can be inlined to save gas | 57 |
| [G-02] |Functions guaranteed to revert when called by normal users can be marked payable| 7 |
| [G-03] |<X> += <Y> COSTS MORE GAS THAN <X> = <X> + <Y> FOR STATE VARIABLES |9 |
| [G-04] | USING FIXED BYTES IS CHEAPER THAN USING STRING  | 8 |
| [G-05] | EXPRESSIONS FOR CONSTANT VALUES SUCH AS A CALL TO KECCAK256(), SHOULD USE IMMUTABLE RATHER THAN CONSTANT |5|
| [G-06] |NOT USING THE NAMED RETURN VARIABLES WHEN A FUNCTION RETURNS, WASTES DEPLOYMENT GAS  | 42 |
| [G-07] | CAN MAKE THE VARIABLE OUTSIDE THE LOOP TO SAVE GAS | 25 |
| [G-08] | USING CALLDATA INSTEAD OF MEMORY FOR READ-ONLY ARGUMENTS IN EXTERNAL FUNCTIONS SAVES GAS | 1 |
| [G-09] |PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD  | 4 |
| [G-10] |SHOULD USE ARGUMENTS INSTEAD OF STATE VARIABLE  | 5 |
| [G-11] |BEFORE SOME FUNCTIONS, WE SHOULD CHECK SOME VARIABLES FOR POSSIBLE GAS SAVE  |6 |
| [G-12] |internal functions not called by the contract should be removed  | 8 |
| [G-13] |Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 2 |
| [G-14] |SPLITTING  REQUIRE() STATEMENTS THAT USE  && SAVES GAS  | 4 |
| [G-15] |Setting the constructor to payable  | 6 |
| [G-16] | Duplicated require()/if() checks should be refactored to a modifier or function | 15 |
| [G-17] |The result of a function call should be cached rather than re-calling the function | 6 |
| [G-18] | Use assembly to write address storage values |27 |
| [G-19] |Using storage instead of memory for structs/arrays saves gas | 1 |
| [G-20] |Use a more recent version of solidity | 8 |
| [G-21] |Use nested if and, avoid multiple check combinations  | 8 |
| [G-22] |Use constants instead of type(uintx).max| 4 |

## [G-01] **internal** functions only called once can be **inlined** to save gas

### Summary

Not inlining costs 20

File: /Ethos-Core/contracts/BorrowerOperations.sol

438    function _getCollChange(
439       uint _collReceived,
440       uint _requestedCollWithdrawal
441     )
442:       internal
443        pure
444        returns(uint collChange, bool isCollIncrease)

455    function _updateTroveFromAdjustment
456        (
457            ITroveManager _troveManager,
458            address _borrower,
459            address _collateral,
460            uint _collChange,
461            bool _isCollIncrease,
462            uint _debtChange,
463            bool _isDebtIncrease
464        )
465:           internal
466            returns (uint, uint)

476    function _moveTokensAndCollateralfromAdjustment
477      (
478         IActivePool _activePool,
479         ILUSDToken _lusdToken,
480         address _borrower,
481         address _collateral,
482         uint _collChange,
483         bool _isCollIncrease,
484         uint _LUSDChange,
485         bool _isDebtIncrease,
486         uint _netDebtChange
487      )
488:         internal

524:   function _requireValidCollateralAddress(address _collateral) internal view {
525        require(collateralConfig.isCollateralAllowed(_collateral), "BorrowerOps: Invalid collateral address");
526     }

533:    function _requireSingularCollChange(uint _collTopUp, uint _collWithdrawal) internal pure {
534        require(_collTopUp == 0 || _collWithdrawal == 0, "BorrowerOperations: Cannot withdraw and add coll");
535    }

537:    function _requireNonZeroAdjustment(uint _collTopUp, uint _collWithdrawal, uint _LUSDChange) internal pure {
538        require(_collTopUp != 0 || _collWithdrawal != 0 || _LUSDChange != 0, "BorrowerOps: There must be either a collateral change or a debt change");
539    }

546:    function _requireTroveisNotActive(ITroveManager _troveManager, address _borrower, address _collateral) internal view {
547        uint status = _troveManager.getTroveStatus(_borrower, _collateral);
548        require(status != 1, "Borrow

File: /Ethos-Core/contracts/TroveManager.sol

478    function _getCappedOffsetVals
479    (
480        uint _entireTroveDebt,
481        uint _entireTroveColl,
482        uint _price,
483        uint256 _MCR,
484        uint _collDecimals
485    )
486:       internal
487        pure
488        returns (LiquidationValues memory singleLiquidation)

671    function _getTotalsFromLiquidateTrovesSequence_NormalMode
672    (
673        IActivePool _activePool,
674        IDefaultPool _defaultPool,
675        address _collateral,
676        uint _price,
677        uint256 _MCR,
678        uint _LUSDInStabPool,
679        uint _n
680    )
681:        internal

785    function _getTotalFromBatchLiquidate_RecoveryMode
786    (
787        IActivePool _activePool,
788        IDefaultPool _defaultPool,
789        address _collateral,
790        uint _price,
791        uint _LUSDInStabPool,
792        address[] memory _troveArray
793    )
794:        internal
795         returns(LiquidationTotals memory totals)

864    function _getTotalsFromBatchLiquidate_NormalMode
865    (
866        IActivePool _activePool,
867        IDefaultPool _defaultPool,
868        address _collateral,
869        uint _price,
870        uint256 _MCR,
871        uint _LUSDInStabPool,
872        address[] memory _troveArray
873    )
874:      internal
875       returns(LiquidationTotals memory totals)

1082:  function _applyPendingRewards(IActivePool _activePool, IDefaultPool _defaultPool, address _borrower, address _collateral) internal {

1213:   function _computeNewStake(address _collateral, uint _coll) internal view returns (uint) {

1321:   function _addTroveOwnerToArray(address _borrower, address _collateral) internal returns (uint128 index) {

1339:   function _removeTroveOwner(address _borrower, address _collateral, uint TroveOwnersArrayLength) internal {

1516:   function _minutesPassedSinceLastFeeOp() internal view returns (uint) {

1538:   function _requireMoreThanOneTroveInSystem(uint Tro


7ashraf Q

# Quality Assurance Report

## Summary

The QA report highlights several issues in the codebase. One critical issue ([L-01]) involves the addition of a cooldown period even when the price is not changed, leading to false emissions. Additionally, the report identifies issues such as checking ballot existence before removal ([L-02]), verifying the existence of a country before proposing exclusion ([L-03]), and checking for the existence of a ballotId before counting votes ([L-04]). Other issues include ensuring values are not smaller than a specified threshold ([L-05]), checking for pool whitelisting ([L-06]), and addressing various non-critical concerns ([N-01] to [N-06]). Remedial actions and mitigations are provided for each issue.

### Summary Table:

| Issue Number | Title                                           | Number of Instances |
|--------------|-------------------------------------------------|---------------------|
| L-01         | Cooldown period added without price change      | 1                   |
| L-02         | Check if ballot exists before removal           | 1                   |
| L-03         | Check if country already exists                 | 1                   |
| L-04         | Check if ballotId exists before counting votes | 3                   |
| L-05         | Check if values are smaller than `pool.dust`    | 3                   |
| L-06         | Check if pool already whitelisted              | 1                   |
| N-01         | Revert with an error message                    | 3                   |
| N-02         | Add else and revert on else                     | 1                   |
| N-03         | Require increment amount to be > 0              | 1                   |
| N-04         | Emit vote updated and vote casted events        | 1                   |
| N-05         | Check for token decimals under 18               | 1                   |
| N-06         | Function requires more comments                | 1

else if ( priceFeedNum == 3 )
			priceFeed3 = newPriceFeed;

		priceFeedModificationCooldownExpiration = block.timestamp + priceFeedModificationCooldown;
		emit PriceFeedSet(priceFeedNum, newPriceFeed);

	function markBallotAsFinalized( uint256 ballotID ) external nonReentrant
		{
		require( msg.sender == address(exchangeConfig.dao()), "Only the DAO can mark a ballot as finalized" );

		Ballot storage ballot = ballots[ballotID];

		// Remove finalized whitelist token ballots from the list of open whitelisting proposals
		if ( ballot.ballotType == BallotType.WHITELIST_TOKEN )
			_openBallotsForTokenWhitelisting.remove( ballotID );

		// Remove from the list of all open ballots
		_allOpenBallots.remove( ballotID );

		ballot.ballotIsLive = false;

		// Indicate that the user who posted the proposal no longer has an active proposal
		address userThatPostedBallot = _usersThatProposedBallots[ballotID];
		_userHasActiveProposal[userThatPostedBallot] = false;

		delete openBallotsByName[ballot.ballotName];

		emit BallotFinalized(ballotID);
		}


0xta G

# Gas Optimization

# Summary

| Number | Gas                                                                                | Context |
| :----: | :------------------------------------------------------------------------------------------------------- | :-----: |
| [G-01] | Avoid emitting storage values                                                                            |    8    |
| [G-02] | Use constants instead of type(uintx).max                                                                 |   15    |
| [G-03] | Use mappings instead of arrays                                                                           |    5    |
| [G-04] | Use assembly to write address storage values                                                             |    4    |
| [G-05] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant |   11    |
| [G-06] | Zero value check posiible Gas Save                                                                       |    3    |
| [G-07] | Using > 0 costs more gas than != 0 when used on a uint in a require() statement                          |    5    |
| [G-08] | Do not calculate constants                                                                               |    3    |
| [G-09] | Pre-increment and pre-decrement are cheaper than +1,-1                                                   |    5    |
| [G-10] | Use hardcode address instead `address(this)`                                                             |   38    |
| [G-11] | Using storage instead of memory for structs/arrays saves gas                                             |    3    |
| [G-12] | USING CALLDATA INSTEAD OF MEMORY FOR READ-ONLY ARGUMENTS IN EXTERNAL FUNCTIONS SAVES GAS                 |    4    |
| [G-13] | Shorten the array rather than copying to a new one                                                       |    5    |
| [G-14] | Duplicated if()/require checks should

349    emit LogSetAddresses(addresses);

370    emit LogSetPricingOracleAddresses(pricingOracleAddresses);

211    emit AddressesSet(addresses);

389    emit PayFunding(
390      msg.sender,
391      totalFundingForEpoch[latestFundingPaymentPointer],
392      latestFundingPaymentPointer
393    );

451     emit CalculateFunding(
452        msg.sender,
453        amount,
454        strike,
455        premium,
456:        latestFundingPaymentPointer
457      );

485   emit FundingPaid(
486          msg.sender,
487          ((currentFundingRate * (nextFundingPaymentTimestamp() - startTime)) /
488            1e18),
489          latestFundingPaymentPointer
480        );

494      emit FundingPaymentPointerUpdated(latestFundingPaymentPointer);    storage values

519    emit FundingPaid(
520      msg.sender,
521      ((currentFundingRate * (block.timestamp - startTime)) / 1e18),
522      latestFundingPaymentPointer
523    );


DavidGiladi G

### Gas Optimization Issues
|Title|Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
|[G-1] Inefficient use of abi.encode() | [Inefficient use of abi.encode()](#inefficient-use-of-abiencode) | 6 | 600 |
|[G-2] Use assembly to emit events | [Use assembly to emit events](#use-assembly-to-emit-events) | 78 | 2964 |
|[G-3] Avoid unnecessary storage updates | [Avoid unnecessary storage updates](#avoid-unnecessary-storage-updates) | 5 | 4000 |
|[G-4] Multiplication and Division by 2 Should use in Bit Shifting | [Multiplication and Division by 2 Should use in Bit Shifting](#multiplication-and-division-by-2-should-use-in-bit-shifting) | 1 | 20 |
|[G-5] Using bools for storage incurs overhead | [Using bools for storage incurs overhead](#using-bools-for-storage-incurs-overhead) | 2 | 34200 |
|[G-6] Optimizing Small Data Storage with Bytes32 | [Optimizing Small Data Storage with Bytes32](#optimizing-small-data-storage-with-bytes32) | 11 | 4158 |
|[G-7] Check Arguments Early | [Check Arguments Early](#check-arguments-early) | 4 | - |
|[G-8] Division operations between unsigned could be unchecked | [Division operations between unsigned could be unchecked](#division-operations-between-unsigned-could-be-unchecked) | 3 | 255 |
|[G-9] Modulus operations that could be unchecked | [Modulus operations that could be unchecked](#modulus-operations-that-could-be-unchecked) | 1 | 85 |
|[G-10] Potential Optimization by Combining Multiple Mappings into a Struct | [Potential Optimization by Combining Multiple Mappings into a Struct](#potential-optimization-by-combining-multiple-mappings-into-a-struct) | 2 | 1000 |
|[G-11] Constants Variable Should Be Private for Gas Optimization | [Constants Variable Should Be Private for Gas Optimization](#constants-variable-should-be-private-for-gas-optimization) | 18 | 61200 |
|[G-12] State variables that could be declared constant | [State variables that could be declared constant](#state-variables-that-could-be-declared-constant) | 10 | 20970 |
|[G-13] Us

File: contracts/BorrowerOperations.sol

682    return keccak256(abi.encode(typeHash, name, version, _chainID(), address(this)))

File: contracts/BorrowerOperations.sol

717    bytes32 digest = keccak256(
718                abi.encodePacked(
719                    "\x19\x01",
720                    domainSeparator(),
721                    keccak256(
722                        abi.encode(
723                            _PERMIT_POSITION_MANAGER_TYPEHASH,
724                            _borrower,
725                            _positionManager,
726                            _approval,
727                            _nonces[_borrower]++,
728                            _deadline
729                        )
730                    )
731                )
732            )


Shubham Q

## Non-Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | Using while for unbounded loops isn’t recommended | 3 |
| [NC-2](#NC-2) | Immutables should be in uppercase | 5 |
| [NC-3](#NC-3) | Use underscores for number literals | 4 |
| [NC-4](#NC-4) | Assembly Codes Specific – Should Have Comments | 2 |
| [NC-5](#NC-5) | Not using the named return variables anywhere in the function is confusing | 1 |
| [NC-6](#NC-6) | Add a timelock to critical functions | 1 |

## [NC-1] Using while for unbounded loops isn’t recommended

Don’t write loops that are unbounded as this can hit the gas limit, causing your transaction to fail.

For the reason above, while and do while loops are rarely used.

```solidity
File: main/ajna-core/src/RewardsManager.sol

L:616      while (claimEpoch <= burnEpochToStartClaim_) {
              burnEpochsClaimed_[i] = claimEpoch;

             // iterations are bounded by array length (which is itself bounded), preventing overflow / underflow
             unchecked {
                  ++i;
                  ++claimEpoch;
              }
           }
```
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L616-624

```solidity
File: main/ajna-grants/src/grants/base/StandardFunding.sol

L:820          while (
                   targetProposalId_ != 0
                   &&
                   _standardFundingProposals[proposals_[targetProposalId_]].votesReceived >
                   _standardFundingProposals[proposals_[targetProposalId_ - 1]].votesReceived
                ) {
                  // swap values if left item < right item
                  uint256 temp = proposals_[targetProposalId_ - 1];

                   proposals_[targetProposalId_ - 1] = proposals_[targetProposalId_];
                   proposals_[targetProposalId_] = temp;

                   unchecked { --targetProposalId_; }
                }
```
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/Sta

// Code block 1 (solidity):
File: main/ajna-core/src/RewardsManager.sol

L:616      while (claimEpoch <= burnEpochToStartClaim_) {
              burnEpochsClaimed_[i] = claimEpoch;

             // iterations are bounded by array length (which is itself bounded), preventing overflow / underflow
             unchecked {
                  ++i;
                  ++claimEpoch;
              }
           }

// Code block 2 (solidity):
File: main/ajna-grants/src/grants/base/StandardFunding.sol

L:820          while (
                   targetProposalId_ != 0
                   &&
                   _standardFundingProposals[proposals_[targetProposalId_]].votesReceived >
                   _standardFundingProposals[proposals_[targetProposalId_ - 1]].votesReceived
                ) {
                  // swap values if left item < right item
                  uint256 temp = proposals_[targetProposalId_ - 1];

                   proposals_[targetProposalId_ - 1] = proposals_[targetProposalId_];
                   proposals_[targetProposalId_] = temp;

                   unchecked { --targetProposalId_; }
                }

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

L:616      while (claimEpoch <= burnEpochToStartClaim_) {
              burnEpochsClaimed_[i] = claimEpoch;

             // iterations are bounded by array length (which is itself bounded), preventing overflow / underflow
             unchecked {
                  ++i;
                  ++claimEpoch;
              }
           }

File: main/ajna-grants/src/grants/base/StandardFunding.sol

L:820          while (
                   targetProposalId_ != 0
                   &&
                   _standardFundingProposals[proposals_[targetProposalId_]].votesReceived >
                   _standardFundingProposals[proposals_[targetProposalId_ - 1]].votesReceived
                ) {
                  // swap values if left item < right item
                  uint256 temp = proposals_[targetProposalId_ - 1];

                   proposals_[targetProposalId_ - 1] = proposals_[targetProposalId_];
                   proposals_[targetProposalId_] = temp;

                   unchecked { --targetProposalId_; }
                }


Jorgect G

# GAS REPORT

## [G-01] The RpdxV2Core.sol contract is performing aditional calculation in some cases.

The _curveSwap is calculating the minOut always but this depend of minAmount input so if the minAmount is set the minOut is gonna be calculated but never used.

```
file:contracts/core/RdpxV2Core.sol
function _curveSwap(
    uint256 _amount,
    bool _ethToDpxEth,
    bool validate,
    uint256 minAmount
  ) internal returns (uint256 amountOut) {
    ...
    // calculate minimum amount out
    uint256 minOut = _ethToDpxEth
      ? (((_amount * getDpxEthPrice()) / 1e8) -
        (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16))
      : (((_amount * getEthPrice()) / 1e8) -
        (((_amount * getEthPrice()) * slippageTolerance) / 1e16));

    // Swap the tokens
    amountOut = dpxEthCurvePool.exchange(
      _ethToDpxEth ? int128(int256(a)) : int128(int256(b)),
      _ethToDpxEth ? int128(int256(b)) : int128(int256(a)),
      _amount,
      minAmount > 0 ? minAmount : minOut
    );
  }

```
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L515C3-L559C1

### Recomendation

Calculate minOut just when minAmount is set to 0;

## [G-02] The RpdxV2Core.sol contract is performing an adittional call that is not need it.

When the bonds function is called it is returning the owner and is no used, but then is calling ownerOf of function to  get the owner

```
function _transfer(uint256 _rdpxAmount, uint256 _wethAmount, uint256 _bondAmount, uint256 _bondId) internal {
        if (_bondId != 0) {
            (, uint256 expiry, uint256 amount) = IRdpxDecayingBonds(addresses.rdpxDecayingBonds).bonds(_bondId); //@audit (gas) you can get the owner here and not call ownerOf

            _validate(amount >= _rdpxAmount, 1);
            _validate(expiry >= block.timestamp, 2);
            _validate(IRdpxDecayingBonds(addresses.rdpxDecayingBonds).ownerOf(_bondId) == msg.sender, 9);
            ...
}
```

// Code block 1 (unknown):
file:contracts/core/RdpxV2Core.sol
function _curveSwap(
    uint256 _amount,
    bool _ethToDpxEth,
    bool validate,
    uint256 minAmount
  ) internal returns (uint256 amountOut) {
    ...
    // calculate minimum amount out
    uint256 minOut = _ethToDpxEth
      ? (((_amount * getDpxEthPrice()) / 1e8) -
        (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16))
      : (((_amount * getEthPrice()) / 1e8) -
        (((_amount * getEthPrice()) * slippageTolerance) / 1e16));

    // Swap the tokens
    amountOut = dpxEthCurvePool.exchange(
      _ethToDpxEth ? int128(int256(a)) : int128(int256(b)),
      _ethToDpxEth ? int128(int256(b)) : int128(int256(a)),
      _amount,
      minAmount > 0 ? minAmount : minOut
    );
  }

// Code block 2 (unknown):
function _transfer(uint256 _rdpxAmount, uint256 _wethAmount, uint256 _bondAmount, uint256 _bondId) internal {
        if (_bondId != 0) {
            (, uint256 expiry, uint256 amount) = IRdpxDecayingBonds(addresses.rdpxDecayingBonds).bonds(_bondId); //@audit (gas) you can get the owner here and not call ownerOf

            _validate(amount >= _rdpxAmount, 1);
            _validate(expiry >= block.timestamp, 2);
            _validate(IRdpxDecayingBonds(addresses.rdpxDecayingBonds).ownerOf(_bondId) == msg.sender, 9);
            ...
}


contractcops Q

## [L-01] changeFee can only be set once & unused parameters in flashFee(address, uint256)
In PrivatePool.sol:
```
87:   /// @notice The change/flash fee to 4 decimals of precision. For example, 0.0025 ETH = 25. 500 USDC = 5_000_000.
88:   uint56 public changeFee;
```
The change fee is then initialized in the initialize(...) function
```
    function initialize(
        address _baseToken,
        address _nft,
        uint128 _virtualBaseTokenReserves,
        uint128 _virtualNftReserves,
        uint56 _changeFee,
        ...
        ...
               changeFee = _changeFee;
```

There are no available setters or ways to change the changeFee (flashFee) after the pool is initialized. Furthermore, flashFee() function takes two parameters that are not used to alter the changeFee in any way.

```
750:    function flashFee(address, uint256) public view returns (uint256) {
            return changeFee;
        }
...
631:   // calculate the fee
632:   uint256 fee = flashFee(token, tokenId);
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L632

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

## [I-01] Comment fixes
// add the royalty fee amount to the net input aount
- fix the "aount" to "amount"
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L251

// loop through and execute the the buys
- remove "the"

https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L105

## [I-02] Confusing/unnecessary imports

Change the import from
`import {IERC3156FlashBorrower} from         "openzeppelin/interfaces/IERC3156FlashLender.sol";`
to
```
import {IERC3156FlashBorrower} from
"openzeppelin/interfaces/IERC3156FlashBorrower.sol";
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L34

## [I-03] Remove ReservoirOracle as it is not necessary
The Message struct used in `IStolenNftOracle` is copied from `ReservoirOracle`. Therefore it is not ne

// Code block 1 (unknown):
87:   /// @notice The change/flash fee to 4 decimals of precision. For example, 0.0025 ETH = 25. 500 USDC = 5_000_000.
88:   uint56 public changeFee;

// Code block 2 (unknown):
function initialize(
        address _baseToken,
        address _nft,
        uint128 _virtualBaseTokenReserves,
        uint128 _virtualNftReserves,
        uint56 _changeFee,
        ...
        ...
               changeFee = _changeFee;

// Code block 3 (unknown):
750:    function flashFee(address, uint256) public view returns (uint256) {
            return changeFee;
        }
...
631:   // calculate the fee
632:   uint256 fee = flashFee(token, tokenId);

// Code block 4 (unknown):
import {IERC3156FlashBorrower} from
"openzeppelin/interfaces/IERC3156FlashBorrower.sol";

PrivatePool.sol#L632, PrivatePool.sol#L750, PrivatePool.sol#L251, EthRouter.sol#L105, PrivatePool.sol#L34


carlitox477 G

# `SafEth.stake()` can use `derivatives` and `weights` storage pointer to save gas in both for loop
```diff
    function stake() external payable {
        require(pauseStaking == false, "staking is paused");
        require(msg.value >= minAmount, "amount too low");
        require(msg.value <= maxAmount, "amount too high");

        uint256 underlyingValue = 0;
+       mapping(uint256 => IDerivative) storage _derivatives = derivatives;
+       IDerivative _derivative;
        // Getting underlying value in terms of ETH for each derivative
        for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
-               (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
-                   derivatives[i].balance()) /
+               _derivative = _derivatives[i];
+               (_derivative.ethPerDerivative(_derivative.balance()) *
+                   _derivative.balance()) /
                10 ** 18;

        uint256 totalSupply = totalSupply();
        uint256 preDepositPrice; // Price of safETH in regards to ETH
        if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

        uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
+       mapping(uint256 => uint256) storage _weights = weights;
        for (uint i = 0; i < derivativeCount; i++) {
-           uint256 weight = weights[i];
+           uint256 weight = _weights[i];
-           IDerivative derivative = derivatives[i];
+           _derivative = _derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            // This is slightly less than ethAmount because slippage
            uint256 depositAmount = derivative.deposit{value: ethAmount}();
            uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
                depositAmount
            )

# `SafEth.stake()` can cache `derivativeCount` and `totalWeight` to save gas
This will reduce storage access in both for loops in each iteration

# `SafEth.stake()` can cache `derivatives[i].balance()`
In the first for loop, 2 external calls can be reduced to one by saving the queried value:


0xSmartContract G

### Gas Optimizations List
| Number | Optimization Details | Context |
|:--:|:-------| :-----:|
| [G-01] |Structs can be packed into fewer storage slots| 2 |
| [G-02] |Function Calls in a Loop Can Cause Denial of Service (DOS) and out of gas due to not checking the Array length| 8 |
| [G-03] |Missing `zero-address` check in `constructor`|2 |
| [G-04] |Duplicated require()/if() checks should be refactored to a modifier or function| 7 |
| [G-05] |if () / require() statements that check input arguments should be at the top of the functions |2|
| [G-06] |Use ``calldata`` instead of ``memory`` for function arguments that do not get mutated|11 |
| [G-07] |Use assembly to check for `address(0)` |12 |
| [G-08] | `x += y (x -= y)` costs more gas than `x = x + y (x = x - y)` for state variables |4 |
| [G-09] | Change ``public`` function visibility to ``external`` | 17 |
| [G-10] |Use ``assembly`` to write _address storage values_ | 9 |
| [G-11] | Setting the _constructor_ to `payable`|3 |
| [G-12] |Empty blocks should be removed or emit something | 4 |
| [G-13] |Functions guaranteed to _revert_ when callled by normal users can be marked `payable`  | 11 |
| [G-14] |Use nested if and, avoid multiple check combinationsd|10 |
| [G-15] |Optimize names to save gas|  |

Total 15 issues

### [G-01] Structs can be packed into fewer storage slots

As the solidity EVM works with 32 bytes, variables less than 32 bytes should be packed inside a struct so that they can be stored in the same slot, this saves gas when writing to storage ~20000 gas.

2 results - 2 files:

|Title | Total Gas Saved | Instance|
|:--:|:-------:|:--:|
|Struct packed | 4k | 2 |

1. The ``Buy`` structure can be packaged as suggested below  (from 7 slots to 6 slots)

    **1 slot win: 1 * 2k  =  2k gas saved**

```solidity
src\EthRouter.sol:

  48:     struct Buy {
  49:         address payable pool;
  50:         address nft;
  51:         uint256[] tokenIds;
  52:         uint256[] tokenWeights;
  53:         Priva

src\EthRouter.sol:

  48:     struct Buy {
  49:         address payable pool;
  50:         address nft;
  51:         uint256[] tokenIds;
  52:         uint256[] tokenWeights;
  53:         PrivatePool.MerkleMultiProof proof;
  54:         uint256 baseTokenAmount;
  55:         bool isPublicPool;
  56:     }

2. The ``Sell`` structure can be packaged as suggested below. (from 6 slots to 5 slots)

    **1 slot win: 1 * 2k  =  2k gas saved**


0xAnah G

# REVERT LEND GAS OPTIMIZATIONS

## INTRODUCTION

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

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

## [G-01] Use the existing Local variable/global variable when equal to a state variable to avoid reading from state

### 1 Instances
1. #### Use `currentShares` in-place of `loan.debtShares` in the statement `uint256 loanDebtShares = loan.debtShares - shares`
- https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L990

In the `_repay()` function as shown below on [line 959](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L959) the state variable `loan.debtShares` is being cached in `currentShares` variable. Therefore we could use `currentShares` in place of `loan.debtShares` subsequently but this was not the case on line [line 959](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L959) where `loan.debtShares` was used in the statement `uint256 loanDebtShares = loan.debtShares - shares` thereby incurring an `SLOAD` Gwarmaccess `100` gas units where we could have use a stack variable. The diff below shows how the code should be refactored:

```solidity
file: src/V3Vault.sol

954:     function _repay(uint256 tokenId, uint256 amount, bool isShare, bytes memory permitData) internal {
955:         (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96) = _updateGlobalInterest();
956:
957:         Loan storage loan = loans[tokenId];
958:
959:         uint256 currentShares = loan.debtShares;
960:
961:         uint256 shares;
962:         uint256 assets;
963:

file: src/V3Vault.sol

954:     function _repay(uint256 tokenId, uint256 amount, bool isShare, bytes memory permitData) internal {
955:         (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96) = _updateGlobalInterest();
956:
957:         Loan storage loan = loans[tokenId];
958:
959:         uint256 currentShares = loan.debtShares;
960:
961:         uint256 shares;
962:         uint256 assets;
963:
964:         if (isShare) {
965:             shares = amount;
966:             assets = _convertToAssets(amount, newDebtExchangeRateX96, Math.Rounding.Up);
967:         } else {
968:             assets = amount;
969:             shares = _convertToShares(amount, newDebtExchangeRateX96, Math.Rounding.Down);
970:         }
971:
972:         // fails if too much repayed
973:         if (shares > currentShares) {
974:             revert RepayExceedsDebt();
975:         }
976:
977:         if (assets > 0) {
978:             if (permitData.length > 0) {
979:                 (ISignatureTransfer.PermitTransferFrom memory permit, bytes memory signature) =
980:                     abi.decode(permitData, (ISignatureTransfer.PermitTransferFrom, bytes));
981:                 permit2.permitTransferFrom(
982:                     permit, ISignatureTransfer.SignatureTransferDetails(address(this), assets), msg.sender, signature
983:                 );
984:             } else {
985:                 // fails if not enough token approved
986:                 SafeERC20.safeTransferFrom(IERC20(asset), msg.sender, address(this), assets);
987:             }
988:         }
989:
990:         uint256 loanDebtShares = loan.debtShares - shares;    //@audit use stack variable currentShares in place of loan.debtShares
991:         loan.debtShares = loanDebtShares;
992:         debtSharesTotal -= shares;
993:
994:         // when amounts are repayed - they maybe borrowed again
995:         dailyDebtIncreaseLimitLeft += assets;
.
.
.
1014:    }

## [G-02] Calculations should be memoized rather than re-calculating them
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls to pure functions and returning the cached result when the same inputs occur again.

### 2 Instances
1. ####  We can memoize the computations of `SafeCast.toUint192(oldShares - newShares)`
- https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L1217
2. ####  We can memoize the computations of `SafeCast.toUint192(newShares - oldShares)`
- https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L1220

We can reduce the gas usage of the `_updateAndCheckCollateral()` function by memoizing the computations of `SafeCast.toUint192(oldShares - newShares)` and `SafeCast.toUint192(newShares - oldShares)` i.e cache the results of their computation so that for subsequent computations we can use the cached values rather than having to recompute the values. The diff below shows how the code could be refactored:

V3Vault.sol#L990, V3Vault.sol#L959, V3Vault.sol#L1217, V3Vault.sol#L1220


oyc_109 G

## [G-01] Don't Initialize Variables with Default Value

Uninitialized variables are assigned with the types default value. Explicitly initializing a variable with it's default value costs unnecesary gas.

```
2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::108 => for(uint256 i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::56 => for(uint256 i = 0; i < _collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::206 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::228 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::240 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::72 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::78 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::101 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::107 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::113 => for (uint256 i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::351 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::397 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::640 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::810 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::831 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::859 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::117

2023-02-ethos/Ethos-Core/contracts/ActivePool.sol::108 => for(uint256 i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::56 => for(uint256 i = 0; i < _collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::206 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::228 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::240 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::72 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::78 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::101 => for (uint idx = 0; idx < _startIdx; ++idx) {
2023-02-ethos/Ethos-Core/contracts/MultiTroveGetter.sol::107 => for (uint idx = 0; idx < _count; ++idx) {
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::113 => for (uint256 i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::351 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::397 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::640 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::810 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::831 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::859 => for (uint i = 0; i < numCollaterals; i++) {
2023-02-ethos/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::117 => for (uint256 i = 0; i < numSteps; i = i.uncheckedInc()) {
2023-02-ethos/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::165 => for (uint256 i = 0; i < numSteps; i = i.uncheckedInc()) {
2023-02-et

2023-02-ethos/Ethos-Core/contracts/CollateralConfig.sol::56 => for(uint256 i = 0; i < _collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::206 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/LQTY/LQTYStaking.sol::228 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::640 => for (uint i = 0; i < assets.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::810 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/StabilityPool.sol::831 => for (uint i = 0; i < collaterals.length; i++) {
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::808 => for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::882 => for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {


codeslide G

### Gas Optimizations

| Number | Issue                                     | Instances |
| :----: | :---------------------------------------- | :-------: |
| [G-01] | Don’t initialize integers to zero         |    25     |
| [G-02] | Cache array size before loop              |     8     |
| [G-03] | Unnecessary computation                   |     4     |
| [G-04] | Addition and subtraction syntax           |    15     |
| [G-05] | Check amount before transfer              |     1     |
| [G-06] | Use short circuiting to save gas          |     3     |
| [G-07] | Use a constant instead of type(uintX).max |     1     |
| [G-08] | Use assembly to check for address(0)      |     1     |

#### [G-01] Don’t initialize integers to zero

It costs more gas to initialize variables to zero than to let the default of zero be applied.

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

181:         for (uint256 i = 0; i < indexesLength; ) {
```

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

181:         for (uint256 i = 0; i < indexesLength; ) {

364:         for (uint256 i = 0; i < indexesLength; ) {

474:         uint256 filteredIndexesLength = 0;

476:         for (uint256 i = 0; i < indexesLength; ) {
```

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

163:         for (uint256 i = 0; i < fromBucketLength; ) {

229:         for (uint256 i = 0; i < positionIndexes.length; ) {

290:         for (uint256 i = 0; i < positionIndexes.length; ) {

440:         for (uint256 i = 0; i < positionIndexes_.length; ) {

680:             for (uint256 i = 0; i < indexes_.length; ) {

704:                 for (uint256 i = 0; i < indexes_.length; ) {
```

```solidity
File: ajna-grants/src/grants/base/Funding.sol

62:         for (uint256 i = 0; i < targets_.length; ++i) {

112:         for (uint256 i = 0; i < targets_.length;) {
```

```solidity
File: ajna-grants/src/grants/base/StandardFunding.sol

63:     uint24 internal _currentDistributionId = 0;

208:         for (uint i =

// Code block 1 (solidity):
File: ajna-core/src/PositionManager.sol

181:         for (uint256 i = 0; i < indexesLength; ) {

// Code block 2 (solidity):
File: ajna-core/src/PositionManager.sol

181:         for (uint256 i = 0; i < indexesLength; ) {

364:         for (uint256 i = 0; i < indexesLength; ) {

474:         uint256 filteredIndexesLength = 0;

476:         for (uint256 i = 0; i < indexesLength; ) {

// Code block 3 (solidity):
File: ajna-core/src/RewardsManager.sol

163:         for (uint256 i = 0; i < fromBucketLength; ) {

229:         for (uint256 i = 0; i < positionIndexes.length; ) {

290:         for (uint256 i = 0; i < positionIndexes.length; ) {

440:         for (uint256 i = 0; i < positionIndexes_.length; ) {

680:             for (uint256 i = 0; i < indexes_.length; ) {

704:                 for (uint256 i = 0; i < indexes_.length; ) {

// Code block 4 (solidity):
File: ajna-grants/src/grants/base/Funding.sol

62:         for (uint256 i = 0; i < targets_.length; ++i) {

112:         for (uint256 i = 0; i < targets_.length;) {

File: ajna-core/src/PositionManager.sol

181:         for (uint256 i = 0; i < indexesLength; ) {

File: ajna-core/src/PositionManager.sol

181:         for (uint256 i = 0; i < indexesLength; ) {

364:         for (uint256 i = 0; i < indexesLength; ) {

474:         uint256 filteredIndexesLength = 0;

476:         for (uint256 i = 0; i < indexesLength; ) {

File: ajna-core/src/RewardsManager.sol

163:         for (uint256 i = 0; i < fromBucketLength; ) {

229:         for (uint256 i = 0; i < positionIndexes.length; ) {

290:         for (uint256 i = 0; i < positionIndexes.length; ) {

440:         for (uint256 i = 0; i < positionIndexes_.length; ) {

680:             for (uint256 i = 0; i < indexes_.length; ) {

704:                 for (uint256 i = 0; i < indexes_.length; ) {

File: ajna-grants/src/grants/base/Funding.sol

62:         for (uint256 i = 0; i < targets_.length; ++i) {

112:         for (uint256 i = 0; i < targets_.length;) {


0x_kmr_ Q

GAS OPTIMIZATIONS:

### G[1] Use arithmetic operating instead using loop for calculating totalWeight adjustment
[Link to github permalink](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L165-L175)
```
function adjustWeight(
    uint256 _derivativeIndex,
    uint256 _weight
) external onlyOwner {
+    uint256 oldWeight = weights[_derivativeIndex];
+    weights[_derivativeIndex] = _weight;
+    totalWeight = totalWeight - oldWeight + _weight;

-    weights[_derivativeIndex] = _weight;
-    uint256 localTotalWeight = 0;
-    for (uint256 i = 0; i < derivativeCount; i++)
-       localTotalWeight += weights[i];
-    totalWeight = localTotalWeight;
    emit WeightChange(_derivativeIndex, _weight);

}
```

The code above shows a function called `adjustWeight` that allows the owner to adjust the weight of a derivative. The function has been optimized to reduce gas consumption by eliminating the loop and instead, tracking the old weight, updating the new weight, and recalculating the total weight with a simple arithmetic operation.

This new code adds a variable `oldWeight` to store the original weight of the derivative before updating it. Then, the function updates the derivative's weight with the new weight provided by the owner and recalculates the total weight by subtracting the old weight and adding the new weight. This eliminates the need for a loop that calculates the total weight of all derivatives.

## G[2] Use arithmetic operating instead using loop for new totalWeight calculating
[Link to github permalink](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182-L195)
```
function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = IDerivative(_contractAddress);
        weights[derivativeCount] = _weight;
        derivativeCount++;

       // @remind
 gas/extra calcualti

function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = IDerivative(_contractAddress);
        weights[derivativeCount] = _weight;
        derivativeCount++;

       // @remind
 gas/extra calcualtion
+      totalWeight = totalWeight + _weight;

-      uint256 localTotalWeight = 0;
-       for (uint256 i = 0; i < derivativeCount; i++)
-           localTotalWeight += weights[i];
-       totalWeight = localTotalWeight;
        emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
    }

// Code block 1 (unknown):
function adjustWeight(
    uint256 _derivativeIndex,
    uint256 _weight
) external onlyOwner {
+    uint256 oldWeight = weights[_derivativeIndex];
+    weights[_derivativeIndex] = _weight;
+    totalWeight = totalWeight - oldWeight + _weight;

-    weights[_derivativeIndex] = _weight;
-    uint256 localTotalWeight = 0;
-    for (uint256 i = 0; i < derivativeCount; i++)
-       localTotalWeight += weights[i];
-    totalWeight = localTotalWeight;
    emit WeightChange(_derivativeIndex, _weight);

}


hunter_w3b G

# Gas Optimization

# Summary

| Number | Optimization Details                                                                                | Context |
| :----: | :--------------------------------------------------------------------------------------------------------- | :-----: |
| [G-01] | Gas ineﬃcient implementation of a loop in `VaultController::_getVaultBorrowingPower()`                     |    1    |
| [G-02] | Before some functions we should `check` some variables for possible gas save                               |    7    |
| [G-03] | Internal functions not called by the contract should be `removed` to save deployment gas                   |    3    |
| [G-04] | Make 3 `event` parameters indexed when possible                                                            |   32    |
| [G-05] | Use assembly to write address `storage` values                                                             |   10    |
| [G-06] | Use nested if statements instead of `&&`                                                                   |    7    |
| [G-07] | `keccak256()` should only need to be called on a specific string literal once                              |    4    |
| [G-08] | Should use `arguments` instead of state variables in `emit`                                                |   16    |
| [G-09] | Using `XOR (^)` and `OR ` bitwise equivalents                                                              |    7    |
| [G-10] | Duplicated `if()` checks should be refactored to a modifier or function                                    |   17    |
| [G-11] | Do not calculate constants                                                                                |    8    |
| [G-12] | Use hardcode address instead `address(this)`                                                               |    8    |
| [G-13] | Not using the named return variables when a function returns, wastes deployment gas                        |    6    |
| [G-14] | Can Make

File: contracts/core/VaultController.sol

  function _getVaultBorrowingPower(IVault _vault) private returns (uint192 _borrowPower) {
    // loop over each registed token, adding the indivuduals ltv to the total ltv of the vault
    for (uint192 _i; _i < enabledTokens.length; ++_i) {
      CollateralInfo memory _collateral = tokenAddressCollateralInfo[enabledTokens[_i]];
      // if the ltv is 0, continue
      if (_collateral.ltv == 0) continue;
      // get the address of the token through the array of enabled tokens
      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index
      address _tokenAddress = enabledTokens[_i];
      // the balance is the vault's token balance of the current collateral token in the loop
      uint256 _balance = _vault.balances(_tokenAddress);
      if (_balance == 0) continue;
      // the raw price is simply the oracle price of the token
      uint192 _rawPrice = _safeu192(_collateral.oracle.currentValue());
      if (_rawPrice == 0) continue;
      // the token value is equal to the price * balance * tokenLTV
      uint192 _tokenValue = _safeu192(
        _truncate(_truncate(_balance * _collateral.ltv * _getPriceWithDecimals(_rawPrice, _collateral.decimals)))
      );
      // increase the ltv of the vault by the token value
      _borrowPower += _tokenValue;
    }
  }

File:  core/solidity/contracts/core/AMPHClaimer.sol

129       IERC20(_token).transfer(owner(), _amount);


0xWaitress Q

1. Modify the moveLiquidity function in Position Manager, or add a new one `moveLiquidities` such that the RewardsManager can call moveStakedLiquidity with all indexes, instead of making repeated calls.

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L163-L175

```solidity
        for (uint256 i = 0; i < fromBucketLength; ) {
            fromIndex = fromBuckets_[i];
            toIndex = toBuckets_[i];

            // call out to position manager to move liquidity between buckets
            IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
                tokenId_,
                ajnaPool,
                fromIndex,
                toIndex,
                expiry_
            );
            positionManager.moveLiquidity(moveLiquidityParams);
```

--- \n

2. EnumerableSet from Openzeppelin advises against using `values()` in a write function, however the protocol has been using it for positionIndexes in lots of places, for example `moveStakedLiquidity`, gas estimate is needed for measuring the maximal number of input given a gas limit.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol

```solidity
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }
```

--- \n

3. reedemPositions should be renamed to redeem

// Code block 1 (solidity):
for (uint256 i = 0; i < fromBucketLength; ) {
            fromIndex = fromBuckets_[i];
            toIndex = toBuckets_[i];

            // call out to position manager to move liquidity between buckets
            IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
                tokenId_,
                ajnaPool,
                fromIndex,
                toIndex,
                expiry_
            );
            positionManager.moveLiquidity(moveLiquidityParams);

// Code block 2 (solidity):
/**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

for (uint256 i = 0; i < fromBucketLength; ) {
            fromIndex = fromBuckets_[i];
            toIndex = toBuckets_[i];

            // call out to position manager to move liquidity between buckets
            IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
                tokenId_,
                ajnaPool,
                fromIndex,
                toIndex,
                expiry_
            );
            positionManager.moveLiquidity(moveLiquidityParams);

/**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }


petrichor G

# gas

# summary

|       | issue | instance |
|-------|-------|----------|
|[G-01]|Functions guaranteed to revert when called by normal users can be marked payable|25|
|[G-02]|Use assembly to perform efficient back-to-back calls|2|
|[G-03]|Use assembly to perform efficient back-to-back calls|2|
|[G-04]|Avoid emitting storage values|2|
|[G-05]|Use Assembly To Check For address(0)|8|
|[G-06]|Use do while loops instead of for loops|2|
|[G-07]|not equal to zero" (!= 0) or the "equal to zero" (== 0)|1|

## [G-01] 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.

```solidity
File:   contracts/bridge/DestinationBridge.sol
210  function addApprover(address approver) external onlyOwner {

220  function removeApprover(address approver) external onlyOwner {

234    function addChainSupport(
    string calldata srcChain,
    string calldata srcContractAddress
  ) external onlyOwner {

255    function setThresholds(
    string calldata srcChain,
    uint256[] calldata amounts,
    uint256[] calldata numOfApprovers
  ) external onlyOwner {

286   function setMintLimit(uint256 mintLimit) external onlyOwner {

295  function setMintLimitDuration(uint256 mintDuration) external onlyOwner {

304  function pause() external onlyOwner {

313   function unpause() external onlyOwner {

322   function rescueTokens(address _token) external onlyOwner {
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L210
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L220
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L234
https://github.com

File:  contracts/bridge/SourceBridge.sol
121     function setDestinationChainContractAddress(
    string memory destinationChain,
    address contractAddress
  ) external onlyOwner {

136  function pause() external onlyOwner {

145  function unpause() external onlyOwner {

// Code block 1 (solidity):
File:   contracts/bridge/DestinationBridge.sol
210  function addApprover(address approver) external onlyOwner {

220  function removeApprover(address approver) external onlyOwner {

234    function addChainSupport(
    string calldata srcChain,
    string calldata srcContractAddress
  ) external onlyOwner {

255    function setThresholds(
    string calldata srcChain,
    uint256[] calldata amounts,
    uint256[] calldata numOfApprovers
  ) external onlyOwner {

286   function setMintLimit(uint256 mintLimit) external onlyOwner {

295  function setMintLimitDuration(uint256 mintDuration) external onlyOwner {

304  function pause() external onlyOwner {

313   function unpause() external onlyOwner {

322   function rescueTokens(address _token) external onlyOwner {


minhquanym Q

# Summary

| Id | Title |
| -- | ----- |
| 1 | No Storage Gap for Upgradeable Contracts |
| 2 | Admin manager can steal funds approved to CashManager |

# 1. No Storage Gap for Upgradeable Contracts

https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/cash/kyc/KYCRegistryClient.sol#L28

## Detail
For upgradeable contracts, there must be storage gap to "allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments". Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts.

Refer to the bottom part of this article: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

## Recommendation
Recommend adding appropriate storage gap at the end of upgradeable contracts such as the below. Please reference OpenZeppelin upgradeable contract templates.
```solidity
uint256[50] private __gap;
```

# 2. Admin manager can steal funds approved to CashManager

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

## Detail
In CashManager contract, admin has a `multiexcall()` function, allowing them to call to arbitrary contract with any calldata. It can be abused to call to ERC20 token contract, and steal all funds that users approved to CashManager contract.
```solidity
function multiexcall(
  ExCallData[] calldata exCallData
)
  external
  payable
  override
  nonReentrant
  onlyRole(MANAGER_ADMIN)
  whenPaused
  returns (bytes[] memory results)
{
  results = new bytes[](exCallData.length);
  for (uint256 i = 0; i < exCallData.length; ++i) {
    (bool success, bytes memory ret) = address(exCallData[i].target).call{
      // @audit m

function multiexcall(
  ExCallData[] calldata exCallData
)
  external
  payable
  override
  nonReentrant
  onlyRole(MANAGER_ADMIN)
  whenPaused
  returns (bytes[] memory results)
{
  results = new bytes[](exCallData.length);
  for (uint256 i = 0; i < exCallData.length; ++i) {
    (bool success, bytes memory ret) = address(exCallData[i].target).call{
      // @audit manager can steal funds approved to this contract of users
      value: exCallData[i].value
    }(exCallData[i].data);
    require(success, "Call Failed");
    results[i] = ret;
  }
}

// Code block 1 (solidity):
uint256[50] private __gap;


Rolezn Q

 ## Summary<a name="Summary">

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#LOW‑1) | Init functions are susceptible to front-running | 2 |
| [LOW‑2](#LOW‑2) | Loss of precision due to rounding | 3 |
| [LOW‑3](#LOW‑3) | Missing parameter validation in `constructor` | 9 |
| [LOW‑4](#LOW‑4) | Missing Contract-existence Checks Before Low-level Calls | 4 |
| [LOW‑5](#LOW‑5) | The `nonReentrant` modifier should occur before all other modifiers | 4 |
| [LOW‑6](#LOW‑6) | Contracts are not using their OZ Upgradeable counterparts | 14 |
| [LOW‑7](#LOW‑7) | Prevent division by 0 | 1 |
| [LOW‑8](#LOW‑8) | `require()` should be used instead of `assert()` | 3 |
| [LOW‑9](#LOW‑9) | Use `safetransfer` Instead Of `transfer`  | 6 |
| [LOW‑10](#LOW‑10) | Admin privilege - A single point of failure can allow a hacked or malicious owner use critical functions in the project | 10 |
| [LOW‑11](#LOW‑11) | TransferOwnership Should Be Two Step | 3 |
| [LOW‑12](#LOW‑12) | No Storage Gap For Upgradeable Contracts | 4 |
| [LOW‑13](#LOW‑13) | Use `safeTransferOwnership` instead of `transferOwnership` function | 3 |
| [LOW‑14](#LOW‑14) | Missing Non Reentrancy Modifiers | 1 |

Total: 67 contexts over 14 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#NC‑1) | Add a timelock to critical functions | 36 |
| [NC‑2](#NC‑2) | Avoid Floating Pragmas: The Version Should Be Locked | 11 |
| [NC‑3](#NC‑3) | Compliance with Solidity Style rules in Constant expressions | 15 |
| [NC‑4](#NC‑4) | Expressions for constant values such as a call to `keccak256()`, should use `immutable` rather than `constant` | 14 |
| [NC‑5](#NC‑5) | Critical Changes Should Use Two-step Procedure | 36 |
| [NC‑6](#NC‑6)


RaymondFam G

## Private function with embedded modifier reduces contract size
Consider having the logic of a modifier embedded through a private function to reduce contract size if need be. A private visibility that saves more gas on function calls than the internal visibility is adopted because the modifier will only be making this call inside the contract.

For instance, the modifier instance below may be refactored as follows just as it has been implemented in [SelfAuthorized.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/common/SelfAuthorized.sol):

[File: SmartAccount.sol#L82-L85](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L82-L85)

```diff
+    function _mixedAuth() private view {
+        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+    }

   modifier mixedAuth {
-        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+        _mixedAuth();
       _;
   }
```
## Unneeded `uint256()` cast
Casting an unsigned integer to `uint256()` is unnecessary.

For instance, the code line below may be refactored to save gas both on contract deployment and function call as follows:

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

```diff
-              require(uint256(s) >= uint256(1) * 65, "BSA021");
+              require(uint256(s) >= 65, "BSA021");
```
Note: Since `1 * 65 == 65`, replacing `uint256(1) * 65` with `65` instead of `1 * 65` saves even more gas.

## Use of named returns for local variables saves gas
You can have further advantages in term of gas cost by simply using named return values as temporary local variable.

For instance, the code block below may be refactored as follows:

[File: SmartAccountNoAuth.sol#L155-L159](https://github.com/code-423n4/2023-01

Note: Since `1 * 65 == 65`, replacing `uint256(1) * 65` with `65` instead of `1 * 65` saves even more gas.

## Use of named returns for local variables saves gas
You can have further advantages in term of gas cost by simply using named return values as temporary local variable.

For instance, the code block below may be refactored as follows:

[File: SmartAccountNoAuth.sol#L155-L159](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol#L155-L159)

// Code block 1 (diff):
+    function _mixedAuth() private view {
+        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+    }

   modifier mixedAuth {
-        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+        _mixedAuth();
       _;
   }

// Code block 2 (diff):
-              require(uint256(s) >= uint256(1) * 65, "BSA021");
+              require(uint256(s) >= 65, "BSA021");

+    function _mixedAuth() private view {
+        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+    }

   modifier mixedAuth {
-        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
+        _mixedAuth();
       _;
   }

-              require(uint256(s) >= uint256(1) * 65, "BSA021");
+              require(uint256(s) >= 65, "BSA021");

SelfAuthorized.sol, SmartAccount.sol#L82-L85, SmartAccount.sol#L322, SmartAccountNoAuth.sol#L155-L159


0xBinChook Q

# AI Arena - QA Report

| Id            | Issue                                                                                |
|---------------|---------------------------------------------------------------------------------------------------------------|
| [L-1](#l-1)   | Consider two-step confirmation for ownership transfer                                                         |
| [L-2](#l-2)   | `Verification::verify()` allows malleable (non-unique) signature                                              |
| [L-3](#l-3)   | `Verification::verify()` can return `true` for invalid signatures                                             |
| [L-4](#l-4)   | `FighterFarm::_delegatedAddress` cannot be updated                                                            |
| [L-5](#l-5)   | Neuron allowance spend during burn is inconsistent with OZ ERC20 being extended                               |
| [L-6](#l-6)   | Incrementing roundId in pickWinner allows ending rounds accidentally                                          |
| [L-7](#l-7)   | Players are not allowed to claim only a portion of their reward                                               |
| [L-8](#l-8)   | Inconsistent array lengths unnecessarily penalize the Player                                                  |
| [L-9](#l-9)   | Player can set invalid customAttributes which is undesirable                                                  |
| [L-10](#l-10) | Fighter point retrieval will be unable to access higher order Fighters                                        |
| [L-11](#l-11) | Iterating through all rounds means Fighter NFTs created after a certain round number can never claim a reward |
| [L-12](#l-12) | Allowing mid-round NRN Distribution alteration is unfair to Players                                           |
| [L-13](#l-13) | Allowing mid-round bpsLostPerLoss alteration is unfair to Players                                             |
| [NC-1](#nc-1) | Players

### L-4
#### `FighterFarm::_delegatedAddress` cannot be updated
Unlike the other members of `FighterFarm`, `_delegatedAddress` lacks any setter or instantiate function to update the address.

The uses for `_delegatedAddress` are setting the tokenURI and signing the messages players send in `FighterFarm::claimFighters()` to mint Fighter NFTs.
If control of the address is compromised, and infinite number of Fighter NFTs could be minted, and despite still controlling the owner address, nothing can be done.

As  `_delegatedAddress` is not `immutable`, there's a fair chance the setter was simply overlooked.

##### Recommended Mitigation
Add the setter to [FighterFarm](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L184)

### L-5
#### Neuron allowance spend during burn is inconsistent with OZ ERC20 being extended
In `Neuron::burn()` the allowance is always decremented by the amount burnt, while the Open Zeppelin ERC20 [spend allowance](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/96e5c0830a83b61197dd4e29df59cb56499600ef/contracts/token/ERC20/ERC20.sol#L305-L315) treats ` type(uint256).max` as unlimited (does not decrement).

The transfer functions of `Neuorn` will use the OZ behaviour, which is inconsistent with how the burn function will behave.

##### Recommended Mitigation
Replicate the OZ allowance behaviour in [Neuron::burn()](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L201-L203)