Infect3d Q

# Low Risk and Non-Critical Issues

## N-01 Better to use `__ownable_init()` instead of `_transferOwnership()` as this is the recommended way to initialize the Ownable contract.
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L53

##### SafEth::initialize

```solidity
File: contracts\SafeEth.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);

```

## N-02 No event triggered when `minAmount` and `maxAmount` are set
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L54-L55
Importnat changes to the protocol should be emitted through events

##### SafeEth::initialize
```solidity
File: contracts\SafeEth.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:     }

```

// Code block 1 (solidity):
File: contracts\SafeEth.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);

// Code block 2 (solidity):
File: contracts\SafeEth.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:     }

File: contracts\SafeEth.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);

File: contracts\SafeEth.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:     }


seeu G

## Missing implementation Shift Right/Left for division and multiplication

### Description

The `SHR` opcode only utilizes 3 gas, compared to the 5 gas used by the `DIV` opcode. Additionally, shifting is used to get around Solidity's division operation's division-by-0 prohibition.

### Findings

```
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L36 => return (a & b) + (a ^ b) / 2;
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
```

### References
- [G008 - Use Shift Right/Left instead of Division/Multiplication if possible](https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md#g008---use-shift-rightleft-instead-of-divisionmultiplication-if-possible)
- [EVM Opcodes](https://www.evm.codes/)

// Code block 1 (unknown):
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L36 => return (a & b) + (a ^ b) / 2;
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;


0xta G

# Gas Optimization

## [G-01] Pre-increment and pre-decrement are cheaper than +1 ,-1

```solidity
File: src/stable/CollateralAndLiquidity.sol

350		return findLiquidatableUsers( 0, numberOfUsersWithBorrowedUSDS() - 1 );
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L350

```solidity
File: src/stable/StableConfig.sol

52			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - rewardPercentForCallingLiquidation - 1;

128			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - 1 - rewardPercentForCallingLiquidation;
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/StableConfig.sol#L52

```solidity
File: src/staking/Staking.sol

160        Unstake[] memory unstakes = new Unstake[](end - start + 1);

179		return unstakesForUser( user, 0, unstakeIDs.length - 1 );
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/Staking.sol#L160

```solidity
File: src/stable/CollateralAndLiquidity.sol

313		address[] memory liquidatableUsers = new address[](endIndex - startIndex + 1);
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L313

## [G-02] Use ASSEMBLY to validate msg.sender

```solidity
File: src/AccessManager.sol

43    	require( msg.sender == address(dao), "AccessManager.excludedCountriedUpdated only callable by the DAO" );
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/AccessManager.sol#L43

```solidity
File: src/launch/Airdrop.sol

48    	require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Only the BootstrapBallot can call Airdrop.authorizeWallet" );

58    	require( msg.sender == address(exchangeConfig.initialDistribution()), "Airdrop.allowClaiming can only be called by the InitialDistribution contract" );

```

https://github.com/code-423n4/2024-01-salty/blob/main/src/launch/Airdrop.sol#L48

```solidity
File: src/stable/CollateralAndLiquidity.sol

142		r

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

350		return findLiquidatableUsers( 0, numberOfUsersWithBorrowedUSDS() - 1 );

// Code block 2 (solidity):
File: src/stable/StableConfig.sol

52			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - rewardPercentForCallingLiquidation - 1;

128			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - 1 - rewardPercentForCallingLiquidation;

// Code block 3 (solidity):
File: src/staking/Staking.sol

160        Unstake[] memory unstakes = new Unstake[](end - start + 1);

179		return unstakesForUser( user, 0, unstakeIDs.length - 1 );

// Code block 4 (solidity):
File: src/stable/CollateralAndLiquidity.sol

313		address[] memory liquidatableUsers = new address[](endIndex - startIndex + 1);

// Code block 5 (solidity):
File: src/AccessManager.sol

43    	require( msg.sender == address(dao), "AccessManager.excludedCountriedUpdated only callable by the DAO" );

// Code block 6 (solidity):
File: src/launch/Airdrop.sol

48    	require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Only the BootstrapBallot can call Airdrop.authorizeWallet" );

58    	require( msg.sender == address(exchangeConfig.initialDistribution()), "Airdrop.allowClaiming can only be called by the InitialDistribution contract" );

File: src/stable/CollateralAndLiquidity.sol

350		return findLiquidatableUsers( 0, numberOfUsersWithBorrowedUSDS() - 1 );

File: src/stable/StableConfig.sol

52			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - rewardPercentForCallingLiquidation - 1;

128			uint256 remainingRatioAfterReward = minimumCollateralRatioPercent - 1 - rewardPercentForCallingLiquidation;

File: src/staking/Staking.sol

160        Unstake[] memory unstakes = new Unstake[](end - start + 1);

179		return unstakesForUser( user, 0, unstakeIDs.length - 1 );

File: src/stable/CollateralAndLiquidity.sol

313		address[] memory liquidatableUsers = new address[](endIndex - startIndex + 1);

File: src/AccessManager.sol

43    	require( msg.sender == address(dao), "AccessManager.excludedCountriedUpdated only callable by the DAO" );

File: src/launch/Airdrop.sol

48    	require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Only the BootstrapBallot can call Airdrop.authorizeWallet" );

58    	require( msg.sender == address(exchangeConfig.initialDistribution()), "Airdrop.allowClaiming can only be called by the InitialDistribution contract" );

CollateralAndLiquidity.sol#L350, StableConfig.sol#L52, Staking.sol#L160, CollateralAndLiquidity.sol#L313, AccessManager.sol#L43, Airdrop.sol#L48

Airdrop.sol, AccessManager.sol, CollateralAndLiquidity.sol, Staking.sol, StableConfig.sol


cheatc0d3 G

## Gas Optimization Report for Kelp DAO

### Use Smaller Integer Types for State Variables

Smaller integer types can reduce gas costs significantly when used in state variables.

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L16

**Instances:**
- `depositLimitByAsset` mapping uses `uint256` for deposit limits.

```solidity
mapping(address token => uint256 amount) public depositLimitByAsset;
```

**Proposed Changes:**
If the expected range of `depositLimitByAsset` values is within the limits of a smaller integer type, consider using `uint128` or another smaller type.

```solidity
mapping(address token => uint128 amount) public depositLimitByAsset;
```

### Caching Results of External Functions

Repeatedly calling the same external function in a transaction can be optimized by caching its result in a local variable.

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L80

**Instances:**
- Multiple checks of `isSupportedAsset[asset]` in `_addNewSupportedAsset` function.

```solidity
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    if (isSupportedAsset[asset]) {
        revert AssetAlreadySupported();
    }
    // ...
}
```

**Proposed Changes:**
Cache the value of `isSupportedAsset[asset]` in a local variable to reduce gas costs.

```solidity
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    bool assetSupported = isSupportedAsset[asset];
    if (assetSupported) {
        revert AssetAlreadySupported();
    }
    // ...
}
```

### Using Immutable for Fixed Values

Immutable variables are cheaper to access than regular state variables.

**Instances:**
- `rsETH` address is a regular state variable.

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L21

```solidity
address public rsETH;
```

**Proposed Changes:**
If `rsETH` is set once and does not change, conside

// Code block 1 (solidity):
mapping(address token => uint256 amount) public depositLimitByAsset;

// Code block 2 (solidity):
mapping(address token => uint128 amount) public depositLimitByAsset;

// Code block 3 (solidity):
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    if (isSupportedAsset[asset]) {
        revert AssetAlreadySupported();
    }
    // ...
}

// Code block 4 (solidity):
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    bool assetSupported = isSupportedAsset[asset];
    if (assetSupported) {
        revert AssetAlreadySupported();
    }
    // ...
}

// Code block 5 (solidity):
address public rsETH;

mapping(address token => uint256 amount) public depositLimitByAsset;

mapping(address token => uint128 amount) public depositLimitByAsset;

function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    if (isSupportedAsset[asset]) {
        revert AssetAlreadySupported();
    }
    // ...
}

function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
    UtilLib.checkNonZeroAddress(asset);
    bool assetSupported = isSupportedAsset[asset];
    if (assetSupported) {
        revert AssetAlreadySupported();
    }
    // ...
}

address public rsETH;


0xMango Q

## It is possible to use invalid signatures to vote in ```BoostrapBallot.sol```

Signature malleability:
Due to the lack of checks in the ```SigningTools.sol``` contract, given signatures can be modified for invalid ```s``` and ```v```. These signatures if passed into ```SigningTools._verifySignature()``` will still generate the desired public key & have the function call pass.

Affected line in question:
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/SigningTools.sol#L11

As proof of this being possible, this modified signature can be used instead of the provided signature in dev/Deployment.sol ln:120.
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dev/Deployment.sol#L120

Modified Signature:
```
hex"0x291f777bcf554105b4067f14d2bb3da27f778af49fe2f008e718328a91cae2f8e1314f4b12e29a3ab940f9fc393caa97141250333d7957bb1d3c1093d96480e11b"
```

Original Signature:
```
hex"291f777bcf554105b4067f14d2bb3da27f778af49fe2f008e718328a91cae2f81eceb0b4ed1d65c546bf0603c6c35567a69c8cb371cf4880a2964df8f6d1c0601c";
```

This signature will successfully sign tx's with the dataHash of ```keccak256(abi.encode(block.chainid, msg.sender));```
Where the chainid is the one from Sepolia & msg.sender being Alice's public key.

Solution:
Using OZ ECDSA.sol contract, which has the proper error handling for invalid signatures and ensures for proper lower ```s``` values.

## Uniswapv3's TWAP could potentially be manipulated to cause a DOS or price manipulation

Uniswap's v3 TWAP oracle can be manipulated by validators with high stake.
Due to the nature of Uniswapv3's geometric mean, the price change needs to occur multiple times, to eventually affect the resulting price feed.
Source:
https://chainsecurity.com/oracle-manipulation-after-merge/
Unfortunately, the amount of times might not have to be that many. To write an observation in UniswapV3 pools, users must pay extra gas, to make the addition to the a

// Code block 1 (unknown):
Signature malleability:
Due to the lack of checks in the

// Code block 2 (unknown):
hex"0x291f777bcf554105b4067f14d2bb3da27f778af49fe2f008e718328a91cae2f8e1314f4b12e29a3ab940f9fc393caa97141250333d7957bb1d3c1093d96480e11b"

// Code block 3 (unknown):
hex"291f777bcf554105b4067f14d2bb3da27f778af49fe2f008e718328a91cae2f81eceb0b4ed1d65c546bf0603c6c35567a69c8cb371cf4880a2964df8f6d1c0601c";

// Code block 4 (unknown):
Where the chainid is the one from Sepolia & msg.sender being Alice's public key.

Solution:
Using OZ ECDSA.sol contract, which has the proper error handling for invalid signatures and ensures for proper lower


0x180db Q

# L-01
Potential DoS: If the admin sets the `_fundingDuration` variable to a value of `0` in the `updateFundingDuration` function, then the `nextFundingPaymentTimestamp()` function will always return the value of the `genesis` variable. As a result, the expression in the loop of the `updateFundingPaymentPointer()` function will always evaluate to `true`.
```
  function updateFundingDuration(
    uint256 _fundingDuration
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    fundingDuration = _fundingDuration;
  }
```
[https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L237](https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L237)

```
  function nextFundingPaymentTimestamp()
    public
    view
    returns (uint256 timestamp)
  {
    return genesis + (latestFundingPaymentPointer * fundingDuration);
  }
```
[https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L568](https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L568)

```
 function updateFundingPaymentPointer() public {
    while (block.timestamp >= nextFundingPaymentTimestamp()) {
        ...
    }
 }
```
[https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L463](https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVault.sol#L463)

## Recommended Mitigation Steps
To address this, add validation for the value of the `_funding` variable
```
    _validate(_fundingDuration > 0, 1);
```

# L-02
Unused function `getLpTokenBalanceInWeth()`

```
> grep -r -n "getLpTokenBalanceInWeth" .
./amo/UniV2LiquidityAmo.sol:372:  function

// Code block 1 (unknown):
function updateFundingDuration(
    uint256 _fundingDuration
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    fundingDuration = _fundingDuration;
  }

// Code block 2 (unknown):
function nextFundingPaymentTimestamp()
    public
    view
    returns (uint256 timestamp)
  {
    return genesis + (latestFundingPaymentPointer * fundingDuration);
  }

// Code block 3 (unknown):
function updateFundingPaymentPointer() public {
    while (block.timestamp >= nextFundingPaymentTimestamp()) {
        ...
    }
 }

// Code block 4 (unknown):
_validate(_fundingDuration > 0, 1);


IllIllI G

## Summary

### Gas Optimizations
| |Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
| [G‑01] | `internal` functions only called once can be inlined to save gas | 9 |  180 |
| [G‑02] | Add `unchecked {}` for subtractions where the operands cannot underflow because of a previous `require()` or `if`-statement | 2 |  170 |
| [G‑03] | `++i`/`i++` should be `unchecked{++i}`/`unchecked{i++}` when it is not possible for them to overflow, as is the case when used in `for`- and `while`-loops | 5 |  300 |
| [G‑04] | `require()`/`revert()` strings longer than 32 bytes cost extra gas | 1 |  - |
| [G‑05] | `keccak256()` should only need to be called on a specific string literal once | 1 |  42 |
| [G‑06] | Optimize names to save gas | 21 |  462 |
| [G‑07] | `>=` costs less gas than `>` | 1 |  3 |
| [G‑08] | Splitting `require()` statements that use `&&` saves gas | 3 |  9 |
| [G‑09] | `require()` or `revert()` statements that check input arguments should be at the top of the function | 6 |  - |
| [G‑10] | Functions guaranteed to revert when called by normal users can be marked `payable` | 4 |  84 |

Total: 53 instances over 10 issues with **1250 gas** saved

Gas totals use lower bounds of ranges and count two iterations of each `for`-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

## Gas Optimizations

### [G‑01]  `internal` functions only called once can be inlined to save gas
Not inlining costs **20 to 40 gas** because of two extra `JUMP` instructions and additional stack operations needed for function calls.

*There are 9 instances of this issue:*

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

203:      function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUse

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

203:      function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure {

248:      function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal view returns (uint256 requiredPrefund) {

261:      function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal {

289       function _validateAccountPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, address aggregator, uint256 requiredPrefund)
290:      internal returns (uint256 gasUsedByValidateAccountPrepayment, address actualAggregator, uint256 deadline) {

349:      function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment) internal returns (bytes memory context, uint256 deadline) {

496:      function min(uint256 a, uint256 b) internal pure returns (uint256) {

500:      function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) {

504:      function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) {

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

181:      function max(uint256 a, uint256 b) internal pure returns (uint256) {


JCN G

# Summary
A majority of the optimizations were benchmarked via the protocol's tests, i.e. using the following config: `solc version 0.8.19`, `optimizer on`, and `200 runs`. Optimizations that were not benchmarked are explained via EVM gas costs and opcodes.

Below are the overall average gas savings for the following tested functions (with all the optimizations applied):
| Function |    Before   |    After   | Avg Gas Savings |
| ------ | -------- | -------- | ------- |
| EthRouter.buy |  199750  |  190464  |  9286 |
| EthRouter.change |  217295  |  202568  |  14727 |
| EthRouter.deposit |  29900  |  29393  | 507 |
| EthRouter.sell |  232102  |  223981  | 8121 |
| Factory.create |  148801  |  148672 | 129 |
| PrivatePool.buy |  70884  |  69821  | 1063 |
| PrivatePool.change |  82138  |  78083  | 4055 |
| PrivatePool.execute |  18890  |  18550  | 340 |
| PrivatePool.flashLoan |  83063  |  82915  | 148 |
| PrivatePool.sell |  81969  |  81284  | 685 |
| PrivatePool.withdraw |  62023  |  61038  | 985 |

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

*Notes*:

- The [Gas report](#gasreport-output-with-all-optimizations-applied) output, after all optimizations have been applied, can be found at the end of the report.
- The final diffs for each contract, with all the optimizations applied, can be found [here](https://gist.github.com/0xJCN/b03b5f1f8cabc937c8bbe4d4a46b8d47).
- Some code snippets may be truncated to save space. Code snippets may also be accompanied by `@audit` tags in comments to aid in explaining the issue.

## Gas Optimizations
| Number |Issue|Instances|
|-|:-|:-:|
| [G-01](#cache-calldatamemory-pointers-for-complex-types-to-avoid-offset-calculations) | Cache calldata/memory pointers for complex types to avoid offset calculations | 52 |
| [G-02](#use-calldata-instead-of-memory-for-function-arguments-that-do-not-get-mutated) | Use calldata instead of memory for function arguments that do not get mutated | 4 |
| [G-03](#state-variable

File: src/EthRouter.sol
106:        for (uint256 i = 0; i < buys.length; i++) {
107:            if (buys[i].isPublicPool) {
108:                // execute the buy against a public pool
109:                uint256 inputAmount = Pair(buys[i].pool).nftBuy{value: buys[i].baseTokenAmount}(
110:                    buys[i].tokenIds, buys[i].baseTokenAmount, 0
111:                );
112:
113:                // pay the royalties if buyer has opted-in
114:                if (payRoyalties) {
115:                    uint256 salePrice = inputAmount / buys[i].tokenIds.length;
116:                    for (uint256 j = 0; j < buys[i].tokenIds.length; j++) {
117:                        // get the royalty fee and recipient
118:                        (uint256 royaltyFee, address royaltyRecipient) =
119:                            getRoyalty(buys[i].nft, buys[i].tokenIds[j], salePrice);
120:
121:                        if (royaltyFee > 0) {
122:                            // transfer the royalty fee to the royalty recipient
123:                            royaltyRecipient.safeTransferETH(royaltyFee);
124:                        }
125:                    }
126:                }
127:            } else {
128:                // execute the buy against a private pool
129:                PrivatePool(buys[i].pool).buy{value: buys[i].baseTokenAmount}(
130:                    buys[i].tokenIds, buys[i].tokenWeights, buys[i].proof
131:                );
132:            }
133:
134:            for (uint256 j = 0; j < buys[i].tokenIds.length; j++) {
135:                // transfer the NFT to the caller
136:                ERC721(buys[i].nft).safeTransferFrom(address(this), msg.sender, buys[i].tokenIds[j]);
137:            }
138:        }

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

### Cache calldata pointers for `sells[i]` and `sells[i].tokenIds`

*Gas Savings for `EthRouter.sell`, obtained via protocol's tests: Avg 5422 gas*

|        |    Med   |    Max   |   Avg   | # calls  |
| ------ | -------- | -------- | ------- | -------- |
| Before |  217300  |  402940  |  232102 |    7     |
| After  |  212264  |  392556  |  226680 |    7     |


SleepingBugs Q

# Low

| | Issue index |
| ----------- | ----------- |
| 1 | [New `tvlCap` can be less than current value locked](#new-`tvlcap`-can-be-less-than-current-value-locked) |
| 2 | [Use of `asserts()`](#use-of-`asserts()`) |
| 3 | [Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions](#upgradeable-contract-is-missing-a-`__gap[50]`-storage-variable-to-allow-for-new-storage-variables-in-later-versions) |
| 4 | [Missing important events emissions affects off-chain monitoring](#missing-important-events-emissions-affects-off-chain-monitoring) |
| 5 | [Critical address change should use two-step procedure](#critical-address-change-should-use-two-step-procedure) |
| 6 | [`updateCollateralRatios` is not safe without a two-step procedure](#`updatecollateralratios`-is-not-safe-without-a-two-step-procedure) |
| 7 | [`_safeTransfer()` should be use rather than `_transfer`](#`_safetransfer()`-should-be-use-rather-than-`_transfer`) |
| 8 | [`distributionPeriod` can be changed without anybody noticing and removing `rewardPerSecond` until changed back](#`distributionperiod`-can-be-changed-without-anybody-noticing-and-removing-`rewardpersecond`-until-changed-back) |
| 9 | [`_approve` can be frontrunned](#`_approve`-can-be-frontrunned) |
| 10 | [Wrong comment](#wrong-comment) |
| 11 | [Collateral is not checked to already exist, this leads to unexpected behaviors](#collateral-is-not-checked-to-already-exist,-this-leads-to-unexpected-behaviors) |
| 12 | [Missing `emit` keyword](#missing-`emit`-keyword) |
| 13 | [Single-step process for critical ownership transfer/renounce is risky](#single-step-process-for-critical-ownership-transfer/renounce-is-risky) |
## New `tvlCap` can be less than current value locked

Add a check to ensure new `tvlCap ` is not less than current value locked.

- [ReaperVaultV2.sol#L580](https://github.com/code-423n4/2023-02-ethos/blob/34ba1e7dd2d259f06f9d5fa13f5d96be7829ff34/Ethos-Vault/contracts/ReaperV

    UUPSUpgradeable,
    AccessControlEnumerableUpgradeable

        function fund(uint amount) external onlyOwner {
        require(amount != 0, "cannot fund 0");
        OathToken.transferFrom(msg.sender, address(this), amount);
        // roll any unissued OATH into new distribution
        if (lastIssuanceTimestamp < lastDistributionTime) {
            uint timeLeft = lastDistributionTime.sub(lastIssuanceTimestamp);
            uint notIssued = timeLeft.mul(rewardPerSecond);
            amount = amount.add(notIssued);
        }

        rewardPerSecond = amount.div(distributionPeriod); //@audit div by high value
        lastDistributionTime = block.timestamp.add(distributionPeriod);
        lastIssuanceTimestamp = block.timestamp;

        emit LogRewardPerSecond(rewardPerSecond);
    }

    // Owner-only function to update the distribution period
    function updateDistributionPeriod(uint256 _newDistributionPeriod) external onlyOwner {
        distributionPeriod = _newDistributionPeriod;
    }


7ashraf G

# Gas Efficiency Report

## Summary:

The gas report highlights potential optimizations to reduce gas consumption in the codebase. Instances in [Proposals.sol #90, #324, #326, #329] involve on-chain computations for known results, suggesting gas savings by avoiding unnecessary calculations. Furthermore, [Dao.sol #121] indicates a redundant second `if` check that can be removed for more efficient gas usage. Additionally, [Dao.sol #345] and [StakingRewards.sol #253] suggest areas where using `unchecked` could enhance gas efficiency while maintaining safety. These optimizations aim to mitigate gas costs during contract execution.

### Summary Table:

| Number | Title                                           | Number of Instances |
|--------|-------------------------------------------------|---------------------|
| [G-01]      | Avoid on-chain computation for known results     | 6                   |
| [G-02]      | Eliminate redundant second `if` check           | 1                   |
| [G-03]      | Use `unchecked` for safe gas optimization       | 2                   |

## [G-01] Avoid on-chain computation if the result is known
### Instances
* [Proposals.sol #90](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L90)
```solidity
			uint256 requiredXSalt = ( totalStaked * daoConfig.requiredProposalPercentStakeTimes1000() ) / ( 100 * 1000 );

```
* [Proposals.sol #324](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L324)
```solidity
			requiredQuorum = ( 1 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

```
* [Proposals.sol #326](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L326)
```solidity
			requiredQuorum = ( 2 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

```
* [Proposals.sol #329](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L329)
```solidity
			requiredQuorum = ( 3 * totalStak

// Code block 1 (solidity):
uint256 requiredXSalt = ( totalStaked * daoConfig.requiredProposalPercentStakeTimes1000() ) / ( 100 * 1000 );

// Code block 2 (solidity):
requiredQuorum = ( 1 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

// Code block 3 (solidity):
requiredQuorum = ( 2 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

uint256 requiredXSalt = ( totalStaked * daoConfig.requiredProposalPercentStakeTimes1000() ) / ( 100 * 1000 );

requiredQuorum = ( 1 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

requiredQuorum = ( 2 * totalStaked * daoConfig.baseBallotQuorumPercentTimes1000()) / ( 100 * 1000 );

Proposals.sol#L90, Proposals.sol#L324, Proposals.sol#L326, Proposals.sol#L329


Breeje Q

## QA Report

| |Issue|Instances|
|-|:-|:-:|
| [L-1](#L-1) | NO ZERO ADDRESS CHECK IN CONSTRUCTOR FOR KEY ADDRESSES | 3 |
| [L-2](#L-2) | USE OF FLOATING PRAGMA  | 26 |
| [L-3](#L-3) | USERS CAN'T CLAIM MINT AFTER DEPOSIT AND DEPEND ON `SETTER_ADMIN` TO SET THE EXCHANGE RATE FIRST | 1 |
| [L-4](#L-4) | MISSING CONTRACT-EXISTENCE CHECKS BEFORE LOW-LEVEL CALLS | 1 |
| [NC-1](#NC-1) | USE REQUIRE INSTEAD OF ASSERT | 3 |
| [NC-2](#NC-2) | STOP USING `V == 27 or V == 28` | 1 |
| [NC-3](#NC-3) | RECOMMENDED TO USE OWNABLE2STEP RATHER THAN OWNABLE | 1 |

### [L-1] NO ZERO ADDRESS CHECK IN CONSTRUCTOR FOR KEY ADDRESSES

In `CashManager.sol`, constructor is used to initialize different state variable and grant roles. But there is no Zero Address check on important variables like `managerAdmin` and `pauser`.

It can result in serious consequences given they have been granted Important roles in the constructor.

*Instances:*
```solidity
File:  cash/factory/CashFactory.sol

130:        address managerAdmin,
131:        address pauser,

```
[Link to Code](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/factory/CashFactory.sol#L130-L131)

```solidity
File:  lending/JumpRateModelV2.sol

66:        owner = owner_;

```
[Link to Code](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/JumpRateModelV2.sol#L66)

### [L-2] USE OF FLOATING PRAGMA

*Instances: (26)*
* [JumpRateModelV2.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/JumpRateModelV2.sol#L1)
* [IOndo.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/ondo/ondo-token/IOndo.sol#L2)
* [LinearTimelock.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/ondo/ondo-token/LinearTimelock.sol#L2)
* [CCash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCash.sol#L2)
* [CCashDelegate.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCashDele

// Code block 1 (unknown):
[Link to Code](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/factory/CashFactory.sol#L130-L131)

CashFactory.sol#L130-L131, JumpRateModelV2.sol#L66, JumpRateModelV2.sol#L1, IOndo.sol#L2, LinearTimelock.sol#L2, CCash.sol#L2

IOndo.sol, LinearTimelock.sol, CashFactory.sol, JumpRateModelV2.sol, CCash.sol


TheSavageTeddy G

# Use `unchecked{}` for subtraction operations where operands cannot underflow because of a previous `require` statement
Using `unchecked{}` for operations that cannot underflow because of a previous `require` statement saves gas as it ignores the overflow/underflow checks.

*There is 1 instance of this issue:*
```solidity
File: Reth.sol
200:             require(rethBalance2 > rethBalance1, "No rETH was minted");
201:             uint256 rethMinted = rethBalance2 - rethBalance1; // @audit use unchecked{} cannot overflow because of previous require
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L200-L201

// Code block 1 (solidity):
File: Reth.sol
200:             require(rethBalance2 > rethBalance1, "No rETH was minted");
201:             uint256 rethMinted = rethBalance2 - rethBalance1; // @audit use unchecked{} cannot overflow because of previous require


dharma09 G

# GAS OPTIMIZATIONS

All the gas optimizations are determined based on opcodes and sample tests.

| Count | Issues | Instances | Gas Saved |
| --- | --- | --- | --- |
| [[G-1]](#) | Use of memory instead of storage for struct/array state variables | 3 | 6300 |
| [[G-2]](#) | Emitting storage values instead of the memory one | 11 | 1100 |
| [[G-3]](#) | Use nested if and, avoid multiple check combinations | 3 | 45 |
| [[G-4]](#) | Multiple accesses of a mapping/array should use a local variable cache | 1 | 100 |
| [[G-5]](#) | Redundant External Self Calls  | 3 | 300 |
| [[G-6]](#) | Use calldata instead of memory for function arguments that do not get mutated | 6 | 2100 |
| [[G-7]](#) | Pre-increments and pre-decrements are cheaper than post-increments and post-decrements |1  | 6 |
| [[G-8]](#) | Cache state variables with stack variables | 2 | 400 |

`Total Gas saves 10351`

### [G-01] Use of `memory` instead of `storage` for struct/array state variables

When fetching data from `storage`, using the `memory` keyword to assign it to a variable reads all fields of the struct/array and incurs a Gcoldsload (**2100 gas**) for each field.

This can be avoided by declaring the variable with the `storage` keyword and caching the necessary fields in stack variables.

The `memory` keyword should only be used if the entire struct/array is being returned or passed to a function that requires `memory`, or if it is being read from another `memory` array/struct.

`Gas save 6300`

- https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/Vault.sol#L170

```solidity
File: [/contracts/core/Vault.sol](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/Vault.sol#L170)

170: IVaultController.CollateralInfo memory _collateralInfo = CONTROLLER.tokenCollateralInfo(_tokenAddresses[_i]);
```

- https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L452

```solidity
File:

File: [/contracts/governance/GovernorCharlie.sol](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L452)

449: function getReceipt(
450:    uint256 _proposalId,
451:    address _voter
452:  ) external view override returns (Receipt memory _votingReceipt) { //@audit
453:    _votingReceipt = proposalReceipts[_proposalId][_voter];
454:  }

// Code block 1 (solidity):
File: [/contracts/core/Vault.sol](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/Vault.sol#L170)

170: IVaultController.CollateralInfo memory _collateralInfo = CONTROLLER.tokenCollateralInfo(_tokenAddresses[_i]);


SAQ G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] | Avoid contract existence checks by using low level calls | 2 | - |
| [G-02] | Pre-increments and pre-decrements are cheaper than post-increments and post-decrements | 1 | - |
| [G-03] | Using fixed bytes is cheaper than using string | 2 | - |
| [G-04] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 7 | - |
| [G-05] | Not using the named return variable when a function returns, wastes deployment gas | 2 | - |
| [G-06] | Should use arguments instead of state variable | 4 | - |
| [G-07] | Before transfer of  some functions, we should check some variables for possible gas save | 2 | - |
| [G-08] | With assembly, .call (bool success)  transfer can be done gas-optimized | 2 | - |
| [G-09] | Duplicated require()/if() checks should be refactored to a modifier or function | 1 | - |
| [G-10] | A modifier used only once and not being inherited should be inlined to save gas | 1 | - |
| [G-11] | abi.encode() is less efficient than  abi.encodepacked() | 3 | - |
| [G-12] | Do not calculate  constant | 1 | - |
| [G-13] | require() Should Be Used Instead Of assert() | 1 | - |
| [G-14] | Use hardcode address instead address(this) | 3 | - |
| [G-15] | Use assembly for math (add, sub, mul, div) | 3 | - |
| [G-16] | Multiplication/division by two should use bit shifting | 1 | - |
| [G-17] | Refactor event to avoid emitting empty data | 1 | - |
| [G-18] | 2**<N> should be re-written as type(uint<N>).max | 1 | - |
| [G-19] | Shorten the array rather than copying to a new one | 1 | - |
| [G-20] | Use assembly to validate msg.sender | 6 | - |
| [G-21] | No need to evaluate all expressions to know if one of them is true | 1 | - |
| [G-22] | Make 3 event parameters indexed when possible | 3 | - |

## Gas Optimizations

## [G-01] Avoid contract existence checks by using low level calls

Prior to 0.8.10 the compiler inserted extra code, including E

file: /contracts/bridge/SourceBridge.sol

125    destChainToContractAddr[destinationChain] = AddressToString.toString(
126      contractAddress
127    );

file: /contracts/bridge/DestinationBridge.sol

323    uint256 balance = IRWALike(_token).balanceOf(address(this));


ether_sky Q

I believe this is an incorrect check for `partial debt size`.
Partial `liquidation` or `redemption` should reserve `collateral` of at least 2 `stEth`.
We have below check for this.
```
function _requirePartialLiqCollSize(uint256 _entireColl) internal pure {
    require(
        _entireColl >= MIN_NET_STETH_BALANCE,
        "LiquidationLibrary: Coll remaining in partially liquidated CDP must be >= minimum"
    );
}
```
However, this check adds another `limitation` to the `debt size`.
https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/LiquidationLibrary.sol#L896-L906
```
require(
     (_partialDebt + _convertDebtDenominationToBtc(MIN_NET_STETH_BALANCE, _price)) <=
           _entireDebt,
     "LiquidationLibrary: Partial debt liquidated must be less than total debt"
);
```

// Code block 1 (unknown):
function _requirePartialLiqCollSize(uint256 _entireColl) internal pure {
    require(
        _entireColl >= MIN_NET_STETH_BALANCE,
        "LiquidationLibrary: Coll remaining in partially liquidated CDP must be >= minimum"
    );
}

// Code block 2 (unknown):
require(
     (_partialDebt + _convertDebtDenominationToBtc(MIN_NET_STETH_BALANCE, _price)) <=
           _entireDebt,
     "LiquidationLibrary: Partial debt liquidated must be less than total debt"
);


hihen Q

# QA Report

## Summary

### Low Issues

Total **23 instances** over **4 issues**:

|ID|Issue|Instances|
|:--:|:---|:--:|
| [[L-01]](#l-01-owner-can-renounce-while-system-is-paused) | Owner can renounce while system is paused | 4 |
| [[L-02]](#l-02-revert-on-transfer-to-the-zero-address) | Revert on transfer to the zero address | 2 |
| [[L-03]](#l-03-critical-functions-should-be-controlled-by-time-locks) | Critical functions should be controlled by time locks | 14 |
| [[L-04]](#l-04-contracts-are-vulnerable-to-rebasing-accounting-related-issues) | Contracts are vulnerable to rebasing accounting-related issues | 3 |

### Non Critical Issues

Total **40 instances** over **8 issues**:

|ID|Issue|Instances|
|:--:|:---|:--:|
| [[N-01]](#n-01-redundant-inheritance-specifier) | Redundant inheritance specifier | 1 |
| [[N-02]](#n-02-events-should-be-emitted-before-external-calls) | Events should be emitted before external calls | 1 |
| [[N-03]](#n-03-openzeppelincontracts-should-be-upgraded-to-a-newer-version) | @openzeppelin/contracts should be upgraded to a newer version | 11 |
| [[N-04]](#n-04-expressions-for-constant-values-should-use-immutable-rather-than-constant) | Expressions for constant values should use `immutable` rather than `constant` | 9 |
| [[N-05]](#n-05-unnecessary-casting) | Unnecessary casting | 2 |
| [[N-06]](#n-06-using-underscore-at-the-end-of-variable-name) | Using underscore at the end of variable name | 5 |
| [[N-07]](#n-07-multiple-mappings-with-same-keys-can-be-combined-into-a-single-struct-mapping-for-readability) | Multiple mappings with same keys can be combined into a single struct mapping for readability | 5 |
| [[N-08]](#n-08-inconsistent-spacing-in-comments) | Inconsistent spacing in comments | 6 |

## Low Issues

### [L-01] Owner can renounce while system is paused

The contract owner or single user with a role is not prevented from renouncing the role/ownership while the contract is paused, which would cause any user assets stored in the

208:     function pause() external onlyLRTManager {
209:         _pause();
210:     }

102:     function pause() external onlyLRTManager {
103:         _pause();
104:     }


devival Q

## Title QA
EUSD shares can be transferred to EUSD contract itself

## Links to affected code:
Provide GitHub links, including line numbers, to all instances of this bug throughout the repo.

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/EUSD.sol#L334-L341
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/EUSD.sol#L391-L400

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

There is a lack of any check in `transferShares` and `_transferShares` for the recipient to not be `address(this)` meaning the `EUSD` contract itself.

`transferShares` and `_transferShares` functions in `EUSD.sol`
```
    function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
        address owner = _msgSender();
        _transferShares(owner, _recipient, _sharesAmount);
        emit TransferShares(owner, _recipient, _sharesAmount);
        uint256 tokensAmount = getMintedEUSDByShares(_sharesAmount);
        emit Transfer(owner, _recipient, tokensAmount);
        return tokensAmount;
    }
    //...
    function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
        require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
        require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

        uint256 currentSenderShares = shares[_sender];
        require(_sharesAmount <= currentSenderShares, "TRANSFER_AMOUNT_EXCEEDS_BALANCE");

        shares[_sender] = currentSenderShares.sub(_sharesAmount);
        shares[_recipient] = shares[_recipient].add(_sharesAmount);
    }
```
[EUSD.sol#L334-L341](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/EUSD.sol#L334-L341)
[EUSD.sol#L391-L400](https://github.com/code-423

// Code block 1 (unknown):
function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
        address owner = _msgSender();
        _transferShares(owner, _recipient, _sharesAmount);
        emit TransferShares(owner, _recipient, _sharesAmount);
        uint256 tokensAmount = getMintedEUSDByShares(_sharesAmount);
        emit Transfer(owner, _recipient, tokensAmount);
        return tokensAmount;
    }
    //...
    function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
        require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
        require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

        uint256 currentSenderShares = shares[_sender];
        require(_sharesAmount <= currentSenderShares, "TRANSFER_AMOUNT_EXCEEDS_BALANCE");

        shares[_sender] = currentSenderShares.sub(_sharesAmount);
        shares[_recipient] = shares[_recipient].add(_sharesAmount);
    }


arialblack14 G

# GAS OPTIMIZATION REPORT
---
### Summary of optimizations
| Number | Issue details | Instances |
|---|---|:---:|
| [G-1](#G1) |Usage of  `uint`s/`int`s smaller than 32 bytes (256 bits) incurs overhead. | 1
| [G-2](#G2) |Multiple accesses of a mapping/array should use a local variable cache. | 7
| [G-3](#G3) |`>=` costs less gas than `>`. | 2
| [G-4](#G4) |`<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables | 4
| [G-5](#G5) |Using fixed bytes is cheaper than using  `string`. | 5

*Total: 5 issues.*

---
### Gas Optimizations
### [G-1] Usage of  `uint`s/`int`s smaller than 32 bytes (256 bits) incurs overhead.

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

##### Recommendation
Use `uint256` instead.

##### *Instances (1):*
File: [2023-03-asymmetry/contracts/SafEth/derivatives/Reth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L86 )
```solidity
86: uint24 _poolFee,
```
### [G-2] Multiple accesses of a mapping/array should use a local variable cache.

##### Description
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage or calldata variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata. [Similar findings](https://github.com/code-423n4/2022-06-infinity-findings/issues/186)

##### Recommendation
Use a local variable cache.

##### *Instances (7):*
File: [2023-03-asymmetr

73: (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74: derivatives[i].balance()) /
115: uint256 derivativeAmount = (derivatives[i].balance() *
118: derivatives[i].withdraw(derivativeAmount);
141: if (derivatives[i].balance() > 0)
142: derivatives[i].withdraw(derivatives[i].balance());
152: derivatives[i].deposit{value: ethAmount}();

// Code block 1 (solidity):
86: uint24 _poolFee,


0x11singh99 Q

# Quality Assurance Report

| QA Issues | Issues                                         | Instances |
| --------- | ---------------------------------------------- | --------- |
| [[L-01](#l-01-in-poolsremoveliquidity-function-reservesreserve0-used-twice-in-one-condition-but-reservesreserve1-not-checked)] | In `Pools::removeLiquidity` function `reserves.reserve0` used twice in one condition but `reserves.reserve1` not checked. | 1 |
| [[L-02](#l-02-check-value-before-dividing-by-zero)] | Check value before dividing by zero | 8 |
| [[L-03](#l-03-unsafe-downcasting)] | Unsafe downcasting | 2 |
| [[L-04](#l-04-use-owanble2step-and-override-renounce-ownership-so-owner-cant-renounce-it)] | Use owanble2step and override renounce ownership so owner can't renounce it. | 2 |
| [[L-05](#l-05-check-return-value-of-approve)] | check return value of approve | 2 |
| [[L-06](#l-06-after-minting-one-time-100-million-salt-tokens-there-is-no-way-to-mint-again-only-burn)] | After minting one time 100 Million Salt tokens there is no way to mint again only burn. | 1 |
| [[N-01](#n-01-refactor-requireif-statement-to-modifier-if-it-is-used-more-than-once)] | Refactor require()/if() statement to modifier if it is used more than once | 2 |
| [[N-02](#n-02-not-used-return-name-variable)] | Not used return name variable | 1 |
| [[N-03](#n-03-use-uint256-instead-of-uint)] | Use `uint256` instead of `uint` | 1 |
| [[N-04](#n-04-typo)] | Typo | 1 |
# Low-Severity

## [L-01] In `Pools::removeLiquidity` function `reserves.reserve0` used twice in one condition but `reserves.reserve1` not checked.
It can lead to continuing a transaction even though `reserves.reserve1` is less than DUST. It is not intended flow of the function. This is to ensure that ratios remain relatively constant even after a maximum withdrawal. But this will not be true. So better would be to `reserves.reserve1` is not less than DUST.

```diff
File : src/pools/Pool.sol

185: // Make sure that removing liquidity doesn't drive ei

https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L185C3-L187C146

## [L-02] Check value before dividing by zero

Checking for zero before performing a division operation is crucial to prevent division by zero errors. If you attempt to divide a number by zero in most programming languages, it will result in an error or an exception. In Solidity and smart contract development, encountering such errors can have severe consequences, potentially leading to a halt in the execution of the smart contract or unexpected behavior.

_8 instances_

### Check `totalLiquidity`

https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L179C1-L180C75

### Check `reserve1` and `reserve0`


K42 G

## Gas Optimization Report for [Amphora](https://github.com/code-423n4/2023-07-amphora) by K42

### Possible Optimization in [VaultController.sol](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol)

General Optimizations =
- Reducing the number of storage operations, as they are the most expensive in terms of gas.
- Using events instead of storage for data that doesn't need to be accessed within the contract.
- Using libraries for reusable code to reduce the size of the contract.
- Using short-circuiting in conditionals to avoid unnecessary computation.

Possible Optimization 1 = Packing variables into a struct:
- You have several [uint192](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L65C1-L71C33) variables. You can pack these variables into a ``struct`` to save gas. Here's an example:

```solidity
struct VaultControllerStorage {
    uint192 totalBaseLiability;
    uint192 protocolFee;
    uint192 initialBorrowingFee;
    uint192 liquidationFee;
}

VaultControllerStorage public vaultControllerStorage;
```

You can then access these variables like this:

```solidity
vaultControllerStorage.totalBaseLiability
```

- Estimated gas saved =  This optimization can save approximately 20,000 gas per transaction that involves these variables due to reducing the number of ``SSTORE`` operations.

Possible Optimization 2 = Removing redundant checks:
- In the [_liquidationMath](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L743C1-L757C4) function, there is a check to see if the vault is solvent. However, this check is also performed in the [liquidateVault](https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L646C1-L697C4) function, which is the only function that calls [_liquidationMath](https://github.com/code-423n4/2023-07-amphora/blob/main

// Code block 1 (solidity):
struct VaultControllerStorage {
    uint192 totalBaseLiability;
    uint192 protocolFee;
    uint192 initialBorrowingFee;
    uint192 liquidationFee;
}

VaultControllerStorage public vaultControllerStorage;

// Code block 2 (solidity):
vaultControllerStorage.totalBaseLiability

struct VaultControllerStorage {
    uint192 totalBaseLiability;
    uint192 protocolFee;
    uint192 initialBorrowingFee;
    uint192 liquidationFee;
}

VaultControllerStorage public vaultControllerStorage;

vaultControllerStorage.totalBaseLiability

VaultController.sol, VaultController.sol#L65-L1, VaultController.sol#L743-L1, VaultController.sol#L646-L1


atharvasama G

# Gas Optimizations list

| Number | Details                                                                                | Instances |
| ------ | ------------------------------------------------------------------------------------------------- | --------- |
| 1      | ```x += y```/```x -= y``` COSTS MORE GAS THAN ```x = x + y```/```x = x - y``` FOR STATE VARIABLES | 4        |
| 2      | CAN DECLARE VARIABLE OUTSIDE LOOP TO SAVE GAS                                                     | 13         |
| 3      | PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD                   | 21         |
| 4      | OPTIMIZE NAMES TO SAVE GAS                                                                        | 4        |
| 5     | BEFORE SOME FUNCTIONS, WE SHOULD CHECK SOME VARIABLES FOR POSSIBLE GAS SAVE                       | 1         |
| 6     | REORDER STRUCTURE LAYOUT                                                                          | 2         |
| 7     | USE ASSEMBLY TO CHECK FOR ```address(0)```                                                        | 21        |

# Gas Optimizations
## [G-01]  ```x += y```/```x -= y``` COSTS MORE GAS THAN ```x = x + y```/```x = x - y``` FOR STATE VARIABLES
Using the addition operator instead of plus-equals saves some gas for state variables.

src\PrivatePool.sol
```js
230:         virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);

231:         virtualNftReserves -= uint128(weightSum);

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

324:         virtualNftReserves += uint128(weightSum);
```

## [G-02] CAN DECLARE VARIABLE OUTSIDE LOOP TO SAVE GAS
Declaring the stack variable outside the loop will save gas that would otherwise be spent on declaring the variable over and over again.

src\EthRouter.sol
```js
109:                 uint256 inputAmount = Pair(buys[i].pool).nftBuy{value: buys[i].bas

109:                 uint256 inputAmount = Pair(buys[i].pool).nftBuy{value: buys[i].baseTokenAmount}(

115:                     uint256 salePrice = inputAmount / buys[i].tokenIds.length;

118:                         (uint256 royaltyFee, address royaltyRecipient) =

170:                 uint256 outputAmount = Pair(sells[i].pool).nftSell(

182:                     uint256 salePrice = outputAmount / sells[i].tokenIds.length;

185:                         (uint256 royaltyFee, address royaltyRecipient) =

262:             Change memory _change = changes[i];

// Code block 1 (js):
230:         virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);

231:         virtualNftReserves -= uint128(weightSum);

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

324:         virtualNftReserves += uint128(weightSum);


ayden Q

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L143
1.Lack of a zero address(0) check

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L157
2.Initializers could be front-run, allowing an attacker to either set their own values, take ownership of the contract, and in the best case forcing a re-deployment

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211#L289
3.ensure that the tokenIds and tokenWeights arrays have a length greater than zero and that they have the same length.
And at line 236, tokenIds.length is used as the denominator, but it has been previously ensured that this length is greater than 0

```solidity
+ require(tokenIds.length > 0 && tokenIds.length == tokenWeights.length, "Invalid input arrays");
```

https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L142
4.The protocol fee should be set within a reasonable range,if the value of protocolFeeRate is mistakenly set, the protocolFeeAmount in sellQuote may be larger than the outputAmount

```solidity
if (_protocolFeeRate > 5_000) revert ProtocolRateTooHigh();
```

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230#L231
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L323#L324
To ensure that there is no overflow when converting uint256 to uint128,and the totalNetInputAmount can be extracted so that it does not need to be calculated again later

```solidity

- virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
- virtualNftReserves -= uint128(weightSum);

+ uint256 totalNetInputAmount = netInputAmount - feeAmount - protocolFeeAmount;
+ require(uint128(totalNetInputAmount) == totalNetInputAmount, "totalNetInputAmount is not an int");
+ require(uint128(weightSum) == weightSum, "weightSum is not an int");
```

https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L506
5.When the baseToken is a nati

// Code block 1 (solidity):
+ require(tokenIds.length > 0 && tokenIds.length == tokenWeights.length, "Invalid input arrays");

// Code block 2 (solidity):
if (_protocolFeeRate > 5_000) revert ProtocolRateTooHigh();

// Code block 3 (solidity):
- virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
- virtualNftReserves -= uint128(weightSum);

+ uint256 totalNetInputAmount = netInputAmount - feeAmount - protocolFeeAmount;
+ require(uint128(totalNetInputAmount) == totalNetInputAmount, "totalNetInputAmount is not an int");
+ require(uint128(weightSum) == weightSum, "weightSum is not an int");

+ require(tokenIds.length > 0 && tokenIds.length == tokenWeights.length, "Invalid input arrays");

if (_protocolFeeRate > 5_000) revert ProtocolRateTooHigh();

- virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
- virtualNftReserves -= uint128(weightSum);

+ uint256 totalNetInputAmount = netInputAmount - feeAmount - protocolFeeAmount;
+ require(uint128(totalNetInputAmount) == totalNetInputAmount, "totalNetInputAmount is not an int");
+ require(uint128(weightSum) == weightSum, "weightSum is not an int");

PrivatePool.sol#L143, PrivatePool.sol#L157, PrivatePool.sol#L211, Factory.sol#L142, PrivatePool.sol#L230, PrivatePool.sol#L323, PrivatePool.sol#L506


luxartvinsec Q

# 1. Possible integer overflow in `CollateralConfig` contract

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

### Summary:

The `CollateralConfig` contract contains a constant variable `MIN_ALLOWED_CCR` with the value 1.5 ether. The value of ether is assumed to be 10^18, but if ether were to be assigned a different value, there could be an integer overflow during the execution of the contract. An attacker could exploit this vulnerability to cause unexpected behavior in the contract.

### Impact:

An integer overflow caused by a change in the value of ether could result in a breach of the security of the contract. Depending on the severity of the overflow, the attacker could be able to steal funds or cause a denial-of-service attack. In the worst case, the contract could be rendered completely unusable.

### Recommendation:

It is recommended to replace the constant variables with the explicit values of 110% and 150%, respectively, to prevent potential integer overflow issues in the future. Additionally, it is recommended to use `SafeMath` to perform arithmetic operations in the contract, which would prevent integer overflow attacks.

### Example:

The `MIN_ALLOWED_CCR` and `MIN_ALLOWED_MCR` variables could be replaced with the following code snippet:

```
uint256 constant public MIN_ALLOWED_MCR = 110e16; // 110%
uint256 constant public MIN_ALLOWED_CCR = 150e16; // 150%
```

The `SafeMath` library could be imported and used for arithmetic operations, as shown below:

```
import "./Dependencies/SafeMath.sol";

// ...

uint256 constant public MIN_ALLOWED_MCR = 110e16; // 110%
uint256 constant public MIN_ALLOWED_CCR = 150e16; // 150%

using SafeMath for uint256;

// ...

require(_MCRs[i].safeMul(1e18) >= MIN_ALLOWED_MCR, "MCR below allowed minimum");
config.MCR = _MCRs[i];

require(_CCRs[i].safeMul(1e18) >= MIN_ALLOWED_CCR, "CCR below allowed minimum");
config.CCR = _CCRs[i];
```

# 2. Potential Re-Ent

// Code block 1 (unknown):
uint256 constant public MIN_ALLOWED_MCR = 110e16; // 110%
uint256 constant public MIN_ALLOWED_CCR = 150e16; // 150%

// Code block 2 (unknown):
import "./Dependencies/SafeMath.sol";

// ...

uint256 constant public MIN_ALLOWED_MCR = 110e16; // 110%
uint256 constant public MIN_ALLOWED_CCR = 150e16; // 150%

using SafeMath for uint256;

// ...

require(_MCRs[i].safeMul(1e18) >= MIN_ALLOWED_MCR, "MCR below allowed minimum");
config.MCR = _MCRs[i];

require(_CCRs[i].safeMul(1e18) >= MIN_ALLOWED_CCR, "CCR below allowed minimum");
config.CCR = _CCRs[i];


0xSmartContract Analysis

# 🛠 Analysis - Dopex Protocol  ![image](https://github.com/code-423n4/2023-08-dopex/assets/104318932/6c6fb6aa-df12-4c09-aa49-fc7b6709a628)

### Summary
| List |Head |Details|
|:--|:----------------|:------|
|a) |The approach I followed when reviewing the code | Stages in my code review and analysis |
|b) |Analysis of the code base | What is unique? How are the existing patterns used? |
|c) |Test analysis | Test scope of the project and quality of tests |
|d) |Centralization risks | How was the risk of centralization handled in the project, what could be alternatives? |
|e) |Systemic risks | Potential systemic risks in the project |
|f) |Competition analysis| What are similar projects? |
|g) |Security Approach of the Project | Audit approach of the Project |
|h) |Other Audit Reports and Automated Findings | What are the previous Audit reports and their analysis |
|i) |Gas Optimization | Gas usage approach of the project and alternative solutions to it |
|j) |Other recommendations | What is unique? How are the existing patterns used? |
|k) |New insights and learning from this audit | Things learned from the project |

## a) The approach I followed when reviewing the code

First, by examining the scope of the code, I determined my code review and analysis strategy.
https://github.com/code-423n4/2023-08-dopex

Accordingly, I analyzed and audited the subject in the following steps;

| Number |Stage |Details|Information|
|:--|:----------------|:------|:------|
|1|Compile and Run Test|[Installation](https://github.com/code-423n4/2023-08-dopex#running-tests)|Test and installation structure is simple, cleanly designed|
|2|Architecture Review|The [Dopex](https://dopex.notion.site/rDPX-V2-RI-b45b5b402af54bcab758d62fb7c69cb4) explainer provides a high-level overview of the Project system and the describe the core components.|Provides a basic architectural teaching for General Architecture|
|3|Graphical Analysis  |Graphical Analysis with [Solidity-metrics](https://github.com/C

Readme.md
- What is the overall line coverage percentage provided by your tests?: 95%

contracts\core\RdpxV2Core.sol:
  1015     **/
  1016:   function redeem(
  1017:     uint256 id,
  1018:     address to
  1019:   ) external returns (uint256 receiptTokenAmount) {
  1020:     // Validate bond ID
  1021:     _validate(bonds[id].timestamp > 0, 6);
  1022:     // Validate if bond has matured
  1023:     _validate(block.timestamp > bonds[id].maturity, 7);
  1024:
  1025:     _validate(
  1026:       msg.sender == RdpxV2Bond(addresses.receiptTokenBonds).ownerOf(id),
  1027:       9
  1028:     );
  1029:
  1030:     // Burn the bond token
  1031:     // Note requires an approval of the bond token to this contract
  1032:     RdpxV2Bond(addresses.receiptTokenBonds).burn(id);
  1033:
  1034:     // transfer receipt tokens to user
  1035:     receiptTokenAmount = bonds[id].amount;
  1036:     IERC20WithBurn(addresses.rdpxV2ReceiptToken).safeTransfer(
  1037:       to,
  1038:       receiptTokenAmount
  1039:     );
  1040:
  1041:     emit LogRedeem(to, receiptTokenAmount);
  1042:   }


RaymondFam G

## For loop pre-condition
In `rebalanceToWeights()` of SatEth.sol, `ethAmountToRebalance == 0` should be moved out of the if block before the for loop to save gas on iterations. This is because if `ethAmountToRebalance` is ever true, it means there is no ETH available to deposit into any of the derivatives.

Consider refactoring the affected code as follows:

[File: SafEth.sol#L147-L155](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L147-L155)

```diff
+        if (ethAmountToRebalance == 0) return;

        for (uint i = 0; i < derivativeCount; i++) {
-            if (weights[i] == 0 || ethAmountToRebalance == 0) continue;
+            if (weights[i] == 0) continue;
            uint256 ethAmount = (ethAmountToRebalance * weights[i]) /
                totalWeight;
            // Price will change due to slippage
            derivatives[i].deposit{value: ethAmount}();
        }
        emit Rebalanced();
    }
```
## Use array of mappings
When a derivative has turned obsolete, i.e. weights assigned zero and the associated underlyingValue has been fully drained after [`rebalanceToWeights()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L138-L155) is called, it does not make sense the derivative will have to be called and skipped each time `unstake()` is called, which incurs additional gas.

Consider adding `derivatives[x]` and `weights[x]` to their respective arrays in [`addDerivative()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L182-L195) and have the obsolete mappings removed from the arrays via `rebalanceToWeights()` or a separately implemented function when need be and deemed fit. Use the array length to iterate the for loop in [`stake()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L71-L96) and [`unstake()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L1

## Use array of mappings
When a derivative has turned obsolete, i.e. weights assigned zero and the associated underlyingValue has been fully drained after [`rebalanceToWeights()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L138-L155) is called, it does not make sense the derivative will have to be called and skipped each time `unstake()` is called, which incurs additional gas.

Consider adding `derivatives[x]` and `weights[x]` to their respective arrays in [`addDerivative()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L182-L195) and have the obsolete mappings removed from the arrays via `rebalanceToWeights()` or a separately implemented function when need be and deemed fit. Use the array length to iterate the for loop in [`stake()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L71-L96) and [`unstake()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L113-L119) instead of `derivativeCount`.

## Avoid comparing boolean expressions to boolean literals
Comparing a boolean value to a boolean literal incurs the `ISZERO` operation and costs more gas than using a boolean expression.

Here are some of the affected code lines that may be refactored as follows:

[File: SafEth.sol#L64](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L64)

// Code block 1 (diff):
+        if (ethAmountToRebalance == 0) return;

        for (uint i = 0; i < derivativeCount; i++) {
-            if (weights[i] == 0 || ethAmountToRebalance == 0) continue;
+            if (weights[i] == 0) continue;
            uint256 ethAmount = (ethAmountToRebalance * weights[i]) /
                totalWeight;
            // Price will change due to slippage
            derivatives[i].deposit{value: ethAmount}();
        }
        emit Rebalanced();
    }

SafEth.sol#L147-L155, SafEth.sol#L138-L155, SafEth.sol#L182-L195, SafEth.sol#L71-L96, SafEth.sol#L1, SafEth.sol#L113-L119, SafEth.sol#L64, SafEth.sol#L109


chriszhou G

### Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

There are 11 instances of this issue.

```
File: ./contracts/SafEth/derivatives/Reth.sol

20:     address public constant ROCKET_STORAGE_ADDRESS =
22:     address public constant W_ETH_ADDRESS =
24:     address public constant UNISWAP_ROUTER =
26:     address public constant UNI_V3_FACTORY =
```

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

14:     address public constant SFRX_ETH_ADDRESS =
16:     address public constant FRX_ETH_ADDRESS =
18:     address public constant FRX_ETH_CRV_POOL_ADDRESS =
20:     address public constant FRX_ETH_MINTER_ADDRESS =
```

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

13:     address public constant WST_ETH =
15:     address public constant LIDO_CRV_POOL =
17:     address public constant STETH_TOKEN =
```

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

20:     address public constant ROCKET_STORAGE_ADDRESS =
22:     address public constant W_ETH_ADDRESS =
24:     address public constant UNISWAP_ROUTER =
26:     address public constant UNI_V3_FACTORY =

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

14:     address public constant SFRX_ETH_ADDRESS =
16:     address public constant FRX_ETH_ADDRESS =
18:     address public constant FRX_ETH_CRV_POOL_ADDRESS =
20:     address public constant FRX_ETH_MINTER_ADDRESS =

// Code block 3 (unknown):
File: ./contracts/SafEth/derivatives/WstEth.sol

13:     address public constant WST_ETH =
15:     address public constant LIDO_CRV_POOL =
17:     address public constant STETH_TOKEN =


decade Q

## Q/A
---
#### 1. ProtocolFee should have max limit check before being initialized or updated.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L141
```
    /// @notice Sets the protocol fee that is taken on each buy/sell/change. It's in basis points: 350 = 3.5%.
    /// @param _protocolFeeRate The protocol fee.
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
        protocolFeeRate = _protocolFeeRate;
    }
```
-- ProtocolFee set by admin should have a check for max limit.
-- Admin should not be able to set protocol fees above a certain limit. This allows protection against future admin key theft.

---

#### 2. Place 2-step verification before changing private pool implementation.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L135
```
    function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {
        privatePoolImplementation = _privatePoolImplementation;
    }
```
-- As theft of admin key could lead to Parity bug type situation where faulty implementation will affect **all the future pools**.
https://github.com/openethereum/parity-ethereum/issues/6995

---

#### 3. Place 2-step ownership transfer for factory contract .
-- Factory contract currently uses direct ownership transfer via [solmate](https://github.com/transmissions11/solmate/blob/1b3adf677e7e383cc684b5d5bd441da86bf4bf1c/src/auth/Owned.sol), which directly transfers ownership to the given address. It is recommended not to use this approach as direct transfer of ownership that could lead to misconfiguration.
-- 2 step ownership transfer supports protocol not lose ownership to a dead address.

---

#### 4. No check present for length mismatch of dynamic arrays.
-- Where - [`sumWeightsAndValidateProof()`](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L661-L6

// Code block 1 (unknown):
/// @notice Sets the protocol fee that is taken on each buy/sell/change. It's in basis points: 350 = 3.5%.
    /// @param _protocolFeeRate The protocol fee.
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
        protocolFeeRate = _protocolFeeRate;
    }

// Code block 2 (unknown):
function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {
        privatePoolImplementation = _privatePoolImplementation;
    }

Factory.sol#L141, Factory.sol#L135, Owned.sol, PrivatePool.sol#L661-L6


zigtur G

# SharesBurnt event is incorrect
In _burnShares, `preRebaseTokenAmount` and `postRebaseTokenAmount` get their value from `getRUSDYByShares(_sharesAmount)` before and after burning shares (`totalShares` and `shares[account]` are updated).

As `getRUSDYByShares` doesn't use any of the updated value and `sharesAmount` is not modified, then `preRebaseTokenAmount` and `postRebaseTokenAmount` will always be equal.

## Code fixed
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L586-L592

```solidity
    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = preRebaseTokenAmount;
```

// Code block 1 (solidity):
uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = preRebaseTokenAmount;


brgltd Q

# [01] Add checks for weight values

Currently it's possible to set any value for the weights. Some combinations for weights could result in issues while calculating `ethAmount`.

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

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

For example, assuming the minimum value for msg.value, three derivatives and strange values for the weights.

```
msg.value = 5e17 = 0.5e18
weight1 = 5e17   = 0.5e18
weight2 = 19e18  =  19e18
weight3 = 19e19  = 190e18

ethAmount = (msg.value * weight) / totalWeight
5e17 * 5e17 / (5e17 + 19e18 + 19e19)
```

This would result in `1193317422434367.5` which would round down in solidity.

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

## Recommendation

Add checks for min and max values for weights.

# [02] Lack of method to remove derivatives

In case a derivative gets added by mistake or with incorrect parameters, currently this derivative would remain stuck in `SafEth.sol`.

## Recommendation

Consider adding a method that allows removing derivatives from `SafEth.sol`.

# [03] Reentrancy for `SafEth.unstake()`

There is a reentrancy possibility in `SafEth.unstake()` where the tokens are burned only after the derivative withdraw.

If the `derivatives[i].withdraw()` external call where to reenter into `SafEth.unstake()`, the `safEthAmount` is still not updated, since the `_burn()` is only called after, and the function doesn't contain a nonReentranct modifier.

Note: this would only be an issue for a malicious derivative contract.

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

## Recommendation

Call `_burn()` before `derivatives[i].withdraw()` in `SafEth.unstake()`.

# [04] Unbounded loop

There are multiple instances of loops executing external calls where the number of iterations is unbounded and controlled by the nu

require(!pausedStaking && !pauseUnstaking, "error");

// Code block 1 (unknown):
msg.value = 5e17 = 0.5e18
weight1 = 5e17   = 0.5e18
weight2 = 19e18  =  19e18
weight3 = 19e19  = 190e18

ethAmount = (msg.value * weight) / totalWeight
5e17 * 5e17 / (5e17 + 19e18 + 19e19)

SafEth.sol#L169, SafEth.sol#L187, SafEth.sol#L88, SafEth.sol#L113-L120


MohammedRizwan G

## Summary

### Gas Optimizations
| |Issue|Instances| |
|-|:-|:-:|:-:|
| [G‑01] | <x> += <y> costs more gas than <x> = <x> + <y> for state variables | 30 |
| [G‑02] | Don’t initialize variables with default value | 22 |
| [G‑03] | Use nested if and, avoid multiple check combinations | 5 |

### [G‑01]  <x> += <y> costs more gas than <x> = <x> + <y> for state variables
Using compound assignment operators for state variables (like State += X or State -= X …) it’s more expensive than using operator assignment (like State = State + X or State = State - X …).
Using the addition operator instead of plus-equals saves 113 gas. [Link to reference](https://gist.github.com/IllIllI000/cbbfb267425b898e5be734d4008d4fe8)

Total gas saved can be ~ 3300 approx.

There are 30 instances of this issue.

```solidity
File: ajna-grants/src/grants/GrantFund.sol

62        treasury += fundingAmount_;
```
[Link to code](https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/GrantFund.sol#LL62C1-L62C36)

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

202            position.lps += lpBalance;
```
[Link to code](https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/PositionManager.sol#LL202C1-L202C39)

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

320        fromPosition.lps -= vars.lpbAmountFrom;
321        toPosition.lps   += vars.lpbAmountTo;
```
[Link to code](https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/PositionManager.sol#LL320C1-L321C46)

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

339            rewards_ += _calculateNextEpochRewards(
```
[Link to code](https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/RewardsManager.sol#LL339C1-L339C52)

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

406           rewards_ += nextEpoch

// Code block 1 (solidity):
File: ajna-grants/src/grants/GrantFund.sol

62        treasury += fundingAmount_;

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

202            position.lps += lpBalance;

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

320        fromPosition.lps -= vars.lpbAmountFrom;
321        toPosition.lps   += vars.lpbAmountTo;

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

339            rewards_ += _calculateNextEpochRewards(

File: ajna-grants/src/grants/GrantFund.sol

62        treasury += fundingAmount_;

File: ajna-core/src/PositionManager.sol

202            position.lps += lpBalance;

File: ajna-core/src/PositionManager.sol

320        fromPosition.lps -= vars.lpbAmountFrom;
321        toPosition.lps   += vars.lpbAmountTo;

File: ajna-core/src/RewardsManager.sol

339            rewards_ += _calculateNextEpochRewards(


Topmark Q

### Report 1:
Typographical Error in L48 & L61 of CdpManagerStorage.sol contract, It should be "purposes" not "pruposes"
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L48
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L61
```solidity
  /// @dev Internal notify called by Redemptions and Liquidations
>>>    /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    function _startGracePeriod(uint256 _tcr) internal {
```
```solidity
>>>   /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    /// @dev Internal notify called by Redemptions and Liquidations
    function _endGracePeriod(uint256 _tcr) internal {
```
###  Report 2:
## Title
Code Reversion due to improper Validation
## Impact
Missing Validation to ensure prevChainlinkResponse is not zero would allow reversion and Denial of Service if  _currentRoundEthBtcId or _currentRoundStEthEthId is equal 1 in the _getPrevChainlinkResponse(...) function of the PriceFeed.sol contract.
## Proof of Concept
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/PriceFeed.sol#L687-L722

As seen in the code provided below and as noted by the arrows, subtraction is done for _currentRoundEthBtcId and _currentRoundStEthEthId i.e minus 1, the problem is id for prevChainlinkResponse would be zero which opens up reversion since this zero value is used to interact with external contracts as noted in the "getRoundData(...)" function, thereby causing denial of service.
```solidity
   function _getPrevChainlinkResponse(
>>>        uint80 _currentRoundEthBtcId,
>>>        uint80 _currentRoundStEthEthId
    ) internal view returns (ChainlinkResponse memory prevChainlinkResponse) {
        // If first round, early return
        // Handles revert from underflow in _curre

// Code block 1 (solidity):
/// @dev Internal notify called by Redemptions and Liquidations
>>>    /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    function _startGracePeriod(uint256 _tcr) internal {

// Code block 2 (solidity):
>>>   /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    /// @dev Internal notify called by Redemptions and Liquidations
    function _endGracePeriod(uint256 _tcr) internal {

/// @dev Internal notify called by Redemptions and Liquidations
>>>    /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    function _startGracePeriod(uint256 _tcr) internal {

>>>   /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set
    /// @dev Internal notify called by Redemptions and Liquidations
    function _endGracePeriod(uint256 _tcr) internal {


rfa G

## [G-1] Using storage pointer to declare `positions` var

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L190

By using `storage` to declare `positions` and remove L207, we can save at least 103 gas per loop

Recommended mitigation step:
```solidity
	//change this line
	Position storage position = positions[params_.tokenId][index];

            // check for previous deposits
            if (position.depositTime != 0) {
                // check that bucket didn't go bankrupt after prior memorialization
                if (_bucketBankruptAfterDeposit(pool, index, position.depositTime)) {
                    // if bucket did go bankrupt, zero out the LP tracked by position manager
                    position.lps = 0;
                }
            }

            // update token position LP
            position.lps += lpBalance;
            // set token's position deposit time to the original lender's deposit time
            position.depositTime = depositTime;

            // remove this part
            //positions[params_.tokenId][index] = position;
```

// Code block 1 (solidity):
//change this line
	Position storage position = positions[params_.tokenId][index];

            // check for previous deposits
            if (position.depositTime != 0) {
                // check that bucket didn't go bankrupt after prior memorialization
                if (_bucketBankruptAfterDeposit(pool, index, position.depositTime)) {
                    // if bucket did go bankrupt, zero out the LP tracked by position manager
                    position.lps = 0;
                }
            }

            // update token position LP
            position.lps += lpBalance;
            // set token's position deposit time to the original lender's deposit time
            position.depositTime = depositTime;

            // remove this part
            //positions[params_.tokenId][index] = position;


Audinarey Q

## [L-01] Parameter Ballot can be proposed with invalid `parameterType`

If [`Proposals.proposeParameterBallot(...)`](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/Proposals.sol#L155-L159) is called with an invalid `parameterType` the function does not revert. I am reporting this as a LOW severity because it does not break the protocols functionality as an invalid `parameterType` does not do anything.

## Suggestion

Modify the `Proposals.proposeParameterBallot(...)` as shown below

```
function proposeParameterBallot( uint256 parameterType, string calldata description ) external nonReentrant returns (uint256 ballotID)
		{
+		require(parameterType < 26, "Invalid ParameterType");
		string memory ballotName = string.concat("parameter:", Strings.toString(parameterType) );
		return _possiblyCreateProposal( ballotName, BallotType.PARAMETER, address(0), parameterType, "", description );
		}
```

## [L-02] `SendSALT` can be proposed with zero amount

If [`Proposals.proposeSendSALT(...)`](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/Proposals.sol#L196-L209) is called with zero as the `amount` of SALT to send, the function does not revert.

Although no SALT is sent even when the proposal passes but the caller still spends gas fee when the call is made.

## Suggestion

- Modify the `Proposals.proposeSendSALT(...)` to revert when the SALT balance is less than `100 wei` (to avoid rounding errors)

```solidity
function proposeSendSALT( address wallet, uint256 amount, string calldata description ) external nonReentrant returns (uint256 ballotID)
		{
		...
		uint256 balance = exchangeConfig.salt().balanceOf( address(exchangeConfig.dao()) );
		require(balance > 100 wei, "Insufficient SALT balance");
		uint256 maxSendable = balance * 5 / 100;

		...
		}
```

// Code block 1 (unknown):
function proposeParameterBallot( uint256 parameterType, string calldata description ) external nonReentrant returns (uint256 ballotID)
		{
+		require(parameterType < 26, "Invalid ParameterType");
		string memory ballotName = string.concat("parameter:", Strings.toString(parameterType) );
		return _possiblyCreateProposal( ballotName, BallotType.PARAMETER, address(0), parameterType, "", description );
		}

// Code block 2 (solidity):
function proposeSendSALT( address wallet, uint256 amount, string calldata description ) external nonReentrant returns (uint256 ballotID)
		{
		...
		uint256 balance = exchangeConfig.salt().balanceOf( address(exchangeConfig.dao()) );
		require(balance > 100 wei, "Insufficient SALT balance");
		uint256 maxSendable = balance * 5 / 100;

		...
		}


Tomio G

Title: Consider delete empty block or emit something

impact:
The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting.

Proof of Concept:
[Factory.sol#L55](https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L55)
[EthRouter.sol#L88](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L88)
________________________________________________________________________

Title: Move `SafeTransferLib` and `LibClone` functions into the `PrivatePool` contract to avoid unnecessary function calls and gas costs.

Proof of Concept:
Instead of using SafeTransferLib and LibClone in the Factory contract, we can move these functions into the PrivatePool contract and call them directly from there. This can help to reduce the number of function calls and gas costs.
[Factory.sol#L38-L39](https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L38-L39)
________________________________________________________________________

Title: Use `uint256` instead of `uint128` to avoid unnecessary conversions and gas costs.

Proof of Concept:
[Factory.sol#L74-L75](https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L74-L75)
________________________________________________________________________

Title: function `sumWeightsAndValidateProof()` gas improvement on returning value

Proof of Concept:
[PrivatePool.sol#L671](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L671)

Recommended Mitigation Steps:
by set `sum` in returns L#665 and delete L#671 can save gas

```
function sumWeightsAndValidateProof(
        uint256[] memory tokenIds,
        uint256[] memory tokenWeights,
        MerkleMultiProof memory proof
    ) public view returns (uint256 sum) { //@audit-info: set here
```
________________________________________________________________________

Title: Use `require` instead of `revert`

Proof of Concept:
Use

require(
            (_baseToken == address(0) && msg.value != baseTokenAmount) ||
            (_baseToken != address(0) && msg.value > 0),
            "Invalid ETH amount"
        );

// Code block 1 (unknown):
function sumWeightsAndValidateProof(
        uint256[] memory tokenIds,
        uint256[] memory tokenWeights,
        MerkleMultiProof memory proof
    ) public view returns (uint256 sum) { //@audit-info: set here

Factory.sol#L55, EthRouter.sol#L88, Factory.sol#L38-L39, Factory.sol#L74-L75, PrivatePool.sol#L671


QiuhaoLi Q

## [LOW] LRTConfigRoleChecker should add a storage gap

`LRTConfigRoleChecker` is a contract inherited by `LRTDepositPool`, `LRTOracle`, `NodeDelegator`, `RSETH`, etc, which are upgradable contracts. E.g. for `LRTDepositPool`, it's defined as:

```solidity
contract LRTDepositPool is ILRTDepositPool, LRTConfigRoleChecker, PausableUpgradeable, ReentrancyGuardUpgradeable {
```

In `LRTConfigRoleChecker`, we have a storage variable, but don't have storage gap:

```solidity
abstract contract LRTConfigRoleChecker {
    ILRTConfig public lrtConfig;
......
}
```

To avoid future needs to add storage slot in `LRTConfigRoleChecker` and storage conflicts. Better add a storage gap:

```solidity
    **
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     *
    uint256[49] private __gap;
```

// Code block 1 (solidity):
contract LRTDepositPool is ILRTDepositPool, LRTConfigRoleChecker, PausableUpgradeable, ReentrancyGuardUpgradeable {

// Code block 2 (solidity):
abstract contract LRTConfigRoleChecker {
    ILRTConfig public lrtConfig;
......
}

// Code block 3 (solidity):
**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     *
    uint256[49] private __gap;

contract LRTDepositPool is ILRTDepositPool, LRTConfigRoleChecker, PausableUpgradeable, ReentrancyGuardUpgradeable {

abstract contract LRTConfigRoleChecker {
    ILRTConfig public lrtConfig;
......
}

**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     *
    uint256[49] private __gap;


amshirif G

Require statements are used after making calls/not the first line(s) of a function call. In most cases only a very small amount of gas would be lost. I will list them below, however they do not amount to much. The main concern would be in contracts `contracts/lending/tokens/cCash/CCashDelegate.sol` and `contracts/lending/tokens/cToken/CTokenDelegate.sol`. The reason being is the stated intention (in the code comments) that an update using the `data` field is to come in the future. It should be noted that all logic is done after initial checks. In both cases here is a recommended version.

Both data and the unnecessary check to set to zero can be removed without any alteration to the code's behavior. (This in itself could be considered an optimization).

```solidity
function _becomeImplementation(bytes) public virtual override {
    require(
      msg.sender == admin,
      "only the admin may call _becomeImplementation"
    );
  }
```

Here are all the occurrences, most of these calls are minor unless `_beforeTokenTransfer()` is changed.

https://github.com/code-423n4/2023-01-ondo/blob/83a45fa7cbc427a62ccf04d0e5cf9b0e780ec26c/contracts/lending/tokens/cCash/CCashDelegate.sol#L21-L34

https://github.com/code-423n4/2023-01-ondo/blob/83a45fa7cbc427a62ccf04d0e5cf9b0e780ec26c/contracts/lending/tokens/cToken/CTokenDelegate.sol#L21-L34

https://github.com/code-423n4/2023-01-ondo/blob/83a45fa7cbc427a62ccf04d0e5cf9b0e780ec26c/contracts/cash/token/Cash.sol#L29-L40

https://github.com/code-423n4/2023-01-ondo/blob/83a45fa7cbc427a62ccf04d0e5cf9b0e780ec26c/contracts/cash/token/CashKYCSender.sol#L56-L75

https://github.com/code-423n4/2023-01-ondo/blob/83a45fa7cbc427a62ccf04d0e5cf9b0e780ec26c/contracts/cash/token/CashKYCSenderReceiver.sol#L56-L83

// Code block 1 (solidity):
function _becomeImplementation(bytes) public virtual override {
    require(
      msg.sender == admin,
      "only the admin may call _becomeImplementation"
    );
  }

CCashDelegate.sol#L21-L34, CTokenDelegate.sol#L21-L34, Cash.sol#L29-L40, CashKYCSender.sol#L56-L75, CashKYCSenderReceiver.sol#L56-L83

CashKYCSenderReceiver.sol, CTokenDelegate.sol, CashKYCSender.sol, Cash.sol, CCashDelegate.sol


Benterkiii G

# Improvements to `createPhysicalAttributes` Function

 **Variable Declaration Order:**
The function variable `attributesLength` should be declared before the array `finalAttributeProbabilityIndexes`.

```solidity
   uint256 attributesLength = attributes.length;
   uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributesLength);
```
by doing this we save 12 gas every time the function get executed.

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

Updated function:

```solidity
function createPhysicalAttributes(
        uint256 dna,
        uint8 generation,
        uint8 iconsType,
        bool dendroidBool
    )
        external
        view
        returns (FighterOps.FighterPhysicalAttributes memory)
    {
        if (dendroidBool) {
            return FighterOps.FighterPhysicalAttributes(99, 99, 99, 99, 99, 99);
        } else {
            uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);

            uint256 attributesLength = attributes.length;
            for (uint8 i = 0; i < attributesLength; i++) {
                if (
                  i == 0 && iconsType == 2 || // Custom icons head (beta helmet)
                  i == 1 && iconsType > 0 || // Custom icons eyes (red diamond)
                  i == 4 && iconsType == 3 // Custom icons hands (bowling ball)
                ) {
                    finalAttributeProbabilityIndexes[i] = 50;
                } else {
                    uint256 rarityRank = (dna / attributeToDnaDivisor[attributes[i]]) % 100;
                    uint256 attributeIndex = dnaToIndex(generation, rarityRank, attributes[i]);
                    finalAttributeProbabilityIndexes[i] = attributeIndex;
                }
            }
            return FighterOps.FighterPhysicalAttributes(
                finalAttributeProbabilityIndexes[0],
                finalAttributeProbabilityIndexes[1],
                fin

function createPhysicalAttributes(
        uint256 dna,
        uint8 generation,
        uint8 iconsType,
        bool dendroidBool
    )
        external
        view
        returns (FighterOps.FighterPhysicalAttributes memory)
    {
        if (dendroidBool) {
            return FighterOps.FighterPhysicalAttributes(99, 99, 99, 99, 99, 99);
        } else {
            uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);

            uint256 attributesLength = attributes.length;
            for (uint8 i = 0; i < attributesLength; i++) {
                if (
                  i == 0 && iconsType == 2 || // Custom icons head (beta helmet)
                  i == 1 && iconsType > 0 || // Custom icons eyes (red diamond)
                  i == 4 && iconsType == 3 // Custom icons hands (bowling ball)
                ) {
                    finalAttributeProbabilityIndexes[i] = 50;
                } else {
                    uint256 rarityRank = (dna / attributeToDnaDivisor[attributes[i]]) % 100;
                    uint256 attributeIndex = dnaToIndex(generation, rarityRank, attributes[i]);
                    finalAttributeProbabilityIndexes[i] = attributeIndex;
                }
            }
            return FighterOps.FighterPhysicalAttributes(
                finalAttributeProbabilityIndexes[0],
                finalAttributeProbabilityIndexes[1],
                finalAttributeProbabilityIndexes[2],
                finalAttributeProbabilityIndexes[3],
                finalAttributeProbabilityIndexes[4],
                finalAttributeProbabilityIndexes[5]
            );
        }
    }

// Code block 1 (solidity):
uint256 attributesLength = attributes.length;
   uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributesLength);