Dinesh11G G

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

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

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

Example
🤦 Bad:

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

uint256 x;
bool y;

#### Findings:
```
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::234 => uint256 payment = 0;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::305 => uint256 i = 0;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::458 => for (uint i = 0; i < dest.length;) { uint256 missingAccountFunds = 0;
```
#### Tools used
Manual code review on vs code

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

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

Example
🤦 Bad:

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

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

#### Findings:
```
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::64 => if (userOp.initCode.length == 0) {
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::199 => //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::201 => //console.log("init %s", 21000 + msg.data.length * 8);
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::324 => // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)

2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::64 => if (userOp.initCode.length == 0) {
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::199 => //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::201 => //console.log("init %s", 21000 + msg.data.length * 8);
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::324 => // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::325 => require(uint256(s) + 32 <= signatures.length, "BSA022");
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::327 => // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::333 => require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::467 => require(dest.length == func.length, "wrong array lengths");
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::199 => //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::200 => uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::201 => //console.log("init %s", 21000 + msg.data.length * 8);
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::319 => // Check that signature data pointer

// Code block 1 (unknown):
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::234 => uint256 payment = 0;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::305 => uint256 i = 0;
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::458 => for (uint i = 0; i < dest.length;) { uint256 missingAccountFunds = 0;


Bnke0x0 Q

### [L01] require() should be used instead of assert()

#### Findings:
```
2023-01-ondo/contracts/cash/factory/CashFactory.sol::97 => assert(cashProxyAdmin.owner() == guardian);
2023-01-ondo/contracts/cash/factory/CashKYCSenderFactory.sol::106 => assert(cashKYCSenderProxyAdmin.owner() == guardian);
2023-01-ondo/contracts/cash/factory/CashKYCSenderReceiverFactory.sol::106 => assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian);
```

### [L02] Missing checks for `address(0x0)` when assigning values to `address` state variables

#### Findings:
```
2023-01-ondo/contracts/cash/CashManager.sol::165 => feeRecipient = _feeRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::166 => assetRecipient = _assetRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::167 => assetSender = _assetSender;
2023-01-ondo/contracts/cash/CashManager.sol::168 => currentEpoch = currentEpoch;
2023-01-ondo/contracts/cash/CashManager.sol::170 => mintLimit = _mintLimit;
2023-01-ondo/contracts/cash/CashManager.sol::171 => redeemLimit = _redeemLimit;
2023-01-ondo/contracts/cash/CashManager.sol::172 => epochDuration = _epochDuration;
2023-01-ondo/contracts/cash/CashManager.sol::377 => lastSetMintExchangeRate = _lastSetMintExchangeRate;
2023-01-ondo/contracts/cash/CashManager.sol::395 => uint256 oldExchangeRateDeltaLimit = exchangeRateDeltaLimit;
2023-01-ondo/contracts/cash/CashManager.sol::396 => exchangeRateDeltaLimit = _exchangeRateDeltaLimit;
2023-01-ondo/contracts/cash/CashManager.sol::417 => mintFee = _mintFee;
2023-01-ondo/contracts/cash/CashManager.sol::440 => minimumDepositAmount = _minimumDepositAmount;
2023-01-ondo/contracts/cash/CashManager.sol::456 => feeRecipient = _feeRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::469 => assetRecipient = _assetRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::491 => uint256 amountE24 = _scaleUp(collateralAmountIn) * 1e6;
2023-01-ondo/contracts/cash/CashManager.sol::550 => epochDuration = _epochDuration;
2023-01-ondo/cont

2023-01-ondo/contracts/cash/CashManager.sol::165 => feeRecipient = _feeRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::166 => assetRecipient = _assetRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::167 => assetSender = _assetSender;
2023-01-ondo/contracts/cash/CashManager.sol::168 => currentEpoch = currentEpoch;
2023-01-ondo/contracts/cash/CashManager.sol::170 => mintLimit = _mintLimit;
2023-01-ondo/contracts/cash/CashManager.sol::171 => redeemLimit = _redeemLimit;
2023-01-ondo/contracts/cash/CashManager.sol::172 => epochDuration = _epochDuration;
2023-01-ondo/contracts/cash/CashManager.sol::377 => lastSetMintExchangeRate = _lastSetMintExchangeRate;
2023-01-ondo/contracts/cash/CashManager.sol::395 => uint256 oldExchangeRateDeltaLimit = exchangeRateDeltaLimit;
2023-01-ondo/contracts/cash/CashManager.sol::396 => exchangeRateDeltaLimit = _exchangeRateDeltaLimit;
2023-01-ondo/contracts/cash/CashManager.sol::417 => mintFee = _mintFee;
2023-01-ondo/contracts/cash/CashManager.sol::440 => minimumDepositAmount = _minimumDepositAmount;
2023-01-ondo/contracts/cash/CashManager.sol::456 => feeRecipient = _feeRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::469 => assetRecipient = _assetRecipient;
2023-01-ondo/contracts/cash/CashManager.sol::491 => uint256 amountE24 = _scaleUp(collateralAmountIn) * 1e6;
2023-01-ondo/contracts/cash/CashManager.sol::550 => epochDuration = _epochDuration;
2023-01-ondo/contracts/cash/CashManager.sol::598 => mintLimit = _mintLimit;
2023-01-ondo/contracts/cash/CashManager.sol::613 => redeemLimit = _redeemLimit;
2023-01-ondo/contracts/cash/factory/CashFactory.sol::54 => guardian = _guardian;
2023-01-ondo/contracts/cash/factory/CashKYCSenderFactory.sol::54 => guardian = _guardian;
2023-01-ondo/contracts/cash/factory/CashKYCSenderReceiverFactory.sol::54 => guardian = _guardian;
2023-01-ondo/contracts/cash/kyc/KYCRegistryClient.sol::56 => kycRequirementGroup = _kycRequirementGroup;
2023-01-ondo/contracts/lending/OndoPriceOracleV2.sol:

// Code block 1 (unknown):
2023-01-ondo/contracts/cash/factory/CashFactory.sol::97 => assert(cashProxyAdmin.owner() == guardian);
2023-01-ondo/contracts/cash/factory/CashKYCSenderFactory.sol::106 => assert(cashKYCSenderProxyAdmin.owner() == guardian);
2023-01-ondo/contracts/cash/factory/CashKYCSenderReceiverFactory.sol::106 => assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian);


leegh G

## [G-01] Redundant require check.
One ```require``` statement's conditiion is already included in another ```require``` statement, and thus can be removed.
```solidity
File: packages/contracts/contracts/BorrowerOperations.sol

394:            _requireValidDebtRepayment(vars.debt, vars.netDebtChange); // @audit: included in L396
395:            _requireSufficientEbtcTokenBalance(msg.sender, vars.netDebtChange);
396:            _requireNonZeroDebt(vars.debt - vars.netDebtChange);

454:        _requireAtLeastMinNetStEthBalance(vars.netStEthBalance);
463:        require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!"); // @audit: included in L454
```
[[L394-L396](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L394-L396), [L454-L463](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L454-L463)]

## [G-02] Unnecessary intialization of local variables.
```solidity
1038:        uint256 newCollShares = _collShares; // @audit: L1041 overrides this initialization
1039:        uint256 newDebt = _debt; // @audit: L1044 overrides this initialization
1040:
1041:        newCollShares = _isCollIncrease
1042:            ? _collShares + _collSharesChange
1043:            : _collShares - _collSharesChange;
1044:        newDebt = _isDebtIncrease ? _debt + _debtChange : _debt - _debtChange;
```
[[L1038-L1044](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L1038-L1044)]

## [G-03] Transfer tokens after approve operation to save gas when approve operation reverts.
```solidity
File: packages/contracts/contracts/EBTCToken.sol

140:        _transfer(sender, recipient, amount); // @audit: move `transfer` before return, after `approve`
141:
142:        uint256 cachedAllowance = _allowances[sender][msg.sender];
143:        if (cachedAllowance != type(uint256).max) {
144:            requi

// Code block 1 (solidity):
File: packages/contracts/contracts/BorrowerOperations.sol

394:            _requireValidDebtRepayment(vars.debt, vars.netDebtChange); // @audit: included in L396
395:            _requireSufficientEbtcTokenBalance(msg.sender, vars.netDebtChange);
396:            _requireNonZeroDebt(vars.debt - vars.netDebtChange);

454:        _requireAtLeastMinNetStEthBalance(vars.netStEthBalance);
463:        require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!"); // @audit: included in L454

// Code block 2 (solidity):
1038:        uint256 newCollShares = _collShares; // @audit: L1041 overrides this initialization
1039:        uint256 newDebt = _debt; // @audit: L1044 overrides this initialization
1040:
1041:        newCollShares = _isCollIncrease
1042:            ? _collShares + _collSharesChange
1043:            : _collShares - _collSharesChange;
1044:        newDebt = _isDebtIncrease ? _debt + _debtChange : _debt - _debtChange;

File: packages/contracts/contracts/BorrowerOperations.sol

394:            _requireValidDebtRepayment(vars.debt, vars.netDebtChange); // @audit: included in L396
395:            _requireSufficientEbtcTokenBalance(msg.sender, vars.netDebtChange);
396:            _requireNonZeroDebt(vars.debt - vars.netDebtChange);

454:        _requireAtLeastMinNetStEthBalance(vars.netStEthBalance);
463:        require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!"); // @audit: included in L454

1038:        uint256 newCollShares = _collShares; // @audit: L1041 overrides this initialization
1039:        uint256 newDebt = _debt; // @audit: L1044 overrides this initialization
1040:
1041:        newCollShares = _isCollIncrease
1042:            ? _collShares + _collSharesChange
1043:            : _collShares - _collSharesChange;
1044:        newDebt = _isDebtIncrease ? _debt + _debtChange : _debt - _debtChange;


Bauchibred Q

# QA Report

## Table of Contents

| Issue ID                                                                                | Description                                                                                |
| ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| [QA-01](#qa-01-proposecountryinclusion-does-not-have-proper-checks-to-ensure-that-a-valid-country-is-proposed-to-be-included) | `proposeCountryInclusion()` does not have proper checks to ensure that a valid country is proposed to be included |
| [QA-02](#qa-02-hardcoding-recipient-while-withdrawing-is-bad-practice)                                                        | Hardcoding recipient while withdrawing is bad practice                                                            |
| [QA-03](#qa-03-protocols-implementation-of-feed-addresses-could-lead-to-a-dos-of-not-less-than-a-month)                       | Protocol's implementation of feed addresses could lead to a DOS of not less than a month                          |
| [QA-04](#qa-04-liquidation-could-be-frontrun)                                                                                | Liquidation could be frontrun                                                                                |
| [QA-05](#qa-05-latestrounddata-returns-0-in-any-failure-would-probably-fault-multiple-logics)                                 | `latestRoundData()` returns 0 in any failure would probably fault multiple logics                                 |
| [QA-06](#qa-06-values-are-still-being-stored-in-ether-value)                                                                  | Values are still being stored in ether value

	function proposeCountryInclusion( string calldata country, string calldata description ) external nonReentrant returns (uint256 ballotID)
		{
		require( bytes(country).length == 2, "Country must be an ISO 3166 Alpha-2 Code" );

		string memory ballotName = string.concat("include:", country );
		return _possiblyCreateProposal( ballotName, BallotType.INCLUDE_COUNTRY, address(0), 0, country, description );
		}

// Example of a vulnerable input
string calldata country = "A,";  // Contains a comma
string calldata description = "Some description";

// Calling the function with the vulnerable input
uint256 ballotID = proposeCountryInclusion(country, description);


giovannidisiena Q

## Low Issues
### `UniswapV3OracleRelay` should validate token0/1 is actually the quote token
The `UniswapV3OracleRelay` constructor contains the following logic:
```solidity
constructor(uint32 _lookback, address _poolAddress, bool _quoteTokenIsToken0) OracleRelay(OracleType.Uniswap) {
    ...
    QUOTE_TOKEN_IS_TOKEN0 = _quoteTokenIsToken0;
    POOL = IUniswapV3Pool(_poolAddress);

    address _token0 = POOL.token0();
    address _token1 = POOL.token1(); // @audit-info - LOW: should validate that quote token is actually weth

    (BASE_TOKEN, QUOTE_TOKEN) = QUOTE_TOKEN_IS_TOKEN0 ? (_token1, _token0) : (_token0, _token1);
    BASE_TOKEN_DECIMALS = IERC20Metadata(BASE_TOKEN).decimals();
    QUOTE_TOKEN_DECIMALS = IERC20Metadata(QUOTE_TOKEN).decimals();
    ...
}
```
This is inherited by only `UniswapV3TokenOracleRelay` which expects the quote token to be ETH, corresponding to the ETH/USDC data feed address, which will be wrapped as WETH on Uniswap. Currently, as above, the pool tokens are retrieved directly from the pool address but there is no validation that either token is indeed WETH. A check should be added to ensure the quote token assigned is actually WETH.

### Unchecked return values of `EnumerableSet.AddressSet::add/remove`
```solidity
function addVaultController(address _vaultController) external onlyOwner {
    _vaultControllers.add(_vaultController); // @audit-info - LOW: unchecked return value, only grant/revoke role & emit event if true
    _grantRole(VAULT_CONTROLLER_ROLE, _vaultController);

    emit VaultControllerAdded(_vaultController);
  }
```
When calling `add/remove` on `EnumerableSet.AddressSet` it is important to check the boolean return values as if the address in question has already been added/does not exist in the set respectively then these functions will return false. In this case, events will be erroneously emitted when calling `USDA::addVaultController`, `USDA::removeVaultController` and `USDA::removeVaultControllerFromList` which cou

// Code block 1 (solidity):
constructor(uint32 _lookback, address _poolAddress, bool _quoteTokenIsToken0) OracleRelay(OracleType.Uniswap) {
    ...
    QUOTE_TOKEN_IS_TOKEN0 = _quoteTokenIsToken0;
    POOL = IUniswapV3Pool(_poolAddress);

    address _token0 = POOL.token0();
    address _token1 = POOL.token1(); // @audit-info - LOW: should validate that quote token is actually weth

    (BASE_TOKEN, QUOTE_TOKEN) = QUOTE_TOKEN_IS_TOKEN0 ? (_token1, _token0) : (_token0, _token1);
    BASE_TOKEN_DECIMALS = IERC20Metadata(BASE_TOKEN).decimals();
    QUOTE_TOKEN_DECIMALS = IERC20Metadata(QUOTE_TOKEN).decimals();
    ...
}

// Code block 2 (solidity):
function addVaultController(address _vaultController) external onlyOwner {
    _vaultControllers.add(_vaultController); // @audit-info - LOW: unchecked return value, only grant/revoke role & emit event if true
    _grantRole(VAULT_CONTROLLER_ROLE, _vaultController);

    emit VaultControllerAdded(_vaultController);
  }

constructor(uint32 _lookback, address _poolAddress, bool _quoteTokenIsToken0) OracleRelay(OracleType.Uniswap) {
    ...
    QUOTE_TOKEN_IS_TOKEN0 = _quoteTokenIsToken0;
    POOL = IUniswapV3Pool(_poolAddress);

    address _token0 = POOL.token0();
    address _token1 = POOL.token1(); // @audit-info - LOW: should validate that quote token is actually weth

    (BASE_TOKEN, QUOTE_TOKEN) = QUOTE_TOKEN_IS_TOKEN0 ? (_token1, _token0) : (_token0, _token1);
    BASE_TOKEN_DECIMALS = IERC20Metadata(BASE_TOKEN).decimals();
    QUOTE_TOKEN_DECIMALS = IERC20Metadata(QUOTE_TOKEN).decimals();
    ...
}

function addVaultController(address _vaultController) external onlyOwner {
    _vaultControllers.add(_vaultController); // @audit-info - LOW: unchecked return value, only grant/revoke role & emit event if true
    _grantRole(VAULT_CONTROLLER_ROLE, _vaultController);

    emit VaultControllerAdded(_vaultController);
  }


Madalad Q

# Low Risk Summary
| |Issue|Instances|
|:-:|:-|:-:|
|[L-01]|Use `safeTransfer` instead of `transfer` for token transfers|5|
|[L-02]|Downcasting `uint` or `int` may result in overflow|2|

Total issues: 2

Total instances: 7

# Non-Critical Summary
| |Issue|Instances|
|:-:|:-|:-:|
|[N-01]|Use `indexed` for event parameters|11|
|[N-02]|Use fixed compiler version|4|
|[N-03]|Use appropriate function naming convention|1|
|[N-04]|Lines too long|4|
|[N-05]|Missing `address(0)` checks in constructor/initialize|3|

Total issues: 5

Total instances: 23

# Low Risk Issues
## [L-01] Use `safeTransfer` instead of `transfer` for token transfers

`SafeERC20` is a wrapper around ERC20 operations that throw on failure (when the token contract returns false). This prevents calls executing if a token transfer is unsuccessful. Simply add `using SafeERC20 for ERC20`.

Instances: 5
```solidity
File: src/Factory.sol

115:             ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);

152:             ERC20(token).transfer(msg.sender, amount);

```
- [src/Factory.sol#L115](https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L115)
- [src/Factory.sol#L152](https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L152)

```solidity
File: src/PrivatePool.sol

365:             ERC20(baseToken).transfer(msg.sender, netOutputAmount);

527:             ERC20(token).transfer(msg.sender, tokenAmount);

651:         if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);

```
- [src/PrivatePool.sol#L365](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L365)
- [src/PrivatePool.sol#L527](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L527)
- [src/PrivatePool.sol#L651](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L651)

## [L-02] Downcasting `uint` or `int` may result in overflow

Consider using Open

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

115:             ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);

152:             ERC20(token).transfer(msg.sender, amount);

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

365:             ERC20(baseToken).transfer(msg.sender, netOutputAmount);

527:             ERC20(token).transfer(msg.sender, tokenAmount);

651:         if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);

File: src/Factory.sol

115:             ERC20(_baseToken).transferFrom(msg.sender, address(privatePool), baseTokenAmount);

152:             ERC20(token).transfer(msg.sender, amount);

File: src/PrivatePool.sol

365:             ERC20(baseToken).transfer(msg.sender, netOutputAmount);

527:             ERC20(token).transfer(msg.sender, tokenAmount);

651:         if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);

Factory.sol#L115, Factory.sol#L152, PrivatePool.sol#L365, PrivatePool.sol#L527, PrivatePool.sol#L651


adriro Q

# Report

## Low Issues

| |Issue|Instances|
|-|:-|:-:|
| [L-1](#L-1) | Contract files should define a locked compiler version | 4 |
| [L-2](#L-2) | Royalties are paid assuming all NFTs in the batch are equally priced | - |
| [L-3](#L-3) | Potential loss of funds when paying royalties | - |
| [L-4](#L-4) | NFT address is not validated in Factory `create` | - |
| [L-5](#L-5) | Missing event for important parameter change | 3 |
| [L-6](#L-6) | Factory `setProtocolFeeRate` should validate that the fee rate is within an acceptable range | - |
| [L-7](#L-7) | Potential overflow while updating reserves values in PrivatePool contract | - |
| [L-8](#L-8) | Zero amount ERC20 token transfers may fail some implementations | - |
| [L-9](#L-9) | `price` function will revert for ERC20 token with high decimals | - |

### [L-1] Contract files should define a locked compiler version
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.

*Instances (4)*:
```solidity
File: src/EthRouter.sol

2: pragma solidity ^0.8.19;

```

```solidity
File: src/Factory.sol

2: pragma solidity ^0.8.19;

```

```solidity
File: src/PrivatePool.sol

2: pragma solidity ^0.8.19;

```

```solidity
File: src/PrivatePoolMetadata.sol

2: pragma solidity ^0.8.19;

```

### [L-2] Royalties are paid assuming all NFTs in the batch are equally priced

- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L236
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L335
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L115
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L182

In the EthRouter and PrivatePool contracts, when b

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

2: pragma solidity ^0.8.19;

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

2: pragma solidity ^0.8.19;

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

2: pragma solidity ^0.8.19;

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

2: pragma solidity ^0.8.19;

File: src/EthRouter.sol

2: pragma solidity ^0.8.19;

File: src/Factory.sol

2: pragma solidity ^0.8.19;

File: src/PrivatePool.sol

2: pragma solidity ^0.8.19;

File: src/PrivatePoolMetadata.sol

2: pragma solidity ^0.8.19;

PrivatePool.sol#L236, PrivatePool.sol#L335, EthRouter.sol#L115, EthRouter.sol#L182


Shubham G

## Gas Optimization

| |Issue|Instances|
|-|:-|:-:|
| [G-01](#G-01) | `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state variables | 9 |
| [G-02](#G-02) | Setting the constructor to `payable` | 2 |

## [G-01] `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state variables

*There are 9 instances of this issue*

```solidity
File: main/src/PrivatePool.sol

L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
L:231        virtualNftReserves -= uint128(weightSum);
L:247        royaltyFeeAmount += royaltyFee;
L:252        netInputAmount += royaltyFeeAmount;
L:323        virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);
L:324        virtualNftReserves += uint128(weightSum);
L:341        royaltyFeeAmount += royaltyFee;
L:355        netOutputAmount -= royaltyFeeAmount;
L:678        sum += tokenWeights[i];
```
## Recommended Step

```solidity
File: main/src/PrivatePool.sol

 - L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
 + L:230        virtualBaseTokenReserves = virtualBaseTokenReserves + uint128(netInputAmount - feeAmount - protocolFeeAmount);
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230

```solidity
File: main/src/PrivatePool.sol

 - L:231        virtualNftReserves -= uint128(weightSum);
 + L:231        virtualNftReserves = virtualNftReserves - uint128(weightSum);
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L231

```solidity
File: main/src/PrivatePool.sol

 - L:247        royaltyFeeAmount += royaltyFee;
 + L:247        royaltyFeeAmount = royaltyFeeAmount + royaltyFee;
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L247

```solidity
File: main/src/PrivatePool.sol

 - L:252        netInputAmount += royaltyFeeAmount;
 + L:252        netInputAmount = netInputAmount + royaltyFeeAmount;
```
https://github.com/co

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

L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
L:231        virtualNftReserves -= uint128(weightSum);
L:247        royaltyFeeAmount += royaltyFee;
L:252        netInputAmount += royaltyFeeAmount;
L:323        virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);
L:324        virtualNftReserves += uint128(weightSum);
L:341        royaltyFeeAmount += royaltyFee;
L:355        netOutputAmount -= royaltyFeeAmount;
L:678        sum += tokenWeights[i];

// Code block 2 (solidity):
File: main/src/PrivatePool.sol

 - L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
 + L:230        virtualBaseTokenReserves = virtualBaseTokenReserves + uint128(netInputAmount - feeAmount - protocolFeeAmount);

// Code block 3 (solidity):
File: main/src/PrivatePool.sol

 - L:231        virtualNftReserves -= uint128(weightSum);
 + L:231        virtualNftReserves = virtualNftReserves - uint128(weightSum);

// Code block 4 (solidity):
File: main/src/PrivatePool.sol

 - L:247        royaltyFeeAmount += royaltyFee;
 + L:247        royaltyFeeAmount = royaltyFeeAmount + royaltyFee;

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

 - L:252        netInputAmount += royaltyFeeAmount;
 + L:252        netInputAmount = netInputAmount + royaltyFeeAmount;

File: main/src/PrivatePool.sol

L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
L:231        virtualNftReserves -= uint128(weightSum);
L:247        royaltyFeeAmount += royaltyFee;
L:252        netInputAmount += royaltyFeeAmount;
L:323        virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);
L:324        virtualNftReserves += uint128(weightSum);
L:341        royaltyFeeAmount += royaltyFee;
L:355        netOutputAmount -= royaltyFeeAmount;
L:678        sum += tokenWeights[i];

File: main/src/PrivatePool.sol

 - L:230        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
 + L:230        virtualBaseTokenReserves = virtualBaseTokenReserves + uint128(netInputAmount - feeAmount - protocolFeeAmount);

File: main/src/PrivatePool.sol

 - L:231        virtualNftReserves -= uint128(weightSum);
 + L:231        virtualNftReserves = virtualNftReserves - uint128(weightSum);

File: main/src/PrivatePool.sol

 - L:247        royaltyFeeAmount += royaltyFee;
 + L:247        royaltyFeeAmount = royaltyFeeAmount + royaltyFee;

File: main/src/PrivatePool.sol

 - L:252        netInputAmount += royaltyFeeAmount;
 + L:252        netInputAmount = netInputAmount + royaltyFeeAmount;


TeamSS G

## Disclaimer
*Gas findings in this report has been thoroughly tested to hold no inherent risk (security or otherwise) to the operations of the protocol but we strongly advise the Kelp DAO team to properly test before applying the recommended fix.*

## [G-0] Use modifier instead of a function for zero address checks.
In multiple instances of the Kelp DAO codebase, `UtilLib::checkNonZeroAddress` is used to check for the presence of _zero address_ in addresses passed as arguments to core protocol functions. The problem with this approach is that it consumes more gas vs a modifier version. Although, it is worth noting that modifiers can't be defined in Libraries. For this, we suggest the usage of a helper/wrapper contract to contain the zero address modifier logic. It is true that modifiers

[Reference](https://github.com/code-423n4/2022-01-elasticswap-findings/issues/77)

#### Instances:  used across 6 contracts.
example:
```
    function updatePriceFeedFor(address asset, address priceFeed) external onlyLRTManager onlySupportedAsset(asset) {
        UtilLib.checkNonZeroAddress(priceFeed); // This line.
        assetPriceFeed[asset] = priceFeed;
        emit AssetPriceFeedUpdate(asset, priceFeed);
    }
```

#### POC

```
// SPDX-License-Identifier: MIT

 pragma solidity 0.8.21;

/// @notice helper contract with the modifier for zero address check.
 contract Modifier {
   // deployment tx cost: 66854 gas
   // usage cost:  685 gas

    error ZeroAddressNotAllowed();

   modifier checkNonZeroAddress(address address_) {
        if (address_ == address(0)) revert ZeroAddressNotAllowed();
        _;
    }

 }

 /// @notice Library with the function with zero address check.

 library Function {
   // deployment tx cost: 72062 gas
   //  731 gas
    error ZeroAddressNotAllowed();

    function checkNonZeroAddress(address address_) internal pure {
        if (address_ == address(0)) revert ZeroAddressNotAllowed();
    }

 }

 /// @dev contract to test t

// Code block 1 (unknown):
function updatePriceFeedFor(address asset, address priceFeed) external onlyLRTManager onlySupportedAsset(asset) {
        UtilLib.checkNonZeroAddress(priceFeed); // This line.
        assetPriceFeed[asset] = priceFeed;
        emit AssetPriceFeedUpdate(asset, priceFeed);
    }


inmarelibero G

## Summary

The function `burnTokensInContract()` in `SALT.sol` calls `_burn()` and emits an Event even if the balance is zero (there are no tokens to burn)

## Impact

Useless operations can be avoided by performing them only if the balance is > 0

## Proof of Concept

The function body shows how there are no controls before calling `_burn()`:

```
function burnTokensInContract() external
	{
	uint256 balance = balanceOf( address(this) );
	_burn( address(this), balance );

	emit SALTBurned(balance);
	}
```

## Tools Used

Manual review

## Recommended Mitigation Steps

Call `_burn()` and emit Event only when balance is > 0

```diff
diff --git a/src/Salt.sol b/src/Salt.sol
index da51dc1..1bd1510 100644
--- a/src/Salt.sol
+++ b/src/Salt.sol
@@ -25,10 +25,13 @@ contract Salt is ISalt, ERC20
     function burnTokensInContract() external
     	{
     	uint256 balance = balanceOf( address(this) );
+
+       if (balance > 0) {
     	_burn( address(this), balance );

     	emit SALTBurned(balance);
     	}
+       }
```

// Code block 1 (unknown):
function burnTokensInContract() external
	{
	uint256 balance = balanceOf( address(this) );
	_burn( address(this), balance );

	emit SALTBurned(balance);
	}

// Code block 2 (diff):
diff --git a/src/Salt.sol b/src/Salt.sol
index da51dc1..1bd1510 100644
--- a/src/Salt.sol
+++ b/src/Salt.sol
@@ -25,10 +25,13 @@ contract Salt is ISalt, ERC20
     function burnTokensInContract() external
     	{
     	uint256 balance = balanceOf( address(this) );
+
+       if (balance > 0) {
     	_burn( address(this), balance );

     	emit SALTBurned(balance);
     	}
+       }


CatsSecurity Q

## [NC - 1] - Add `onlySupportedAsset(asset)` modifier to `getAssetBalance()` in NodeDelegator.sol
The modifier can be added to the function to catch the mistake earlier instead of reverting letter
```diff
  function getAssetBalance(address asset)
external
view
override
+ onlySupportedAsset(asset)
returns (uint256) {
        address strategy = lrtConfig.assetStrategy(asset);
        return IStrategy(strategy).userUnderlyingView(address(this));
    }
   ```

   https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/NodeDelegator.sol#L121-L124

  ## [NC-2] `updateMaxNodeDelegatorCount` can increase or decrease the maxDelegatorCount but there is no mechanism to remove the extra delegators in case the maxNodeDelegatorCount is decreased from current value

  The following function can either increase or decrease the max number of node delegators, lets say currently there are 10 node delegators, all handling the funds. There is no mechanism to remove extras from `nodeDelegatorQueue` in case the max is decreased from current value.

  ```solidity
      function updateMaxNodeDelegatorCount(uint256 maxNodeDelegatorCount_) external onlyLRTAdmin {
        maxNodeDelegatorCount = maxNodeDelegatorCount_;
        emit MaxNodeDelegatorCountUpdated(maxNodeDelegatorCount);
    }
```

## [NC-3] Emit the depositers address too in `depositAsset()`
    This instance was not reported in the bot so adding it here.
`depositAsset` function should emit the address of the depositer too along with the other parameters for easy off-chain tracking.

```diff
    function depositAsset(
        address asset,
        uint256 depositAmount
    )
        external
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
    {
        // checks
        if (depositAmount == 0) {
            revert InvalidAmount();
        }
        if (depositAmount > getAssetCurrentLimit(asset)) {
            revert MaximumDepositLimitReached()

## [NC-4] There is no mechanism to handle in case the deposit limit is decreased below the current deposits in the contracts.
Deposit limit can be increased or decreased anytime but the contracts implement no mechanism to handle in case it is decreased and current deposits in the system are greater than the deposit limit. There should be a mechanism whenever the limit is decreased below current funds the extra should be immediately taken out of eigen layer

// Code block 1 (diff):
function getAssetBalance(address asset)
external
view
override
+ onlySupportedAsset(asset)
returns (uint256) {
        address strategy = lrtConfig.assetStrategy(asset);
        return IStrategy(strategy).userUnderlyingView(address(this));
    }

// Code block 2 (solidity):
function updateMaxNodeDelegatorCount(uint256 maxNodeDelegatorCount_) external onlyLRTAdmin {
        maxNodeDelegatorCount = maxNodeDelegatorCount_;
        emit MaxNodeDelegatorCountUpdated(maxNodeDelegatorCount);
    }


Aymen0909 Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1 | `preRebaseTokenAmount` and `postRebaseTokenAmount` will both have the same value which is misleading | Low | 1 |
| 2 | Wrong fields emitted in `wrap` function events | Low | 2 |
| 3 | `MINTER_ROLE` not usable as there is no external/public mint function | NC | 1 |
| 4 | Comments at the top of `USDYFactory` are wrong | NC | 1 |

## Findings

### 1- `preRebaseTokenAmount` and `postRebaseTokenAmount` will both have the same value which is misleading :

#### Risk : Low

In the `_burnShares` function, there two values that are computed `preRebaseTokenAmount` and `postRebaseTokenAmount` which represent the RUSDY amount before and after the change in the value of `totalShares` :

```
function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    emit SharesBurnt(
      _account,
      preRebaseTokenAmount,
      postRebaseTokenAmount,
      _sharesAmount
    );

    ...
}
```

The issue is that those two values will have exactly the same value as they are both obtained from the `getRUSDYByShares` function at the same block timestamp :

```
function getRUSDYByShares(uint256 _shares) public view returns (uint256) {
    return (_shares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
}
```

As you can see the value returned from `getRUSDYByShares` doesn't depend on the `totalShares` cha

// Code block 1 (unknown):
function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    emit SharesBurnt(
      _account,
      preRebaseTokenAmount,
      postRebaseTokenAmount,
      _sharesAmount
    );

    ...
}

// Code block 2 (unknown):
function getRUSDYByShares(uint256 _shares) public view returns (uint256) {
    return (_shares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
}


Bjorn_bug Q

## [L-01] In case of an emergency exit a compromised KEEPER can liquidate all positions
According to the Natspec at `ReaperBaseStrategyv4.sol`, it does not mention that the `KEEPER` role is a multi-signature role. Therefore, in case of an emergency exit being activated, a compromised `KEEPER` could call the `harvest()` and `liquidateAllPositions()` back to the vault, even if it is not the desired action by the protocol team.
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L105-L138
## Recommended Mitigation Steps
Limit access to non-multi-sign wallets Roles for calling sensitive functions in the protocol to mitigate the impact of a compromised actor

## [L-02] `SafeMath` library is used but not imported in `LiquityBase.sol`
The contract `LiquityBase` uses `SafeMath` library for units without importing it, and it uses the `0.6.11` compiler version. which makes the contract and others contracts that will interact with it(e.g,`BorrowerOperations.sol`) vulnerable to underflow and overflow attacks.
https://github.com/OpenZeppelin/openzeppelin-conSafeMath library not imported in LiquityBase contracttracts/blob/master/contracts/utils/math/SafeMath.sol
## Recommended Mitigation Steps
Import and use the latest version of the SafeMath library from OpenZeppelin and update the compiler version to ^0.8.16.

## [L-03] Use OpenZeppelin `ECDA` for `erecover`
The `LUSDToken.sol` contract's permit function uses the `ecrecover` to verify signatures. I would recommend using the `ECDSA` from OpenZeppelin as it does more validations when verifying the signature.
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L287
## Recommended Mitigation Steps
```
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
//...
//Before
address recoveredAddress = ecrecover(digest, v, r, s);
// After
address recoveredAddress = ECDSA.recover(digest, v, r, s);
...//
```

## [N-01] Use a

// Code block 1 (unknown):
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
//...
//Before
address recoveredAddress = ecrecover(digest, v, r, s);
// After
address recoveredAddress = ECDSA.recover(digest, v, r, s);
...//


Walter Q

## DynamicOracle
Expected prices on test(rwaDynamicOracle) are different from real prices of usdy over time,in fact,the first 2 months are good expect from October on that are all wrongly expected in the array,resulting in all usdy real prices of October:
Expected(wrongly set):
```
    1.00823938 * 1e18,
     1.00842629 * 1e18,
     1.00861323 * 1e18,
     1.00880021 * 1e18,
     1.00898722 * 1e18,
     1.00917427 * 1e18,
     1.00936135 * 1e18,
     1.00954846 * 1e18,
     1.00973561 * 1e18,
     1.00992280 * 1e18,
     1.01011002 * 1e18,
     1.01029727 * 1e18,
     1.01048456 * 1e18,
     1.01067188 * 1e18,
     1.01085924 * 1e18,
     1.01104664 * 1e18,
     1.01123406 * 1e18,
     1.01142153 * 1e18,
     1.01160902 * 1e18,
     1.01179655 * 1e18,
     1.01198412 * 1e18,
     1.01217172 * 1e18,
     1.01235936 * 1e18,
     1.01254703 * 1e18,
     1.01273474 * 1e18,
     1.01292248 * 1e18,
     1.01311025 * 1e18,
     1.01329806 * 1e18,
     1.01348591 * 1e18,
     1.01367379 * 1e18,
     1.01386170 * 1e18
```
Vs the effective real price,gain using the already available test,by customizing a function that will continue printing every real prices without comparing to the ones quoted above
```
The derived price is:  1008187270000000000
  The derived price is:  1008322040000000000
  The derived price is:  1008456840000000000
  The derived price is:  1008591650000000000
  The derived price is:  1008726470000000000
  The derived price is:  1008861320000000000
  The derived price is:  1008996190000000000
  The derived price is:  1009131070000000000
  The derived price is:  1009265970000000000
  The derived price is:  1009400890000000000
  The derived price is:  1009535820000000000
  The derived price is:  1009670780000000000
  The derived price is:  1009805750000000000
  The derived price is:  1009940740000000000
  The derived price is:  1010075750000000000
  The derived price is:  1010210780000000000
  The derived price is:  1010345820000


tsvetanovv Q

## [QA-01] Use latest Solidity version

Solidity pragma versioning should be upgraded to latest available version.
***

## [QA-02] Use stable pragma statement

Using a floating pragma `^0.8.10` statement is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode.
```
CCashDelegate.sol:
pragma solidity ^0.8.10;

CTokenDelegate.sol:
pragma solidity ^0.8.10;

JumpRateModelV2.sol:
pragma solidity ^0.5.16;

CCash.sol:
pragma solidity ^0.8.10;

CErc20.sol:
pragma solidity ^0.8.10;

CTokenInterfacesModifiedCash.sol:
pragma solidity ^0.8.10;

CTokenInterfacesModified.sol:
pragma solidity ^0.8.10;

cErc20ModifiedDelegator.sol:
pragma solidity ^0.5.12;

CTokenCash.sol:
pragma solidity ^0.8.10;

CTokenModified.sol::
pragma solidity ^0.8.10;
```
***

## [QA-03] Different pragma directives are used

Use one Solidity version on each contract.
***

## [QA-04] USE NAMED IMPORTS INSTEAD OF PLAIN `IMPORT ‘FILE.SOL’
```
Proxy.sol:
18: import "contracts/cash/external/openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";

Cash.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";

CCashDelegate.sol:
4: import "./CCash.sol";

CTokenDelegate.sol:
4: import "./CErc20.sol";

CashKYCSender.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";
19: import "contracts/cash/kyc/KYCRegistryClientInitializable.sol";

OndoPriceOracle.sol:
17: import "./IOndoPriceOracle.sol";
18: import "contracts/lending/compound/Ownable.sol";

CashKYCSenderReceiver.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";
19: import "contracts/cash/kyc/KYCRegistryClientInitializable.sol";

CashFactory.sol:
19: import "contracts/cash/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
20: impo

Proxy.sol:
18: import "contracts/cash/external/openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";

Cash.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";

CCashDelegate.sol:
4: import "./CCash.sol";

CTokenDelegate.sol:
4: import "./CErc20.sol";

CashKYCSender.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";
19: import "contracts/cash/kyc/KYCRegistryClientInitializable.sol";

OndoPriceOracle.sol:
17: import "./IOndoPriceOracle.sol";
18: import "contracts/lending/compound/Ownable.sol";

CashKYCSenderReceiver.sol:
18: import "contracts/cash/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";
19: import "contracts/cash/kyc/KYCRegistryClientInitializable.sol";

CashFactory.sol:
19: import "contracts/cash/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
20: import "contracts/cash/Proxy.sol";
21: import "contracts/cash/token/Cash.sol";
22: import "contracts/cash/interfaces/IMulticall.sol";

CashKYCSenderFactory.sol:
19: import "contracts/cash/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
20: import "contracts/cash/Proxy.sol";
21: import "contracts/cash/token/CashKYCSender.sol";
22: import "contracts/cash/interfaces/IMulticall.sol";

CashKYCSenderReceiverFactory.sol:
19: import "contracts/cash/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
20: import "contracts/cash/Proxy.sol";
21: import "contracts/cash/token/CashKYCSenderReceiver.sol";
22: import "contracts/cash/interfaces/IMulticall.sol";

JumpRateModelV2.sol:
3: import "./compound/InterestRateModel.sol";
4: import "./compound/SafeMath.sol";

KYCRegistry.sol:
18: import "contracts/cash/interfaces/IKYCRegistry.sol";
19: import "contracts/cash/external/openzeppelin/contracts/access/AccessControlEnumerable.sol";
20: import "contracts/cash/external/chainalysis/ISanctionsList.sol";
21: impo

// Code block 1 (unknown):
CCashDelegate.sol:
pragma solidity ^0.8.10;

CTokenDelegate.sol:
pragma solidity ^0.8.10;

JumpRateModelV2.sol:
pragma solidity ^0.5.16;

CCash.sol:
pragma solidity ^0.8.10;

CErc20.sol:
pragma solidity ^0.8.10;

CTokenInterfacesModifiedCash.sol:
pragma solidity ^0.8.10;

CTokenInterfacesModified.sol:
pragma solidity ^0.8.10;

cErc20ModifiedDelegator.sol:
pragma solidity ^0.5.12;

CTokenCash.sol:
pragma solidity ^0.8.10;

CTokenModified.sol::
pragma solidity ^0.8.10;


climber2002 G

# Cache derivativeCount in a local variable when looping
`derivativeCount` is a storage variable. When looping it's more gas efficient to cache it in a local variable.

Change
```solidity
for (uint256 i = 0; i < derivativeCount; i++)
```

to
```solidity
uint256 localDerivativeCount = derivativeCount;
for (uint256 i = 0; i < localDerivativeCount; i++)
```

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L71
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L84
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L113
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L140
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L147
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L171
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L191

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

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

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

uint256 localDerivativeCount = derivativeCount;
for (uint256 i = 0; i < localDerivativeCount; i++)

SafEth.sol#L71, SafEth.sol#L84, SafEth.sol#L113, SafEth.sol#L140, SafEth.sol#L147, SafEth.sol#L171, SafEth.sol#L191


Krace Q

## [Low]  PerpetualAtlanticVaultLP.constructor should use && to check the address
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L81
`require(_perpetualAtlanticVault != address(0) || _rdpx != address(0),"ZERO_ADDRESS");` means one of `_perpetualAtlanticVault` and `_rdpx` could be zero address, I think `&&` should be used here.

```
  constructor(
    address _perpetualAtlanticVault,
    address _rdpxRdpxV2Core,
    address _collateral,
    address _rdpx,
    string memory _collateralSymbol
  )
    ERC20(
      "PerpetualAtlanticVault LP Token",
      _collateralSymbol,
      ERC20(_collateral).decimals()
    )
  {
    require(
      _perpetualAtlanticVault != address(0) || _rdpx != address(0),
      "ZERO_ADDRESS"
    );
    perpetualAtlanticVault = IPerpetualAtlanticVault(_perpetualAtlanticVault);
    rdpxRdpxV2Core = _rdpxRdpxV2Core;
    collateralSymbol = _collateralSymbol;
    rdpx = _rdpx;
    collateral = ERC20(_collateral);

    symbol = string.concat(_collateralSymbol, "-LP");

    collateral.approve(_perpetualAtlanticVault, type(uint256).max);
    ERC20(rdpx).approve(_perpetualAtlanticVault, type(uint256).max);
  }
```

## [Low] Address in UniV3LiquidityAmo should be immutable
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L82-L88
The address of `univ3_factory`, `univ3_positions` and `univ3_router` are set in Constructor, and is not changeable, maybe you should set them to `immutable` or add a function to let admin change them.

## [Low] Implementation of `numPositions` may not be as expected
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L111
The comment of `UniV3LiquidityAmo.numPositions` said that it `Only counts non-withdrawn positions`, but it just returns the length of `positions_array` directly. It s

// Code block 1 (unknown):
constructor(
    address _perpetualAtlanticVault,
    address _rdpxRdpxV2Core,
    address _collateral,
    address _rdpx,
    string memory _collateralSymbol
  )
    ERC20(
      "PerpetualAtlanticVault LP Token",
      _collateralSymbol,
      ERC20(_collateral).decimals()
    )
  {
    require(
      _perpetualAtlanticVault != address(0) || _rdpx != address(0),
      "ZERO_ADDRESS"
    );
    perpetualAtlanticVault = IPerpetualAtlanticVault(_perpetualAtlanticVault);
    rdpxRdpxV2Core = _rdpxRdpxV2Core;
    collateralSymbol = _collateralSymbol;
    rdpx = _rdpx;
    collateral = ERC20(_collateral);

    symbol = string.concat(_collateralSymbol, "-LP");

    collateral.approve(_perpetualAtlanticVault, type(uint256).max);
    ERC20(rdpx).approve(_perpetualAtlanticVault, type(uint256).max);
  }


Polaris_tow Q

## possible address conflict

https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L92
```
        privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(_salt)));
```

This contract uses the cloneDeterministic() function from the LibClone library to create a cloned copy of the minimal proxy contract, which computes a deterministic address. However, there may be potential risks and issues if the computed address is already occupied by someone else.
## Code readability
This smart contract uses multiple nested conditional statements and loops, making the code complex and hard to understand. This could lead to errors by developers while writing or modifying the code, thus increasing the risk of vulnerabilities.
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol

## Token recovery not implemented
The contract does not have a token recovery mechanism in place. If users accidentally send tokens to the contract address, these tokens will be permanently lost and cannot be retrieved from the contract.
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol

## Unconsidered reentrancy attack
If a reentrant attack occurs before the receiver.onFlashLoan() function is executed in the flashLoan function, it may result in multiple calls to the onFlashLoan() function and potential fund loss. It is recommended to use OpenZeppelin's ReentrancyGuard or similar protection measures to prevent such attacks.
 In the _getRoyalty function, if the lookupAddress returned by the royaltyRegistry smart contract does not implement the ERC-2981 interface, it will result in uninitialized recipient and royaltyFee variables being returned by the function. This may allow malicious attackers to call this function through reentrancy attacks or other means and potentially lead to fund loss or other vulnerabilities. It is recommended to add an extra check to ensure that the lookupAddre

// Code block 1 (unknown):
privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(_salt)));


sivanesh_808 G

### G-01: Rearrange the Struct to save slot

#### Description:
Rearranging the structure of the `MoveTokensParams` struct can lead to significant gas savings in smart contracts. Each slot saved in the struct can potentially avoid an extra SSTORE operation, which costs around 20,000 gas for the first setting of the struct.

#### File:
[BorrowerOperations.sol](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol)

#### Code Snippet:
**Before Rearrangement:**
```solidity
struct MoveTokensParams {
    address user;
    uint256 collSharesChange;
    uint256 collAddUnderlying; // ONLY for isCollIncrease=true
    bool isCollIncrease;
    uint256 netDebtChange;
    bool isDebtIncrease;
}
```

**After Rearrangement:**
```solidity
struct MoveTokensParams {
    uint256 collSharesChange;
    uint256 collAddUnderlying;
    uint256 netDebtChange;
    address user;
    bool isCollIncrease;
    bool isDebtIncrease;
    // potentially more bools or small fields here
}
```

#### Gas Savings Analysis:
- **Slot Savings**: The rearranged structure saves 2 slots.
- **Gas Savings**: With each slot potentially saving 20,000 gas on the first setting, the total gas savings could be up to 40,000 gas.

#### GitHub Link:
[BorrowerOperations.sol](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol)

#### File:
[IPriceFeed.sol](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/Interfaces/IPriceFeed.sol#L17C5-L24C6)

#### Code Snippet:
**Before Rearrangement:**
```solidity
struct ChainlinkResponse {
    uint80 roundEthBtcId;
    uint80 roundStEthEthId;
    uint256 answer;
    uint256 timestampEthBtc;
    uint256 timestampStEthEth;
    bool success;
}
```

**After Rearrangement:**
```solidity
struct ChainlinkResponse {
    uint80 roundEthBtcId;
    uint80 roundStEthEthId;
    bool success;
    uint256 answer;
    uint256 timestampEthBtc;
    uint256 ti

// Code block 1 (solidity):
struct MoveTokensParams {
    address user;
    uint256 collSharesChange;
    uint256 collAddUnderlying; // ONLY for isCollIncrease=true
    bool isCollIncrease;
    uint256 netDebtChange;
    bool isDebtIncrease;
}

// Code block 2 (solidity):
struct MoveTokensParams {
    uint256 collSharesChange;
    uint256 collAddUnderlying;
    uint256 netDebtChange;
    address user;
    bool isCollIncrease;
    bool isDebtIncrease;
    // potentially more bools or small fields here
}

// Code block 3 (solidity):
struct ChainlinkResponse {
    uint80 roundEthBtcId;
    uint80 roundStEthEthId;
    uint256 answer;
    uint256 timestampEthBtc;
    uint256 timestampStEthEth;
    bool success;
}

struct MoveTokensParams {
    address user;
    uint256 collSharesChange;
    uint256 collAddUnderlying; // ONLY for isCollIncrease=true
    bool isCollIncrease;
    uint256 netDebtChange;
    bool isDebtIncrease;
}

struct MoveTokensParams {
    uint256 collSharesChange;
    uint256 collAddUnderlying;
    uint256 netDebtChange;
    address user;
    bool isCollIncrease;
    bool isDebtIncrease;
    // potentially more bools or small fields here
}

struct ChainlinkResponse {
    uint80 roundEthBtcId;
    uint80 roundStEthEthId;
    uint256 answer;
    uint256 timestampEthBtc;
    uint256 timestampStEthEth;
    bool success;
}


Myd Analysis

**Kelp DAO**

The Kelp DAO is designed to collectively govern and grow the liquid restaking ecosystem.

- The DAO treasury will receive fees and rewards from the protocol.

- Kelp token holders can submit and vote on proposals to direct treasury funds.

- Proposals could include adding new asset integrations, funding development, etc.

- The DAO can set parameters like deposit limits and reward rates.

- Smart contracts will execute proposal outcomes.

**rsETH Token**

The rsETH token represents a claim on assets deposited into the protocol:

- When users deposit supported assets, they receive rsETH in return.

- The total rsETH supply corresponds to the value of assets deposited.

- rsETH can be freely transferred or used in other DeFi protocols.

- Users burn rsETH when they want to withdraw their deposited assets.

- The token is minted/burned by the LRTDepositPool contract.

**Benefits**

- rsETH unlocks liquidity and DeFi for staked ETH and other staked assets.

- Users can earn yields on assets while still earning staking rewards.

- Governance by Kelp DAO token holders provides decentralized control.

**Architecture**

The architecture centers around the LRTConfig as the main configuration contract that stores addresses of other core contracts like LRTDepositPool and LRTOracle. This provides flexibility to upgrade contracts over time. The admin role in LRTConfig has significant control and permissions.

Other contracts inherit from base contracts like LRTConfigRoleChecker to enforce admin/manager roles. This is a simple access control scheme without decentralized governance.

**Issue:** Significant centralization risks due to administrative privileges in LRTConfig

**Impact:** The admin role in LRTConfig has permission to update core contract addresses, add/remove assets, update rates, and pause contracts. This provides a central point of control over critical operations.

**Root cause:**

[LRTConfig.sol](https://github.com/code-423n4/2023-11-kelp/blob/ma

// Admin can set any contract address
function setContract(bytes32 key, address addr) external onlyRole(DEFAULT_ADMIN_ROLE)

// Admin can add/remove assets
function addAsset(address asset) external onlyRole(DEFAULT_ADMIN_ROLE)

// Admin can pause any contract
function pause(address contractAddr) external onlyRole(DEFAULT_ADMIN_ROLE)

function deposit(address asset, uint amount) external {

  // No check if asset is approved
  // Vulnerable to depositing unapproved tokens

  balances[asset] += amount;
}


DeFiHackLabs Q

# C4_ethos_QA_report

## [L1] Solidity outdate version

### Proof of concept
```js
pragma solidity 0.6.11;
```

in all Ethos contract, ex:
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L3

### **Recommendation:**
Update solidity 0.8.18 or higher which has the fix for some vulnerabilites.

## [L2] Susceptible to Signature Malleability

In Solidity ecrecover function directly to verify the given signatures. However, the ecrecover EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L287
```js
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress == owner, 'LUSD: invalid signature');
```

### **Recommendation:**
Use the recover function from [OpenZeppelin's ECDSA library](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) for signature verification.

```js
function permit
    (
        address owner,
        address spender,
        uint amount,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    )
        external
        override
    {
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert('LUSD: Invalid s value');
        }

        require(deadline >= block.timestamp, 'LUSD: expired deadline');
        bytes32 digest = keccak256(abi.encodePacked(
            '\x19\x01',
            domainSeparator(),
            keccak256(abi.encode(
                _PERMIT_TYPEHASH,
                owner,
                spender,
                amount,
                nonces[owner]++,
                deadline
            ))
        ));

        address recoveredAddress = recover(digest, r, s);
        require(recoveredAddress == owner, 'LUSD: invalid signature');
        _approve(owner, spender

// Code block 1 (js):
pragma solidity 0.6.11;

// Code block 2 (js):
address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress == owner, 'LUSD: invalid signature');


_Adam G

#### [G01] Unchecked increments in for loops
**Description:**
When incrementing i in for loops there is no chance of overflow so unchecked can be used to save gas. I ran a simple test in remix and found deployment savings of ~31,653 gas and on each function call saved ~141 gas per iteration.

**LOC:**
[EntryPoint.sol#L100](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L100)
[EntryPoint.sol#L107](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L107)
[EntryPoint.sol#L112](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L112)
[EntryPoint.sol#L128](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L128)
[EntryPoint.sol#L134](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L134)

**Gas Savings Test:**
```
contract Test {
	function loopTest() external {
		for (uint256 i; i < 1; ++i) {
			Deployment Cost: 125,637, Cost on function call: 24,601

		vs

		for (uint256 i; i < 1; ) {
			// for loop body
			unchecked { ++i }
			Deployment Cost: 93,984, Cost on function call: 24,460
		}
	}
}
```

#### [G02] x = x + y is Cheaper than x += y
**Description:**
When incrementing a state variables current value by y, you can save a small amount of gas by using the pattern x = x +/- y instead of x +/-= y. Based on a test in remix you can save ~1,007 gas on deployment and ~15 gas on each execution.

**LOC:**
[VerifyingSingletonPaymaster.sol#L51](https://github.com/code-423n4/2023-

contract Test {
	uint256 x = 1;
	function test() external {
		x += 3;
		(Deployment Cost: 153,124, Execution Cost: 30,369)

		vs

		x = x + 1;
		(Deployment Cost: 152,117, Execution Cost: 30,354)
	}
}

EntryPoint.sol#L100, EntryPoint.sol#L107, EntryPoint.sol#L112, EntryPoint.sol#L128, EntryPoint.sol#L134


ReyAdmirado G

| | issue |
| ----------- | ----------- |
| 1 | [state variables can be packed into fewer storage slots](#) |
| 2 | [expressions for constant values such as a call to keccak256(), should use immutable rather than constant](#) |
| 3 | [Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate](#) |
| 4 | [state variables should be cached in stack variables rather than re-reading them from storage](#) |
| 5 | [`<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables](#) |
| 6 | [not using the named return variables when a function returns, wastes deployment gas](#) |
| 7 | [can make the variable outside the loop to save gas](#) |
| 8 | [splitting require() statements that use `&&` saves gas](#) |
| 9 | [require() or revert() statements that check input arguments should be at the top of the function](#) |
| 10 | [Avoid a SLOAD optimistically](#) |
| 11 | [use a more recent version of solidity](#) |
| 12 | [internal functions only called once can be inlined to save gas](#) |
| 13 | [avoid an unnecessary sstore by not writing a default value for bools](#) |
| 14 | [Using fixed bytes is cheaper than using string](#) |
| 15 | [ Ternary over if ... else](#) |
| 16 | [public functions not called by the contract should be declared external instead](#) |
| 17 | [state var is defined but not used anywhere but](#) |
| 18 | [Optimize names to save gas](#) |
| 19 | [Non-strict inequalities are cheaper than strict ones](#) |
| 20 | [instead of assigning to zero use `delete`](#) |
| 21 | [precalculating part of code will reduce the number of operations being done](#) |
| 22 | [use argument instead of state var](#) |

## 1. state variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper.

`stabilityPoolAddress` and `initialized` can be put together in

        collAmount[_collateral] = collAmount[_collateral].sub(_amount);
        emit ActivePoolCollateralBalanceUpdated(_collateral, collAmount[_collateral]);

        uint256 someVariable = collAmount[_collateral].sub(_amount);
        collAmount[_collateral] = someVariable
        emit ActivePoolCollateralBalanceUpdated(_collateral, someVariable);


Tekken Q

## Low
### [L-1] [`MergingPool::getFighterPoints`](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L205) should take two arguments instead of one, to be able to preview a range of fighter's points, instead of iterating through all of them up to the `maxId`

**Impact:** The function `getFighterPoints` currently does not provide a way to set a starting index for points to be retrieved. It always starts from the beginning (i = 0). If you want to provide a way to get fighter points inside a range, you should include a startId parameter to the function.

**Recommended Adjustment:**
```js
    function getFighterPoints(uint256 startId, uint256 endId) public view returns(uint256[] memory) {
        uint256 range = endId - startId;
        uint256[] memory points = new uint256[](range);
        for (uint256 i = startId; i < range; i++) {
            points[i] = fighterPoints[i];
        }
        return points;
    }
}
```

### [L-2] Unused [event `Neuron::TokensMinted`](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L21) which is missing from the [`Neuron::constructor`](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L74-L75) and [`Neuron::mint`](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L158), to emit of the event of minting tokens

**Description:** Event `TokensMinted` is not emitted during minting in the `constructor` and `mint` functions. The ERC20's event for the `_mint` function only displays a Transfer function, which could be cofusing when inspecting the events, even-though it is a transfer, it's still a minting function and that should be made clear by utilizing the unused `TokensMinted` event.

**Impact:** It's bad practice to miss out on emitting events for essential state modifications or significant function cal

## Informational
### [I-1] In the function `Neuron::claim`, [`transferFrom`](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L143) is not checked for success

**Recommended Adjustment:**

// Code block 1 (js):
function getFighterPoints(uint256 startId, uint256 endId) public view returns(uint256[] memory) {
        uint256 range = endId - startId;
        uint256[] memory points = new uint256[](range);
        for (uint256 i = startId; i < range; i++) {
            points[i] = fighterPoints[i];
        }
        return points;
    }
}

MergingPool.sol#L205, Neuron.sol#L21, Neuron.sol#L74-L75, Neuron.sol#L158, Neuron.sol#L143


bigtone Q

## Non Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | The `USDA_ZeroAmount` check should be moved to the `_mint` function from the current location in the `mint` function | 1 |
| [NC-2](#NC-2) | The `USDA_ZeroAmount` check should be moved to the `_burn` function from the current location in the `burn` function | 1 |

### [NC-1] The `USDA_ZeroAmount` check should be moved to the `_mint` function from the current location in the `mint` function

There's other functions to use _mint function, so zeroAmount check should be in the `_mint`

*Instances (1)*:
```diff
File: https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/AMPHClaimer.sol:L161-L164

    function mint(uint256 _susdAmount) external override paysInterest onlyOwner {
-        if (_susdAmount == 0) revert USDA_ZeroAmount(); // @audit here
        _mint(_msgSender(), _susdAmount);
    }

File: https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/AMPHClaimer.sol:L167-L178
    function _mint(address _target, uint256 _amount) internal {
+        if (_amount == 0) revert USDA_ZeroAmount();
        uint256 __gonsPerFragment = _gonsPerFragment;
        // the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment
        _gonBalances[_target] += _amount * __gonsPerFragment;
        // total supply is in fragments, and so we add amount
        _totalSupply += _amount;
        // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
        _totalGons += _amount * __gonsPerFragment;
        // emit both a mint and transfer event
        emit Transfer(address(0), _target, _amount);
        emit Mint(_target, _amount);
    }

```

### [NC-2] The `USDA_ZeroAmount` check should be moved to the `_burn` function from the current location in the `burn` funct

### [NC-2] The `USDA_ZeroAmount` check should be moved to the `_burn` function from the current location in the `burn` function

There's other functions to use _burn function, so zeroAmount check should be in the `_burn`

*Instances (1)*:

// Code block 1 (diff):
File: https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/AMPHClaimer.sol:L161-L164

    function mint(uint256 _susdAmount) external override paysInterest onlyOwner {
-        if (_susdAmount == 0) revert USDA_ZeroAmount(); // @audit here
        _mint(_msgSender(), _susdAmount);
    }

File: https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/AMPHClaimer.sol:L167-L178
    function _mint(address _target, uint256 _amount) internal {
+        if (_amount == 0) revert USDA_ZeroAmount();
        uint256 __gonsPerFragment = _gonsPerFragment;
        // the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment
        _gonBalances[_target] += _amount * __gonsPerFragment;
        // total supply is in fragments, and so we add amount
        _totalSupply += _amount;
        // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
        _totalGons += _amount * __gonsPerFragment;
        // emit both a mint and transfer event
        emit Transfer(address(0), _target, _amount);
        emit Mint(_target, _amount);
    }


Tumelo_Crypto Q

# [L-01] DepositLimit can be exceeded due to stETH being a rebase token

## Impact
Although low risk as deposit limit is said to be 100 000 Eth, this will cause problems when interacting with Eigenlayer contracts as they share similar deposit limit.

## Proof of Concept
stETH is a rebasing token that can increase or decrease as the balance of Eth in certain pools changes to maintain 1:1 peg. Should total deposits reach limit specified at any time and stETH supply increases, the protocol will be over the deposit Limit and could encounter problems interacting with the Eignelayer contracts as they are said to have a similar limit.

```solidity
        if (depositAmount > getAssetCurrentLimit(asset)) {
            revert MaximumDepositLimitReached();
        }
```

Likelihood of this happening is low as protocol would have to reach or come really close to deposit limit of 100K ETH first.

## Tools Used
Manuel review

## Recommended Mitigation Steps
Protocol should include separate checks to see if changes in stETH have pushed past depositLimit before depositing into NodeDelegator or EigenLayer strategies. If limit has been passed then protocol should remove some stETH from contract to remain under or equal to depositLimit.

// Code block 1 (solidity):
if (depositAmount > getAssetCurrentLimit(asset)) {
            revert MaximumDepositLimitReached();
        }


0xAgro Q

# QA Report
## Finding Summary

[**Low Severity**](#Low-Severity)
1. [**Unchecked Cast May Overflow**](#1-Unchecked-Cast-May-Overflow)
2. [**Early Use of Compiler Version**](#2-Early-Use-of-Compiler-Version)
3. [**Single Step Owner Transfer**](#3-Single-Step-Owner-Transfer)
4. [**Tokens Without Decimals Will Not Give Price**](#4-Tokens-Without-Decimals-Will-Not-Give-Price)
5. [**Withdraw Event Manipulation**](#5-Withdraw-Event-Manipulation)
6. [**Private Pool Creation Event Manipulation**](#6-Private-Pool-Creation-Event-Manipulation)

[**Non-Critical**](#Non-Critical)
1. [**Incomplete NatSpec**](#1-Incomplete-NatSpec)
2. [**Inconsistent Named Returns**](#2-Inconsistent-Named-Returns)
3. [**Spelling Mistakes**](#3-Spelling-Mistakes)
4. [**Events Not Indexed**](#4-Events-Not-Indexed)
5. [**bytes.concat() Can Be Used Over abi.encodePacked()**](#5-bytesconcat-can-be-used-over-abiencodepacked)
6. [**Inconsistent ASCII Art Starting Point**](#6-Inconsistent-ASCII-Art-Starting-Point)
7. [**Unbounded Compiler Version**](#7-Unbounded-Compiler-Version)
8. [**Value Can Be Used Again**](#8-Value-Can-Be-Used-Again)
9. [**Documentation Should Describe Functionality**](#9-Documentation-Should-Describe-Functionality)
10. [**Assembly Should Be Documented**](#10-Assembly-Should-Be-Documented)
11. [**Magic Number Used**](#11-Magic-Number-Used)*

[**Style Guide Violations**](#Style-Guide-Violations)
1. [**Maximum Line Length**](#1-Maximum-Line-Length)
2. [**Order of Functions**](#2-Order-of-Functions)
3. [**Order of Layout**](#3-Order-of-Layout)
4. [**Single Quote String**](#4-Single-Quote-String)

# Low Severity

## 1. Unchecked Cast May Overflow

As of Solidity 0.8 overflows are handled automatically; however, not for casting. For example `uint32(4294967300)` will result in `4` without reversion. Consider using OpenZepplin's SafeCast library. Even if it seems as though a value cannot overflow, it is best to be safe.

*/src/PrivatePool.sol*

```solidity
230:	virtualBaseTokenReserves +=

230:	virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
231:	virtualNftReserves -= uint128(weightSum);
323:	virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);
324:	virtualNftReserves += uint128(weightSum);

39:    function transferOwnership(address newOwner) public virtual onlyOwner {
40:            owner = newOwner;
41:
42:            emit OwnershipTransferred(msg.sender, newOwner);
43:    }


shark Q

## 0. Zero price is considered valid

In the following contract, `int answer` is required to be `>= 0`. However, this allows `int answer` to be zero, meaning a pointlessly scaled zero value could be returned.

File: `OndoPriceOracleV2.sol` [Line 297](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L297)

```solidity
    require(answer >= 0, "Price cannot be negative");
```

Instead of the above, consider refactoring to:

```solidity
    require(answer > 0, "Price must be greater than zero");
```

## 1. Use time units when applicable

See this: [docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html#time-units](https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html#time-units)

With that in mind, the following declaration may be refactored:

File: `OndoPriceOracleV2.sol` [Line 77](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L77)

```solidity
  uint256 public maxChainlinkOracleTimeDelay = 90000; // 25 hours
```

From the above to, this:

```solidity
  uint256 public maxChainlinkOracleTimeDelay = 25 hours;
```

## 2. Use delete to clear variables instead of zero assignment

Using the [`delete`](https://docs.soliditylang.org/en/v0.8.17/types.html#delete) keyword more clearly states your intention.

For example:

File: `CashManager.sol` [Line 259](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L259)

```solidity
    mintRequestsPerEpoch[epochToClaim][user] = 0;
```

The above instance could be refactored to use the `delete` keyword:

```solidity
    delete mintRequestsPerEpoch[epochToClaim][user];
```

## 3. Typo mistakes

It is not recommended to spell words incorrectly as this will decreases readability. However, this is issue is present in many contracts. Consider fixing the following typos to increase readability:

File: `KYCRegistry.sol` ([Line 62](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts

// Code block 1 (solidity):
require(answer >= 0, "Price cannot be negative");

// Code block 2 (solidity):
require(answer > 0, "Price must be greater than zero");

// Code block 3 (solidity):
uint256 public maxChainlinkOracleTimeDelay = 90000; // 25 hours

// Code block 4 (solidity):
uint256 public maxChainlinkOracleTimeDelay = 25 hours;

// Code block 5 (solidity):
mintRequestsPerEpoch[epochToClaim][user] = 0;

// Code block 6 (solidity):
delete mintRequestsPerEpoch[epochToClaim][user];

require(answer >= 0, "Price cannot be negative");

require(answer > 0, "Price must be greater than zero");

uint256 public maxChainlinkOracleTimeDelay = 90000; // 25 hours

uint256 public maxChainlinkOracleTimeDelay = 25 hours;

mintRequestsPerEpoch[epochToClaim][user] = 0;

delete mintRequestsPerEpoch[epochToClaim][user];


kutugu Q

# Findings Summary

| ID     | Title                                                                                | Severity |
| ------ | ------------------------------------------------------------------------------------------ | -------- |
| [L-01] | UFragments transferAllFrom should round up when calculating allowance value                | Low      |
| [L-02] | GovernorCharlie queue proposal time check is invalid and always success                    | Low      |
| [L-03] | UniswapV3Oracle obtains the price at a single tick that is susceptible to manipulation     | Low      |
| [L-04] | The failure of the user to repay after the pause of the protocol may lead to liquidation   | Low      |
| [L-05] | VaultController _getVaultBorrowingPower function possible exhaustes gas                    | Low      |
| [L-06] | The decimal of the oracle price may not be 18, causing vault to miscalculate               | Low      |
| [L-07] | If sUSD is depegged, The vault can be arbitraged                                           | Low      |

# Detailed Findings

# [L-01] UFragments transferAllFrom should round up when calculating value

## Description

```solidity
  function transferAllFrom(address _from, address _to) external validRecipient(_to) returns (bool _success) {
    uint256 _gonValue = _gonBalances[_from];
    uint256 _value = _gonValue / _gonsPerFragment;

    _allowedFragments[_from][msg.sender] = _allowedFragments[_from][msg.sender] - _value;

    delete _gonBalances[_from];
    _gonBalances[_to] = _gonBalances[_to] + _gonValue;

    emit Transfer(_from, _to, _value);
    return true;
  }
```

The allowance to be consumed should be rounded up. Since _gonsPerFragment is variable, it is possible that _gonValue will eventually divide by _gonsPerFragment with a remainder, so there will be a precision error in division, resulting in the reduced allowance being 1 lower than the actual allowance.
If _gonsPerFragment = 10000, _gonValue = 19999, then allowance con

  function queue(uint256 _proposalId) external override {
    if (state(_proposalId) != ProposalState.Succeeded) revert GovernorCharlie_ProposalNotSucceeded();
    Proposal storage _proposal = proposals[_proposalId];
    uint256 _eta = block.timestamp + _proposal.delay;
    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {
      if (
        queuedTransactions[keccak256(
          abi.encode(
            _proposal.targets[_i], _proposal.values[_i], _proposal.signatures[_i], _proposal.calldatas[_i], _eta
          )
        )]
      ) revert GovernorCharlie_ProposalAlreadyQueued();
      _queueTransaction(
        _proposal.targets[_i],
        _proposal.values[_i],
        _proposal.signatures[_i],
        _proposal.calldatas[_i],
        _eta,
        _proposal.delay
      );
    }
    _proposal.eta = _eta;
    emit ProposalQueuedIndexed(_proposalId, _eta);
    emit ProposalQueued(_proposalId, _eta);
  }

  function _queueTransaction(
    address _target,
    uint256 _value,
    string memory _signature,
    bytes memory _data,
    uint256 _eta,
    uint256 _delay
  ) internal returns (bytes32 _txHash) {
    if (_eta < (_getBlockTimestamp() + _delay)) revert GovernorCharlie_DelayNotReached();

    _txHash = keccak256(abi.encode(_target, _value, _signature, _data, _eta));
    queuedTransactions[_txHash] = true;

    emit QueueTransaction(_txHash, _target, _value, _signature, _data, _eta);
  }

// Code block 1 (solidity):
function transferAllFrom(address _from, address _to) external validRecipient(_to) returns (bool _success) {
    uint256 _gonValue = _gonBalances[_from];
    uint256 _value = _gonValue / _gonsPerFragment;

    _allowedFragments[_from][msg.sender] = _allowedFragments[_from][msg.sender] - _value;

    delete _gonBalances[_from];
    _gonBalances[_to] = _gonBalances[_to] + _gonValue;

    emit Transfer(_from, _to, _value);
    return true;
  }


ch0bu G

1. ## Use a more recent version of solidity

- Use a solidity version of at least 0.8.0 to get overflow/underflow protection without `SafeMath`
- Use a solidity version of at least 0.8.2 to get compiler automatic inlining
- Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
- Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than `revert()/require()` strings
- Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
- Use a solidity version of at least 0.8.12 to get `string.concat()` to be used instead of `abi.encodePacked(<str>,<str>)`
- Use a solidity version of at least 0.8.13 to get the ability to use `using for` with a list of free functions
- Use a solidity version of 0.8.15 where conditions necessary for inlining are relaxed. It shows significant dicrease in the bytecode size (and thus reduced deployment cost)
- Use a solidity version of 0.8.17 to prevent the incorrect removal of storage writes before calls to Yul functions that conditionally terminate the external EVM call; Simplify the starting offset of zero-length operations to zero; More efficient overflow checks for multiplication

```
All contracts in scope
```

2. ## Using unchecked blocks to save gas - increments in for loop can be unchecked

If the team decides to migrate all contracts in scope to solidity ^0.8.0 then the majority of Solidity for loops increment a uint256 variable that starts at 0. These increment operations never need to be checked for overflow/underflow because the variable will never reach the max number of uint256 (will run out of gas long before that happens). The default overflow/underflow check wastes gas in every iteration of virtually every for loop.

This saves 30-40 gas per loop iteration.

e.g Let’s work with a sample loop below.
```
for(uint256 i; i < 10; i++){
//doSomething
}
```
can be

// Code block 1 (unknown):
All contracts in scope

// Code block 2 (unknown):
for(uint256 i; i < 10; i++){
//doSomething
}


seeu Q

## [L-01] Compiler version Pragma is non-specific

### Description

For non-library contracts, floating pragmas may be a security risk for application implementations, since a known vulnerable compiler version may accidentally be selected or security tools might fallback to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.

### Findings

- [contracts/lending/tokens/cCash/CCash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCash.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cCash/CCashDelegate.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCashDelegate.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cToken/CErc20.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CErc20.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cCash/CTokenCash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CTokenCash.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cToken/CTokenDelegate.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenDelegate.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cToken/CTokenInterfacesModified.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenInterfacesModified.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/tokens/cToken/CTokenModified.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenModified.sol) => `pragma solidity ^0.8.10;`
- [contracts/lending/JumpRateModelV2.sol](https://github.com/code-423n4/20

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCash.sol
::66 =>   function mint(uint mintAmount) external override returns (uint) {
::77 =>   function redeem(uint redeemTokens) external override returns (uint) {
::100 =>   function borrow(uint borrowAmount) external override returns (uint) {
::110 =>   function repayBorrow(uint repayAmount) external override returns (uint) {
::168 =>   function _addReserves(uint addAmount) external override returns (uint) {
::179 =>   function getCashPrior() internal view virtual override returns (uint) {

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CErc20.sol
::66 =>   function mint(uint mintAmount) external override returns (uint) {
::77 =>   function redeem(uint redeemTokens) external override returns (uint) {
::100 =>   function borrow(uint borrowAmount) external override returns (uint) {
::110 =>   function repayBorrow(uint repayAmount) external override returns (uint) {
::168 =>   function _addReserves(uint addAmount) external override returns (uint) {
::179 =>   function getCashPrior() internal view virtual override returns (uint) {

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CTokenCash.sol
::210 =>   function balanceOf(address owner) external view override returns (uint256) {
::220 =>   function balanceOfUnderlying(address owner) external override returns (uint) {
::246 =>   function getBlockNumber() internal view virtual returns (uint) {
::254 =>   function borrowRatePerBlock() external view override returns (uint) {
::267 =>   function supplyRatePerBlock() external view override returns (uint) {
::281 =>   function totalBorrowsCurrent() external override nonReentrant returns (uint) {
::338 =>   function exchangeRateCurrent() public override nonReentrant returns (uint) {
::348 =>   function exchangeRateStored() public view override returns (uint) {
::357 =>   function exchangeRateStoredInternal() internal

CCash.sol, CCashDelegate.sol, CErc20.sol, CTokenCash.sol, CTokenDelegate.sol, CTokenInterfacesModified.sol, CTokenInterfacesModifiedCash.sol, CTokenModified.sol

CTokenCash.sol, CErc20.sol, CTokenModified.sol, CTokenInterfacesModified.sol, CTokenDelegate.sol, CTokenInterfacesModifiedCash.sol, CCash.sol, CCashDelegate.sol


Krace Analysis

The comment in the function `_bisectionSearch` says that:

```
	// Assuming that the profit function is unimodal (has only one peak), the two profit calculations can show us which half of the range the maximum profit is in (where to keep looking).
```

But I think this profit function is monotonically increasing. The profit function is composed of three parts, where the output of the former is the input of the latter. If these three functions are monotonically increasing, then the entire profit function is also increasing on [0, $\infty$).

We have:
`amountOut = (reservesA1 * bestArbAmountIn) / (reservesA0 + bestArbAmountIn);`

so we can get this funciton: `y = (reservesA1 * x) / (reservesA0 + x)` where $x > 0,  reservesA0 > 100, reservesA1 > 100$.

Let $x_1 > x_0 >= 0$, so $y_1 - y_0 = (reservesA1 * x_1) / (reservesA0 + x_1) - (reservesA1 * x_0) / (reservesA0 + x_0)$
$= reservesA1 * (reservesA0*x_1 + x_1*x_0 - reservesA0*x_0 - x_1*x_0) / (reservesA0 + x_0)*(reservesA0 + x_1)$
$= reservesA1 * reservesA0 * (x_1 - x_0) / (reservesA0 + x_0)*(reservesA0 + x_1)$

Because $x_1 > x_0$, so $x_1 - x_0 > 0$, which means $y_1 > y_0$ when $x_1 > x_0$, this function is monotonically increasing.

Therefore, I think there is no need to use binary search. The maximum return is always obtained at the time of maximum input.

### Time spent:
30 hours

// Code block 1 (unknown):
// Assuming that the profit function is unimodal (has only one peak), the two profit calculations can show us which half of the range the maximum profit is in (where to keep looking).


SpicyMeatball Q

### `transferOwnership` doesn't revoke admin rights from the old owner
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/VoltageManager.sol#L64
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/Neuron.sol#L85
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/GameItems.sol#L108
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/MergingPool.sol#L89
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/RankedBattle.sol#L167

The owner is granted admin rights when the contract is created
```solidity
    constructor(address ownerAddress, address gameItemsContractAddress) {
        _ownerAddress = ownerAddress;
        _gameItemsContractInstance = GameItems(gameItemsContractAddress);
>>      isAdmin[_ownerAddress] = true;
    }
```
When transferring ownership to another address, the old owner still retains that role. Consider setting `isAdmin` for the old owner to `false`.

### User can bypass voltage limit by participating in battles from different addresses

Initiator of the ranked battle needs to spend 10 voltage units to participate
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/RankedBattle.sol#L345-L347
```solidity
    function spendVoltage(address spender, uint8 voltageSpent) public {
        require(spender == msg.sender || allowedVoltageSpenders[msg.sender]);
        if (ownerVoltageReplenishTime[spender] <= block.timestamp) {
            _replenishVoltage(spender);
        }
        ownerVoltage[spender] -= voltageSpent;
        emit VoltageRemaining(spender, ownerVoltage[spender]);
    }
```
The voltage is replenished after 24 hours or by sacrificing a special NFT
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/VoltageManager.sol#L117
```solidity
    function _replenishVoltage(address owner) private {
>>      ownerVoltage[owner] = 100;
        ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days);
    }
```
As we can see, the voltage is bound to the fighter owner's add

// Code block 1 (solidity):
constructor(address ownerAddress, address gameItemsContractAddress) {
        _ownerAddress = ownerAddress;
        _gameItemsContractInstance = GameItems(gameItemsContractAddress);
>>      isAdmin[_ownerAddress] = true;
    }

// Code block 2 (solidity):
function spendVoltage(address spender, uint8 voltageSpent) public {
        require(spender == msg.sender || allowedVoltageSpenders[msg.sender]);
        if (ownerVoltageReplenishTime[spender] <= block.timestamp) {
            _replenishVoltage(spender);
        }
        ownerVoltage[spender] -= voltageSpent;
        emit VoltageRemaining(spender, ownerVoltage[spender]);
    }

// Code block 3 (solidity):
function _replenishVoltage(address owner) private {
>>      ownerVoltage[owner] = 100;
        ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days);
    }

constructor(address ownerAddress, address gameItemsContractAddress) {
        _ownerAddress = ownerAddress;
        _gameItemsContractInstance = GameItems(gameItemsContractAddress);
>>      isAdmin[_ownerAddress] = true;
    }

function spendVoltage(address spender, uint8 voltageSpent) public {
        require(spender == msg.sender || allowedVoltageSpenders[msg.sender]);
        if (ownerVoltageReplenishTime[spender] <= block.timestamp) {
            _replenishVoltage(spender);
        }
        ownerVoltage[spender] -= voltageSpent;
        emit VoltageRemaining(spender, ownerVoltage[spender]);
    }

function _replenishVoltage(address owner) private {
>>      ownerVoltage[owner] = 100;
        ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days);
    }

VoltageManager.sol#L64, Neuron.sol#L85, GameItems.sol#L108, MergingPool.sol#L89, RankedBattle.sol#L167, RankedBattle.sol#L345-L347, VoltageManager.sol#L117

VoltageManager.sol, Neuron.sol, GameItems.sol, RankedBattle.sol, MergingPool.sol


cryptostellar5 G

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

*Number of Instances Identified: 9*

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

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

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

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

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

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

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

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

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

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

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

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

*Number of Instances Identified: 9*

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

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

```
221: mintRequestsPerEpoch[currentEpoch][msg.sender] += depositValueAfterFees;
582: currentEpoch += epochDifference;
630: currentMintAmount += collateralAmountIn;
649: currentRedeemAmount += amount;
678-680: redemptionInfoPerEp

// Code block 1 (unknown):
750: for (uint256 i = 0; i < size; ++i)
786: for (uint256 i = 0; i < size; ++i)
933: for (uint256 i = 0; i < size; ++i)
961: for (uint256 i = 0; i < exCallData.length; ++i)

// Code block 2 (unknown):
127: for (uint256 i = 0; i < exCallData.length; ++i)

// Code block 3 (unknown):
137: for (uint256 i = 0; i < exCallData.length; ++i)

// Code block 4 (unknown):
137: for (uint256 i = 0; i < exCallData.length; ++i)

// Code block 5 (unknown):
163: for (uint256 i = 0; i < length; i++)
180: for (uint256 i = 0; i < length; i++)

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

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