JCN G

## Summary
Certian optimizations were benchmarked to use as baselines for issues + illustrate average savings. All benchmarks were done via the protocol's tests, i.e. using the following config: `solc version 0.8.11`, `optimizer on` , `200 runs` for `Ethos-Vault` contracts. Instances are illustrated with diffs.

Functions that are not covered in the protocol's tests and/or not benchmarked: gas savings are explained via opcodes. Instances are also illustrated with diffs.

**Note**: Some code snippets may be truncated to save space. Code snippets may also be accompanied by `@audit` tags in comments to aid in explaining the issue.

### Gas Optimizations
| Number |Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
| [G-01](#state-variables-can-be-packed-to-use-fewer-storage-slots) | State variables can be packed to use fewer storage slots | 12 | 24000 |
| [G-02](#structs-can-be-packed-to-use-fewer-storage-slots) | Structs can be packed to use fewer storage slots | 4 | 8000 |
| [G-03](#avoid-contract-existence-checks-by-using-solidity-version-0810-or-later) | Avoid contract existence checks by using solidity version 0.8.10 or later | 87 |  8700 |
| [G-04](#multiple-accesses-of-a-mappingarray-should-use-a-storage-pointer) | Multiple accesses of a mapping/array should use a storage pointer | 41 | 1969 |
| [G-05](#state-variables-can-be-cached-instead-of-re-reading-them-from-storage) | State variables can be cached instead of re-reading them from storage | 34 |  3400 |
| [G-06](#avoid-emitting-storage-values) | Avoid emitting storage values | 26 | 2600 |
| [G-07](#refactor-code-to-save-1-sstore-and-2-sloads) | Refactor code to save 1 SSTORE and 2 SLOADs | 1 |  337 |
| [G-08](#internal-functions-are-cheaper-to-call-compared-to-public-functions) | `Internal` functions are cheaper to call compared to `public` functions | 13 |  286 |
| [G-09](#x--yx---y-costs-more-gas-than-x--x--yx--x---y-for-state-variables) | `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state va

File: Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol
30:    bool public emergencyExit;
31:    uint256 public lastHarvestTimestamp;
32:
33:    uint256 public upgradeProposalTime;
34:
35:    /**
36:     * Reaper Roles in increasing order of privilege.
37:     * {KEEPER} - Stricly permissioned trustless access for off-chain programs or third party keepers.
38:     * {STRATEGIST} - Role conferred to authors of the strategy, allows for tweaking non-critical params.
39:     * {GUARDIAN} - Multisig requiring 2 signatures for emergency measures such as pausing and panicking.
40:     * {ADMIN}- Multisig requiring 3 signatures for unpausing.
41:     *
42:     * The DEFAULT_ADMIN_ROLE (in-built access control role) will be granted to a multisig requiring 4
43:     * signatures. This role would have upgrading capability, as well as the ability to grant any other
44:     * roles.
45:     *
46:     * Also note that roles are cascading. So any higher privileged role should be able to perform all the functions
47:     * of any lower privileged role.
48:     */
49:    bytes32 public constant KEEPER = keccak256("KEEPER");
50:    bytes32 public constant STRATEGIST = keccak256("STRATEGIST");
51:    bytes32 public constant GUARDIAN = keccak256("GUARDIAN");
52:    bytes32 public constant ADMIN = keccak256("ADMIN");
53:
54:    /**
55:     * @dev Reaper contracts:
56:     * {vault} - Address of the vault that controls the strategy's funds.
57:     */
58:    address public vault;

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L44-L57

### We are able to pack `totalAllocBPS` & `emergencyShutdown` and `lastReport`, `withdrawMaxLoss`, & `lockedProfitDegradation` into 2 storage slots to save 3 SLOTs (~6000 gas).

`lastReport` is simply a timestamp and therefore can be packed as a `uint40` value without fear of overflowing. See [reference 1](https://twitter.com/PaulRBerg/status/1591832937179250693) and [reference 2](https://twitter.com/RareSkills_io/status/1632033211609137156). `totalAllocBPS`, `withdrawMaxLoss`, and `lockedProfitDegradation` are asserted to stay within certain bounds. `uint80` is sufficient for `totalAllocBPS`, `withdrawMaxLoss`, and `lockedProfitDegradation` (larger uints could also work). `emergencyShutdown` is bool and therefore only occupies 1 byte.

Below are instances where `totalAllocBPS` is asserted to be `<= PERCENT_DIVISOR`:

[ReaperVaultV2.sol#L144-L156](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L144-L156)

[ReaperVaultV2.sol#L191-L199](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L191-L199)

Below are instances where `withdrawMaxLoss` is asserted to be `<= PERCENT_DIVISOR`:

[ReaperVaultV2.sol#L566-L571](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L566-L571)

Below are instances where `lockedProfitDegradation` is asserted to be `<= DEGRADATION_COEFFICIENT`:

[ReaperVaultV2.sol#L617-L622](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L617-L622)

Functions which would benefit from these values occupying the same storage slot by incurring a `Gwarmaccess (100 gas)` versus a `Gcoldsload (2100 gas)` for every subsequent storage slot access: [ReaperVaultV2.addStrategy()](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L144-L171), [Reaper

ReaperVaultV2.sol#L44-L57, ReaperVaultV2.sol#L144-L156, ReaperVaultV2.sol#L191-L199, ReaperVaultV2.sol#L566-L571, ReaperVaultV2.sol#L617-L622


Daniel526 Q

## A. Defensive Check Absence Before Deposit in depositAssetIntoStrategy Function
[Link](https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/NodeDelegator.sol#L51-L68)
The `depositAssetIntoStrategy` function retrieves the balance of the asset and directly proceeds to deposit into the strategy without checking if the balance is non-zero. Without this check, the function might execute unnecessary deposit operations, potentially leading to unexpected behavior or wasted gas.

```solidity
function depositAssetIntoStrategy(address asset)
    external
    override
    whenNotPaused
    nonReentrant
    onlySupportedAsset(asset)
    onlyLRTManager
{
    address strategy = lrtConfig.assetStrategy(asset);
    IERC20 token = IERC20(asset);
    address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

    // No check for a non-zero balance before proceeding
    uint256 balance = token.balanceOf(address(this));

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

    emit AssetDepositIntoStrategy(asset, strategy, balance);
}
```
The absence of a check for a non-zero balance before proceeding with the deposit operation might result in unnecessary calls to `depositIntoStrategy`. This is suboptimal both in terms of gas efficiency and the potential for unintended consequences within the strategies.
## Impact:
The impact of this issue is primarily efficiency-related, with unnecessary gas consumption and potential unexpected behavior within the strategies due to redundant deposit calls with a zero balance.
## Mitigation:
To mitigate this issue, it is recommended to include a defensive check before the deposit operation to ensure that the balance is non-zero. This can be achieved by adding the following code snippet:

```solidity
// Check if the balance is non-zero before proceeding with the deposit
require(balance > 0, "Zero balance,

// Check if the balance is non-zero before proceeding with the deposit
require(balance > 0, "Zero balance, nothing to deposit");

// Code block 1 (solidity):
function depositAssetIntoStrategy(address asset)
    external
    override
    whenNotPaused
    nonReentrant
    onlySupportedAsset(asset)
    onlyLRTManager
{
    address strategy = lrtConfig.assetStrategy(asset);
    IERC20 token = IERC20(asset);
    address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

    // No check for a non-zero balance before proceeding
    uint256 balance = token.balanceOf(address(this));

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

    emit AssetDepositIntoStrategy(asset, strategy, balance);
}


catellatech Q

# QA report for Ondo Finance contest by Catellatech

## [01] assert is used only in tests
in the rUSDYFactory contract in the line 100 the devs used the assert instead of require or custom errors.

check the Solidity docs ➡ https://docs.soliditylang.org/en/v0.8.19/control-structures.html#panic-via-assert-and-error-via-require

## [02] in rUSDYFactory contract and in so many instances the devs doesn't check the imput
Please ckeck all the input addresses in the functions, constructor.

## [03] If onlyOwner runs renounceOwnership() the contract become unavailable
The `onlyOwner` authority here is very important for some contracts, but the `Ownable.sol library` imported with the import has the renounceOwnership() feature, in case the owner accidentally triggers this function, the remove functions will not work and the contract will block gas due to arrays, may have a continuous structure that exceeds its limit.

The solution to this is to overide and disable the renounceOwnership() function as implemented in many contracts in his project, it is important to include this code in the contracts.

```solidity
function renounceOwnership() public payable override onlyOwner {
    revert("Cannot renounce ownership");
}
```

## [04] In RWADynamicOracle contract utilize Assembly - should have comments
The Moonwell team makes use of Assembly on RWADynamicOracle contract, since this is a low level language that is more difficult to parse by readers, include extensive documentation, comments on the rationale behind its use, clearly explaining what each assembly instruction does.

This will make it easier for users to trust the code, for reviewers to validate the code, and for developers to build on or update the code.

Note that using Assembly removes several important security features of Solidity, which can make the code more insecure and more error-prone.

### Recommendation
It is essential to clearly and comprehensively document all activities related to critical function `

DestinationBridge.sol
/*//////////////////////////////////////////////////////////////
                        Public Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice internal function to mint a transaction if it has passed the threshold
   *         for the number of approvers
   *
   * @param txnHash The hash of the transaction we wish to mint
   */
  function _mintIfThresholdMet(bytes32 txnHash) internal {..//}

// Code block 1 (solidity):
function renounceOwnership() public payable override onlyOwner {
    revert("Cannot renounce ownership");
}


castle_chain Q

# destination bridge
## 1) `rescueTokens()` function should prevent the withdrawal of USDY tokens .
the function `rescueTokens()`should make sure that the token to be withdrawn is not the core token of the bridge , but any tokens that had been sent by mistake , consider add this check :
```diff
  function rescueTokens(address _token) external onlyOwner {
+   if(_token == address(TOKEN)) revert();
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
  }
```
**code snippet**
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L322C1-L325C4

## 2) if the numOfApprovals is 0 this will freeze the execute function add prevent receiving the messages .
in the function `setThresholds()` there is no check if the `numberOfApprovers` is equal to 0 which be able to prevent receiving a messages of this amount .
consider add a check to make sure that the number of approvers in greater than zero .
```diff
    for (uint256 i = 0; i < amounts.length; ++i) {
+   if (numOfApprovers[i] == 0 ) revert();
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      } else {
        if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
          revert ThresholdsNotInAscendingOrder();
        }
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }
    }
```
**code snippet**
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L255-L279

## 3) consider adding a function to remove a chain from the mapping `chainToApprovedSender` to allow the owner to deactivate receiving messages from a specific chain .
there is only the method to add chain and there is no removing mechanism
**code snippet**

https://github.com/code-423n4/2023-09-ondo/blob/47d34

**code snippet**
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L255-L279

## 3) consider adding a function to remove a chain from the mapping `chainToApprovedSender` to allow the owner to deactivate receiving messages from a specific chain .
there is only the method to add chain and there is no removing mechanism
**code snippet**

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L234-L240

## 4) add a check to prevent the approvers to approve a deleted transaction and mint to address(zero) .
the function `approve()` allow approving a deleted transaction, and if the number reach the threshold the function will mint zero tokens to the zero address , so it is better to prevent this from happen .
should make sure that if(txn.sender == address(0)) revert();

// Code block 1 (diff):
function rescueTokens(address _token) external onlyOwner {
+   if(_token == address(TOKEN)) revert();
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
  }

// Code block 2 (diff):
for (uint256 i = 0; i < amounts.length; ++i) {
+   if (numOfApprovers[i] == 0 ) revert();
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      } else {
        if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
          revert ThresholdsNotInAscendingOrder();
        }
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }
    }

function rescueTokens(address _token) external onlyOwner {
+   if(_token == address(TOKEN)) revert();
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
  }

for (uint256 i = 0; i < amounts.length; ++i) {
+   if (numOfApprovers[i] == 0 ) revert();
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      } else {
        if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
          revert ThresholdsNotInAscendingOrder();
        }
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }
    }


Sathish9098 Q

# LOW FINDINGS

| LOW COUNT | ISSUES | INSTANCES |
|-----------------|-----------------|-----------------|
| [L-1] | ``assetAmount * 2 <= depositedAsset[onBehalfOf]`` check is conflict with docs  | 1 |
| [L-2] | Lack of ``check-effects-interactions (CEI) pattern`` is vulnerable to ``reentrancy attacks`` | 3 |
| [L-3] | User could deposit a small amount of ``ether`` and then ``mint`` a large amount of ``PEUSD``, which could create a security risk | 1 |
| [L-4] | Use the ``safeAllowance()`` function instead of the normal ``approve()`` function | 1 |
| [L-5] | Use ``call`` instead of ``transfer`` to send ethers | 3 |
| [L-6] | ``Timelocks`` not implemented as per documentation  | - |
| [L-7] | ``depositAssetToMint()`` does not check for a maximum limit on asset deposits | 1 |
| [L-8] | Project Upgrade and Stop Scenario should be | - |
| [L-9] | Lack of ``nonReentrant`` modifiers for critical external functions  | 1 |
| [L-10] | ``Divide by zero`` should be avoided  | 2 |
| [L-11] | ````name masking`` should be avoided to ``prevent errors``  | 2 |
| [L-12] | ``Division`` before ``multiplication`` can lead to ``precision errors`` | 2 |
| [L-13] | ``Hardcoding addresses`` in smart contracts can make them more ``vulnerable to attack``| 1 |
| [L-14] | ``NATSPEC comments`` should be ``increased`` in contracts| - |
| [L-15] | ``Negative values`` may return the ``unexpected`` values when using ``uint256(int)``conversion| 1|
| [L-16] | ``Array`` does not have a ``pop function``| 1 |
| [L-17] | ``Setters`` should have ``initial value check``| 1 |
| [L-18] | ``External calls`` in an ``un-bounded`` for-loop may result in a ``DOS``| 2 |

##

## [L-1] ``assetAmount * 2 <= depositedAsset[onBehalfOf]`` check is conflict with docs

``* - collateralAmount should be less than 50% of collateral``

### Impact
The comment for the function states that the collateral amount should be ``less than 50%`` of the ``deposited asset``. However, the ``implementation`` of the function actually allow

FILE: 2023-06-lybra/contracts/lybra/pools/base/LybraEUSDVaultBase.sol

* - collateralAmount should be less than 50% of collateral

159:require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");

FILE: Breadcrumbs2023-06-lybra/contracts/lybra/pools/LybraWbETHVault.sol

23: IWBETH(address(collateralAsset)).deposit{value: msg.value}(address(configurator));
24:        uint256 balance = collateralAsset.balanceOf(address(this));
25:        depositedAsset[msg.sender] += balance - preBalance;
26:
27:        if (mintAmount > 0) {
28:            _mintPeUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
29:        }


bowtiedvirus Q

# [L1] Lack of contract existence check on call

**Severity:** Low

**Effected Contract:** SourceBridge.sol, rUSDYFactory.sol

## Summary
The rUSDYFactory and SourceBridge have a multiexcall function, which Allows for arbitrary batched calls to arbitrary address. If the target address of a given call is incorrect, has been destroyed, or is otherwise non-callable, the call will still return true. To fix this, there should be a contract existence check before each call.

```solidity
function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
}
```

The Solidity documentation has the following warning about using `call`:
The low-level functions call, delegatecall and staticcall return true as their first return value if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed.

### Exploit Scenario
Bob, the rUSDY Guardian, calls execute with `calldata[0].target == address(a self-destructed or non-contract address)`. Even though the address no longer points to an existing contract, the operation still succeeds.

## Recommendations
1. Implement a contract existence check before each call in `multiexcall`.

# [L2] Ownership Transfers are a 1-step Process

**Severity:** Low

**Effected Contract:** SourceBridge.sol, rUSDYFactory.sol, DestinationBridge.sol

## Summary
Contracts using OpenZeppelin's Ownable contract only utilize a single-step `transferOwnership` step.
Consider using Ownable2Step to prevent accidentally locking the owner out.

## Vulnerability Details
Bob, the Owner of SourceBridge, calls `transferOwners

// Code block 1 (solidity):
function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
}


Polaris_tow G

## CAN MAKE THE VARIABLE OUTSIDE THE LOOP TO SAVE GAS
Make it outside and only use it inside.
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L113-L119
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L84-L96
```
        for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            // This is slightly less than ethAmount because slippage
            uint256 depositAmount = derivative.deposit{value: ethAmount}();
            uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
                depositAmount
            ) * depositAmount) / 10 ** 18;
            totalStakeValueEth += derivativeReceivedEthValue;
        }
```
##  SETTING THE CONSTRUCTOR TO PAYABLE
Saves ~13 gas per instance
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L38
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L33
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L27
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L24
##  FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE
If a function modifier or require such as onlyOwner/onlyX is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler wi

// Code block 1 (unknown):
for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

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

SafEth.sol#L113-L119, SafEth.sol#L84-L96, SafEth.sol#L38, Reth.sol#L33, SfrxEth.sol#L27, WstEth.sol#L24

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


twicek Q

# [L-01] For all block.timestamp > range.end  the price will be stale

## Proof of Concept
The README mentions the following regarding the `getPrice` function used in the calculation of the price of a rUSDY from the USDY amount:
> There is also functionality within the contract that if a range has elapsed and there is no subsequent range set, the oracle will return the maximum price of the previous range for all `block.timestamp > Range.end`

This functionality will lead to a stale price if the condition is met.

Example:

Initial state
1.0 USDY = 1.1 USD; 1.0 USDY = 1.1 rUSDY

Possible state if `block.timestamp > Range.end`
1.0 USDY = 1.2 USD; 1.0 USDY = 1.1 rUSDY
-> The value of rUSDY will not be pegged at 1:1 with USD anymore.

## Impact
The impact is low because the rUSDY is a read only projection of the USDY amount deposited in the contract (more precisely the shares), therefore even if the rUSDY balance for a given USDY amount is frozen, the same amount of USDY will always be unwrapped. What matters is that the same amount of share is redeemable against the same amount of USDY tokens.
The only issue is that the peg is not always guaranteed, which is the purpose of the contract.

# [L-02] CEI pattern is not followed in transferFrom

## Proof of Concept
The CEI pattern is not followed in the following function. The allowance is updated after transferring the tokens.

[rUSDY.sol#L301-L312](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L301-L312)
```
function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
  }
```

## Impact
In this specific case it has no impact because `_t

function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
    if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
    _burnShares(msg.sender, usdyAmount);
    usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
    emit TokensBurnt(msg.sender, _rUSDYAmount);
  }

// Code block 1 (unknown):
function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
  }


0x11singh99 Q

# Low-Level and Non-Critical issues

## Summary

### Low Level List

| Number | Issue Details                                                                                | Instances |
| :----: | :----------------------------------------------------------------------------------------------------------------- | :-------: |
| [L-01] | Address should be checked for 0 address before transferring to it.                                                                 |     1     |

### Non Critical List

| Number  | Issue Details                                                       | Instances |
| :-----: | :------------------------------------------------------------------ | :-------: |
| [NC-01] | Some Contracts are not following proper solidity style guide layout |     17     |

# LOW FINDINGS

## [L-01] Address should be checked for 0 address before transferring to it.

Check address is not zero before transferring to it. some tokens may not revert when tranferring to zero address.

**_1 Instance - 1 File_**

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

301:   function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);///@audit check
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
312:  }

```
[301-312](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L301C1-L312C4)

# NON-CRITICAL FINDINGS

## [NC-01] Some Contracts are not following proper solidity style guide layout

/ Layout of Contract: According to solidity Docs
// version
// imports
// errors
// interfaces, libraries, contracts
// Type declarations
// State variables
// Events
// Modifiers
// Functions

// Layout of Functions:
// constructor
// receive function (if exists)
// fallba

File : contracts/bridge/DestinationBridge.sol

///@audit error should be declared in starting of contract
439: error NotApprover();
     error NoThresholdMatch();
     error ThresholdsNotInAscendingOrder();
     error ChainNotSupported();
     error SourceNotSupported();
     error NonceSpent();
     error AlreadyApproved();
     error InvalidVersion();
448: error ArrayLengthMismatch();

// Code block 1 (solidity):
File : contracts/usdy/rUSDY.sol

301:   function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);///@audit check
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
312:  }


Breeje G

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-1](#GAS-1) | ++I/I++ SHOULD BE UNCHECKED{++I}/UNCHECKED{I++} WHEN IT IS NOT POSSIBLE FOR THEM TO OVERFLOW | 7 |
| [GAS-2](#GAS-2) | X += Y COSTS MORE GAS THAN X = X + Y FOR STATE VARIABLES | 28 |
| [GAS-3](#GAS-3) | SPLITTING REQUIRE() STATEMENTS THAT USE && SAVES GAS | 3 |
| [GAS-4](#GAS-4) | NOT USING THE NAMED RETURN VARIABLES WHEN A FUNCTION RETURNS, WASTES DEPLOYMENT GAS | 4 |

###  [GAS-1] ++I/I++ SHOULD BE UNCHECKED{++I}/UNCHECKED{I++} WHEN IT IS NOT POSSIBLE FOR THEM TO OVERFLOW

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop.

*Instances (7)*:
```solidity
File: aa-4337/core/EntryPoint.sol

100:      for (uint256 i = 0; i < opasLen; i++) {

107:      for (uint256 a = 0; a < opasLen; a++) {

112:      for (uint256 i = 0; i < opslen; i++) {

114:      opIndex++;

128:      for (uint256 a = 0; a < opasLen; a++) {

134:      for (uint256 i = 0; i < opslen; i++) {

136:      opIndex++;

```
[Link to code](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)

###  [GAS-2] X += Y COSTS MORE GAS THAN X = X + Y FOR STATE VARIABLES

*Instances (28)*:
```solidity
File: aa-4337/core/EntryPoint.sol

81:       collected += _executeUserOp(i, ops[i], opInfos[i]);

101:      totalOps += opsPerAggregator[i].userOps.length;

135:      collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

468:      actualGas += preGas - gasleft();

```
[Link to code](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)

```solidity
File: libs/Math.sol

148:       result += 1;

210:       result += 128;

214:       result += 64;

218:       result += 32;

222:       result += 16;

226:       result += 8;

230:       result += 4;

234:       result += 2;

237

// Code block 1 (solidity):
File: aa-4337/core/EntryPoint.sol

100:      for (uint256 i = 0; i < opasLen; i++) {

107:      for (uint256 a = 0; a < opasLen; a++) {

112:      for (uint256 i = 0; i < opslen; i++) {

114:      opIndex++;

128:      for (uint256 a = 0; a < opasLen; a++) {

134:      for (uint256 i = 0; i < opslen; i++) {

136:      opIndex++;

// Code block 2 (solidity):
File: aa-4337/core/EntryPoint.sol

81:       collected += _executeUserOp(i, ops[i], opInfos[i]);

101:      totalOps += opsPerAggregator[i].userOps.length;

135:      collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

468:      actualGas += preGas - gasleft();

File: aa-4337/core/EntryPoint.sol

100:      for (uint256 i = 0; i < opasLen; i++) {

107:      for (uint256 a = 0; a < opasLen; a++) {

112:      for (uint256 i = 0; i < opslen; i++) {

114:      opIndex++;

128:      for (uint256 a = 0; a < opasLen; a++) {

134:      for (uint256 i = 0; i < opslen; i++) {

136:      opIndex++;

File: aa-4337/core/EntryPoint.sol

81:       collected += _executeUserOp(i, ops[i], opInfos[i]);

101:      totalOps += opsPerAggregator[i].userOps.length;

135:      collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

468:      actualGas += preGas - gasleft();


MohammedRizwan G

## Summary

### Gas Optimizations
| |Issue|Instances| |
|-|:-|:-:|:-:|
| [G‑01] | Save gas by preventing zero amount in mint() and burn() | 2 |
| [G‑02] | Catch function parameters as local variables to save gas | 1 |
| [G‑03] | Use nested-if and avoid "&&" to save gas | 2 |
| [G‑04] | Catch proposalId in LybraGovernance.sol within function | 3 |

### [G‑01]  Save gas by preventing zero amount in mint() and burn()
In esLBR.sol, L-30 to L-43.
There are 2 instances of this issue:
[Link to code](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/token/esLBR.sol#L30-L43)

### Recommended Mitigation steps
```Solidity
File: contracts/lybra/token/esLBR.sol

    function mint(address user, uint256 amount) external returns (bool) {
+       require(amount != 0, "invalid amount);
        require(configurator.tokenMiner(msg.sender), "not authorized");
        require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
        try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
        _mint(user, amount);
        return true;
    }

    function burn(address user, uint256 amount) external returns (bool) {
+       require(amount != 0, "invalid amount);
        require(configurator.tokenMiner(msg.sender), "not authorized");
        try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
        _burn(user, amount);
        return true;
    }
```

### [G‑02]  Catch function parameters as local variables to save gas
Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read.

In esLBRBoost.sol, L-57 to L-69
There is 1 instance of this issue:
[Link to code](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/esLBRBoost.sol#L57-L69)

### Recommended Mitigation steps
```Solidity
File:

File: contracts/lybra/miner/esLBRBoost.sol

    function getUserBoost(address user, uint256 userUpdatedAt, uint256 finishAt) external view returns (uint256) {
+       address _user = user;
+       uint256 _userUpdatedAt = userUpdatedAt;
+       uint256 _finishAt = finishAt;
-        uint256 boostEndTime = userLockStatus[user].unlockTime;
+        uint256 boostEndTime = userLockStatus[_user].unlockTime;
-        uint256 maxBoost = userLockStatus[user].miningBoost;
+        uint256 maxBoost = userLockStatus[_user].miningBoost;

-        if (userUpdatedAt >= boostEndTime || userUpdatedAt >= finishAt) {
+        if (_userUpdatedAt >= boostEndTime || _userUpdatedAt >= _finishAt) {
            return 0;
        }
-        if (finishAt <= boostEndTime || block.timestamp <= boostEndTime) {
+        if (_finishAt <= boostEndTime || block.timestamp <= boostEndTime) {
            return maxBoost;
        } else {
-            uint256 time = block.timestamp > finishAt ? finishAt : block.timestamp;
+            uint256 time = block.timestamp > _finishAt ? _finishAt : block.timestamp;
-            return ((boostEndTime - userUpdatedAt) * maxBoost) / (time - userUpdatedAt);
+            return ((boostEndTime - _userUpdatedAt) * maxBoost) / (time - _userUpdatedAt);
        }
    }

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

    function mint(address user, uint256 amount) external returns (bool) {
+       require(amount != 0, "invalid amount);
        require(configurator.tokenMiner(msg.sender), "not authorized");
        require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
        try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
        _mint(user, amount);
        return true;
    }

    function burn(address user, uint256 amount) external returns (bool) {
+       require(amount != 0, "invalid amount);
        require(configurator.tokenMiner(msg.sender), "not authorized");
        try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
        _burn(user, amount);
        return true;
    }


SuperRayss Q

# Issues Found
## [Ethos-Core\contracts\LQTY\CommunityIssuance.sol](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol)
There is a loss of precision in calculating `rewardPerSecond`. This is due to `rewardPerSecond` being assigned the result of the lossy division of `distributionPeriod` from `amount` (line 112). This loss in precision results in a growing deviation between expected and actual issuance of $Oath tokens to the stability pool.
## [Ethos-Core\test\LQTYIssuanceArithmeticTest.js](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/test/LQTYIssuanceArithmeticTest.js)
[The test case on line 170](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/test/LQTYIssuanceArithmeticTest.js#L170) passes when it shouldn’t due to improper usage of arithmetic operators and type conversion on BigNumber variables, resulting in the test case comparing the integers 4 and 3 ([line 181](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/test/LQTYIssuanceArithmeticTest.js#L170)) rather than their intended values. When corrected, this test case does not pass with the current implementation of [`fund()` in CommunityIssuance.sol](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L101) due to loss of precision with calculating `rewardPerSecond`. The corrected test case fails with `AssertionError: expected 2255118 [Wei] to be at most 1000 [Wei]`.

The corrected test case:
```
  it("aggregates multiple funding rounds within a distribution period", async () => {
    await setupFunder(accounts[0], oathToken, communityIssuanceTester, million);
    await communityIssuanceTester.fund(thousand);
    await th.fastForwardTime(604800, web3.currentProvider); // 7 days pass
    await communityIssuanceTester.unprotectedIssueLQTY(); // Issue Oath

  it("aggregates multiple funding rounds within a distribution period", async () => {
    await setupFunder(accounts[0], oathToken, communityIssuanceTester, million);
    await communityIssuanceTester.fund(thousand);
    await th.fastForwardTime(604800, web3.currentProvider); // 7 days pass
    await communityIssuanceTester.unprotectedIssueLQTY(); // Issue Oath
    await communityIssuanceTester.fund(thousand);
    await th.fastForwardTime(604800, web3.currentProvider);
    await communityIssuanceTester.unprotectedIssueLQTY();
    await communityIssuanceTester.fund(thousand);
    await th.fastForwardTime(1209600, web3.currentProvider); // 14 days pass
    await communityIssuanceTester.unprotectedIssueLQTY();
    const final = await communityIssuanceTester.totalOATHIssued(); // Get total issued Oath
    th.assertIsApproximatelyEqual(final, thousand.mul(th.toBN(3)), 1000);
  })

  function getRewardPerSecond() public view returns (uint) {
    return rewardPerSecond.div(DECIMAL_PRECISION);
  }


0xSmartContract Q

In terms of Quality Assurance;

Strengths of the Project;
-- The code has a structure written in a modern way, within the framework of generally accepted Soliditiy principles.
-- Details such as the fact that the `initialize()` function is declared externally, it has quality and short NatSpec comments, and there are almost no typos, show us the importance given to the code.

Development Areas of the Project;
-- The project uses the standard one-step owner model. 2-step ownership transfer is used in the most important projects in the new period, so 2-step ownership should be transferred [L-01]
-- The use of 'emit' in general terms, specifying new and old values in emit parameters is missing, there is a development area here, also sending 'emit' after ether transmission should be re-evaluated in terms of security, [L-03] [L-08]
-- `OnlyOwner` strength is not looked after as it is out of scope, but this is the weakest context of the project, here if timelock - multisign architectures are used together it will be the most robust structure and will increase trust in the project
-- How is the proxy architecture of the project, which pattern is used, how to upgrade parts are missing, adding these strengthens the control and security structure [N-26]

## Summary
### Low Risk Issues List
| Number |Issues Details|Context|
|:--:|:-------|:--:|
|[L-01]|Use `Ownable2StepUpgradeable` instead of ` OwnableUpgradeable ` contract| 4 |
|[L-02]|There may be a problem when changing the `maxSlippage` value because there is no range | 3 |
|[L-03]|Should declare to `emit` before the send ether in codebase for re-entrancy pattern | 1 |
|[L-04]|Omissions in Events|3 |
|[L-05]|Avoid using hardcode address in codebase| 11 |
|[L-06]|Lack of control to assign 0 values in the value assignments of critical state variables in the initialize| 3 |
|[L-07]|Use the latest updated version of OpenZeppelin dependencies| 1 |
|[L-08]|Add parameter to Event-Emit for critical function| 3 |
|[L-09]|Insufficie

4 results

contracts/SafEth/SafEth.sol:
   9: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
  15: contract SafEth is Initializable, ERC20Upgradeable, OwnableUpgradeable, SafEthStorage

contracts/SafEth/derivatives/Reth.sol:
  13: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
  19: contract Reth is IDerivative, Initializable, OwnableUpgradeable {

contracts/SafEth/derivatives/SfrxEth.sol:
   6: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
  13: contract SfrxEth is IDerivative, Initializable, OwnableUpgradeable {

contracts/SafEth/derivatives/WstEth.sol:
   5: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
  12: contract WstEth is IDerivative, Initializable, OwnableUpgradeable {

3 results - 3 files

contracts/SafEth/derivatives/Reth.sol:
  57      */
  58:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
  59:         maxSlippage = _slippage;
  60:     }
  61

contracts/SafEth/derivatives/SfrxEth.sol:
  50      */
  51:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
  52:         maxSlippage = _slippage;
  53:     }
  54

contracts/SafEth/derivatives/WstEth.sol:
  47      */
  48:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
  49:         maxSlippage = _slippage;
  50:     }


luxartvinsec Q

# 1. Insecure Initialization of the contract

Link: https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol#L31

Summary: The CashKYCSender contract uses the _disableInitializers() function in its constructor, which disables the use of the initializer functions. This allows any address to call the initialize() function, potentially allowing them to set arbitrary values for the kycRegistry and kycRequirementGroup variables and bypassing the intended KYC checks.

Impact: An attacker can potentially call the initialize() function and set arbitrary values for the kycRegistry and kycRequirementGroup variables, potentially allowing them to bypass KYC checks and transfer tokens to unauthorized addresses.

Recommendation: The _disableInitializers() function should be removed or replaced with a secure initialization process that properly controls access to the initialize() function. Additionally, the role-based access control in the "onlyRole" function should be reviewed to ensure that it is configured correctly.

# 2. Insecure Internal Function Access in OndoPriceOracle Contract

Link : https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracle.sol#L119

Summary:
The OndoPriceOracle contract contains an internal function "_setFTokenToCToken()" that is called by the external function "setFTokenToCToken()". The "_setFTokenToCToken()" function does not have any access restrictions and can be called by any address, potentially allowing an attacker to associate arbitrary fTokens with arbitrary cTokens.

Impact:
An attacker could associate arbitrary fTokens with arbitrary cTokens, potentially manipulating the underlying prices of the fTokens, leading to financial losses for users and potentially destabilizing the lending market.

Recommendation:
The "_setFTokenToCToken()" function should have the same access restrictions as the "setFTokenToCToken()" function, only allowing the contract owner to call it. Additionally,

require(mintAmount > 0, "Mint amount should be greater than 0");


0x73696d616f G

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

### Allowed in the struct `Config` is not necessary
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L27

The `allowed` property in the struct Config is not required, since the other properties can be checked to verify if the collateral is allowed or not (check if decimals > 0, for example).

### Properties in the struct `Config` can be packed
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L27

 Currently the properties are not packed, taking whole slots unnecessarily. I suggest packing into the following (or similar)
```
struct Config {
   bool allowed;
   uint24 decimals;
   uint112 MCR;
   uint112 CCR;
}
```

### Use a getter for the whole `Config` struct
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L27

Some places use all of the properties in the Config struct (except allowed, but as I mentioned previously, this one is not even required), so a getter for the whole struct is cheaper.

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

### It's not necessary to store both `lqtyStaking` and `lqtyStakingAddress`, one can be casted to the other
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L34
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L35

### Instead of storing all the contract addresses, a hash of the addresses concatenated could be stored instead, reducing sread operations
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L174
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L175
https://github.com/code-423n4/2023-02-ethos/b

CollateralConfig.sol, CollateralConfig.sol#L27, BorrowerOperations.sol, BorrowerOperations.sol#L34, BorrowerOperations.sol#L35, BorrowerOperations.sol#L174, BorrowerOperations.sol#L175


HE1M Q

### No. 1 Two-step authentication
Setting the owner should be done through two-step.
```
function setOwner(address _newOwner) external mixedAuth {
        require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
        address oldOwner = owner;
        owner = _newOwner;
        emit EOAChanged(address(this), oldOwner, _newOwner);
    }
```
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109

Should be:
```
function setPendingOwner(address _newPendingOwner) external mixedAuth {
    require(_newPendingOwner != address(0));
    pendingOwner = _newPendingOwner;
}

function acceptOwnership() external {
    require(msg.sender == pendingOwner);
    owner = msg.sender;
    pendingOwner = address(0);
}
```

### No. 2 Grieving by already deployed smart wallet contract

In the normal scenario, Alice sends fund to her smart wallet (which is not created yet) and then after some time sends the dapp transaction to the SDK. The SDK sends batch of transactions (create wallet and dapp transaction) to the relayer. The relayer first creates the wallet by calling the `SmartAccountFactory.sol`, and then sends the dapp transaction to the created wallet.

In the malicious scenario, Alice sends fund to her smart wallet (which is not created yet). Then Bob (a malicious user) calls the function `deployCounterFactualWallet` to create a smart wallet for Alice (with the same parameters that are used to predict the Alice's smart wallet address), so the smart wallet is deployed for Alice on the expected address. Then after some time Alice sends the dapp transaction to the SDK. The SDK sends batch of transactions (create wallet and dapp transaction) to the relayer. When the ralayer tries to create the wallet for Alice, it reverts, because it is already created by Bob.

Better to modify as:
```
function deployCounterFactualWallet(address _owner, address _e

// Code block 1 (unknown):
function setOwner(address _newOwner) external mixedAuth {
        require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
        address oldOwner = owner;
        owner = _newOwner;
        emit EOAChanged(address(this), oldOwner, _newOwner);
    }

// Code block 2 (unknown):
function setPendingOwner(address _newPendingOwner) external mixedAuth {
    require(_newPendingOwner != address(0));
    pendingOwner = _newPendingOwner;
}

function acceptOwnership() external {
    require(msg.sender == pendingOwner);
    owner = msg.sender;
    pendingOwner = address(0);
}


report

---
sponsor: "Ethos Reserve"
slug: "2023-02-ethos"
date: "2023-05-02"
title: "Ethos Reserve contest"
findings: "https://github.com/code-423n4/2023-02-ethos-findings/issues"
contest: 216
---

# 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 contest 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 contest outlined in this document, C4 conducted an analysis of the Ethos Reserve smart contract system written in Solidity. The audit contest took place between February 16—March 7 2023.

## Wardens

166 Wardens contributed reports to the Ethos Reserve contest:

  1. 0x3b
  2. [0x52](https://twitter.com/IAm0x52)
  3. [0x6980](https://twitter.com/0x6980)
  4. [0x73696d616f](https://twitter.com/3xJanx2009)
  5. [0xAgro](https://twitter.com/0xAgro)
  6. [0xBeirao](https://twitter.com/0xBeirao)
  7. [0xDING99YA](https://twitter.com/ding99ya)
  8. 0xRobocop
  9. [0xSmartContract](https://twitter.com/0xSmartContract)
  10. [0xTheC0der](https://twitter.com/MarioPoneder)
  11. 0xackermann
  12. 0xbepresent
  13. 0xhacksmithh
  14. 0xmuxyz
  15. [0xnev](https://twitter.com/bigbuttdev)
  16. [0xsomeone](https://github.com/alex-ppg)
  17. 2997ms
  18. 7siech
  19. [ABA](https://twitter.com/abarbatei)
  20. [AkshaySrivastav](https://twitter.com/akshaysrivastv)
  21. BRONZEDISC
  22. Bauer
  23. Bjorn\_bug
  24. Bnke0x0
  25. [Bobface](https://twitter.com/bobface16)
  26. Bough
  27. Breeje
  28. Budaghyan
  29. [Co0nan](https://twitter.com/Conan0x3)
  30. [CodeFoxInc](https://twitter.com/CodeFoxInc) ([thurendous](https://twitter.com/Markwu_crypto) and TerrierLover and retocrooman)
  31. CodingNameKiki
  32. [DadeKuma](https://twitter.com/DadeKuma)
  33. Darshan
  34. DeFiHackLabs (SunSec and gbaleee and 0x

if(_debtToOffset != 0){
	[StabilityPool.sol#L526-L538](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L526-L538)
}

    // Internal helper function to burn {_shares} of vault shares belonging to {_owner}
    // and return corresponding assets to {_receiver}. Returns the number of assets that were returned.
    function _withdraw(
        uint256 _shares,
        address _receiver,
        address _owner
    ) internal nonReentrant returns (uint256 value) {
        ...

            vaultBalance = token.balanceOf(address(this));
            if (value > vaultBalance) {
                value = vaultBalance;
            }

            require(
                totalLoss <= ((value + totalLoss) * withdrawMaxLoss) / PERCENT_DIVISOR,
                "Withdraw loss exceeds slippage"
            );
        }

        token.safeTransfer(_receiver, value);
        emit Withdraw(msg.sender, _receiver, _owner, value, _shares);
    }


matrix_0wl Q

## Non Critical Issues

|       | Issue                                                                   |
| ----- | :---------------------------------------------------------------------- |
| NC-1  | ADD A TIMELOCK TO CRITICAL FUNCTIONS                                    |
| NC-2  | ADD TO BLACKLIST FUNCTION                                               |
| NC-3  | USE OF BYTES.CONCAT() INSTEAD OF ABI.ENCODEPACKED(,)                    |
| NC-4  | GENERATE PERFECT CODE HEADERS EVERY TIME                                |
| NC-5  | MARK VISIBILITY OF INITIALIZE(…) FUNCTIONS AS EXTERNAL                  |
| NC-6  | MISSING EVENT FOR CRITICAL PARAMETER CHANGE                             |
| NC-7  | MISSING FEE PARAMETER VALIDATION                                        |
| NC-8  | NATSPEC COMMENTS SHOULD BE INCREASED IN CONTRACTS                       |
| NC-9  | INCLUDE RETURN PARAMETERS IN NATSPEC COMMENTS                           |
| NC-10 | NO SAME VALUE INPUT CONTROL                                             |
| NC-11 | OMISSIONS IN EVENTS                                                     |
| NC-12 | SOLIDITY COMPILER OPTIMIZATIONS CAN BE PROBLEMATIC                      |
| NC-13 | FUNCTION WRITING THAT DOES NOT COMPLY WITH THE SOLIDITY STYLE GUIDE     |
| NC-14 | NO CONFIG.SOL FILE                                                      |
| NC-15 | DON’T USE PERIODS WITH FRAGMENTS                                        |
| NC-16 | PRAGMA VERSION^0.8.19 VERSION TOO RECENT TO BE TRUSTED                  |
| NC-17 | UNUSED IMPORTS                                                          |
| NC-18 | FOR FUNCTIONS AND VARIABLES FOLLOW SOLIDITY STANDARD NAMING CONVENTIONS |
| NC-19 | THE TOKENADDRESS STATE VARIABLE SHOULD BE RENAMED TO TOKEN              |
| NC-20 | LINES ARE TOO LONG                                                      |
| NC-21 | USE OZ MERKLETREE IMPLEMENTATION INSTEAD OF CREATING A NEW ONE          |

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

File: src/EthRouter.sol

166:             ERC721(sells[i].nft).setApprovalForAll(sells[i].pool, true);

244:         ERC721(nft).setApprovalForAll(privatePool, true);

270:             ERC721(_change.nft).setApprovalForAll(_change.pool, true);

File: src/Factory.sol

129:     function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {

135:     function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner {

141:     function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {


0x11singh99 Q

# Quality Assurance Report

### Low-Severity

## [L-01] NFT should not be minted on 0 tokenId

The current implementation of storing fighter structs in the fighters array by using the tokenId as an index may lead to inefficiencies, especially when minting NFTs with tokenId 0.

```solidity
File : src/FighterFarm.sol

68:    /// @notice List of all fighter structs, accessible by using tokenId as index.
69:    FighterOps.Fighter[] public fighters;

```

[FighterFarm.sol#L68C1-L69C42](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L68C1-L69C42)

## [L-02] Add require check in `incrementGeneration` function to ensure that generation won't exceeds the limit of uint8

The `incrementGeneration` function in your contract appears to be designed to increment the generation count for a specified fighterType. However, there's a consideration to ensure that the generation count doesn't exceed 255.

```solidity
File : src/FighterFarm.sol

42: uint8[2] public generation = [0, 0];

129:     function incrementGeneration(uint8 fighterType) external returns (uint8) {
130:        require(msg.sender == _ownerAddress);
131:        generation[fighterType] += 1;
132:        maxRerollsAllowed[fighterType] += 1;
133:        return generation[fighterType];
134:    }

```

[src/FighterFarm.sol#L129C1-L134C6](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L129C1-L134C6)

## [L-03] Change the data type of `state variable` `totalNumTrained` `uint32` to `uint256`

The maximum value that can be held by a `uint32` variable is 2^32 - 1, which is 4,294,967,295. If someone wants to reach the maximum value of a `uint32` variable by using a loop in a smart contract, it is technically feasible. However, if there's a possibility that the value could exceed 4,294,967,295 it might be prudent to use `uint256` to ensure sufficient range.

```diff
File : src/FighterFarm.sol

44:        /// @notice Aggregate number of training sessions recorded.
-45:

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

68:    /// @notice List of all fighter structs, accessible by using tokenId as index.
69:    FighterOps.Fighter[] public fighters;

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

42: uint8[2] public generation = [0, 0];

129:     function incrementGeneration(uint8 fighterType) external returns (uint8) {
130:        require(msg.sender == _ownerAddress);
131:        generation[fighterType] += 1;
132:        maxRerollsAllowed[fighterType] += 1;
133:        return generation[fighterType];
134:    }

File : src/FighterFarm.sol

68:    /// @notice List of all fighter structs, accessible by using tokenId as index.
69:    FighterOps.Fighter[] public fighters;

File : src/FighterFarm.sol

42: uint8[2] public generation = [0, 0];

129:     function incrementGeneration(uint8 fighterType) external returns (uint8) {
130:        require(msg.sender == _ownerAddress);
131:        generation[fighterType] += 1;
132:        maxRerollsAllowed[fighterType] += 1;
133:        return generation[fighterType];
134:    }


Blank_Space Q

#[L-01] Missing length check for `iconsTypes`

The `redeemMintPass` function does not include the `iconsTypes` array in its sanity length checks. This could cause an unintended revert. Consider adding this check as per the diff:

```diff
require(
            mintpassIdsToBurn.length == mintPassDnas.length && mintPassDnas.length == fighterTypes.length
                && fighterTypes.length == modelHashes.length && modelHashes.length == modelTypes.length
++ && iconsTypes.length == modelTypes.length
        );
```
Location: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L243-L248

#[L-02] The `claimRewards` function does not validate the lengths of the parameter arrays

There could be unintended reverts in `claimRewards` due to the fact that the function relies on the input array lengths to be the same (as the index positions are compared), but does not validate this fact. Consider adding a sanity check that compares the array lengths.

```diff
++ require(modelURIs.length == modelTypes.length, "array length mismatch");
```
Location: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L139-L167

#[L-03] Players can update the `fighterType` due to lack of validation
A player can change the fighterType by calling `reRoll`. The `reRoll` function takes two parameters namely `tokenId` and `fighterType`, but the function doesn’t compare what the existing fighterType was. Which means that a player could `reRoll` their fighter type if they choose to do so. It will also skew the physicalAttributes of the fighter as the function will call `createPhysicalAttributes` with a false dendroidBool. Consider retrieving the fighterType from the fighters object in stead of passing it as a parameter.

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

#[L-04] The `incrementGeneration

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

#[L-03] Players can update the `fighterType` due to lack of validation
A player can change the fighterType by calling `reRoll`. The `reRoll` function takes two parameters namely `tokenId` and `fighterType`, but the function doesn’t compare what the existing fighterType was. Which means that a player could `reRoll` their fighter type if they choose to do so. It will also skew the physicalAttributes of the fighter as the function will call `createPhysicalAttributes` with a false dendroidBool. Consider retrieving the fighterType from the fighters object in stead of passing it as a parameter.

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

#[L-04] The `incrementGeneration` function increases the `maxRerollsAllowed` per fighter type

The `incrementGeneration` function increments the generation per fighter type, but it also increases the `maxRerollsAllowed` for every generation increase, thus the new generation fighters will get an added re-roll every time `incrementGeneration` is called. Also consider that any existing fighter will gain a re-roll with each generation increment with reference to this line in the `reRoll` function: `require(numRerolls[tokenId] < maxRerollsAllowed[fighterType]);` as re-rolls are checked on a global level. Consider adding a function that is callable by the owner to specifically increment the re-rolls:

// Code block 1 (diff):
require(
            mintpassIdsToBurn.length == mintPassDnas.length && mintPassDnas.length == fighterTypes.length
                && fighterTypes.length == modelHashes.length && modelHashes.length == modelTypes.length
++ && iconsTypes.length == modelTypes.length
        );

// Code block 2 (diff):
++ require(modelURIs.length == modelTypes.length, "array length mismatch");

require(
            mintpassIdsToBurn.length == mintPassDnas.length && mintPassDnas.length == fighterTypes.length
                && fighterTypes.length == modelHashes.length && modelHashes.length == modelTypes.length
++ && iconsTypes.length == modelTypes.length
        );

++ require(modelURIs.length == modelTypes.length, "array length mismatch");


chaduke G

G1. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cToken/CTokenModified.sol#L123
Putting it inside unchecked can save gas since overflow is impossible here:
```
uint dstTokensNew = accountTokens[dst] + tokens;

```

G2. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cToken/CTokenModified.sol#L182-L190
https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenCash.sol#L182-L190

No need to introduce ``src`` to save gas:
```
 function approve(
    address spender,
    uint256 amount
  ) external override returns (bool) {

    transferAllowances[msg.sender][spender] = amount;
    emit Approval(msg.sender, spender, amount);
    return true;
  }
```

G3. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cToken/CTokenModified.sol#L818-L819
Enclosing them inside unchecked can save gas since underflow is impossible due to previous checks
```
unchecked{
      uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
     uint totalBorrowsNew = totalBorrows - actualRepayAmount;
}

```

G4. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cToken/CTokenModified.sol#L1284
Encoding it inside unchecked can save gas - underflow is impossible due to previous check.
```
unchecked{
       totalReservesNew = totalReserves - reduceAmount;
}
```

G5. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenCash.sol#L624-L626
This check should be performed earlier (at L588) to save gas.

G6. https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenCash.sol#L641
Enclosing this line into unchecked can save gas as underflow

// Code block 1 (unknown):
uint dstTokensNew = accountTokens[dst] + tokens;

// Code block 2 (unknown):
function approve(
    address spender,
    uint256 amount
  ) external override returns (bool) {

    transferAllowances[msg.sender][spender] = amount;
    emit Approval(msg.sender, spender, amount);
    return true;
  }

// Code block 3 (unknown):
unchecked{
      uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
     uint totalBorrowsNew = totalBorrows - actualRepayAmount;
}

// Code block 4 (unknown):
unchecked{
       totalReservesNew = totalReserves - reduceAmount;
}

CTokenModified.sol#L123, CTokenModified.sol#L182-L190, CTokenCash.sol#L182-L190, CTokenModified.sol#L818-L819, CTokenModified.sol#L1284, CTokenCash.sol#L624-L626, CTokenCash.sol#L641


eyexploit Q

# Use SafeMath or Math library to do the arithmetic operation
 Consider a example below,
```
// calculating using  operators
max((_tx.targetTxGas * 64) / 63,_tx.targetTxGas + 2500) + 500

// calculating using SafeMath
max((_tx.targetTxGas.mul(64).div(63), _tx.targetTxGas.add(2500)).add(500)
```

The first expression uses parentheses and the `/` and `+` operators to perform arithmetic operations. This can be confusing to read and understand, especially if you are not familiar with the operator precedence rules in Solidity.

The second expression uses the `mul`, `div`, and `add` functions from the `SafeMath` library to perform the same arithmetic operations. This is generally considered to be a better practice, because it is easier to read and understand. It also automatically handles overflows and underflows, which can help to protect against  any potential bugs or vulnerabilities.

It's a good idea to use functions from the `SafeMath` library whenever you are performing arithmetic operations in your Solidity code. This can help make your code more readable, maintainable, and secure.

*Impacted code* : https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

// Code block 1 (unknown):
// calculating using  operators
max((_tx.targetTxGas * 64) / 63,_tx.targetTxGas + 2500) + 500

// calculating using SafeMath
max((_tx.targetTxGas.mul(64).div(63), _tx.targetTxGas.add(2500)).add(500)


catellatech Analysis

# Ondo Finance Smart Contracts Analysis

## Description overview of Ondo Finance

``Ondo Finance`` is a blockchain platform that offers investment opportunities with cryptocurrencies ``backed`` by U.S. dollar bank deposits ``(USDY)``. ``USDY`` earns interest over time, increasing its value. ``Ondo Finance`` introduces ``rUSDY``, a variant of ``USDY`` that automatically adjusts in quantity to reflect interest but maintains a nominal value of 1`` dollar`` per ``token``. ``Oracles`` track the value of ``USDY``. ``Ondo Finance`` enables asset transfers between ``blockchains`` through ``bridge`` contracts.

![Ondo](https://github.com/catellaTech/Ondo/blob/main/Ondo1.drawio.png?raw=true)

## 1- System Overview

### **Scope**

- [SourceBridge.sol](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol)This ``contract`` facilitates the transfer of tokens between different blockchains using the ``Axelar protocol``, ensuring that tokens burned on one chain are minted on the corresponding destination chain, provided certain conditions are met, and the necessary gas is paid. Additionally, it provides administrative functionalities and a ``pause mechanism`` when needed.

- [DestinationBridge.sol](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol): This ``contract`` functions as the destination ``chain bridge`` for ``USDY`` tokens. Its primary purpose is to facilitate the seamless transfer of ``USDY`` tokens from a source blockchain to the destination blockchain where this contract resides. This ``bridge`` contract plays a pivotal role in enabling ``cross-chain`` interoperability and ensuring that ``USDY`` tokens can be securely and efficiently moved between different ``blockchain networks``.

- [rUSDY.sol](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol): This ``contract`` serves as the backbone for an ``interest-bearing`` token where users can ``wrap`` and ``unwrap`` their ``

   /// @dev Role based access control roles
  bytes32 public constant USDY_MANAGER_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
  bytes32 public constant LIST_CONFIGURER_ROLE =
    keccak256("LIST_CONFIGURER_ROLE");

bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);


PetarTolev Q

## Low Risk Findings Overview

| ID     | Finding                                                             |
| ------ | ------------------------------------------------------------------- |
| [L-01] | Staker Role Cannot Be Revoked                                       |
| [L-02] | `getFighterPoints` only returns the points for figter with id 1     |
| [L-03] | The `Neuron` Contract Uses a Deprecated `AccessControl` Function    |
| [L-04] | Unstaked transferred fighter is unusable in the same roundId        |
| [L-05] | Loss of NRNs on token transfer failure in `RankedBattle.unstakeNRN` |
| [L-06] | Possible Cross-Chain Replay Attack in `FighterFarm.claimFighters`   |
| [L-07] | The user can choose fighters' attributes in their favor             |

## Non-critical Findings Overview

| ID      | Finding                                                         |
| ------- | --------------------------------------------------------------- |
| [NC-01] | Implement `_ableToTransfer` in the `ERC20._beforeTokenTransfer` |
| [NC-02] | Use OpenZeppelin's `AccessControl` for Role Management          |
| [NC-03] | Use OpenZeppelin's `Ownable` for Ownership Management           |
| [NC-04] | In `Neuron` Contract Utilize `AccessControl` Functionality      |
| [NC-05] | In `Neuron` Contract, Inherit OpenZeppelin's `ERC20Burnable`    |
| [NC-06] | Duplicated Validations Should Be Refactored to a Function       |

---

# Low Risk Findings

### [L-01] Staker Role Cannot Be Revoked

In `FighterFarm`, the staker role has permission to lock/unlock fighters, determining their transferability.

```solidity
function updateFighterStaking(uint256 tokenId, bool stakingStatus) external {
    require(hasStakerRole[msg.sender]);
    fighterStaked[tokenId] = stakingStatus;
    ...
}
```

```solidity
function _ableToTransfer(uint256 tokenId, address to) private view returns(bool) {
    return (
      _isApprovedOrOwner(msg.sender, tokenId) &&
      balanceOf(to) < MAX_FIGHTERS_ALLOWED &&
      !fi

function _ableToTransfer(uint256 tokenId, address to) private view returns(bool) {
    return (
      _isApprovedOrOwner(msg.sender, tokenId) &&
      balanceOf(to) < MAX_FIGHTERS_ALLOWED &&
      !fighterStaked[tokenId]
    );
}

// Code block 1 (solidity):
function updateFighterStaking(uint256 tokenId, bool stakingStatus) external {
    require(hasStakerRole[msg.sender]);
    fighterStaked[tokenId] = stakingStatus;
    ...
}


Aymen0909 G

# Gas Optimizations

## Summary

|               | Issue         | Instances     |
| :-------------: |:-------------|:-------------:|
| 1  | `storage` variable should be cached into `memory` variables instead of re-reading them  |  7 |
| 2  | `salePrice` should be calculated outside the for loop |  1 |
| 3  | Usage of `uints/ints` smaller than 32 bytes (256 bits) incurs overhead  |  5 |
| 4  | `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state variables  |  4 |
| 5  | Input check statements should be placed at the start of the functions |  2 |
| 6  | Duplicated input check statements should be refactored to a modifier | 4 |
| 7  | `public` functions not called by the contract should be declared `external` instead | 20 |

## Findings

### 1- `storage` variable should be cached into `memory` variables instead of re-reading them :

The instances below point to the second+ access of a state variable within a function, the caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read, thus saves **100gas** for each instance.

There are 7 instances of this issue :

File: PrivatePool.sol

[Line 225-279](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L225-L279)

In the code linked above the value of `baseToken` is read multiple times (6) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

[Line 345-368](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L345-L368)

In the code linked above the value of `baseToken` is read multiple times (5) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

[Line 397-426](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L397-L426)

In the code linked above the value of `baseToken` is read multiple times (4) from s

uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length;


0xhacksmithh G

### [Gas-01] A no. of ways present to `Short-circuit` Function call which significantly save high gas
Like in ```buy()```
*First way*
Is to move ```if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount(); ``` to first step of function
If any user send any ETH(when basetoken is not ETH) then this function will revert right after, So no further gas wasted in further calculation

*Second way*
To add a check whether both input array are of same size or not.

*Third way*
Gas can save in ```buy()``` by simply checking user input ids are owned by pool or not
buy() simply makes all weight calculation, price calculation, then update state variables and then try to send nfts to caller.
If anyone of nfts not owned by pool then whole call will failed, and all gas which used to calculate above things will wasted.
So if you move important checks ```like is all nft ids input by caller is owned by protocal``` to the above and then proceed with weigth, price etc, it will cost less gas if any user intentionaly/unintensionaly input wrong ids.
```solidity
function buy(uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof) // @audit-issue function expose to reentrancy
        public
        payable
        returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount) // @audit much gas can save by making initial check whether these nft own by this contract or not, Save caller gas in case of front-running
    {
        // ~~~ Checks ~~~ // // @audit short-circuiting by checking if length of Ids and Weights are same or not.
+       if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();
+       require(tokenIds.length == tokenWeights.length);
        // calculate the sum of weights of the NFTs to buy
        uint256 weightSum = sumWeightsAndValidateProof(tokenIds, tokenWeights, proof);

        // calculate the required net input amount and fee amount
        (netInputAmount, feeAmount, protocolFe

```
File: src/PrivatePool.sol
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211-L289

// Code block 1 (unknown):
*First way*
Is to move


coryli Q

# CHECK TODOs
Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment.
## Instances
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L335
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L380

# `decimals()` NOT PART OF `ERC20` STANDARD
`decimals()` is not part of the official `ERC20` standard and could potentially fail for tokens that do not implement it.
Unlikely in practice, but something to keep in mind as it could be potentially an issue.
## Instances
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L63
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L651

# IMMUTABLE ADDRESSES LACK ZERO-ADDRESS CHECK
Checking addresses against zero-address during initialization or during setting is a security best-practice.
## Instances
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L120

# NatSpec COMMENTS SHOULD BE INCREASED IN CONTRACTS
It's good practice to make sure Solidity contracts are fully annotated using NatSpec. Especially, in complex DeFi projects, clear comments about all functions, their arguments and returns is important for readability and auditability.

# STAKING CONTRACT `LQTYStaking.sol` SHOULD HAVE PAUSE/UNPAUSE FUNCTIONALITY
In case a hack occurs or an exploit is discovered, the team should be able to pause functionality until the necessary changes are made to the system. The deposits should be paused with Pause modifier.

# UNUSED VARIABLES
## Instances

- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L240
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L50

# UNUSED IMPORTS
## Instances
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQT

    mapping (address => uint256) public yieldingPercentage;
    mapping (address => uint256) public yieldingAmount;
    mapping (address => address) public yieldGenerator;
    mapping (address => uint256) public yieldClaimThreshold;

struct Yield {
	uint256 percentage;
	uint256 amount;
	address generator;
	uint256 claimThreshold;
}

StabilityPool.sol#L335, StabilityPool.sol#L380, CollateralConfig.sol#L63, ReaperVaultV2.sol#L651, ReaperVaultV2.sol#L120, StabilityPool.sol#L240, LQTYStaking.sol#L50

ReaperVaultV2.sol, LQTYStaking.sol, CollateralConfig.sol, StabilityPool.sol


ktg Analysis

# My approach taken in evaluating the codebase
I mainly focus on voting feature by analyzing how things work from
creating proposal to executing it. I also take deep look into how configurator
module works.

Lybra protocol takes extensive uses of Openzeppelin's libraries like `Governor`,
`TimelockController`, etc,...So I read these libraries carefully, test it
myself and see how the protocol uses these libraries, as this often causes
problems.

It takes quite sometime to set up the tests and understand how things work
because there are no tests and the document is not that detailed.

# About Lybra protocol architecture
I think the architecture is already properly designed. The protocol takes
extensive use of openzeppelin's libraries, which is a good idea comparing to
implementing them yourselves. The vulnerabilities I found usually involves
proper usage of these libraries.

# Access control risks
I decided to set aside a separate part to talk about Lybra protocols'
access control risk; because this is the part where I spent the most times
working on. This involves understanding Lybra's `LybraGovernance`, `LybraConfigurator`
modules and also openzeppelin's `Governor`, `TimelockController`,...
-   First of all, although the protocol is designed to be `governed by esLBR holders` through
    proposal process (create proposal, vote, queue, execute,..), there are many users having
    administration privileges:
    + In `GovernanceTimelock`:
        ```solidity
        _setRoleAdmin(DAO, GOV);
        _setRoleAdmin(TIMELOCK, GOV);
        _setRoleAdmin(ADMIN, GOV);
        _grantRole(DAO, address(this));
        _grantRole(DAO, msg.sender);
        _grantRole(GOV, msg.sender);
        ```
    the deployer `msg.sender` automatically have all the rights to grant/revoke
    `TIMELOCK`, `ADMIN`, `DAO` role (users with `DAO` role can directly call
    functions in `Configurator` and make changes without going through voting process).

    + The `proposers`,`executors` and

// Code block 1 (solidity):
_setRoleAdmin(DAO, GOV);
        _setRoleAdmin(TIMELOCK, GOV);
        _setRoleAdmin(ADMIN, GOV);
        _grantRole(DAO, address(this));
        _grantRole(DAO, msg.sender);
        _grantRole(GOV, msg.sender);


damoklov G

## Ondo Finance Audit Report

## Gas Optimizations

### Increments should be unchecked

In Solidity 0.8+, there’s a default overflow check on unsigned integers. It’s possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.

Also increments can be made pre-increment instead of post-increment, so `i++` would become `++i` where necessary.

https://github.com/ethereum/solidity/issues/10695

Instances include:

```solidity
CashManager._processRedemption(): for (uint256 i = 0; i < size; ++i);
CashManager._processRefund(): for (uint256 i = 0; i < size; ++i);
CashManager._checkAddressesKYC(): for (uint256 i = 0; i < size; ++i);

CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderReceiverFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

KYCRegistry.addKYCAddresses(): for (uint256 i = 0; i < length; i++);
KYCRegistry.removeKYCAddresses(): for (uint256 i = 0; i < length; i++);
```

The code would go from:

```solidity
for (uint256 i; i < numIterations; i++) {
 // ...
}
```

To:

```solidity
for (uint256 i; i < numIterations;) {
 // ...
 unchecked { ++i; }
}
```

### An array's length should be cached to save gas in for-loops

Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Caching the array length in the stack saves around 3 gas per iteration.

Instances include:

```solidity
CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

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

The code would go from:

```solidity
for (uint256 i; i < arr.length; ++i) {
 // ...
}
```

To:

```solidity
uint

// Code block 1 (solidity):
CashManager._processRedemption(): for (uint256 i = 0; i < size; ++i);
CashManager._processRefund(): for (uint256 i = 0; i < size; ++i);
CashManager._checkAddressesKYC(): for (uint256 i = 0; i < size; ++i);

CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderReceiverFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

KYCRegistry.addKYCAddresses(): for (uint256 i = 0; i < length; i++);
KYCRegistry.removeKYCAddresses(): for (uint256 i = 0; i < length; i++);

// Code block 2 (solidity):
for (uint256 i; i < numIterations; i++) {
 // ...
}

// Code block 3 (solidity):
for (uint256 i; i < numIterations;) {
 // ...
 unchecked { ++i; }
}

// Code block 4 (solidity):
CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderReceiverFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

// Code block 5 (solidity):
for (uint256 i; i < arr.length; ++i) {
 // ...
}

CashManager._processRedemption(): for (uint256 i = 0; i < size; ++i);
CashManager._processRefund(): for (uint256 i = 0; i < size; ++i);
CashManager._checkAddressesKYC(): for (uint256 i = 0; i < size; ++i);

CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderReceiverFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

KYCRegistry.addKYCAddresses(): for (uint256 i = 0; i < length; i++);
KYCRegistry.removeKYCAddresses(): for (uint256 i = 0; i < length; i++);

for (uint256 i; i < numIterations; i++) {
 // ...
}

for (uint256 i; i < numIterations;) {
 // ...
 unchecked { ++i; }
}

CashFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

CashKYCSenderReceiverFactory.multiexcall(): for (uint256 i = 0; i < exCallData.length; ++i);

for (uint256 i; i < arr.length; ++i) {
 // ...
}


igbinosuneric G

## UNUSED NAMED RETURNS

Using both named returns and a return statement isn't necessary. Removing unused named return variables can reduce gas usage and improve code clarity.

https://github.com//code-423n4/2024-01-salty/blob/main/src/dev/Utils.sol#L255-L282
```
function quoteAmountOut( IPools pools, IERC20[] memory tokens, uint256 amountIn ) external view returns (uint256 amountOut)
		{
		require( tokens.length >= 2, "Must have at least two tokens swapped" );

		IERC20 tokenIn = tokens[0];
		IERC20 tokenOut;

		for( uint256 i = 1; i < tokens.length; i++ )
			{
			tokenOut = tokens[i];

			(uint256 reserve0, uint256 reserve1) = pools.getPoolReserves(tokenIn, tokenOut);

			if ( reserve0 <= PoolUtils.DUST || reserve1 <= PoolUtils.DUST || amountIn <= PoolUtils.DUST )
				return 0;

			uint256 k = reserve0 * reserve1;

			// Determine amountOut based on amountIn and the reserves
			reserve0 += amountIn;
			amountOut = reserve1 - k / reserve0;

			tokenIn = tokenOut;
			amountIn = amountOut;
			}

		return amountOut;
		}

```
https://github.com//code-423n4/2024-01-salty/blob/main/src/dev/Utils.sol#L287-L314

```
function quoteAmountIn(  IPools pools, IERC20[] memory tokens, uint256 amountOut ) external view returns (uint256 amountIn)
		{
		require( tokens.length >= 2, "Must have at least two tokens swapped" );

		IERC20 tokenOut = tokens[ tokens.length - 1 ];
		IERC20 tokenIn;

		for( uint256 i = 2; i <= tokens.length; i++ )
			{
			tokenIn = tokens[ tokens.length - i];

			(uint256 reserve0, uint256 reserve1) = pools.getPoolReserves(tokenIn, tokenOut);

			if ( reserve0 <= PoolUtils.DUST || reserve1 <= PoolUtils.DUST || amountOut >= reserve1 || amountOut < PoolUtils.DUST)
				return 0;

			uint256 k = reserve0 * reserve1;

			// Determine amountIn based on amountOut and the reserves
			// Round up here to err on the side of caution
			amountIn = Math.ceilDiv( k, reserve1 - amountOut ) - reserve0;

			tokenOut = tokenIn;
			amountOut = amountIn;
			}

		return amountIn

function quoteAmountIn(  IPools pools, IERC20[] memory tokens, uint256 amountOut ) external view returns (uint256 amountIn)
		{
		require( tokens.length >= 2, "Must have at least two tokens swapped" );

		IERC20 tokenOut = tokens[ tokens.length - 1 ];
		IERC20 tokenIn;

		for( uint256 i = 2; i <= tokens.length; i++ )
			{
			tokenIn = tokens[ tokens.length - i];

			(uint256 reserve0, uint256 reserve1) = pools.getPoolReserves(tokenIn, tokenOut);

			if ( reserve0 <= PoolUtils.DUST || reserve1 <= PoolUtils.DUST || amountOut >= reserve1 || amountOut < PoolUtils.DUST)
				return 0;

			uint256 k = reserve0 * reserve1;

			// Determine amountIn based on amountOut and the reserves
			// Round up here to err on the side of caution
			amountIn = Math.ceilDiv( k, reserve1 - amountOut ) - reserve0;

			tokenOut = tokenIn;
			amountOut = amountIn;
			}

		return amountIn;
		}

// Code block 1 (unknown):
function quoteAmountOut( IPools pools, IERC20[] memory tokens, uint256 amountIn ) external view returns (uint256 amountOut)
		{
		require( tokens.length >= 2, "Must have at least two tokens swapped" );

		IERC20 tokenIn = tokens[0];
		IERC20 tokenOut;

		for( uint256 i = 1; i < tokens.length; i++ )
			{
			tokenOut = tokens[i];

			(uint256 reserve0, uint256 reserve1) = pools.getPoolReserves(tokenIn, tokenOut);

			if ( reserve0 <= PoolUtils.DUST || reserve1 <= PoolUtils.DUST || amountIn <= PoolUtils.DUST )
				return 0;

			uint256 k = reserve0 * reserve1;

			// Determine amountOut based on amountIn and the reserves
			reserve0 += amountIn;
			amountOut = reserve1 - k / reserve0;

			tokenIn = tokenOut;
			amountIn = amountOut;
			}

		return amountOut;
		}


Kaysoft Q

## [L-1] Lack of `deadline` check on add and removing liquidity.
There are 2 instances of this

- https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/Pools.sol#L140
- https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/Pools.sol#L170

The `ensureNotExpired` modifier is used on the `Pool.swap()` function but not used on the `addLiquidity(...)` and `removeLiquidity(...)` functions of Pool.sol

The `ensureNotExpired` modifier is used to protect transactions from being kept by the miners till it is profitable for them before execution especially in unstable market swings.

```
File: Pool.sol
modifier ensureNotExpired(uint deadline)
		{
		require(block.timestamp <= deadline, "TX EXPIRED");
		_;
		}
```

```
File: Pool.sol
function addLiquidity( ... ) external nonReentrant returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity){
...
}
```
However, the `ensureNotExpired` modifier is not added to the `addLiquidity(...)` and `removeLiquidity(...)` functions.

Impact: `removeLiquidity` and `addLiquidity` transactions can be "saved for later" for as long as possible until it incurs enough loss based on slippage.

Recommendation: add a `deadline` parameter and the `ensureNotExpired` modifier to the `addLiquidity(...)` and `removeLiquidity(...)` functions as it is done on the `swap(...)` function.
```
function addLiquidity( ..., deadline) external nonReentrant ensureNotExpired(deadline) returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity){
...
}
```

## [L-2] Use the available `private` `visibility specifier` instead of creating the `onlySameContract` modifier

There are 11 instances of this
- https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/Upkeep.sol#L95C2-L112C58
- https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/Upkeep.sol#L145C2-L231C43

Accordi

// Code block 1 (unknown):
File: Pool.sol
modifier ensureNotExpired(uint deadline)
		{
		require(block.timestamp <= deadline, "TX EXPIRED");
		_;
		}

// Code block 2 (unknown):
File: Pool.sol
function addLiquidity( ... ) external nonReentrant returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity){
...
}

// Code block 3 (unknown):
function addLiquidity( ..., deadline) external nonReentrant ensureNotExpired(deadline) returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity){
...
}

Pools.sol#L140, Pools.sol#L170, Upkeep.sol#L95-L2, Upkeep.sol#L145-L2


mgf15 G

## **G-1** | Possible Optimizations in increaseSystemDebt and decreaseSystemDebt

By using assembly, we can directly interact with the storage slot of the stored variable, allowing us to efficiently write and read address values in storage.

```diff

diff --git a/ActivePool.sol b/aActivePool.sol
index 40b6a1f..345d3dd 100644
--- a/ActivePool.sol
+++ b/aActivePool.sol
@@ -193,10 +197,13 @@ contract ActivePool is IActivePool, ERC3156FlashLender, ReentrancyGuard, BaseMat

     function increaseSystemDebt(uint256 _amount) external override {
         _requireCallerIsBOorCdpM();
-
+
         uint256 cachedSystemDebt = systemDebt + _amount;
-
-        systemDebt = cachedSystemDebt;
+        //@audit use assembly to update storge
+        assembly{
+            sstore(systemDebt.slot,cachedSystemDebt)
+        }
+        //systemDebt = cachedSystemDebt;
         emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
     }

@@ -206,10 +213,13 @@ contract ActivePool is IActivePool, ERC3156FlashLender, ReentrancyGuard, BaseMat

     function decreaseSystemDebt(uint256 _amount) external override {
         _requireCallerIsBOorCdpM();
-
-        uint256 cachedSystemDebt = systemDebt - _amount;
-
-        systemDebt = cachedSystemDebt;
+        uint256 cachedSystemDebt;
+        unchecked{
+            cachedSystemDebt = systemDebt - _amount;
+        }
+        assembly{
+            sstore(systemDebt.slot,cachedSystemDebt)
+        }
         emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
     }

```

> Gas diff

```diff

 |----------------------------------------------|-----------------|-------|--------|--------|---------|
 | Deployment Cost                              | Deployment Size |       |        |        |         |
 | Function Name                                | min             | avg   | median | max    | # calls |
-| increaseSystemDebt                           | 1777            | 2746  | 1777   | 21677  | 3078    |
+| increaseSystemDebt

## **G-2** | struct can be packed

Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.

to

[link](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/BorrowerOperations.sol#L97-L104)

// Code block 1 (diff):
diff --git a/ActivePool.sol b/aActivePool.sol
index 40b6a1f..345d3dd 100644
--- a/ActivePool.sol
+++ b/aActivePool.sol
@@ -193,10 +197,13 @@ contract ActivePool is IActivePool, ERC3156FlashLender, ReentrancyGuard, BaseMat

     function increaseSystemDebt(uint256 _amount) external override {
         _requireCallerIsBOorCdpM();
-
+
         uint256 cachedSystemDebt = systemDebt + _amount;
-
-        systemDebt = cachedSystemDebt;
+        //@audit use assembly to update storge
+        assembly{
+            sstore(systemDebt.slot,cachedSystemDebt)
+        }
+        //systemDebt = cachedSystemDebt;
         emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
     }

@@ -206,10 +213,13 @@ contract ActivePool is IActivePool, ERC3156FlashLender, ReentrancyGuard, BaseMat

     function decreaseSystemDebt(uint256 _amount) external override {
         _requireCallerIsBOorCdpM();
-
-        uint256 cachedSystemDebt = systemDebt - _amount;
-
-        systemDebt = cachedSystemDebt;
+        uint256 cachedSystemDebt;
+        unchecked{
+            cachedSystemDebt = systemDebt - _amount;
+        }
+        assembly{
+            sstore(systemDebt.slot,cachedSystemDebt)
+        }
         emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
     }


0x6980 Q

# 1. For functions, follow Solidity standard naming conventions (internal function style rule)
- [PrivatePoolMetadata.sol#L112](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePoolMetadata.sol#L112)
```js
File: src/PrivatePoolMetadata.sol

112:    function trait(string memory traitType, string memory value) internal pure returns (string memory) {
113:        // forgefmt: disable-next-item
114:        return string(
115:            abi.encodePacked(
116:                '{ "trait_type": "', traitType, '",', '"value": "', value, '" }'
117:            )
118:        );
119:    }
```
The above codes don’t follow Solidity’s standard naming convention,

internal and private functions : the mixedCase format starting with an underscore (_mixedCase starting with an underscore)
# 2. Floating pragma
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
- [EthRouter.sol#L2](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L2)
```js
File: src/EthRouter.sol

2:  pragma solidity ^0.8.19;
```
- [Factory.sol#L2](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L2)
```js
File: src/Factory.sol

2:  pragma solidity ^0.8.19;
```
- [PrivatePool.sol#L2](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L2)
```js
File: src/PrivatePool.sol

2:  pragma solidity ^0.8.19;
```
- [PrivatePoolMetadata.sol#L2](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePoolMetadata.sol#L2)
```js
File: src/PrivatePoolMetadata.sol

2:  pragma solidity ^0.8.19;
```
- [IStolen

// Code block 1 (js):
File: src/PrivatePoolMetadata.sol

112:    function trait(string memory traitType, string memory value) internal pure returns (string memory) {
113:        // forgefmt: disable-next-item
114:        return string(
115:            abi.encodePacked(
116:                '{ "trait_type": "', traitType, '",', '"value": "', value, '" }'
117:            )
118:        );
119:    }

// Code block 2 (js):
File: src/EthRouter.sol

2:  pragma solidity ^0.8.19;

// Code block 3 (js):
File: src/Factory.sol

2:  pragma solidity ^0.8.19;

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

2:  pragma solidity ^0.8.19;

// Code block 5 (js):
File: src/PrivatePoolMetadata.sol

2:  pragma solidity ^0.8.19;

PrivatePoolMetadata.sol#L112, EthRouter.sol#L2, Factory.sol#L2, PrivatePool.sol#L2, PrivatePoolMetadata.sol#L2

EthRouter.sol, Factory.sol, PrivatePool.sol, PrivatePoolMetadata.sol


orion Q

The contracts on the require() function uses very long revert messages, as example :

```
CashKYCSender: must be KYC'd to initiate transfer
CashKYCSender: `from` address must be KYC'd to send tokens
only the admin may call _resignImplementation
only the admin may call _becomeImplementation
```

...etc

However there's something about solidity which is that the longer is the revert message the more gas is being consumed
see : https://www.youtube.com/watch?v=59MRDldSItU

it's recommended to use less chars as revert messages, example "

```
!admin
from!=kyced
```

============

Function of CCashDelegate contract can all be avoided, the function of CCashDelegate are made just to ensure that. :

```
 require(
      msg.sender == admin,
      "only the admin may call _becomeImplementation"
    );
```

as both of the function have no other purpose but that, while this is just an extra code and all can be replaced,
_becomeImplementation and _resignImplementation can be made as modifiers to ensure `msg.sender == admin` or remove them as there is no logical action they are making

(Manual review)

// Code block 1 (unknown):
CashKYCSender: must be KYC'd to initiate transfer
CashKYCSender: `from` address must be KYC'd to send tokens
only the admin may call _resignImplementation
only the admin may call _becomeImplementation

// Code block 2 (unknown):
!admin
from!=kyced

// Code block 3 (unknown):
require(
      msg.sender == admin,
      "only the admin may call _becomeImplementation"
    );