0xAadi Q

# [L-01] Checking `MINTER` access control twice in `mint()` function

The `mint()` function performs a double verification of the MINTER access control. This is by through the use of both the `onlyRole(MINTER_ROLE)` modifier and the `require(hasRole(MINTER_ROLE, msg.sender))` statement. Consequently, the message "Caller is not a minter" will never be triggered.

There is 1 instances of this issue:

File: contracts/decaying-bonds/RdpxDecayingBonds.sol

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/decaying-bonds/RdpxDecayingBonds.sol#L114C3-L125C4

```solidity
  function mint(
    address to,
    uint256 expiry,
    uint256 rdpxAmount
  ) external onlyRole(MINTER_ROLE) {
    _whenNotPaused();
    require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
    uint256 bondId = _mintToken(to);
    bonds[bondId] = Bond(to, expiry, rdpxAmount);

    emit BondMinted(to, bondId, expiry, rdpxAmount);
  }
```

### Tool Used

Manual Review

### Recommendation

The require statement can be removed.

# [L-02] `emergencyWithdraw()` can be reverted if `to` address is a contract that does not implement the `receive()`

The `emergencyWithdraw()` function will be reverted the native coin transfer if the recipient `to` address is a contract that does not implement the `receive()` function. This restriction, in turn, limits the emergency withdrawal of ERC20 coins from the `RdpxDecayingBonds` contract.

There is 1 instance of this issue:

File: contracts/decaying-bonds/RdpxDecayingBonds.sol

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/decaying-bonds/RdpxDecayingBonds.sol#L89C1-L107C4

### Tool Used

Manual Review

### Recommendation

Implement code to check the `to` address is contract address or not

# [L-03] There are several setter functions that do not check if the amount is greater than 100%.

There is 8 instance of this issue:

File: contracts/core/RdpxV2Core.s

emit LogBond(rdpxRequired, wethRequired, receiptTokenAmount);

// Code block 1 (solidity):
function mint(
    address to,
    uint256 expiry,
    uint256 rdpxAmount
  ) external onlyRole(MINTER_ROLE) {
    _whenNotPaused();
    require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
    uint256 bondId = _mintToken(to);
    bonds[bondId] = Bond(to, expiry, rdpxAmount);

    emit BondMinted(to, bondId, expiry, rdpxAmount);
  }


Shawon Q

# Use a hardcoded address instead of address(this)

It can be more gas-efficient to use a hardcoded address instead of the address(this) expression,
especially if you need to use the same address multiple times in your contract.

The reason for this is that using address(this) requires an additional EXTCODESIZE operation to retrieve the contract’s address from its bytecode,
 which can increase the gas cost of your contract. By pre-calculating and using a hardcoded address,
 you can avoid this additional operation and reduce the overall gas cost of your contract.

``` solidity
file: src/NodeDelegator.sol

63 uint256 balance = token.balanceOf(address(this));

103 IEigenStrategyManager(eigenlayerStrategyManagerAddress).getDeposits(address(this));

111 assetBalances[i] = IStrategy(strategies[i]).userUnderlyingView(address(this));

123 return IStrategy(strategy).userUnderlyingView(address(this));

```

 # If already paused/unpaused, revert.

``` solidity
file: utils/LRTDeposit.sol

 207    /// @dev Triggers stopped state. Contract must not be paused.
 208   function pause() external onlyLRTManager {
 209       _pause();
 210   }

 212  /// @dev Returns to normal state. Contract must be paused
 213   function unpause() external onlyLRTAdmin {
 214      _unpause();
 215  }

```
the dev comments states that the paused function must be called when it is not already paused, Same with the unpaused function.
But we see no such checks inside those functions to check if the contracted has already been paused by another LRTManager before
executing the function.

Recommending to add :

``` solidity
function pause() external onlyLRTManager {
 if (paused=true){
revert };
...

```

``` solidity
 function unpause() external onlyLRTAdmin {
 if (unpaused=true){
revert };
...

```

Instances: `NodeDelegator.sol` , `LRTOracle.sol` , `RSETH.sols`

// Code block 1 (unknown):
# If already paused/unpaused, revert.

// Code block 2 (unknown):
the dev comments states that the paused function must be called when it is not already paused, Same with the unpaused function.
But we see no such checks inside those functions to check if the contracted has already been paused by another LRTManager before
executing the function.

Recommending to add :


0x11singh99 G

**Note:**  I've removed all findings instances appearing in the bot and 4naly3er reports and have only kept the ones that they missed.

# Gas-Optimization for Olas Protocol

## Gas Optimizations

| Number                                                                                | Issue                                                                                | Instances |
| ----------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | :-------: |
| [[G-01](#g-01-change-the-order-of-declaration-of-state-variables-to-save-storage-slots-by-packing-tightly)] | Change the order of declaration of state variables to save storage slots by packing tightly    |     1     |
| [[G-02](#g-02-state-variables-can-be-packed-into-fewer-storage-slot-by-reducing-their-size)]                | State variables can be packed into fewer storage slot by reducing their size                   |     1     |
| [[G-03](#g-03-state-variables-only-set-in-the-constructor-should-be-declared-immutablemissed-by-bot)]       | `State` variables only set in the `constructor` should be declared `immutable` (Missed by bot) |     1     |
| [[G-04](#g-04-state-variables-can-be-cached-instead-of-re-reading-them-from-storage)]                       | `State` variables can be cached instead of re-reading them from storage                        |     2     |
| [[G-05](#g-05-here-no-need-to-check-for-address0-in-constructor)]                                           | Here no need to check for `address(0)` in `constructor`                                        |     3     |
| [[G-06](#g-06-cache-value-in-a-local-variable-instead-of-re-calculate)]                                     | `Cache` value in a local variable instead of `re-calculate`                                    |     1     |
| [[G-07](#g-07-use-calldata-instead

File : lockbox-solana/solidity/liquidity_lockbox.sol

27:    // Current program owned PDA account address
28:    address public pdaProgram;
29:    // Bridged token mint address
30:    address public bridgedTokenMint;
31:    // PDA bridged token account address
32:    address public pdaBridgedTokenAccount;
33:    // PDA header for position account
34:    uint64 public pdaHeader = 0xd0f7407ae48fbcaa;
35:    // Program PDA seed
36:    bytes public constant pdaProgramSeed = "pdaProgram";
37:    // Program PDA bump
38:    bytes1 public pdaBump;
39:    int32 public constant minTickLowerIndex = -443632;
40:    int32 public constant maxTickLowerIndex = 443632;
41:
42:    // Total number of token accounts (even those that hold no positions anymore)
43:    uint32 public numPositionAccounts;//@audit
44:    // First available account index in the set of accounts;
45:    uint32 public firstAvailablePositionAccountIndex;//@audit
46:    // Total liquidity in a lockbox
47:    uint64 public totalLiquidity;//@audit

## [G-02] State variables can be packed into fewer storage slot by reducing their size

The EVM works with 32 byte words. Variables less than 32 bytes can be declared next to each other in storage and this
will pack the values together into a single 32 byte storage slot (if the values combined are <= 32 bytes). If the
variables packed together are retrieved together in functions, we will effectively save ~2000 gas with every subsequent
SLOAD for that storage slot. This is due to us incurring a Gwarmaccess (100 gas) versus a Gcoldsload (2100 gas).

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 can also be cheaper

_1 Instance_

### After reducing size of `_locked` from `uint256` to `uint8`, it can be packed with `address manager` in single SLOT :`SAVES 2000 GAS`, `1 SLOT`

Reduce uint type of `_locked` from `uint256` to `uint8` and pack with `address manager` because `_locked` can have only two values `1` and `2` assigned in `UnitRegistry::create` function at the [line-56](https://github.com/code-423n4/2023-12-autonolas/blob/main/registries/contracts/UnitRegistry.sol#L56) and at [line-113](https://github.com/code-423n4/2023-12-autonolas/blob/main/registries/contracts/UnitRegistry.sol#L113) . Since `GenericRegistry.sol` is inherited by `UnitRegistry.sol`. At the time of declaration also 1 is assigned in `_locked`.

### So `uint8` is more than enough to hold these values


albahaca G

## Use assembly for math (add, sub, mul, div)
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety.

### Befor
| src/InterestRateModel.sol:InterestRateModel contract |                 |      |        |      |         |
|------------------------------------------------------|-----------------|------|--------|------|---------|
| Deployment Cost                                      | Deployment Size |      |        |      |         |
| 415204                                               | 2390            |      |        |      |         |
| Function Name                                        | min             | avg  | median | max  | # calls |
| getUtilizationRateX96                                | 387             | 535  | 609    | 609  | 3       |
```solidity
50   return debt * Q96 / (cash + debt);
```
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/InterestRateModel.sol#L50

### After
| src/InterestRateModel.sol:InterestRateModel contract |                 |      |        |      |         |
|------------------------------------------------------|-----------------|------|--------|------|---------|
| Deployment Cost                                      | Deployment Size |      |        |      |         |
| 410404                                               | 2366            |      |        |      |         |
| Function Name                                        | min             | avg  | median | max  | # calls |
| getUtilizationRateX96                                | 387             | 399  | 405    | 405  | 3       |

```diff
    function getUtilizationRateX96(uint256 cash, uint256 debt) public pure returns (uint256) {
        if (debt == 0) {
            return 0;
        }
-       return debt * Q96 / (cash + debt);
+       uint256 result;
+       assembly {
+           // Calculate debt * Q96
+          let debtQ96 := mul(debt, Q96)

+           // Calculate cash + debt
+           let

// Code block 1 (solidity):
50   return debt * Q96 / (cash + debt);


DadeKuma Q

## Summary

---

### Low Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[L-01]| Solmate's SafeTransferLib doesn't check whether the ERC20 contract exists | 24 |
|[L-02]| Lack of two-step update for critical addresses | 2 |
|[L-03]| Loss of precision on division | 1 |
|[L-04]| Missing events in sensitive functions | 4 |
|[L-05]| Gas griefing/theft is possible on an unsafe external call | 1 |
|[L-06]| Unused/empty `receive()`/`fallback()` function | 3 |

Total: 35 instances over 6 issues.

### Non Critical Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[NC-01]| Use of `abi.encodePacked` instead of `bytes.concat` | 7 |
|[NC-02]| Parameter omission in events | 5 |
|[NC-03]| Some functions don't follow the Solidity naming conventions | 1 |
|[NC-04]| Use of floating pragma | 5 |

Total: 18 instances over 4 issues.

## Low Findings

---

### [L-01] Solmate's SafeTransferLib doesn't check whether the ERC20 contract exists

Solmate's SafeTransferLib, which is often used to interact with non-compliant/unsafe ERC20 tokens, does not check whether the ERC20 contract exists. The following code will not revert in case the token doesn’t exist (yet).
This is stated in the [Solmate library](https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol#L9).

*There are 24 instances of this issue.*

<details>
<summary>Expand findings</summary>

[src/EthRouter.sol#L123](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L123)

[src/EthRouter.sol#L142](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L142)

[src/EthRouter.sol#L190](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L190)

[src/EthRouter.sol#L208](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L208)

[src/EthRouter.sol#L291](https://github.com/code-423n4/20

File: src/EthRouter.sol

123: 		                            royaltyRecipient.safeTransferETH(royaltyFee);

142: 		            msg.sender.safeTransferETH(address(this).balance);

190: 		                            royaltyRecipient.safeTransferETH(royaltyFee);

208: 		        msg.sender.safeTransferETH(address(this).balance);

291: 		            msg.sender.safeTransferETH(address(this).balance);

File: src/Factory.sol

112: 		            address(privatePool).safeTransferETH(baseTokenAmount);

150: 		            msg.sender.safeTransferETH(amount);

SafeTransferLib.sol#L9, EthRouter.sol#L123, EthRouter.sol#L142, EthRouter.sol#L190, EthRouter.sol#L208


0xbrett8571 Analysis

![kel](https://github.com/code-423n4/2023-11-canto/assets/125544245/8468335f-eaf2-4fb8-856d-0a697886bc50)

Kelp aims to provide liquidity rewards for staked ETH assets by integrating with the EigenLayer protocol. It consists of core contracts for configuration, deposits, delegating to strategies, pricing, and receipt tokens.

I reviewed the contracts through manual analysis, focused on security, intended behavior, and integration.

[LRTConfig](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol): An upgradeable contract which is responsible for storing the configuration of the Kelp product. It is also responsible for storing the addresses of the other contracts in the Kelp product.

[LRTDepositPool](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol): An upgradeable contract which receives the funds deposited by users into the Kelp product. From here, the funds are transferred to [NodeDelegator](https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol)s contracts.

NodeDelegator: These are contracts that receive funds from the LRDepositPool and delegate them to the EigenLayer strategy. The funds are then used to provide liquidity on the EigenLayer protocol. It is also an upgradeable contract.

[LRTOracle](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol): An upgradeable contract which is responsible for fetching the price of Liquid Stasking Tokens tokens from oracle services.

[RSETH](https://github.com/code-423n4/2023-11-kelp/blob/main/src/RSETH.sol): Receipt token for depositing into the Kelp product. It is an upgradeable ERC20 token contract.

**Scope**

| Contract              | Purpose                                                        | SLOC | Libraries Used            |
|-----------------------|----------------------------------------------------------------|------|----------------------------|
| [LRTConfig](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTCon

**Key Points**

- LRTConfig stores addresses and parameters for other contracts
- LRTDepositPool is the frontend for user deposits
- It mints RSETH tokens in sync with deposits
- NodeDelegator receives assets from LRTDepositPool and delegates to strategies
- LRTOracle provides pricing data to the system
- Modular architecture allows flexibility and separation of concerns

The core dependency is the unified configuration in LRTConfig, which must be secure and authorize only valid contract addresses.

**LRTConfig**

The LRTConfig contract is relied upon by other contracts for address lookups via the [`getContract`](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L131-L133) and `getToken` functions:

```js
// Example usage in LRTDepositPool
address lrtOracle = LRTConfig.getContract(LRT_ORACLE);

LRTConfig.sol, LRTDepositPool.sol, NodeDelegator.sol, LRTOracle.sol, RSETH.sol, LRTConfig.sol#L131-L133

LRTOracle.sol, RSETH.sol, NodeDelegator.sol, LRTConfig.sol, LRTDepositPool.sol


ABAIKUNANBAEV Q

# Low-level issues

## Finding Summary

| ID | Description | Severity |
| - | - | :-: |
| [L-01](#l-01-relience-on-total-supply) | Reliance on `totalSupply()` in `getRSETHPrice()` makes it susceptible to front-running attacks and manipulations | Low |

## [L-01] Reliance on `totalSupply()` in `getRSETHPrice()` makes it susceptible to front-running attacks and manipulations

Inside of `LRTOracle.sol` the price of rsETH is determined by `totalETHInPool and `rsEthSupply`:

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L78
```
return totalETHInPool / rsEthSupply;

```

The problem is that the `rsEthSupply` can be easily manipulated and, therefore, the `getRsETHAmountToMint()` will be influenced as well:

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

```
rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
```

Possible mitigation: consider using already existing oracles instead of implementing the new one.

// Code block 1 (unknown):
return totalETHInPool / rsEthSupply;

// Code block 2 (unknown):
rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();


immeas Q

# low

## L-1 incorrect comment or check

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

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

```javascript
File: cash/CashManager.sol

408:    * @dev The maximum fee that can be set is 10_000 bps, or 100%
```

If you look at the check it does `_mintFee >= BPS_DENOMINATOR`:

```javascript
File: cash/CashManager.sol

413:    if (_mintFee >= BPS_DENOMINATOR) {
414:      revert MintFeeTooLarge();
415:    }
```

So the maximum `mintFee` is `9999`, 99.99%

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

408:    * @dev The maximum fee that can be set is 10_000 bps, or 100%

// Code block 2 (javascript):
File: cash/CashManager.sol

413:    if (_mintFee >= BPS_DENOMINATOR) {
414:      revert MintFeeTooLarge();
415:    }


Timenov G

# Lybra Finance report by Timenov

## Summary
G-01 Use `constant` for variables that will not be changed in the future.

### [G-01] Use `constant` for variables that will not be changed in the future.
*There are 2 instances of this issue.*

```solidity
File: contracts/lybra/token/esLBR.sol

20: uint256 maxSupply = 100_000_000 * 1e18;
```

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

```solidity
File: contracts/lybra/token/LBR.sol

15: uint256 maxSupply = 100_000_000 * 1e18;
```

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

// Code block 1 (solidity):
File: contracts/lybra/token/esLBR.sol

20: uint256 maxSupply = 100_000_000 * 1e18;

// Code block 2 (solidity):
File: contracts/lybra/token/LBR.sol

15: uint256 maxSupply = 100_000_000 * 1e18;

File: contracts/lybra/token/esLBR.sol

20: uint256 maxSupply = 100_000_000 * 1e18;

File: contracts/lybra/token/LBR.sol

15: uint256 maxSupply = 100_000_000 * 1e18;


report

---
sponsor: "Caviar"
slug: "2023-04-caviar"
date: "2023-05-18"
title: "Caviar Private Pools"
findings: "https://github.com/code-423n4/2023-04-caviar-findings/issues"
contest: 230
---

# Overview

## About C4

Code4rena (C4) is an open organization consisting of security researchers, auditors, developers, and individuals with domain expertise in smart contracts.

A C4 audit is an event in which community participants, referred to as Wardens, review, audit, or analyze smart contract logic in exchange for a bounty provided by sponsoring projects.

During the audit outlined in this document, C4 conducted an analysis of the Caviar Private Pools smart contract system written in Solidity. The audit took place between April 7—April 13 2023.

Following the C4 audit, 3 wardens (rvierdiiev, rbserver, and KrisApostolov) reviewed the mitigations for all identified issues; the mitigation review report is appended below the audit report.

*Note: This published report was updated on August 28, 2023 to include the mitigation review results.*

## Wardens

127 Wardens contributed reports to the Caviar Private Pools:

  1. 0x4db5362c
  2. 0x4non
  3. 0x5rings
  4. [0x6980](https://twitter.com/0x6980)
  5. [0xAgro](https://twitter.com/0xAgro)
  6. [0xLanterns](https://twitter.com/Kiki_developer) ([kiki_dev](https://twitter.com/Kiki_developer) and [wall604](https://twitter.com/ZoumanaCisse6))
  7. 0xNorman
  8. 0xRobocop
  9. [0xSmartContract](https://twitter.com/0xSmartContract)
  10. [0xTheC0der](https://twitter.com/MarioPoneder)
  11. 0xWeiss
  12. 0xbepresent
  13. [ABA](https://twitter.com/abarbatei)
  14. [AkshaySrivastav](https://twitter.com/akshaysrivastv)
  15. [Aymen0909](https://github.com/Aymen1001)
  16. Bason
  17. [Bauchibred](https://twitter.com/bauchibred?s=21&t=7sv-1qcnwtkdTA81Iog0yQ )
  18. Bauer
  19. BenRai
  20. BradMoon
  21. Brenzee
  22. ChrisTina
  23. CodingNameKiki
  24. [Cryptor](https://twitter.com/DeFiCast)
  25. [DadeKuma](https://twitter.co

        #buy(uint256[],uint256[],MerkleMultiProof)

271:    if (payRoyalties) {
            ...
274:        (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);
            ...
278:        if (baseToken != address(0)) {
279:            ERC20(baseToken).safeTransfer(recipient, royaltyFee);
280:        } else {
281:            recipient.safeTransferETH(royaltyFee);
282:        }

        #sell(uint256[],uint256[],MerkleMultiProof,IStolenNftOracle.Message[])

329:    for (uint256 i = 0; i < tokenIds.length; i++) {
            ...
333:        if (payRoyalties) {
                ...
338:            (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);
                ...
345:            if (baseToken != address(0)) {
346:                ERC20(baseToken).safeTransfer(recipient, royaltyFee);
347:            } else {
348:                recipient.safeTransferETH(royaltyFee);
349:            }


decade G

#### 1. No need for arguments in flashFee function:
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L750-L752
```
   function flashFee(address, uint256) public view returns (uint256) {
        return changeFee;
    }
```
___
#### 2. Recursive calculation in _buy_ function can be avoided.
 (netInputAmount - feeAmount - protocolFeeAmount) can be stored in one variable instead of calculating recursively.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L229-L236
```
@@ -227,13 +227,14 @@ contract PrivatePool is ERC721TokenReceiver {
         // ~~~ Effects ~~~ //

         // update the virtual reserves
-        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
+        uint addReserves = netInputAmount - feeAmount - protocolFeeAmount;
+        virtualBaseTokenReserves += uint128(addReserves);
         virtualNftReserves -= uint128(weightSum);

         // ~~~ Interactions ~~~ //

         // calculate the sale price (assume it's the same for each NFT even if weights differ)
-        uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length;
+        uint256 salePrice = (addReserves) / tokenIds.length;
         uint256 royaltyFeeAmount = 0;
         for (uint256 i = 0; i < tokenIds.length; i++) {
             // transfer the NFT to the caller
```
---
#### 3. Static statement can be put outside FOR loop.
`salePrice` calculated in `sell()` function can be put outside for loop instead of calculating on every iteration.
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L335

```
diff --git a/src/PrivatePool.sol b/src/PrivatePool.sol
+        uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length;
         uint256 royaltyFeeAmount = 0;
         for (uint256 i = 0; i < tokenIds.length; i++) {

// Code block 1 (unknown):
function flashFee(address, uint256) public view returns (uint256) {
        return changeFee;
    }

// Code block 2 (unknown):
@@ -227,13 +227,14 @@ contract PrivatePool is ERC721TokenReceiver {
         // ~~~ Effects ~~~ //

         // update the virtual reserves
-        virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
+        uint addReserves = netInputAmount - feeAmount - protocolFeeAmount;
+        virtualBaseTokenReserves += uint128(addReserves);
         virtualNftReserves -= uint128(weightSum);

         // ~~~ Interactions ~~~ //

         // calculate the sale price (assume it's the same for each NFT even if weights differ)
-        uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length;
+        uint256 salePrice = (addReserves) / tokenIds.length;
         uint256 royaltyFeeAmount = 0;
         for (uint256 i = 0; i < tokenIds.length; i++) {
             // transfer the NFT to the caller


DarkTower Q

### [L-01] Bytecode might not be compatible with all EVM-based chains
The pragma statement shows usage of 0.8.21 version of the Solidity compiler. This version (and every version after 0.8.19) will use the PUSH0 opcode, which is still not supported on some EVM-based chains, for example Arbitrum. Consider using version 0.8.19 so that the same deterministic bytecode can be deployed to all chains.

### [L-02] Use of upgradeable tokens may result in improper accounting
According to Eigenlayer [documentation](https://github.com/Layr-Labs/eigenlayer-contracts/blob/75e59432d079c6f90d48d4e950a68c15867c82b2/src/contracts/strategies/StrategyBase.sol#L20), the use of fee-on-transfer tokens may result in accounting issues. For a token like cbETH, which has a proxy and implementation contract, if the implementation behind the proxy is changed, it can introduce features which might cause these issues, like adding a fee on transfer. Any accounting issues on Eigenlayer will translate to the kelp protocol.

### [L-03] Different price feeds decimals can cause accounting issues in `LRTDepositPool::getRsETHAmountToMint`
As of now, all of the supported assets have priceFeeds with 18 decimals. However, there is always a chance that in the future, there might be a new supported asset with a price feed with decimals =! 18. The calculation of the tokens to mint will be wrong as user will lose funds if decimals < 18.
Context :
```javascript
    function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
        returns (uint256 rsethAmountToMint)
    {
        // setup oracle contract
        address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE);
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);

        // calculate rseth amount to mint based on asset amount and asset exchange rate
@>      rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
    }
```
Th

// Code block 1 (javascript):
function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
        returns (uint256 rsethAmountToMint)
    {
        // setup oracle contract
        address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE);
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);

        // calculate rseth amount to mint based on asset amount and asset exchange rate
@>      rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
    }


Atarpara Q

### QA Report

#### Q-01 Use ReentrancyGuard instead of ReentrancyGuardUpgradeable

`ReentrancyGuardUpgradeable.sol` is recommend for the upgradble contract but `SmartAccount.sol` is the non-upgradable contract so use only `ReentrancyGuard.sol`.

**FileName:** *scw-contract/contracts/smart-wallet-contract/SmartAccount.sol*

```solidity=7
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
```
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L7

#### Q-02 Remove Duplicate Comment

**FileName:** *scw-contract/contracts/smart-wallet-contract/base/Executor.sol*

``` solidity=8
    // Could add a flag fromEntryPoint for AA txn
    event ExecutionFailure(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);
    event ExecutionSuccess(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);

    // Could add a flag fromEntryPoint for AA txn
```

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

#### Q-03 Update Lastest Document Link
Solidity current lastest docs link is https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_memory.html#layout-in-memory.

**FileName:** *scw-contract/contracts/smart-wallet-contract/common/SecuredTokenTransfer.sol*

```solidity=20
            // We write the return value to scratch space.
            // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L20-L21

#### Q-04 Remove unimported Lib
Please remove unimported lib opeartion

**FileName:** *scw-contract/contracts/smart-wallet-contract/BaseSmartAccount.sol*

```solidity=34
    using UserOperationLib for UserOperation;
```
https://github.com/code-423n4/2023-01-bicon

// Code block 1 (unknown):
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L7

#### Q-02 Remove Duplicate Comment

**FileName:** *scw-contract/contracts/smart-wallet-contract/base/Executor.sol*

// Code block 2 (unknown):
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L8-L12

#### Q-03 Update Lastest Document Link
Solidity current lastest docs link is https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_memory.html#layout-in-memory.

**FileName:** *scw-contract/contracts/smart-wallet-contract/common/SecuredTokenTransfer.sol*

// Code block 3 (unknown):
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L20-L21

#### Q-04 Remove unimported Lib
Please remove unimported lib opeartion

**FileName:** *scw-contract/contracts/smart-wallet-contract/BaseSmartAccount.sol*


Myd Analysis

## Overview

AI Arena establishes an intricate gaming economy around tradable NFT fighters powered by machine learning models. Users battle their AIs in a decentralized PvP platform to earn token rewards.

**Architecture**

The core gameplay logic resides off-chain in the game servers, which act as oracles submitting match results on-chain. Smart contracts enforce ownership, ratings, and incentive models.

- [`FighterFarm`](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol) - Minting and metadata for NFT fighters
- [`GameItems`](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/GameItems.sol) - Purchase in-game assets
- [`RankedBattles`](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/RankedBattle.sol) - Staking, match adjudication, rewards distribution
- [`Neuron`](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/Neuron.sol) - ERC20 utility token for incentives

```
+----------------------------+\
|              UI             |
+----------------------------+/
|       Front-End App         |
+----------------------------+
|          Oracle            |
|      (Game Server)         |
+----------------------------+
|                            |
|      Smart Contracts       |
|                            |
+----------------------------+
|          EVM               |
+----------------------------+
\           /
 |  Blockchain  |
/           \
+----------------+
```

The game servers act as the oracle managing fight outcomes and rankings.

**Design Patterns**

- **Access Control** - Permissioned roles like MINTER manage capabilities
- **Checks-Effects-Interactions** - State changes before transfers
- **Oracle** - Game servers provide trusted match data
- **Reentrancy Guard** - Reentry protection on external calls

**Security Analysis**

- **Custom Errors** - Precise error signaling for front-ends
- **Input Validation** - Enforces match rules and eligibility checks
- **Rate Limiting** - Voltage cost

points = sqrt(stakeAmount + riskAmount) * eloMultiplier

FighterFarm.sol, GameItems.sol, RankedBattle.sol, Neuron.sol

GameItems.sol, Neuron.sol, FighterFarm.sol, RankedBattle.sol


unique G

## \[G-01\] >= costs less gas than >

The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >=, which saves 3 gas

There are 13 instances of this issue:

```
File:  packages/contracts/contracts/CdpManagerStorage.sol
512   if (_newIndex > _oldIndex && totalStakes > 0) {

556   require(_newIndex > _prevIndex, "CDPManager: only take fee with bigger new index");

585   require(activePool.getSystemCollShares() > _feeTaken, "CDPManager: fee split is too big");

636    if (_scaledCdpColl > _feeSplitDistributed) {

654   CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,

765   if (_newIndex > _oldIndex && totalStakes > 0) {
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L512

```
File:  packages/contracts/contracts/LiquidationLibrary.sol
476    if (_partialState.ICR > LICR) {

553    if (_ICR > LICR) {

578    if (_ICR > LICR) {
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/LiquidationLibrary.sol#L476

```
File:  packages/contracts/contracts/SortedCdps.sol
422   require(_len > 1, "SortedCdps: batchRemove() only apply to multiple cdpIds!");

460   if (data.size > 1) {
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/SortedCdps.sol#L422

```
File:  packages/contracts/contracts/Dependencies/EbtcMath.sol
60   if (_minutes > 525600000) {
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/Dependencies/EbtcMath.sol#L60

## \[G-02\] Structs can be packed to use fewer storage slots

The EVM works with 32 byte words. Variables less than 32 bytes can be declared next to each other in storage and this will pack the values together into a single 32 byte storage slot (if values combined are <= 32 bytes). If the variables packed together are retrieved together in functions (more likely with structs) we will effectively save ~2000 gas with every subsequent

// Code block 1 (unknown):
File:  packages/contracts/contracts/CdpManagerStorage.sol
512   if (_newIndex > _oldIndex && totalStakes > 0) {

556   require(_newIndex > _prevIndex, "CDPManager: only take fee with bigger new index");

585   require(activePool.getSystemCollShares() > _feeTaken, "CDPManager: fee split is too big");

636    if (_scaledCdpColl > _feeSplitDistributed) {

654   CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,

765   if (_newIndex > _oldIndex && totalStakes > 0) {

// Code block 2 (unknown):
File:  packages/contracts/contracts/LiquidationLibrary.sol
476    if (_partialState.ICR > LICR) {

553    if (_ICR > LICR) {

578    if (_ICR > LICR) {

// Code block 3 (unknown):
File:  packages/contracts/contracts/SortedCdps.sol
422   require(_len > 1, "SortedCdps: batchRemove() only apply to multiple cdpIds!");

460   if (data.size > 1) {

// Code block 4 (unknown):
File:  packages/contracts/contracts/Dependencies/EbtcMath.sol
60   if (_minutes > 525600000) {

CdpManagerStorage.sol#L512, LiquidationLibrary.sol#L476, SortedCdps.sol#L422, EbtcMath.sol#L60

SortedCdps.sol, LiquidationLibrary.sol, EbtcMath.sol, CdpManagerStorage.sol


yjrwkk G

### [G-01] Use != 0 instead of > 0 for unsigned integer comparison

When dealing with unsigned integer types, comparisons with != 0 are cheaper than with > 0.

[2023-05-ajna/ajna-grants/src/grants/base/StandardFunding.sol#L129](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol#L129)
```
if (currentDistributionId > 0 && (block.number > _getChallengeStageEndBlock(currentDistributionEndBlock))) {
```
[2023-05-ajna/ajna-grants/src/grants/base/StandardFunding.sol#L641](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol#L641)

// Code block 1 (unknown):
if (currentDistributionId > 0 && (block.number > _getChallengeStageEndBlock(currentDistributionEndBlock))) {


orion G

_becomeImplementation and _resignImplementation function from. the CCashDelegate contract consumes more unnecessary gas by the following code :

```
if (false) {
      implementation = address(0);
    }
```

this will always be passed and will just consumes more gas, it's recommended to delete them

// Code block 1 (unknown):
if (false) {
      implementation = address(0);
    }


debo Q

## [L-01] SWC-120 Weak Sources of Randomness from Chain Attributes.  Use of block number as source of randomness.

# Vulnerable URLS
```url
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/GovernorCharlie.sol#L170

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

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

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

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

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

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

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

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

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

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

## Relationships
```txt
CWE-330: Use of Insufficiently Random Values
```

## Description
```txt
Ability to generate random numbers is very helpful in all kinds of applicatio

GovernorCharlie.sol#L170, GovernorCharlie.sol#L195, GovernorCharlie.sol#L196, GovernorCharlie.sol#L209, GovernorCharlie.sol#L210, GovernorCharlie.sol#L218, GovernorCharlie.sol#L219, GovernorCharlie.sol#L371, GovernorCharlie.sol#L375, GovernorCharlie.sol#L467, GovernorCharlie.sol#L468


matrix_0wl Q

## Non Critical Issues

|       | Issue                                                                                |
| ----- | :--------------------------------------------------------------------------------------- |
| NC-1  | ADD A TIMELOCK TO CRITICAL FUNCTIONS                                                     |
| NC-2  | ADD TO BLACKLIST FUNCTION                                                                |
| NC-3  | GENERATE PERFECT CODE HEADERS EVERY TIME                                                 |
| NC-4  | SAME CONSTANT REDEFINED ELSEWHERE                                                        |
| NC-5  | IMPLEMENTATION CONTRACT MAY NOT BE INITIALIZED                                           |
| NC-6  | MARK VISIBILITY OF INITIALIZE(…) FUNCTIONS AS EXTERNAL                                   |
| NC-7  | CONSTANT VALUES SUCH AS A CALL TO KECCAK256(), SHOULD USE IMMUTABLE RATHER THAN CONSTANT |
| NC-8  | LACK OF EVENT EMISSION AFTER CRITICAL INITIALIZE() FUNCTIONS                             |
| NC-9  | FOR EXTENDED “USING-FOR” USAGE, USE THE LATEST PRAGMA VERSION                            |
| NC-10 | NO SAME VALUE INPUT CONTROL                                                              |
| NC-11 | Add parameter to event-emit                                                              |
| NC-12 | SOLIDITY COMPILER OPTIMIZATIONS CAN BE PROBLEMATIC                                       |
| NC-13 | LINES ARE TOO LONG                                                                       |

### [NC-1] ADD A TIMELOCK TO CRITICAL FUNCTIONS

#### Description:

It is a good practice to give time for users to react and adjust to critical changes. A timelock provides more guarantees and reduces the level of trust required, thus decreasing risk for users. It also indicates that the project is legitimate (less risk of a malicious owner making a sandwich attack on a user).

#### **Proof Of Concept**

```solidity
File: contracts/bridge/DestinationBridg

File: contracts/bridge/DestinationBridge.sol

255:   function setThresholds(

286:   function setMintLimit(uint256 mintLimit) external onlyOwner {

287:     _setMintLimit(mintLimit);

295:   function setMintLimitDuration(uint256 mintDuration) external onlyOwner {

296:     _setMintLimitDuration(mintDuration);

343:         ALLOWLIST.setAccountStatus(

File: contracts/bridge/SourceBridge.sol

121:   function setDestinationChainContractAddress(


jauvany Q

#  LOW IMPACT VULNERABILITIES

## 1: More updated version of solidity can be used

Vulnerability details

### Context:

Using the more updated version of solidity can add new features and enhance security. Version 0.8.23 is the latest version of solidity. Which includes support for Shangai. If Optimism does not support PUSH0 at this moment, Version 0.8.19, which “contains a fix for a long-standing bug that can result in code that is only used in creation code to also be included in runtime bytecode”, can also be used.

For reference, see https://github.com/ethereum/solidity/releases

### Proof of Concept

All contracts in scope

## Tools Used

Manual Analysis

**Recommended Mitigation Steps**

To be more secured and future-proofed, please consider using the more updated version of Solidity for all contracts in scope.

## 2: receive()/payable fallback() function does not authorize requests

Vulnerability details

### Context:

Having no access control on the ```function (e.g. require(msg.sender == address(weth)))``` means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds. If the concern is having to spend a small amount of gas to check the sender against an immutable address, the code should at least have a function to rescue mistakenlysent Ether.

### Proof of Concept

> ***File: SimplifiedDiamondLike.sol***
```
160:    receive() external payable {
161:        // PHP is my favourite language
162:    }
```

### Tools Used

Manual Analysis

***Recommended Mitigation Steps***

The code should at least have a mechanism to rescue mistakenlysent Ether.

## 3: Empty receive()/fallback() function

Vulnerability details

### Context:

If the intention is for Ether sent by a caller to be used for an actual purpose (i.e. the function is not just a WETH withdraw() handler), the function should call another function (e.g. call weth.deposit() and use the token on the caller's behalf) or at least emit an event to tr

// Code block 1 (unknown):
160:    receive() external payable {
161:        // PHP is my favourite language
162:    }


naman1778 G

## [G-01] abi.encode() is less efficient than abi.encodePacked()

Changing abi.encode function to abi.encodePacked can save gas since the abi.encode function pads extra null bytes at the end of the call data, which is unnecessary. Also, in general, abi.encodePacked is more gas-efficient (see [Solidity-Encode-Gas-Comparison](https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison)).

There are 3 instances of this issue in 2 files:

```
File: contracts/bridge/SourceBridge.sol

79: bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);
```

    diff --git a/contracts/bridge/SourceBridge.sol b/contracts/bridge/SourceBridge.sol
    index 457bf41..637934b 100644
    --- a/contracts/bridge/SourceBridge.sol
    +++ b/contracts/bridge/SourceBridge.sol
    @@ -76,7 +76,7 @@ contract SourceBridge is Ownable, Pausable, IMulticall {
         // burn amount
         TOKEN.burnFrom(msg.sender, amount);

    -    bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);
    +    bytes memory payload = abi.encodePacked(VERSION, msg.sender, amount, nonce++);

         _payGasAndCallContract(destinationChain, destContract, payload);
       }

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol

```
File: contracts/bridge/DestinationBridge.sol

99: if (chainToApprovedSender[srcChain] != keccak256(abi.encode(srcAddr))) {

238: chainToApprovedSender[srcChain] = keccak256(abi.encode(srcContractAddress));
```

    diff --git a/contracts/bridge/DestinationBridge.sol b/contracts/bridge/DestinationBridge.sol
    index 8ad410c..b79d28e 100644
    --- a/contracts/bridge/DestinationBridge.sol
    +++ b/contracts/bridge/DestinationBridge.sol
    @@ -96,7 +96,7 @@ contract DestinationBridge is
         if (chainToApprovedSender[srcChain] == bytes32(0)) {
           revert ChainNotSupported();
         }
    -    if (chainToApprovedSender[srcChain] != keccak256(abi.encode(srcAddr))) {
    +    if (chainToApprovedSender[s

// Code block 1 (unknown):
File: contracts/bridge/SourceBridge.sol

79: bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);

// Code block 2 (unknown):
File: contracts/bridge/DestinationBridge.sol

99: if (chainToApprovedSender[srcChain] != keccak256(abi.encode(srcAddr))) {

238: chainToApprovedSender[srcChain] = keccak256(abi.encode(srcContractAddress));


Aymen0909 Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1  | `getChainlinkOraclePrice` function can return null prices | Low | 1 |
| 2  | `maxChainlinkOracleTimeDelay` is too big for some tokens price feeds | Low | 1 |
| 3  | `owner` of `JumpRateModelV2` contract can not be changed | Low | 1 |
| 4  | Related data should be grouped in a `struct` | NC |  5 |
| 5  | Named return variables not used anywhere in the functions | NC | 1 |
| 6  | Use solidity time-based units | NC | 1 |

## Findings

### 1- `getChainlinkOraclePrice` function can return null prices :

The function `getChainlinkOraclePrice` is used in the `OndoPriceOracleV2` contract to get a token price using the Chainlink oracle, and the function will revert if the price returned by the oracle is not greater or equal than zero, but if the returned price is zero (which can happen if there is an isssue in the Chainlink oracle for some reason) the function will not revert and still return the null price this can have a negative impact on other functions (from other contracts) that uses this price in their internal logic.

### Risk : Low

### Proof of Concept

Issue occurs in the `getChainlinkOraclePrice` function below :

File: lending/OndoPriceOracleV2.sol [Line 277-301](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L277-L301)
```
function getChainlinkOraclePrice(
address fToken
) public view returns (uint256) {
    require(
        fTokenToOracleType[fToken] == OracleType.CHAINLINK,
        "fToken is not configured for Chainlink oracle"
    );
    ChainlinkOracleInfo memory chainlinkInfo = fTokenToChainlinkOracle[fToken];
    (
        uint80 roundId,
        int answer,
        ,
        uint updatedAt,
        uint80 answeredInRound
    ) = chainlinkInfo.oracle.latestRoundData();
    require(
        (answeredInRound >= roundId) &&
        (updatedAt >= block.tim

function getChainlinkOraclePrice(
address fToken
) public view returns (uint256) {
    require(
        fTokenToOracleType[fToken] == OracleType.CHAINLINK,
        "fToken is not configured for Chainlink oracle"
    );
    ChainlinkOracleInfo memory chainlinkInfo = fTokenToChainlinkOracle[fToken];
    (
        uint80 roundId,
        int answer,
        ,
        uint updatedAt,
        uint80 answeredInRound
    ) = chainlinkInfo.oracle.latestRoundData();
    require(
        (answeredInRound >= roundId) &&
        (updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),
        "Chainlink oracle price is stale"
    );
    /**
        @audit : it should be answer > 0 and not answer >= 0
        This to avoid the null price in case of chainlink oracle issue
    */
    require(answer >= 0, "Price cannot be negative");
    // Scale to decimals needed in Comptroller (18 decimal underlying -> 18 decimals; 6 decimal underlying -> 30 decimals)
    // Scales by same conversion factor as in Compound Oracle
    return uint256(answer) * chainlinkInfo.scaleFactor;
}

function getChainlinkOraclePrice(
address fToken
) public view returns (uint256) {
    ...
    /**
        @audit : use answer > 0
    */
    require(answer > 0, "Price cannot be negative");
    // Scale to decimals needed in Comptroller (18 decimal underlying -> 18 decimals; 6 decimal underlying -> 30 decimals)
    // Scales by same conversion factor as in Compound Oracle
    return uint256(answer) * chainlinkInfo.scaleFactor;
}


0xackermann Q

### QA Report
[Exclusive findings](#exclusive-findings) is below the [automated findings](#automated-findings).

---

## Automated Findings
## Non Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | Missing checks for `address(0)` when assigning values to address state variables | 26 |
| [NC-2](#NC-2) |  `require()` / `revert()` statements should have descriptive reason strings | 10 |
| [NC-3](#NC-3) | Return values of `approve()` not checked | 5 |
| [NC-4](#NC-4) | TODO Left in the code | 2 |
| [NC-5](#NC-5) | Event is missing `indexed` fields | 93 |
| [NC-6](#NC-6) | Functions not used internally could be marked external | 2 |
| [NC-7](#NC-7) | Typos | 32 |
### [NC-1] Missing checks for `address(0)` when assigning values to address state variables

*Instances (26)*:
```solidity
File: Ethos-Core/contracts/ActivePool.sol

96:         collateralConfigAddress = _collateralConfigAddress;

97:         borrowerOperationsAddress = _borrowerOperationsAddress;

98:         troveManagerAddress = _troveManagerAddress;

99:         stabilityPoolAddress = _stabilityPoolAddress;

100:         defaultPoolAddress = _defaultPoolAddress;

101:         collSurplusPoolAddress = _collSurplusPoolAddress;

103:         lqtyStakingAddress = _lqtyStakingAddress;

```
[Link to code](https://github.com/code-423n4/2023-02-ethos/tree/main/Ethos-Core/contracts/ActivePool.sol)

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

146:         stabilityPoolAddress = _stabilityPoolAddress;

147:         gasPoolAddress = _gasPoolAddress;

152:         lqtyStakingAddress = _lqtyStakingAddress;

```
[Link to code](https://github.com/code-423n4/2023-02-ethos/tree/main/Ethos-Core/contracts/BorrowerOperations.sol)

```solidity
File: Ethos-Core/contracts/LQTY/CommunityIssuance.sol

75:         stabilityPoolAddress = _stabilityPoolAddress;

```
[Link to code](https://github.com/code-423n4/2023-02-ethos/tree/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol)

// Code block 1 (solidity):
File: Ethos-Core/contracts/ActivePool.sol

96:         collateralConfigAddress = _collateralConfigAddress;

97:         borrowerOperationsAddress = _borrowerOperationsAddress;

98:         troveManagerAddress = _troveManagerAddress;

99:         stabilityPoolAddress = _stabilityPoolAddress;

100:         defaultPoolAddress = _defaultPoolAddress;

101:         collSurplusPoolAddress = _collSurplusPoolAddress;

103:         lqtyStakingAddress = _lqtyStakingAddress;

// Code block 2 (solidity):
File: Ethos-Core/contracts/BorrowerOperations.sol

146:         stabilityPoolAddress = _stabilityPoolAddress;

147:         gasPoolAddress = _gasPoolAddress;

152:         lqtyStakingAddress = _lqtyStakingAddress;

// Code block 3 (solidity):
File: Ethos-Core/contracts/LQTY/CommunityIssuance.sol

75:         stabilityPoolAddress = _stabilityPoolAddress;

File: Ethos-Core/contracts/ActivePool.sol

96:         collateralConfigAddress = _collateralConfigAddress;

97:         borrowerOperationsAddress = _borrowerOperationsAddress;

98:         troveManagerAddress = _troveManagerAddress;

99:         stabilityPoolAddress = _stabilityPoolAddress;

100:         defaultPoolAddress = _defaultPoolAddress;

101:         collSurplusPoolAddress = _collSurplusPoolAddress;

103:         lqtyStakingAddress = _lqtyStakingAddress;

File: Ethos-Core/contracts/BorrowerOperations.sol

146:         stabilityPoolAddress = _stabilityPoolAddress;

147:         gasPoolAddress = _gasPoolAddress;

152:         lqtyStakingAddress = _lqtyStakingAddress;

File: Ethos-Core/contracts/LQTY/CommunityIssuance.sol

75:         stabilityPoolAddress = _stabilityPoolAddress;


dharma09 G

**[G-01] Empty blocks should be removed or emit something**

The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be `abstract` and the function signatures be added without any default implementation. If the block is an empty if-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (`if(x){}else if(y){...}else{...}` => `if(!x){if(y){...}else{...}}`)

*There are 1 instances of this issue:*

```solidity
/SmartAccount.sol
550: receive() external payable {}
```

**[G-02]** **Defined Variables Used Only Once**

Certain variables is defined even though they are used only once.Remove these unnecessary variables to save gas.For cases where it will reduce the readability, one can use comments to help describe what the code is doing.

*There are 2 instances of this issue:*

```solidity
/aa-4337/core/StakeManager.sol
48: function depositTo(address account) public payable {
49:        internalIncrementDeposit(account, msg.value);
50:     DepositInfo storage info = deposits[account];
51:     emit Deposited(account, info.deposit);
52: }

/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol
126: PaymasterContext memory data = context.decodePaymasterContext();
127: address extractedPaymasterId = data.paymasterId;
```

**Mitigation**

```diff
48: function depositTo(address account) public payable {
49:        internalIncrementDeposit(account, msg.value);
-50:     DepositInfo storage info = deposits[account];
+50:     emit Deposited(account, deposits[account].deposit);
51: }

-126: PaymasterContext memory data = context.decodePaymasterContext();
+126: address extractedPaymasterId = context

// Code block 1 (solidity):
/SmartAccount.sol
550: receive() external payable {}

// Code block 2 (solidity):
/aa-4337/core/StakeManager.sol
48: function depositTo(address account) public payable {
49:        internalIncrementDeposit(account, msg.value);
50:     DepositInfo storage info = deposits[account];
51:     emit Deposited(account, info.deposit);
52: }

/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol
126: PaymasterContext memory data = context.decodePaymasterContext();
127: address extractedPaymasterId = data.paymasterId;

/SmartAccount.sol
550: receive() external payable {}

/aa-4337/core/StakeManager.sol
48: function depositTo(address account) public payable {
49:        internalIncrementDeposit(account, msg.value);
50:     DepositInfo storage info = deposits[account];
51:     emit Deposited(account, info.deposit);
52: }

/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol
126: PaymasterContext memory data = context.decodePaymasterContext();
127: address extractedPaymasterId = data.paymasterId;


Gde Q

## Empty Event

The Rebalanced event has no parameters in it to emit anything. Consider adding parameters or removing it.

```
event Rebalanced();
```

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

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

## Events Associated With Setter/Update Functions

Consider having events associated with setter/update functions emit both the new and old values instead of just the new value.

[SafEth.sol#L216](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L216)
```
emit ChangeMinAmount(minAmount);
```

[SafEth.sol#L225](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L225)
```
emit ChangeMaxAmount(maxAmount);
```

[SafEth.sol#L207](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L207)
```
emit SetMaxSlippage(_derivativeIndex, _slippage);
```

[SafEth.sol#L174](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L174)
```
emit WeightChange(_derivativeIndex, _weight);
```

## Duplicated code that could be factorized

In SfrxEth.sol, at several places we use:

```
IERC20(SFRX_ETH_ADDRESS).balanceOf(address(this));
```
[SfrxEth.sol#L98](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L98)
[SfrxEth.sol#L102](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L102)

Whereas we could just reuse the public `balance()` method:
[SfrxEth.sol#L122-L124](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L122-L124)

In Reth.sol

We duplicate lines to get the reth token address at different places:
[Reth.sol#L187-L193](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contract

// Code block 1 (unknown):
event Rebalanced();

// Code block 2 (unknown):
emit ChangeMinAmount(minAmount);

// Code block 3 (unknown):
emit ChangeMaxAmount(maxAmount);

// Code block 4 (unknown):
emit SetMaxSlippage(_derivativeIndex, _slippage);

// Code block 5 (unknown):
emit WeightChange(_derivativeIndex, _weight);

// Code block 6 (unknown):
IERC20(SFRX_ETH_ADDRESS).balanceOf(address(this));

SafEth.sol#L34, SafEth.sol#L154, SafEth.sol#L216, SafEth.sol#L225, SafEth.sol#L207, SafEth.sol#L174, SfrxEth.sol#L98, SfrxEth.sol#L102, SfrxEth.sol#L122-L124


DarkTower Q

## [L-01] Voltage replenishment will fail in 81.98 years

In roughly 81.98 years, assuming the AI Arena contract still functions, rather unlikely; voltage replenishment will fail for fighters in the Arena because at that point the `block.timestamp` will be too much of a value the `uint32` type can accomodate, hence leading transactions to the `spendVoltage` >> `_replenishVoltage` functions to fail.

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

```js
/// @notice Replenishes voltage and sets the replenish time to 1 day from now
    /// @dev This function is called internally to replenish the voltage for the owner.
    /// @param owner The address of the owner
    function _replenishVoltage(address owner) private {
        ownerVoltage[owner] = 100;
    @>  ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days); // @audit-info will fail in 81.98 years
    }
```

Switching to a bigger datatype such as the `uint64` or more should be fine for a long time:

```diff
- ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days);
+ ownerVoltageReplenishTime[owner] = uint64(block.timestamp + 1 days);
```

## [L-02] Players can burn 1 full battery even at 90% voltage

The function is called when when a voltage battery to replenish voltage for a player in AI Arena. But it allows players to burn his battery even at 90% voltage which will also lose them the `VoltageReplenishTime`.

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

```solidity
    function useVoltageBattery() public {
        require(ownerVoltage[msg.sender] < 100);
        require(_gameItemsContractInstance.balanceOf(msg.sender, 0) > 0);
        _gameItemsContractInstance.burn(msg.sender, 0, 1);
        ownerVoltage[msg.sender] = 100;
        emit VoltageRemaining(msg.sender, ownerVoltage[msg.sender]);
    }

```

## [L-03] Players can only claim one of two signatures given by the server using `claimFighters`

The issu

// Code block 1 (js):
/// @notice Replenishes voltage and sets the replenish time to 1 day from now
    /// @dev This function is called internally to replenish the voltage for the owner.
    /// @param owner The address of the owner
    function _replenishVoltage(address owner) private {
        ownerVoltage[owner] = 100;
    @>  ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days); // @audit-info will fail in 81.98 years
    }

// Code block 2 (diff):
- ownerVoltageReplenishTime[owner] = uint32(block.timestamp + 1 days);
+ ownerVoltageReplenishTime[owner] = uint64(block.timestamp + 1 days);

// Code block 3 (solidity):
function useVoltageBattery() public {
        require(ownerVoltage[msg.sender] < 100);
        require(_gameItemsContractInstance.balanceOf(msg.sender, 0) > 0);
        _gameItemsContractInstance.burn(msg.sender, 0, 1);
        ownerVoltage[msg.sender] = 100;
        emit VoltageRemaining(msg.sender, ownerVoltage[msg.sender]);
    }


0xWaitress Q

[L-1] there is no validation that rdpxBurnPercentage and rdpxFeePercentage adds up to 100%.

rdpxBurnPercentage and rdpxFeePercentage represents the two destinations of RDPX during transfer, 1 is burnt and 1 is transferred to the fee dsitributor. however they can be set separately and arbitrarily.

```solidity
  /// @notice The % of rdpx to burn while bonding
  uint256 public rdpxBurnPercentage = 50 * DEFAULT_PRECISION;

  /// @notice The % of rdpx sent to fee distributor while bonding
  uint256 public rdpxFeePercentage = 50 * DEFAULT_PRECISION;

...

  function setRdpxBurnPercentage(
    uint256 _rdpxBurnPercentage
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_rdpxBurnPercentage > 0, 3);
    rdpxBurnPercentage = _rdpxBurnPercentage;
    emit LogSetRdpxBurnPercentage(_rdpxBurnPercentage);
  }

  /**
   * @notice Sets the rdpx fee percentage
   * @dev    Can only be called by admin
   * @param  _rdpxFeePercentage the fee percentage to set in 1e8 precision
   **/
  function setRdpxFeePercentage(
    uint256 _rdpxFeePercentage
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_rdpxFeePercentage > 0, 3);
    rdpxFeePercentage = _rdpxFeePercentage;
    emit LogSetRdpxFeePercentage(_rdpxFeePercentage);
  }

```

Recommendation:
combine them into a setter and the other one gets updated automically.

[N-1] _ammFactory is not used in ReLPContract or UniV2LiquidityAmo.

```solidity
  function setAddresses(
    address _tokenA,
    address _tokenB,
    address _pair,
    address _rdpxV2Core,
    address _rdpxOracle,
    address _ammFactory,
    address _ammRouter
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(
      _tokenA != address(0) &&
        _tokenB != address(0) &&
        _pair != address(0) &&
        _rdpxV2Core != address(0) &&
        _rdpxOracle != address(0) &&
        _ammFactory != address(0) &&
        _ammRouter != address(0),
      "reLPContract: address cannot be 0"
    );
    addresses = Addresses({
      tokenA: _tokenA,

  function setAddresses(
    address _tokenA,
    address _tokenB,
    address _pair,
    address _rdpxV2Core,
    address _rdpxOracle,
    address _ammFactory,
    address _ammRouter
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(
      _tokenA != address(0) &&
        _tokenB != address(0) &&
        _pair != address(0) &&
        _rdpxV2Core != address(0) &&
        _rdpxOracle != address(0) &&
        _ammFactory != address(0) &&
        _ammRouter != address(0),
      "reLPContract: address cannot be 0"
    );
    addresses = Addresses({
      tokenA: _tokenA,
      tokenB: _tokenB,
      pair: _pair,
      rdpxV2Core: _rdpxV2Core,
      rdpxOracle: _rdpxOracle,
      ammFactory: _ammFactory,
      ammRouter: _ammRouter
    });
  }

// Code block 1 (solidity):
/// @notice The % of rdpx to burn while bonding
  uint256 public rdpxBurnPercentage = 50 * DEFAULT_PRECISION;

  /// @notice The % of rdpx sent to fee distributor while bonding
  uint256 public rdpxFeePercentage = 50 * DEFAULT_PRECISION;

...

  function setRdpxBurnPercentage(
    uint256 _rdpxBurnPercentage
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_rdpxBurnPercentage > 0, 3);
    rdpxBurnPercentage = _rdpxBurnPercentage;
    emit LogSetRdpxBurnPercentage(_rdpxBurnPercentage);
  }

  /**
   * @notice Sets the rdpx fee percentage
   * @dev    Can only be called by admin
   * @param  _rdpxFeePercentage the fee percentage to set in 1e8 precision
   **/
  function setRdpxFeePercentage(
    uint256 _rdpxFeePercentage
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_rdpxFeePercentage > 0, 3);
    rdpxFeePercentage = _rdpxFeePercentage;
    emit LogSetRdpxFeePercentage(_rdpxFeePercentage);
  }


BigVeezus Q

## [L-1] `RankedBattle::_addResultPoints` function does not update the `globalStakedAmount` after an amount has been staked

**Occurences**: 2 times

1. `RankedBattle::_addResultPoints` function at line 465, RankedBattle.sol.
2. `RankedBattle::_addResultPoints` function at line 500, RankedBattle.sol.

**Impact**:
This could cause wrong information to users or stakeholders if the `globalStakedAmount` doesnt update as it should

**Proof Of Code & Added Mitigation**:

```diff
            /// If the user has stake-at-risk for their fighter, reclaim a portion
            /// Reclaiming stake-at-risk puts the NRN back into their staking pool
            if (curStakeAtRisk > 0) {
                _stakeAtRiskInstance.reclaimNRN(curStakeAtRisk, tokenId, fighterOwner);
                amountStaked[tokenId] += curStakeAtRisk;
                // Added code below as solution
+                 globalStakedAmount += curStakeAtRisk;
            }
```

```diff
{
                /// If the fighter does not have any points for this round, NRNs become at risk of being lost
                bool success = _neuronInstance.transfer(_stakeAtRiskAddress, curStakeAtRisk);
                if (success) {
                    _stakeAtRiskInstance.updateAtRiskRecords(curStakeAtRisk, tokenId, fighterOwner);
                    amountStaked[tokenId] -= curStakeAtRisk;
                     // Added code below as solution
+                    globalStakedAmount -= curStakeAtRisk;
                }
            }
```

globalStakedAmount should be updated to keep proper records of the storage values

## [L-2] `RankedBattle::_addResultPoints` function does not emit events `Staked` or `Unstaked` when `amountStaked[tokenId]` is increased or reduced.

**Occurences**: 2 times

1. `RankedBattle::_addResultPoints`, line 465 RankedBattle.sol
2. `RankedBattle::_addResultPoints`, line 500 RankedBattle.sol

**Impact**: Doesnt follow proper practices and doesnt help readability of code and loss of information

```d

```diff
else {
                /// If the fighter does not have any points for this round, NRNs become at risk of being lost
                bool success = _neuronInstance.transfer(_stakeAtRiskAddress, curStakeAtRisk);
                if (success) {
                    _stakeAtRiskInstance.updateAtRiskRecords(curStakeAtRisk, tokenId, fighterOwner);
                    amountStaked[tokenId] -= curStakeAtRisk;
+                    emit unStaked(msg.sender, amount)
                }
            }

// Code block 1 (diff):
/// If the user has stake-at-risk for their fighter, reclaim a portion
            /// Reclaiming stake-at-risk puts the NRN back into their staking pool
            if (curStakeAtRisk > 0) {
                _stakeAtRiskInstance.reclaimNRN(curStakeAtRisk, tokenId, fighterOwner);
                amountStaked[tokenId] += curStakeAtRisk;
                // Added code below as solution
+                 globalStakedAmount += curStakeAtRisk;
            }

// Code block 2 (diff):
{
                /// If the fighter does not have any points for this round, NRNs become at risk of being lost
                bool success = _neuronInstance.transfer(_stakeAtRiskAddress, curStakeAtRisk);
                if (success) {
                    _stakeAtRiskInstance.updateAtRiskRecords(curStakeAtRisk, tokenId, fighterOwner);
                    amountStaked[tokenId] -= curStakeAtRisk;
                     // Added code below as solution
+                    globalStakedAmount -= curStakeAtRisk;
                }
            }

/// If the user has stake-at-risk for their fighter, reclaim a portion
            /// Reclaiming stake-at-risk puts the NRN back into their staking pool
            if (curStakeAtRisk > 0) {
                _stakeAtRiskInstance.reclaimNRN(curStakeAtRisk, tokenId, fighterOwner);
                amountStaked[tokenId] += curStakeAtRisk;
                // Added code below as solution
+                 globalStakedAmount += curStakeAtRisk;
            }

{
                /// If the fighter does not have any points for this round, NRNs become at risk of being lost
                bool success = _neuronInstance.transfer(_stakeAtRiskAddress, curStakeAtRisk);
                if (success) {
                    _stakeAtRiskInstance.updateAtRiskRecords(curStakeAtRisk, tokenId, fighterOwner);
                    amountStaked[tokenId] -= curStakeAtRisk;
                     // Added code below as solution
+                    globalStakedAmount -= curStakeAtRisk;
                }
            }


aviggiano Q

| QA Issue | Description |
| --- | --- |
| QA-01 | PositionManager NFT operator cannot interact with RewardsManager |
| QA-02 | PositionManager nones start at 0, which is the default value for `uint256`, and can potentially cause issues |
| QA-03 | Anyone can memorialize LP positions from another user |

# QA-01: PositionManager NFT operator cannot interact with RewardsManager

## Description

The PositionManager NFT operator can manage pool lenders positions in whole, as can be seen with the `mayInteract` modifier on `PositionManager.sol`, through `_isApprovedOrOwner`

```solidity
    /**
     *  @dev   Modifier used to check if sender can interact with token id.
     *  @param pool_    `Ajna` pool address.
     *  @param tokenId_ Id of positions `NFT`.
     */
    modifier mayInteract(address pool_, uint256 tokenId_) {

        // revert if token id is not a valid / minted id
        _requireMinted(tokenId_);

        // revert if sender is not owner of or entitled to operate on token id
        if (!_isApprovedOrOwner(msg.sender, tokenId_)) revert NoAuth();

        // revert if the token id is not minted for given pool address
        if (pool_ != poolKey[tokenId_]) revert WrongPool();

        _;
    }
```

However, the operator cannot stake the NFT on `RewardsManager`.

```solidity
    function stake(
        uint256 tokenId_
    ) external override {
        // ...

        // check that msg.sender is owner of tokenId
        if (IERC721(address(positionManager)).ownerOf(tokenId_) != msg.sender) revert NotOwnerOfDeposit();

        // ...
    }
```

This greatly limits the benefits of approving the positions NFT to a third party, without any upsides.

## Recommendation

Consider using `_isApprovedOrOwner` to validate access control on `RewardsManager`. Appropriate care must be taken with rewards, as they should always be sent to the NFT `owner`, not to the `msg.sender`.

# QA-02: PositionManager nones start at 0, which is the default value for `uint256`, and

// Code block 1 (solidity):
/**
     *  @dev   Modifier used to check if sender can interact with token id.
     *  @param pool_    `Ajna` pool address.
     *  @param tokenId_ Id of positions `NFT`.
     */
    modifier mayInteract(address pool_, uint256 tokenId_) {

        // revert if token id is not a valid / minted id
        _requireMinted(tokenId_);

        // revert if sender is not owner of or entitled to operate on token id
        if (!_isApprovedOrOwner(msg.sender, tokenId_)) revert NoAuth();

        // revert if the token id is not minted for given pool address
        if (pool_ != poolKey[tokenId_]) revert WrongPool();

        _;
    }

// Code block 2 (solidity):
function stake(
        uint256 tokenId_
    ) external override {
        // ...

        // check that msg.sender is owner of tokenId
        if (IERC721(address(positionManager)).ownerOf(tokenId_) != msg.sender) revert NotOwnerOfDeposit();

        // ...
    }

/**
     *  @dev   Modifier used to check if sender can interact with token id.
     *  @param pool_    `Ajna` pool address.
     *  @param tokenId_ Id of positions `NFT`.
     */
    modifier mayInteract(address pool_, uint256 tokenId_) {

        // revert if token id is not a valid / minted id
        _requireMinted(tokenId_);

        // revert if sender is not owner of or entitled to operate on token id
        if (!_isApprovedOrOwner(msg.sender, tokenId_)) revert NoAuth();

        // revert if the token id is not minted for given pool address
        if (pool_ != poolKey[tokenId_]) revert WrongPool();

        _;
    }

function stake(
        uint256 tokenId_
    ) external override {
        // ...

        // check that msg.sender is owner of tokenId
        if (IERC721(address(positionManager)).ownerOf(tokenId_) != msg.sender) revert NotOwnerOfDeposit();

        // ...
    }


joestakey Q

## Summary
### Low Risk
|      | Issue                                                                                |
|------|---------------------------------------------------------------------------------------------|
| L-01 | `SmartAccountFactory.deployWallet` does not fully comply with EIP-4337                      |
| L-02 | Wrong error string                                                                          |
| L-03 | The "no smart contract" check in `VerifyingSingletonPaymaster.depositFor()` can be bypassed |

### Non critical
|      | Issue                                                                            |
|------|----------------------------------------------------------------------------------|
| NC-01| Typos                                                                            |
| NC-02| Tautologies                                                                      |
| NC-03| Redundant check                                                                    |
| NC-04| Open TODOs                                                                       |

## Low

### [L‑01] `SmartAccountFactory.deployWallet` does not fully comply with EIP-4337

This function uses [CREATE](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L57) to deploy the account.

As per [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337#first-time-account-creation):

```
The wallet creation itself is done by a “factory” contract, with wallet-specific data.
The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn’t interfere with the generated addresses
```

Consider removing this function, as `deployCounterFactualWallet()` already allows creation of wallets.

### [L‑02] Wrong error string

There is a wrong error string in `SmartAccount.init()`. This can be misleading if

171:         require(_handler != address(0), "Invalid Entrypoint");

// Code block 1 (unknown):
The wallet creation itself is done by a “factory” contract, with wallet-specific data.
The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn’t interfere with the generated addresses


K42 G

## Gas Optimization Report for [AI-Arena](https://github.com/code-423n4/2024-02-ai-arena) by K42

### Possible Optimization in [AiArenaHelper.sol](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol)

Possible Optimization  =
- **Issue**: Storing attribute probabilities for each generation in a nested [mapping](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol#L30) introduces significant gas costs when updating these probabilities.
- **Optimization**: Use a single-level mapping with a composite key combining generation and attribute name. This reduces the depth of mapping access and minimizes gas costs associated with storage operations.

Here is the optimized code snippet:

```solidity
// Before Optimization
mapping(uint256 => mapping(string => uint8[])) public attributeProbabilities;

// After Optimization
mapping(bytes32 => uint8[]) public attributeProbabilitiesOptimized;

function _generateCompositeKey(uint256 generation, string memory attribute) private pure returns (bytes32) {
    return keccak256(abi.encodePacked(generation, attribute));
}

function addAttributeProbabilitiesOptimized(uint256 generation, uint8[][] memory probabilities) public {
    require(msg.sender == _ownerAddress, "Only owner can add probabilities");
    require(probabilities.length == attributes.length, "Invalid probabilities length");

    for (uint8 i = 0; i < attributes.length; i++) {
        bytes32 key = _generateCompositeKey(generation, attributes[i]);
        attributeProbabilitiesOptimized[key] = probabilities[i];
    }
}
```

- Estimated gas saved = This change can significantly reduce gas costs by minimizing the depth of mapping access and the number of storage slots needed. The exact savings depend on the frequency and size of updates but expect reductions in the range of 20-30% for transactions involving these mappings.

### Possible Optimizations in [FighterFarm.sol](https://github.com/code-423n4/2024-02-ai-arena

// Before optimization
function updateModel(uint256 tokenId, string calldata modelHash, string calldata modelType) external {
    require(msg.sender == ownerOf(tokenId));
    fighters[tokenId].modelHash = modelHash;
    fighters[tokenId].modelType = modelType;
    numTrained[tokenId] += 1;
    totalNumTrained += 1;
}

// After optimization
function updateModel(uint256 tokenId, string calldata modelHash, string calldata modelType) external {
    require(msg.sender == ownerOf(tokenId));
    fighters[tokenId].modelHash = modelHash;
    fighters[tokenId].modelType = modelType;
    emit ModelUpdated(tokenId, modelHash, modelType); // Use an event instead of modifying state
}

// Code block 1 (solidity):
// Before Optimization
mapping(uint256 => mapping(string => uint8[])) public attributeProbabilities;

// After Optimization
mapping(bytes32 => uint8[]) public attributeProbabilitiesOptimized;

function _generateCompositeKey(uint256 generation, string memory attribute) private pure returns (bytes32) {
    return keccak256(abi.encodePacked(generation, attribute));
}

function addAttributeProbabilitiesOptimized(uint256 generation, uint8[][] memory probabilities) public {
    require(msg.sender == _ownerAddress, "Only owner can add probabilities");
    require(probabilities.length == attributes.length, "Invalid probabilities length");

    for (uint8 i = 0; i < attributes.length; i++) {
        bytes32 key = _generateCompositeKey(generation, attributes[i]);
        attributeProbabilitiesOptimized[key] = probabilities[i];
    }
}


SBSecurity Q

### Low Risk

| Count | Title |
| --- | --- |
| [L-01](#l-01-no-removesupportedasset-function-in-the-lrtconfig) | No removeSupportedAsset function in the LRTConfig |
| [L-02](#l-02-cbeth-has-blacklisting) | cbETH has blacklisting |
| [L-03](#l-03-consider-use-stethuds-oracle) | Consider use stETH/UDS oracle |

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

### Non-Critical

| Count | Title |
| --- | --- |
| [NC-01](#n01-missing-events-in-sensitive-functions) | Missing events in sensitive functions |
| [NC-02](#n02-nodedelegatoraddedinqueue-event-does-not-follow-camalcase) | NodeDelegatorAddedinQueue event does not follow CamalCase |
| [NC-03](#n03-istrategy-is-not-up-to-date-with-eigenlayeristrategy) | IStrategy is not up to date with EigenLayer::IStrategy |
| [NC-04](#n04-istrategy-is-not-up-to-date-with-eigenlayeristrategy) | No withdraw functionality |

| Total Non-Critical Issues | 4 |
| --- | --- |

## Low Risks

## [L-01] No removeSupportedAsset function in the LRTConfig

**Issue Description:** The contract provides a way to add new supported assets through the `addNewSupportedAsset()` function, but there is no corresponding function to remove supported assets.

Without the ability to remove supported assets, the contract may face challenges in adapting to changing circumstances, such as changes in the project's strategy, token ecosystem, or regulatory requirements.

**Recommendation:** Consider adding a function like **`removeSupportedAsset`** that allows the contract owner or another authorized role to remove an asset from the list of supported assets

## [L-02] cbETH has blacklisting

**Issue Description:** The blacklisting mechanism in the `CBETH` token introduces potential complications and risks across various stages of the transaction lifecycle, from initial deposits in the LRTDepositPool to activities within the NodeDelegator and Eigen Strategy. If the `LRTDepositPool` or any of the `NodeDelegator` is blacklisted, it implies that the associated tokens wi

File: src/LRTDepositPool.sol

183: function transferAssetToNodeDelegator(
184:     uint256 ndcIndex,
185:     address asset,
186:     uint256 amount
187: )
188:     external
189:     nonReentrant
190:     onlyLRTManager
191:     onlySupportedAsset(asset)
192: {
193:     address nodeDelegator = nodeDelegatorQueue[ndcIndex];
194:     if (!IERC20(asset).transfer(nodeDelegator, amount)) {
195:         revert TokenTransferFailed();
196:     }

          // @audit missing event
197: }

File: src/NodeDelegator.sol

38: function maxApproveToEigenStrategyManager(address asset)
39:     external
40:     override
41:     onlySupportedAsset(asset)
42:     onlyLRTManager
43: {
44:     address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
45:     IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);

        // @audit missing event
46: }

74: function transferBackToLRTDepositPool(
75:     address asset,
76:     uint256 amount
77: )
78:     external
79:     whenNotPaused
80:     nonReentrant
81:     onlySupportedAsset(asset)
82:     onlyLRTManager
83: {
84:     address lrtDepositPool = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);
85:
86:     if (!IERC20(asset).transfer(lrtDepositPool, amount)) {
87:         revert TokenTransferFailed();
88:     }
        // @audit missing event
89: }


Awesome G

# 1. Splitting `require()` Statements That Use `&&` Saves Gas - (Saves ~`3` Gas per `&&`)

Instead of using the `&&` operator in a single require statement to check multiple conditions, using various require statements with 1 condition per require statement will save ~3 GAS per `&&`.

Affected lines of code:

- [OndoPriceOracleV2.sol#L292-L296](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L292-L296)
- [CTokenModified.sol#L45-L48](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenModified.sol#L45-L48)

# 2. Save Gas With `payable` Functions

Marking functions as `payable` will be cheaper (by ~20 gas) than using non-`payable` functions because the Solidity compiler inserts a check into non-`payable` functions that requires `msg.value` to be zero.

For instance, the code at [lines 144-L150](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L144-L150) can be refactored as:

```solidity
  function assignRoletoKYCGroup(
    uint256 kycRequirementGroup,
    bytes32 role
  ) external payable onlyRole(REGISTRY_ADMIN) {
    kycGroupRoles[kycRequirementGroup] = role;
    emit RoleAssignedToKYCGroup(kycRequirementGroup, role);
  }
```

> **Note**: Although this optimization can save gas, it is important to be aware of the security considerations involving Ether held in contracts that it introduces.
> More information on this topic can be found in the [Solidity Compiler Discussion](https://github.com/ethereum/solidity/issues/12539).

Affected line of code:

- [KYCRegistry.sol#L144-L150](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L144-L150)

# 3. Using `Storage` Instead Of `Memory` For Structs/Arrays Saves Gas

When fetching data from a `storage` location, assigning the data to a `memory` variable causes all fields of the struct/array to be read from `storage`, which incurs a Gcoldsload (2100 gas) for each fiel

Line 506:    Exp storage exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});

// Code block 1 (solidity):
function assignRoletoKYCGroup(
    uint256 kycRequirementGroup,
    bytes32 role
  ) external payable onlyRole(REGISTRY_ADMIN) {
    kycGroupRoles[kycRequirementGroup] = role;
    emit RoleAssignedToKYCGroup(kycRequirementGroup, role);
  }


niser93 G

## GAS-01 - Use equivalent expression

### Founded in [EthRouter.sol](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol)
* [[L101-L103]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L101-L103)
* [[L154-L156]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L154-L156)
* [[L228-L230]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L228-L230)
* [[L256-L258]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L256-L258)
```
if (block.timestamp > deadline && deadline != 0) {
	revert DeadlinePassed();
}
```

### Description
```
if (block.timestamp > deadline && deadline != 0) {
        revert DeadlinePassed();
        }
```
becomes
```
if (!(block.timestamp <= deadline || deadline == 0)) {
            revert DeadlinePassed();
        }
```

### Proof of concept
Expression equality (using boolean formulas)
```
A>B && C!=0           is equivalent to

!(!(A>B && C!=0))           is equivalent to

!(!(A>B) || !(C!=0))           is equivalent to

!(A<=B || C==0)
```

With Remix IDE and compiling following code using optimization with 10000:

```
contract Attempt{
    error DeadlinePassed();

    function tryGas(uint256 deadline) public {
        if (block.timestamp > deadline && deadline != 0) {
            revert DeadlinePassed();
        }
    }
}

Execution cost: 290
```

```
contract Attempt{
    error DeadlinePassed();

    function tryGas(uint256 deadline) public {
        if (!(block.timestamp <= deadline || deadline == 0)) {
            revert DeadlinePassed();
        }
    }
}
Execution cost: 284
```

## GAS-02 - Require costs less gas than revert + Error

### Founded in [EthRouter.sol](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol)
* [[L101-L103]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L101-L103)
* [[L154-L156]](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.so

// Code block 1 (unknown):
if (block.timestamp > deadline && deadline != 0) {
	revert DeadlinePassed();
}

// Code block 2 (unknown):
if (block.timestamp > deadline && deadline != 0) {
        revert DeadlinePassed();
        }

// Code block 3 (unknown):
if (!(block.timestamp <= deadline || deadline == 0)) {
            revert DeadlinePassed();
        }

// Code block 4 (unknown):
A>B && C!=0           is equivalent to

!(!(A>B && C!=0))           is equivalent to

!(!(A>B) || !(C!=0))           is equivalent to

!(A<=B || C==0)

// Code block 5 (unknown):
contract Attempt{
    error DeadlinePassed();

    function tryGas(uint256 deadline) public {
        if (block.timestamp > deadline && deadline != 0) {
            revert DeadlinePassed();
        }
    }
}

Execution cost: 290

// Code block 6 (unknown):
contract Attempt{
    error DeadlinePassed();

    function tryGas(uint256 deadline) public {
        if (!(block.timestamp <= deadline || deadline == 0)) {
            revert DeadlinePassed();
        }
    }
}
Execution cost: 284

EthRouter.sol, EthRouter.sol#L101-L103, EthRouter.sol#L154-L156, EthRouter.sol#L228-L230, EthRouter.sol#L256-L258


Jean_Pierre_Polnaref G

# c4udit Report

## Files analyzed
- 2024-02-ai-arena/src/AiArenaHelper.sol
- 2024-02-ai-arena/src/FighterFarm.sol
- 2024-02-ai-arena/src/GameItems.sol
- 2024-02-ai-arena/src/MergingPool.sol
- 2024-02-ai-arena/src/Neuron.sol
- 2024-02-ai-arena/src/RankedBattle.sol
- 2024-02-ai-arena/src/StakeAtRisk.sol
- 2024-02-ai-arena/src/VoltageManager.sol

## Issues found

### Don't Initialize Variables with Default Value

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

#### Findings:
```
2024-02-ai-arena/src/AiArenaHelper.sol::48 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::73 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::99 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::136 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::148 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::176 => uint256 cumProb = 0;
2024-02-ai-arena/src/AiArenaHelper.sol::178 => for (uint8 i = 0; i < attrProbabilitiesLength; i++) {
2024-02-ai-arena/src/FighterFarm.sol::211 => for (uint16 i = 0; i < totalToMint; i++) {
2024-02-ai-arena/src/FighterFarm.sol::249 => for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) {
2024-02-ai-arena/src/MergingPool.sol::124 => for (uint256 i = 0; i < winnersLength; i++) {
2024-02-ai-arena/src/MergingPool.sol::147 => uint32 claimIndex = 0;
2024-02-ai-arena/src/MergingPool.sol::152 => for (uint32 j = 0; j < winnersLength; j++) {
2024-02-ai-arena/src/MergingPool.sol::174 => uint256 numRewards = 0;
2024-02-ai-arena/src/MergingPool.sol::178 => for (uint32 j = 0; j < winnersLength; j++) {
2024-02-ai-arena/src/MergingPool.sol::207 => for (uint256 i = 0; i < maxId; i++) {
2024-02-ai-arena/src/Neuron.sol::131 => for (uint32 i = 0; i < recipientsLengt

2024-02-ai-arena/src/AiArenaHelper.sol::48 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::73 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::99 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::136 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::148 => for (uint8 i = 0; i < attributesLength; i++) {
2024-02-ai-arena/src/AiArenaHelper.sol::176 => uint256 cumProb = 0;
2024-02-ai-arena/src/AiArenaHelper.sol::178 => for (uint8 i = 0; i < attrProbabilitiesLength; i++) {
2024-02-ai-arena/src/FighterFarm.sol::211 => for (uint16 i = 0; i < totalToMint; i++) {
2024-02-ai-arena/src/FighterFarm.sol::249 => for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) {
2024-02-ai-arena/src/MergingPool.sol::124 => for (uint256 i = 0; i < winnersLength; i++) {
2024-02-ai-arena/src/MergingPool.sol::147 => uint32 claimIndex = 0;
2024-02-ai-arena/src/MergingPool.sol::152 => for (uint32 j = 0; j < winnersLength; j++) {
2024-02-ai-arena/src/MergingPool.sol::174 => uint256 numRewards = 0;
2024-02-ai-arena/src/MergingPool.sol::178 => for (uint32 j = 0; j < winnersLength; j++) {
2024-02-ai-arena/src/MergingPool.sol::207 => for (uint256 i = 0; i < maxId; i++) {
2024-02-ai-arena/src/Neuron.sol::131 => for (uint32 i = 0; i < recipientsLength; i++) {
2024-02-ai-arena/src/RankedBattle.sol::296 => uint256 claimableNRN = 0;
2024-02-ai-arena/src/RankedBattle.sol::387 => uint256 claimableNRN = 0;
2024-02-ai-arena/src/RankedBattle.sol::427 => uint256 points = 0;

2024-02-ai-arena/src/AiArenaHelper.sol::47 => uint256 attributesLength = attributes.length;
2024-02-ai-arena/src/AiArenaHelper.sol::70 => require(attributeDivisors.length == attributes.length);
2024-02-ai-arena/src/AiArenaHelper.sol::72 => uint256 attributesLength = attributes.length;
2024-02-ai-arena/src/AiArenaHelper.sol::96 => uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);
2024-02-ai-arena/src/AiArenaHelper.sol::98 => uint256 attributesLength = attributes.length;
2024-02-ai-arena/src/AiArenaHelper.sol::133 => require(probabilities.length == 6, "Invalid number of attribute arrays");
2024-02-ai-arena/src/AiArenaHelper.sol::135 => uint256 attributesLength = attributes.length;
2024-02-ai-arena/src/AiArenaHelper.sol::147 => uint256 attributesLength = attributes.length;
2024-02-ai-arena/src/AiArenaHelper.sol::177 => uint256 attrProbabilitiesLength = attrProbabilities.length;
2024-02-ai-arena/src/FighterFarm.sol::208 => require(modelHashes.length == totalToMint && modelTypes.length == totalToMint);
2024-02-ai-arena/src/FighterFarm.sol::214 => uint256(keccak256(abi.encode(msg.sender, fighters.length))),
2024-02-ai-arena/src/FighterFarm.sol::225 => /// @dev This function requires the length of all input arrays to be equal.
2024-02-ai-arena/src/FighterFarm.sol::244 => mintpassIdsToBurn.length == mintPassDnas.length &&
2024-02-ai-arena/src/FighterFarm.sol::245 => mintPassDnas.length == fighterTypes.length &&
2024-02-ai-arena/src/FighterFarm.sol::246 => fighterTypes.length == modelHashes.length &&
2024-02-ai-arena/src/FighterFarm.sol::247 => modelHashes.length == modelTypes.length
2024-02-ai-arena/src/FighterFarm.sol::249 => for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) {
2024-02-ai-arena/src/FighterFarm.sol::324 => uint256(keccak256(abi.encode(msg.sender, fighters.length))),
2024-02-ai-arena/src/FighterFarm.sol::507 => uint256 newId = fighters.length;
2024-02-ai-arena/src/GameItems.sol::258 => if (bytes(customURI).length > 0) {
20


Awesome Q

# 1. Use newer versions of solidity

Consider using the latest version of solidity as newer versions have bug fixes, as well as new features.

To see what the latest versions have to offer check out the [Change Log](https://github.com/ethereum/solidity/blob/develop/Changelog.md)

Affected lines of code:

- [WstEth.sol#L2](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L2)

- [SfrxEth.sol#L2](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L2)

- [SafEth.sol#L2](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L2)

- [Reth.sol#L2](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L2)

# 2. Improve the Readability by following modularity principles

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

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

An example of this syntax is shown below:

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

Affected line of code:

- [WstEth.sol#L4](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L4-L8)

- [Reth.sol#L4-L15](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L4-L15)
- [SafEth.sol#L4-L11](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L4-L11)
- [SfrxEth.sol#L4-L9](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L4

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

WstEth.sol#L2, SfrxEth.sol#L2, SafEth.sol#L2, Reth.sol#L2, WstEth.sol#L4-L8, Reth.sol#L4-L15, SafEth.sol#L4-L11, SfrxEth.sol#L4

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