McToady G

### [G-01] Structs in `FightersOps` can be better packed to save number of SSTOREs used when setting
The structs `FighterPhysicalAttributes` and `Fighter` can both be reorded and/or use smaller uint types to save users gas when these structs are saved in storage.

## FighterPhysicalAttributes
Currently when this struct is created for a fighter in `FighterFarm::_createNewFighter` it requires storing data a separate storage slot for each of the 6 fields of the array. However when the values of this struct struct are set in `AiArenaHelper::createPhysicalAttributes` the maximum a value will be set to is 99. Therefore a uint8 would be enough to store each fo these values meaning only one storage slot would be required.

## Fighter
The fighter struct can similarly be optimised for large gas savings.
As this struct includes the above `FighterPhysicalAttributes` struct, those changes will be part of the changes here too.
As for the other fields of the array:
`weight` has a range between 65-95, so this can safely be set as a uint8.
`element` is initialised to 3 for generation 1 so it seems probably that this number won't inflate past 255 in future rounds, so can also be set as a uint8. `id` is the token id of the fighter, it seems reasonable that this number will safely never supass the maximum uint16 (65,535).

The fields of the struct can then be reordered to ensure the minimum storage slots are used.
```solidity
    struct Fighter {
        uint8 weight;
        uint8 element;
        uint16 id;
        uint8 generation;
        uint8 iconsType;
        bool dendroidBool;
        FighterPhysicalAttributes physicalAttributes;
        string modelHash;
        string modelType;
    }
```

**Recommendation**

This results in the following gas savings when minting fighters:
```diff
    | Function Name      | min    | avg     | median  | max    | # calls |
-   | claimFighters      | 16108  | 350552  | 517775  | 517775 | 3       |
+   | claimFighters      | 16096  | 274958  | 4

## [G-02] `RankedBattle::claimFighters` could use a struct for `nftsClaimed` to reduce the number of SSTORE's required

Currently it requires two SSTOREs to update the number of fighters & dendroids minted by a user.
However it would be possible to reduce this to one by creating a `FightersMinted` struct.

See the following snippet from the original function:

// Code block 1 (solidity):
struct Fighter {
        uint8 weight;
        uint8 element;
        uint16 id;
        uint8 generation;
        uint8 iconsType;
        bool dendroidBool;
        FighterPhysicalAttributes physicalAttributes;
        string modelHash;
        string modelType;
    }


hihen G

# Gas Report

## Summary

Total **722 instances** over **46 issues**with **358776** gas saved:

|ID|Issue|Instances|Gas|
|:--:|:---|:--:|:--:|
| [[G‑01]](#g01-use-storage-instead-of-memory-for-structsarrays) | Use `storage` instead of `memory` for structs/arrays | 1 | 4200 |
| [[G‑02]](#g02-constructors-can-be-marked-as-payable-to-save-deployment-gas) | Constructors can be marked as payable to save deployment gas | 19 | 399 |
| [[G‑03]](#g03-state-variables-only-set-in-their-definitions-should-be-declared-constant) | State variables only set in their definitions should be declared `constant` | 12 | 25164 |
| [[G‑04]](#g04-splitting-require-statements-that-use-) | Splitting `require()` statements that use `&&` | 8 | 24 |
| [[G‑05]](#g05-unnecessary-event-parameters-should-be-removed) | Unnecessary event parameters should be removed | 3 | 1074 |
| [[G‑06]](#g06-unnecessary-ternary-operator) | Unnecessary ternary operator | 3 | 54 |
| [[G‑07]](#g07-use-unchecked-block-for-safe-subtractions) | Use `unchecked` block for safe subtractions | 19 | 1615 |
| [[G‑08]](#g08-comparing-booleans-to-true-or-false) | Comparing booleans to `true` or `false` | 1 | 9 |
| [[G‑09]](#g09-using-constants-directly-is-more-gas-efficient) | Using `constant`s directly is more gas efficient | 1 | - |
| [[G‑10]](#g10-do-not-cache-state-variables-that-are-used-only-once) | Do not cache state variables that are used only once | 4 | 12 |
| [[G‑11]](#g11-do-not-calculate-constants) | Do not calculate constants | 1 | - |
| [[G‑12]](#g12-use-of-emit-inside-a-loop) | Use of `emit` inside a loop | 1 | 375 |
| [[G‑13]](#g13-initializing-mutable-state-variables-with-default-value-wastes-gas) | Initializing mutable state variables with default value wastes gas | 1 | 2900 |
| [[G‑14]](#g14-use-local-variables-for-emitting) | Use local variables for emitting | 6 | 600 |
| [[G‑15]](#g15-contract-storage-ca

522:             Node memory _node = data.nodes[_id];

46:     constructor(
47:         address _borrowerOperationsAddress,
48:         address _cdpManagerAddress,
49:         address _collTokenAddress,
50:         address _collSurplusAddress,
51:         address _feeRecipientAddress
52:     ) {


Rolezn G

## Summary

### Gas Optimizations
| |Issue|Contexts|Estimated Gas Saved|
|-|:-|:-|:-:|
| [GAS‑1](#GAS‑1) | Setting the `constructor` to `payable` | 4 | 52 |
| [GAS‑2](#GAS‑2) | Duplicated `require()`/`revert()` Checks Should Be Refactored To A Modifier Or Function | 2 | 56 |
| [GAS‑3](#GAS‑3) | Empty Blocks Should Be Removed Or Emit Something | 4 | - |
| [GAS‑4](#GAS‑4) | Using `delete` statement can save gas | 3 | - |
| [GAS‑5](#GAS‑5) | Functions guaranteed to revert when called by normal users can be marked `payable` | 17 | 357 |
| [GAS‑6](#GAS‑6) | Use hardcoded address instead `address(this)` | 22 | - |
| [GAS‑7](#GAS‑7) | Optimize names to save gas | 3 | 66 |
| [GAS‑8](#GAS‑8) | `<x> += <y>` Costs More Gas Than `<x> = <x> + <y>` For State Variables | 4 | - |
| [GAS‑9](#GAS‑9) | Public Functions To External | 9 | - |
| [GAS‑10](#GAS‑10) | Save gas with the use of specific import statements | 22 | - |
| [GAS‑11](#GAS‑11) | Using `unchecked` blocks to save gas | 6 | 120 |
| [GAS‑12](#GAS‑12) | Use functions instead of modifiers | 1 | 100 |
| [GAS‑13](#GAS‑13) | Use solidity version 0.8.19 to gain some gas boost | 4 | 352 |
| [GAS‑14](#GAS‑14) | Save loop calls | 3 | - |

Total: 104 contexts over 14 issues

## Gas Optimizations

### [GAS‑1] Setting the `constructor` to `payable`

Saves ~13 gas per instance

#### Proof Of Concept

```solidity
38: constructor()
```

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

```solidity
33: constructor()
```

https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts/SafEth/derivatives/Reth.sol#L33

```solidity
27: constructor()
```

https://github.com/code-423n4/2023-03-asymmetry/tree/main/contracts/Sa

// Code block 1 (solidity):
38: constructor()

// Code block 2 (solidity):
33: constructor()

// Code block 3 (solidity):
27: constructor()

38: constructor()

33: constructor()

27: constructor()


peanuts Q

### [L-01] Initializers can be frontrunnable

If the initializer is not executed in the same transaction as the constructor, a malicious user can front-run the initialize() call, forcing the contract to be redeployed.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166-L176

### [L-02] Contract should adhere to two-step ownership process

Setting the owner within one function can be dangerous if the address of the owner in the parameter is not set properly. Consider using a two-step process whereby the owner has to acknowledge the change before changing the ownership.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109-L125

### [L-03] Unused/Empty receive()/fallback() function

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth)). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L550

### [L-04] OpenZeppelin's ECDSA is imported but not utilized

Use OpenZeppelin’s ECDSA contract rather than calling ecrecover() directly

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L347

### [L-05] Import exact functions instead of the whole contract itself

Example:
```
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
```
instead of
```
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.s

// Code block 1 (unknown):
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

SmartAccount.sol#L166-L176, SmartAccount.sol#L109-L125, SmartAccount.sol#L550, SmartAccount.sol#L347


c3phas G

### Table of Contents
- [FINDINGS](#findings)
- [Cache storage values in memory to minimize SLOADs](#cache-storage-values-in-memory-to-minimize-sloads)
  - [SafEth.sol.stake(): derivativeCount being a storage variable should not be looped into as it's too costly,consider caching it](#safethsolstake-derivativecount-being-a-storage-variable-should-not-be-looped-into-as-its-too-costlyconsider-caching-it)
  - [SafEth.sol.unstake(): derivativeCount should be cached especially because it's being used in a for loop](#safethsolunstake-derivativecount-should-be-cached-especially-because-its-being-used-in-a-for-loop)
  - [SafEth.sol.rebalanceToWeights(): derivativeCount should be cached](#safethsolrebalancetoweights-derivativecount-should-be-cached)
  - [SafEth.sol.addDerivative(): derivativeCount should be cached](#safethsoladdderivative-derivativecount-should-be-cached)
- [Emitting storage values instead of the memory one.](#emitting-storage-values-instead-of-the-memory-one)
- [Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead](#usage-of-uintsints-smaller-than-32-bytes-256-bits-incurs-overhead)
- [Using unchecked blocks to save gas](#using-unchecked-blocks-to-save-gas)
- [`keccak256()` should only need to be called on a specific string literal once](#keccak256-should-only-need-to-be-called-on-a-specific-string-literal-once)
- [Functions guaranteed to revert when called by normal users can be marked `payable`](#functions-guaranteed-to-revert-when-called-by-normal-users-can-be-marked-payable)

## FINDINGS
NB: Some functions have been truncated where necessary to just show affected parts of the code
Through out the report some places might be denoted with audit tags to show the actual place affected.

## Cache storage values in memory to minimize SLOADs
The code can be optimized by minimizing the number of SLOADs.

SLOADs are expensive (100 gas after the 1st one) compared to MLOADs/MSTOREs (3 gas each). Storage values read multiple times should instead be ca

File: /contracts/SafEth/SafEth.sol
63:    function stake() external payable {

70:        // Getting underlying value in terms of ETH for each derivative
71:        for (uint i = 0; i < derivativeCount; i++)

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

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L108-L129
### SafEth.sol.unstake(): derivativeCount should be cached especially because it's being used in a for loop


koxuan Q

# Report

## Non Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 7 |
| [NC-2](#NC-2) | Constants in comparisons should appear on the left side | 31 |
| [NC-3](#NC-3) | delete keyword can be used instead of setting to 0 | 9 |
| [NC-4](#NC-4) | Lines are too long | 74 |
| [NC-5](#NC-5) |  `require()` / `revert()` statements should have descriptive reason strings | 3 |
| [NC-6](#NC-6) | Return values of `approve()` not checked | 14 |
| [NC-7](#NC-7) | Event is missing `indexed` fields | 59 |
| [NC-8](#NC-8) | Constants should be defined rather than using magic numbers | 17 |
| [NC-9](#NC-9) | Functions not used internally could be marked external | 47 |
| [NC-10](#NC-10) | Variable names don't follow the Solidity style guide | 9 |
| [NC-11](#NC-11) | Variables need not be initialized to zero | 2 |
### [NC-1] Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

*Instances (7)*:
```solidity
File: lybra/configuration/LybraConfigurator.sol

76:     bytes32 public constant DAO = keccak256("DAO");

77:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

78:     bytes32 public constant ADMIN = keccak256("ADMIN");

```

```solidity
File: lybra/governance/GovernanceTimelock.sol

10:     bytes32 public constant DAO = keccak256("DAO");

11:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

12:     bytes32 public constant ADMIN = keccak256("ADMIN");

13:     bytes32 public constant GOV = keccak256("GOV");

```

### [NC-2] Constants in comparisons should appear on the left side
Constants should appear on the left side of comparisons, to avoid accidental assignment

*I

// Code block 1 (solidity):
File: lybra/configuration/LybraConfigurator.sol

76:     bytes32 public constant DAO = keccak256("DAO");

77:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

78:     bytes32 public constant ADMIN = keccak256("ADMIN");

// Code block 2 (solidity):
File: lybra/governance/GovernanceTimelock.sol

10:     bytes32 public constant DAO = keccak256("DAO");

11:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

12:     bytes32 public constant ADMIN = keccak256("ADMIN");

13:     bytes32 public constant GOV = keccak256("GOV");

File: lybra/configuration/LybraConfigurator.sol

76:     bytes32 public constant DAO = keccak256("DAO");

77:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

78:     bytes32 public constant ADMIN = keccak256("ADMIN");

File: lybra/governance/GovernanceTimelock.sol

10:     bytes32 public constant DAO = keccak256("DAO");

11:     bytes32 public constant TIMELOCK = keccak256("TIMELOCK");

12:     bytes32 public constant ADMIN = keccak256("ADMIN");

13:     bytes32 public constant GOV = keccak256("GOV");


TarunRao0 Q

## [L-6] Additional Instance of Division before Multiplication

**Description** :

Found another instance where multiplication is performed on the result of division in `PoolMath::_zapSwapAmount`

**Impact** :
Precision loss from rounding errors

**Proof of Code** :

```javascript
uint256 C = r0 * ( r1 * z0 - r0 * z1 ) / ( r1 + z1 );
```

*github permalink*:

https://github.com/code-423n4/2024-01-salty/blob/6a9942091392866c674104dd4b7a12a2d14fcbf5/src/pools/PoolMath.sol#L192

// Code block 1 (javascript):
uint256 C = r0 * ( r1 * z0 - r0 * z1 ) / ( r1 + z1 );


joaovwfreire Q

## lo-01 LRTConfig doesn't remove supported assets
The contract only allows the manager to add new supported assets by calling addNewSupportedAsset, but there is not function to remove an asset.

## lo-02 No upper limits on the amount of supported assets can lead to DOS
[Reference](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L80-L89)
### Impact
The lack of upper bounds on the amount of supported assets can lead to getRSETHPrice reversion and lock the contract's minting functionalities.
### Proof of concept
There's no upper limit on the amount of supported assets that can be added at the LRTConfig contract.
```solidity
    function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);

    }
```

If too many assets are supported, getRSETHPrice at the LRTOracle will revert due to the following code snippet, that represents a loop which iterations amount is determined by the total supported assets:
```solidity
       uint256 supportedAssetCount = supportedAssets.length;
        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);
            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;
            unchecked {
                ++asset_idx;
            }
        }
```
### Mitigation
Introduce a check on the total amount of supported assets in order to protect the function from running loops iterate too many times.

## lo-03 getAssetPrice at the ChainlinkPriceOracle contract assumes all Chainlink Dat

// Code block 1 (solidity):
function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);

    }

// Code block 2 (solidity):
uint256 supportedAssetCount = supportedAssets.length;
        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);
            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;
            unchecked {
                ++asset_idx;
            }
        }

function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true;
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);

    }

uint256 supportedAssetCount = supportedAssets.length;
        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);
            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;
            unchecked {
                ++asset_idx;
            }
        }


excalibor G

# Gas Report
**All gas tables are gotten from the repositories tests, and they do not implement the general design changes unless stated otherwise**
## General Design
1. For loops we should always write them as such
```solidity
for(uint256 i; i<cachedLength;) {
	// Logic
	unchecked { ++i;}
}
```
for example [here](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/USDA.sol#L48C4-L53C6) we don't use pre incrementing which is marginally cheaper then `i++`

2. When storing a storage variable that is being set with `keccak256()` we can compute the `keccak256()` and just put the hash in manually to save deployment gas for example [this](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/USDA.sol#L21) can be turned into this `bytes32  public  constant VAULT_CONTROLLER_ROLE =  0x995a5d6a7cac103b73a94cfaaf03645dcc7d6197e2cb553c21c01213f18592e9;`

3. If we are ever incrementing a counter value we should always pre increment it, for example [here](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L258C8-L258C8) we should also always wrap are counter variable increments in an `unchecked` box, because it is not feasible for them to ever overflow.
4.  We should not emit storage variables in event, we should cache the value and emit the cache for example [here](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L290C2-L290C2)
5. Due to the nature of the solidity compiler and how it compiles the opcodes, we should put functions that we expect our user to call most often at the top of the function session of the file, this will save some gas.

## Findings
1. The storage variables `cvxRewardFee` and `crvRewardFee` found [here](https://github.com/code-423n4/2023-07-amp

  /// @dev Percentage of rewards taken in CVX (1e18 == 100%)
  uint128 public cvxRewardFee;
  /// @dev Percentage of rewards taken in CRV (1e18 == 100%)
  uint128 public crvRewardFee;

// Code block 1 (solidity):
for(uint256 i; i<cachedLength;) {
	// Logic
	unchecked { ++i;}
}

USDA.sol#L48-L4, USDA.sol#L21, VaultController.sol#L258-L8, VaultController.sol#L290-L2


wahedtalash77 G

|     |
| --- |
| \[G-01\] abi.encode() is less efficient than abi.encodePacked() |
| \[G-02\] Events are not indexed |
| \[G-03\] Make 3 event parameters indexed when possible |
| \[G-04\] Expressions for constant values such as a call to `keccak256()`, should use immutable rather than constant |
| \[G-05\] Do not calculate constants |
| \[G-06\]  `10 ** 27` can be changed to `1e27` and save some gas |
| \[G‑07\] Avoid contract existence checks by using low level calls |
| \[G-08\] Make fewer external calls. |
| \[G-09\] Use assembly for math (add, sub, mul, div) |

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

In terms of efficiency, abi.encodePacked() is generally considered to be more gas-efficient than abi.encode(), because it skips the step of adding function signatures and other metadata to the encoded data. However, this comes at the cost.

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

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

## <ins>\[G-02\] Events are not indexed</ins>

The emitted events are not indexed, making off-chain scripts such as front-ends of dApps difficult to filter the events efficiently.

Recommend adding the `indexed` keyword in each event,

```
event ApproverRemoved(address approver);

  /**
   * @notice event emitted when an address is added as an approver
   *
   * @param approver  The address to add
   */
  event ApproverAdded(address approver);

  /**
   * @notice event emitted when a new contract is whitelisted as an approved
   *         message passer.
   *
   * @param srcChain        The chain for the approved address
   * @param approvedSource  The address corresponding to the source bridge contract
   */
  event ChainIdSupported(string srcChain, string approvedSource);

  /**
   * @notice event emitted when a threshold has been set
   *
   * @param chain           The chain for which the t

event ApproverRemoved(address approver);

  /**
   * @notice event emitted when an address is added as an approver
   *
   * @param approver  The address to add
   */
  event ApproverAdded(address approver);

  /**
   * @notice event emitted when a new contract is whitelisted as an approved
   *         message passer.
   *
   * @param srcChain        The chain for the approved address
   * @param approvedSource  The address corresponding to the source bridge contract
   */
  event ChainIdSupported(string srcChain, string approvedSource);

  /**
   * @notice event emitted when a threshold has been set
   *
   * @param chain           The chain for which the threshold was set
   * @param amounts         The amount of tokens to reach this threshold
   * @param numOfApprovers  The number of approvals needed
   */
  event ThresholdSet(string chain, uint256[] amounts, uint256[] numOfApprovers);

  /**
   * @notice event emitted when the user has been minted their tokens on the dst chain
   *
   * @param user    The recipient address of the newly minted tokens
   * @param amount  The amount of tokens that have been minted
   */
  event BridgeCompleted(address user, uint256 amount);

// Code block 1 (unknown):
bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);


Kaysoft G

## [G-1] Refactor to avoid emitting already available data in the transaction - block.timestamp.

Refactor code to remove `block.timestamp` from the emitted events because it is already available in the transaction.
This would save loading data into memory (potentially avoiding memory expansion costs) and Glogdata (8 gas) * bytes emitted.

Files: Almost all events emitted in the codebase contain block.timestamp which is not necessary.
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraRETHVault.sol#L38
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L139
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L149
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L106
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L97
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L146
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L214
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraRETHVault.sol#L38
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraStETHVault.sol#L89
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraWbETHVault.sol#L31
- https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraWstETHVault.sol#L45
-

emit DepositEther(msg.sender, address(collateralAsset), msg.value, balance - preBalance, block.timestamp);

function notifyRewardAmount(uint256 amount, uint256 tokenType) external {
        require(msg.sender == address(configurator));
        if (totalStaked() == 0) return;
        require(amount > 0, "amount = 0"); //@audit require statement should be at the top
        if (tokenType == 0) {
            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);
            rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked();
        } else if (tokenType == 1) {
            ERC20 token = ERC20(configurator.stableToken());
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked();
        } else {
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked();
        }
    }

LybraRETHVault.sol#L38, LybraConfigurator.sol#L139, LybraConfigurator.sol#L149, ProtocolRewardsPool.sol#L106, ProtocolRewardsPool.sol#L97, ProtocolRewardsPool.sol#L146, ProtocolRewardsPool.sol#L214, LybraStETHVault.sol#L89, LybraWbETHVault.sol#L31, LybraWstETHVault.sol#L45

LybraConfigurator.sol, LybraWstETHVault.sol, ProtocolRewardsPool.sol, LybraRETHVault.sol, LybraStETHVault.sol, LybraWbETHVault.sol


KrisApostolov G

## Low severity findings

## Table of contents

|  | Issue | Instances |
| --- | --- | --- |
| [L-01] | mulDivUp always produces a more precise output than division | 1 |
| [L-02] | No 0 length check for tokenIds arrays leads to strange behavior | 9 |

## [L-01] **`mulDivUp`** always produces a more precise output than division

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

Replace the aforementioned line with the following:

```solidity
FixedPointMathLib.mulDivUp(inputAmount, virtualBaseTokenReserves, (virtualNftReserves+ inputAmount));
```

For smaller quantities, this line of code will consistently generate greater precision. Moreover, the addition of 1 will always favor the pool.

## [L-02] No 0 length check for **`tokenIds`** arrays leads to strange behavior

Calling the following

[https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211-L289](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211-L289)

[https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L301-L373](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L301-L373)

[https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L385-L452](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L385-L452)

[https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L484-L507](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L484-L507)

[https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L514-L532](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L514-L532)

with empty arrays will not result in errors and will instead emit their corresponding events with empty payloads.

The following

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

EthRouter.sol

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

// Code block 1 (solidity):
FixedPointMathLib.mulDivUp(inputAmount, virtualBaseTokenReserves, (virtualNftReserves+ inputAmount));

PrivatePool.sol#L719, PrivatePool.sol#L211-L289, PrivatePool.sol#L301-L373, PrivatePool.sol#L385-L452, PrivatePool.sol#L484-L507, PrivatePool.sol#L514-L532, EthRouter.sol#L99-L144


klau5 Q

# `TransferShares` event parameter is not correct

## Links to affected code

[https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L439](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L439)

## Impact

Emit `TransferShares`  with wrong value at `wrap` function.

## Proof of Concept

`TransferShares` should emit `sharesValue` . But at `wrap` function, `TransferShares` emits `_USDYAmount` .

```solidity
event TransferShares(
  address indexed from,
  address indexed to,
  uint256 sharesValue
);

function wrap(uint256 _USDYAmount) external whenNotPaused {
  require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
  _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
  usdy.transferFrom(msg.sender, address(this), _USDYAmount);
  emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
  emit TransferShares(address(0), msg.sender, _USDYAmount);
}
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

```diff
function wrap(uint256 _USDYAmount) external whenNotPaused {
  require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
  _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
  usdy.transferFrom(msg.sender, address(this), _USDYAmount);
  emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
- emit TransferShares(address(0), msg.sender, _USDYAmount);
+ emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR);
}
```

# Remove useless inheritance

## Links to affected code

[https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L21](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L21)

[https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L59](https://github.com/code-423n4/2023-09-

# Remove useless inheritance

## Links to affected code

[https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L21](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L21)

[https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L59](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L59)

## Impact

Imports and inherits useless contract

## Proof of Concept

rUSDY contract imports and inherits ContextUpgradeable contract, but there is no usage of this. There is no code using `_msgData()` or `_msgSender()` . This only increase deploy gas, so it would be better to remove unused import/inheritance.

// Code block 1 (solidity):
event TransferShares(
  address indexed from,
  address indexed to,
  uint256 sharesValue
);

function wrap(uint256 _USDYAmount) external whenNotPaused {
  require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
  _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
  usdy.transferFrom(msg.sender, address(this), _USDYAmount);
  emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
  emit TransferShares(address(0), msg.sender, _USDYAmount);
}

// Code block 2 (diff):
function wrap(uint256 _USDYAmount) external whenNotPaused {
  require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
  _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
  usdy.transferFrom(msg.sender, address(this), _USDYAmount);
  emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
- emit TransferShares(address(0), msg.sender, _USDYAmount);
+ emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR);
}


matrix_0wl Q

# Summary

## Low Risk and Non-Critical Issues

## Non-Critical Issues

|       | Issue                                                                                |
| ----- | :----------------------------------------------------------------------------------------------------- |
| NC-1  | ADD TO INDEXED PARAMETER FOR COUNTABLE EVENTS                                                          |
| NC-2  | ADD A TIMELOCK TO CRITICAL FUNCTIONS                                                                   |
| NC-3  | BE EXPLICIT DECLARING TYPES                                                                            |
| NC-4  | GENERATE PERFECT CODE HEADERS EVERY TIME                                                               |
| NC-5  | SAME CONSTANT REDEFINED ELSEWHERE                                                                      |
| NC-6  | USE A SINGLE FILE FOR ALL SYSTEM-WIDE CONSTANTS                                                        |
| NC-7  | SOLIDITY COMPILER VERSIONS MISMATCH                                                                    |
| NC-8  | SIGNATURE MALLEABILITY OF EVM’S `ECRECOVER()`                                                          |
| NC-9  | FOR MODERN AND MORE READABLE CODE; UPDATE IMPORT USAGES                                                |
| NC-10 | MARK VISIBILITY OF `INITIALIZE(…)` FUNCTIONS AS EXTERNAL                                               |
| NC-11 | CONSTANT VALUES SUCH AS A CALL `TO KECCAK256()`, SHOULD USE IMMUTABLE RATHER THAN CONSTANT             |
| NC-12 | LACK OF EVENT EMISSION AFTER CRITICAL `INITIALIZE()` FUNCTIONS                                         |
| NC-13 | LACK OF CHECKS SUPPORTSINTERFACE                                                                       |
| NC-14 | LARGE MULTIPLES OF TEN SHOULD USE SCIENTIFIC NOTATION                                                  |
| NC-15 | FOR EXTENDED “USING-FOR” USAGE, USE THE LATEST PRAGMA VERSION

File: Ethos-Core/contracts/ActivePool.sol

62:     event ActivePoolLUSDDebtUpdated(address _collateral, uint _LUSDDebt);

File: Ethos-Core/contracts/ActivePool.sol

71:     function setAddresses(

125:     function setYieldingPercentage(address _collateral, uint256 _bps) external onlyOwner {

132:     function setYieldingPercentageDrift(uint256 _driftBps) external onlyOwner {

138:     function setYieldClaimThreshold(address _collateral, uint256 _threshold) external onlyOwner {

144:     function setYieldDistributionParams(uint256 _treasurySplit, uint256 _SPSplit, uint256 _stakingSplit) external onlyOwner {


MohammedRizwan Q

## Summary

### Low Risk Issues
|Number|Issue|Instances| |
|-|:-|:-:|:-:|
| [L‑01] | GrantFund.fundTreasury( ) function does not check sender token balance | 1 |
| [L‑02] | For immutable variables, Zero address checks are missing in constructor | 2 |
| [L‑03] | In PositionManager.sol, modifier mayInteract( ) lacks validation checks | 1 |

### Non-Critical Issues
|Number|Issue|Instances| |
|-|:-|:-:|:-:|
| [N‑01] | Use named parameters for mapping type declarations | 19 |
| [N‑02] | Solidity compiler version should be exactly same in all smart contracts | All Contracts |
| [N‑03] | Use a more recent version of Solidity | All Contracts |

### Low Risk Issues
### [L‑01]  GrantFund.fundTreasury( ) function does not check sender token balance
In GrantFund.sol, fundTreasury( ) function does not check whether the sender has token balance to transfer to treasury or not. It even does not check minimum balance or zero value check which can prevent wastage of gas to users.

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

58    function fundTreasury(uint256 fundingAmount_) external override {
59        IERC20 token = IERC20(ajnaTokenAddress);
60
61        // update treasury accounting
62        treasury += fundingAmount_;
63
64        emit FundTreasury(fundingAmount_, treasury);
65
66        // transfer ajna tokens to the treasury
67        token.safeTransferFrom(msg.sender, address(this), fundingAmount_);
68    }
```
[Link to code](https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/GrantFund.sol#L58-L68)

### Recommended Mitigation steps

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

    function fundTreasury(uint256 fundingAmount_) external override {
+       require(fundingAmount_ > 0, "Funding amount must be greater than 0");
        IERC20 token = IERC20(ajnaTokenAddress);

+       require(token.balanceOf(msg.sender) >= fundingAmount_, "Insuffici

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

    function fundTreasury(uint256 fundingAmount_) external override {
+       require(fundingAmount_ > 0, "Funding amount must be greater than 0");
        IERC20 token = IERC20(ajnaTokenAddress);

+       require(token.balanceOf(msg.sender) >= fundingAmount_, "Insufficient token balance");

        // update treasury accounting
        treasury += fundingAmount_;

        emit FundTreasury(fundingAmount_, treasury);

        // transfer ajna tokens to the treasury
        token.safeTransferFrom(msg.sender, address(this), fundingAmount_);
    }

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

58    function fundTreasury(uint256 fundingAmount_) external override {
59        IERC20 token = IERC20(ajnaTokenAddress);
60
61        // update treasury accounting
62        treasury += fundingAmount_;
63
64        emit FundTreasury(fundingAmount_, treasury);
65
66        // transfer ajna tokens to the treasury
67        token.safeTransferFrom(msg.sender, address(this), fundingAmount_);
68    }


matrix_0wl Q

## Non Critical Issues

|       | Issue                                                                                |
| ----- | :----------------------------------------------------------------------------------------------------- |
| NC-1  | Be explicit declaring types                                                                            |
| NC-2  | Boolean equality                                                                                |
| NC-3  | GENERATE PERFECT CODE HEADERS EVERY TIME                                                               |
| NC-4  | USE A SINGLE FILE FOR ALL SYSTEM-WIDE CONSTANTS                                                        |
| NC-5  | INCONSISTENT SOLIDITY VERSIONS                                                                         |
| NC-6  | Use camel case for all functions, parameters and variables and snake case for constants                |
| NC-7  | LARGE MULTIPLES OF TEN SHOULD USE SCIENTIFIC NOTATION                                                  |
| NC-8  | FOR EXTENDED “USING-FOR” USAGE, USE THE LATEST PRAGMA VERSION                                          |
| NC-9  | NO SAME VALUE INPUT CONTROL                                                                            |
| NC-10 | Add parameter to event-emit                                                                            |
| NC-11 | SOLIDITY COMPILER OPTIMIZATIONS CAN BE PROBLEMATIC                                                     |
| NC-12 | USE A MORE RECENT VERSION OF SOLIDITY                                                                  |
| NC-13 | `require()` / `revert()` statements should have descriptive reason strings                             |
| NC-14 | FOR FUNCTIONS AND VARIABLES FOLLOW SOLIDITY STANDARD NAMING CONVENTIONS                                |
| NC-15 | Functions not used internally could be marked external                                                 |
| NC-16 | VARIABLE NA

File: contracts/lybra/miner/EUSDMiningIncentives.sol

94:         for (uint i = 0; i < _pools.length; i++) {

138:         for (uint i = 0; i < pools.length; i++) {

140:             uint borrowed = pool.getBorrowedOf(user);

153:         uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;

154:         uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;

File: contracts/lybra/miner/ProtocolRewardsPool.sol

31:     uint public rewardPerTokenStored;

33:     mapping(address => uint) public userRewardPerTokenPaid;

35:     mapping(address => uint) public rewards;

36:     mapping(address => uint) public time2fullRedemption;

37:     mapping(address => uint) public unstakeRatio;

38:     mapping(address => uint) public lastWithdrawTime;

167:     function earned(address _account) public view returns (uint) {

191:         uint reward = rewards[msg.sender];

227:     function notifyRewardAmount(uint amount, uint tokenType) external {

227:     function notifyRewardAmount(uint amount, uint tokenType) external {


JrNet Q

# [L-01] `removeLiquidity()` always emit 0 for tokenId

```solidity
    FILE: 2023-08-dopex/contracts/amo/UniV3LiquidityAmo.sol

    263: delete positions_mapping[pos.token_id];

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

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

# [N-01] Check implemented twice to check role
```solidity
  FILE: 2023-08-dopex/contracts/decaying-bonds/RdpxDecayingBonds.sol
  118: ) external onlyRole(MINTER_ROLE) {
  119:  _whenNotPaused();
  120:  require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
```

[RdpxDecayingBonds.sol#L118-L120](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/decaying-bonds/RdpxDecayingBonds.sol#L118-L120)

// Code block 1 (solidity):
FILE: 2023-08-dopex/contracts/amo/UniV3LiquidityAmo.sol

    263: delete positions_mapping[pos.token_id];

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

    268: emit log(positions_array.length);
    269: emit log(positions_mapping[pos.token_id].token_id); // @audit emitting deleted mapping value

// Code block 2 (solidity):
FILE: 2023-08-dopex/contracts/decaying-bonds/RdpxDecayingBonds.sol
  118: ) external onlyRole(MINTER_ROLE) {
  119:  _whenNotPaused();
  120:  require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");

FILE: 2023-08-dopex/contracts/amo/UniV3LiquidityAmo.sol

    263: delete positions_mapping[pos.token_id];

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

    268: emit log(positions_array.length);
    269: emit log(positions_mapping[pos.token_id].token_id); // @audit emitting deleted mapping value

FILE: 2023-08-dopex/contracts/decaying-bonds/RdpxDecayingBonds.sol
  118: ) external onlyRole(MINTER_ROLE) {
  119:  _whenNotPaused();
  120:  require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");


castle_chain Analysis

#  bridge overview
## Architecture improvments
## 1) implement a minimum limit to amount of token burnt on the source chain .
the protocol use a bridge between two chains , source and destination chain .
On the destination chain, there is a limit for the amount of tokens that can be minted over a specified duration, causing the mint function to revert if this limit is exceeded. Conversely, the **source chain imposes no such limit**, allowing users to burn and bridge an unlimited quantity of tokens without reversion. As a result, if a user burns and bridges tokens exceeding the destination chain's minting limit, the call will revert on the destination chain or the message will reaches the destination chain but the user will not get his minted tokens even if the number of approvers exceeds the threshold so the user will not be able to mint his tokens because of the limit on minting amount of tokens .
### **Recommendation**
implement the same limit of minting and burning tokens on the source and destination chains to prevent such an issue .

## 2) pre-determined quantity of gas
The primary issue revolves around the lack of predetermined gas limits, which allows users to specify any gas amount, leading to transaction failures and unrecoverable token burns.
 according to [axelar docs](https://docs.axelar.dev/dev/general-message-passing/monitoring) , the way to recover the transaction if it has an insufficient gas issue is to use the UI or the SDK which need the transaction hash to determine which transaction to be recovered , but it will not be a good solution to force the user to moniter the transaction maually , and it is better to predetermine the gas amount to get it from the user
### **Recommendations**
 1) using external oracle such as [chainLink fast gas oracle](https://data.chain.link/ethereum/mainnet/gas/fast-gas-gwei) which provide the gas price on chain and will allow the protocol to pre-determine the gas cost and take them from the user , which will red

These relayer services are a free, operational convenience Axelar provides , anyone can create his own relayer

currentPrice = (Range.dailyInterestRate ** (Days Elapsed + 1)) * Range.lastSetPrice


Sathish9098 Q

# LOW FINDINGS
## LOW
| | Issues| Instances|
|---------|-----|--------|
| [L-1]    | UNUSED RECEIVE() functions  | 4|
| [L-2]    | Imports can be grouped together   | 3|
| [L-3]    | Sanity/Threshold/Limit Checks  | 6|
| [L-4]    | LOSS OF PRECISION DUE TO ROUNDING  | 9|
| [L-5]    | A single point of failure  | 17|
| [L-6]    | Use safe variant of _mint() function  | 1|
| [L-7]    | No Storage Gap for SafEth contract  | 7|
| [L-8]    | Prevent division by 0  | 5|
| [L-9]    | Consider using OpenZeppelin’s SafeCast library to prevent unexpected behavior when casting uint  | 1|
| [L-10]    | abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()  | 6|
| [L-11]    | Missing Event for critical parameters init and change   | 4|
| [L-12]    | Gas griefing/theft is possible on unsafe external call  | 3|
| [L-13]    | Front running attacks by the onlyOwner  | 2|
| [L-14]    | Function Calls in Loop Could Lead to Denial of Service  | 4|
| [L-15]    | Use safeTransferOwnership instead of transferOwnership function  | 4|

##
## NON CRITICAL

| | Issues| Instances|
|---------|-----|--------|
| [NC-1]    | For modern and more readable code; update import usages  | 34   |
| [NC-2]    | Imports can be grouped together  | -  |
| [NC-3]    | AVOID HARDCODED VALUES  | 11   |
| [NC-4]    | Shorter the inheritance   | 1   |
| [NC-5]    | EMPTY BLOCKS SHOULD BE REMOVED OR EMIT SOMETHING  | 5   |
| [NC-6]    | ADD PARAMETER TO EVENT-EMIT  | 1   |
| [NC-7]    | Pragma float  | 7   |
| [NC-8]    | Interchangeable usage of uint and uint256  | 4   |
| [NC-9]    | TYPOS  | 1   |
| [NC-10]    | Include (@param and @return) parameters in NatSpec comments  | -   |
| [NC-11]    | NatSpec comments should be increased in contracts  | -   |
| [NC-12]    | Function writing that does not comply with the Solidity Style Guide  | -   |
| [NC-13]    | Add a timelock to critical functions  | 4   |
| [NC-14]    | For functions, follow Solidi


0x1f8b G

- [Gas](#gas)
    - [**1. Remove unnecessary variables**](#1-remove-unnecessary-variables)
    - [**2. Optimize internalIncrementDeposit**](#2-optimize-internalincrementdeposit)
    - [**3. Optimize unlockStake**](#3-optimize-unlockstake)
    - [**4. Reorder structure layout**](#4-reorder-structure-layout)
    - [**5. Use the unchecked keyword**](#5-use-the-unchecked-keyword)

# Gas

## **1. Remove unnecessary variables**

The following state variables can be removed without affecting the logic of the contract since they are not used and/or are redundant because they could be used inline.

**Affected source code:**

- In the following case is used, but also is added: [VerifyingSingletonPaymaster.sol:99](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L99)
- [BasePaymaster.sol:50](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L50)
- [VerifyingSingletonPaymaster.sol:124](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L124)

## **2. Optimize `internalIncrementDeposit`**

It's possible to optimize the `internalIncrementDeposit` method like follows:

```diff
-   function internalIncrementDeposit(address account, uint256 amount) internal {
+   function internalIncrementDeposit(address account, uint256 amount) internal returns (uint112 ret) {
        DepositInfo storage info = deposits[account];
        uint256 newAmount = info.deposit + amount;
        require(newAmount <= type(uint112).max, "deposit overflow");
+       ret = uint112(newAmount);
+       info.deposit = ret;
-       info.deposit = uint112(newAmount);
    }
    function depositTo(addre

**Gas diff:**

In red the old version, in green with the applied changes:

**Affected source code:**

- [StakeManager.sol:38-43](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L38-L43)

## **3. Optimize `unlockStake`**

You can use `withdrawTime != 0` as a `staked` flag, so you could remove that field from the `DepositInfo` structure, saving space and gas.

**Affected source code:**

- [StakeManager.sol:86](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L86)

## **4. Reorder structure layout**

The following structures could be optimized moving the position of certain values in order to save some storage slots:

VerifyingSingletonPaymaster.sol#L99, BasePaymaster.sol#L50, VerifyingSingletonPaymaster.sol#L124, StakeManager.sol#L38-L43, StakeManager.sol#L86


owadez Q

From the `LRTDepositPool` contract , the amount sent should be checked and revert early if not enough.
```
    function transferAssetToNodeDelegator(
        uint256 ndcIndex,
        address asset,
        uint256 amount
    )
        external
        nonReentrant
        onlyLRTManager
        onlySupportedAsset(asset)
    {
        address nodeDelegator = nodeDelegatorQueue[ndcIndex];
        if (!IERC20(asset).transfer(nodeDelegator, amount)) {
            revert TokenTransferFailed();
        }
    }
```
The following can be added to achieve the result
```
require(IERC20(asset).balanceOf(address(this) > 0),"error")
```

// Code block 1 (unknown):
function transferAssetToNodeDelegator(
        uint256 ndcIndex,
        address asset,
        uint256 amount
    )
        external
        nonReentrant
        onlyLRTManager
        onlySupportedAsset(asset)
    {
        address nodeDelegator = nodeDelegatorQueue[ndcIndex];
        if (!IERC20(asset).transfer(nodeDelegator, amount)) {
            revert TokenTransferFailed();
        }
    }

// Code block 2 (unknown):
require(IERC20(asset).balanceOf(address(this) > 0),"error")


ck Q

## Pausing functionality can be done with just one function

There are currently separate functions for pausing and unpausing

```solidity
   function setPauseStaking(bool _pause) external onlyOwner {
        pauseStaking = _pause;
        emit StakingPaused(pauseStaking);
    }

    /**
        @notice - Owner only function that enables/disables the unstake function
        @param _pause - true disables unstaking / false enables unstaking
    */
    function setPauseUnstaking(bool _pause) external onlyOwner {
        pauseUnstaking = _pause;
        emit UnstakingPaused(pauseUnstaking);
    }
```

This is unnecessary as one function is enough, the `stakingPaused` variable alone can be used to check were staking is paused or unpaused.

## Validate minimum and maximum values when setting slippage

```solidity
    function setMaxSlippage(uint256 _slippage) external onlyOwner {
        maxSlippage = _slippage;
    }
```

Currently there are no restrictions on the amount of slippage that can be set. To prevent unintended effects, the input should be validated for acceptable minimum and maximum slippage.

## Validate that `totalWeight` should never be 0

```soldity
  function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        emit WeightChange(_derivativeIndex, _weight);
    }
```

When the weights are being set or adjusted, it would be good practice to ensure the `totalWeight` isn't 0 in a scenario where either by malice or mistake, all weights are set to 0.

## Validate that the `_contractAddress` of a derivative cannot be set to 0

```solidity
    function addDerivative(
        address _contractAddress,
        uint256 _weight
    ) external onlyOwner {
        derivatives[derivativeCount] = ID

// Code block 1 (solidity):
function setPauseStaking(bool _pause) external onlyOwner {
        pauseStaking = _pause;
        emit StakingPaused(pauseStaking);
    }

    /**
        @notice - Owner only function that enables/disables the unstake function
        @param _pause - true disables unstaking / false enables unstaking
    */
    function setPauseUnstaking(bool _pause) external onlyOwner {
        pauseUnstaking = _pause;
        emit UnstakingPaused(pauseUnstaking);
    }

// Code block 2 (solidity):
function setMaxSlippage(uint256 _slippage) external onlyOwner {
        maxSlippage = _slippage;
    }

// Code block 3 (soldity):
function adjustWeight(
        uint256 _derivativeIndex,
        uint256 _weight
    ) external onlyOwner {
        weights[_derivativeIndex] = _weight;
        uint256 localTotalWeight = 0;
        for (uint256 i = 0; i < derivativeCount; i++)
            localTotalWeight += weights[i];
        totalWeight = localTotalWeight;
        emit WeightChange(_derivativeIndex, _weight);
    }

function setPauseStaking(bool _pause) external onlyOwner {
        pauseStaking = _pause;
        emit StakingPaused(pauseStaking);
    }

    /**
        @notice - Owner only function that enables/disables the unstake function
        @param _pause - true disables unstaking / false enables unstaking
    */
    function setPauseUnstaking(bool _pause) external onlyOwner {
        pauseUnstaking = _pause;
        emit UnstakingPaused(pauseUnstaking);
    }

function setMaxSlippage(uint256 _slippage) external onlyOwner {
        maxSlippage = _slippage;
    }


nemveer G

## 1. Can set the owner in the state itself instead of calling the external function every time
The current implementation of PrivatePool retrieves the owner by calling the Factory.ownerOf(address(PrivatePool))
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L127-L132
```
    modifier onlyOwner() virtual {
        if (msg.sender != Factory(factory).ownerOf(uint160(address(this)))) {
            revert Unauthorized();
        }
        _;
    }
```
But it can be set in initialization itself in the state of the contract which saves the extra gas cost of calling an external contract.

## 2. Common memory variables created in each iteration can be set outside of the loop
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L329-L352
```
        for (uint256 i = 0; i < tokenIds.length; i++) {
            // transfer each nft from the caller
            ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]);

            if (payRoyalties) {
                // calculate the sale price (assume it's the same for each NFT even if weights differ)
                uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length;

                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);

                // tally the royalty fee amount
                royaltyFeeAmount += royaltyFee;

                // transfer the royalty fee to the recipient if it's greater than 0
                if (royaltyFee > 0 && recipient != address(0)) {
                    if (baseToken != address(0)) {
                        ERC20(baseToken).safeTransfer(recipient, royaltyFee);
                    } else {
                        recipient.safeTransferETH(royaltyFee);
                    }
                }
            }
        }
```
Here `salePrice` is c

// Code block 1 (unknown):
modifier onlyOwner() virtual {
        if (msg.sender != Factory(factory).ownerOf(uint160(address(this)))) {
            revert Unauthorized();
        }
        _;
    }

// Code block 2 (unknown):
for (uint256 i = 0; i < tokenIds.length; i++) {
            // transfer each nft from the caller
            ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]);

            if (payRoyalties) {
                // calculate the sale price (assume it's the same for each NFT even if weights differ)
                uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length;

                // get the royalty fee for the NFT
                (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);

                // tally the royalty fee amount
                royaltyFeeAmount += royaltyFee;

                // transfer the royalty fee to the recipient if it's greater than 0
                if (royaltyFee > 0 && recipient != address(0)) {
                    if (baseToken != address(0)) {
                        ERC20(baseToken).safeTransfer(recipient, royaltyFee);
                    } else {
                        recipient.safeTransferETH(royaltyFee);
                    }
                }
            }
        }


EvanW G

[G-01] Merge the two for-loop as one in stake()

In SafEth.sol, the stake() method is running the same for-loop twice which is unecessary. Do all the things in 1 loop could save 132 gas.

```solidity
contracts/SafEth/SafEth.sol
71: for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

84: 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;
        }
```

Recommandation:

```solidity
for (uint i = 0; i < derivativeCount; i++) {
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight != 0) {
	            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;
						};
        }
```

[G-02] zero value checking of “_safEthAmount”

Add a require statement for checkin

// Code block 1 (solidity):
contracts/SafEth/SafEth.sol
71: for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

84: 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;
        }

// Code block 2 (solidity):
for (uint i = 0; i < derivativeCount; i++) {
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight != 0) {
	            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;
						};
        }

contracts/SafEth/SafEth.sol
71: for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

84: 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;
        }

for (uint i = 0; i < derivativeCount; i++) {
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight != 0) {
	            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;
						};
        }


DadeKuma Q

## Summary

---

### Low Risk Issues
|Id|Title|Instances|
|:--:|:-------|:--:|
|[L-01]| Revert by undeflow when extraordinary proposals exceeds the maximum amount | 1 |
|[L-02]| Wasteful/redundant operations when calculating the votes squared | 1 |
|[L-03]| Anyone can `memorialize` other users' position if the owner approves `PositionManager` | 1 |

Total: 3 instances over 3 issues.

### Non Critical Issues
|Id|Title|Instances|
|:--:|:-------|:--:|
|[NC-01]| `newTopSlate_` condition can be simplified | 1 |

Total: 1 instances over 1 issues.

## Low Risk Issues

---

### [L-01] Revert by undeflow when extraordinary proposals exceeds the maximum amount

Add a custom revert when proposals exceed the maximum, as it's possible to send only 9 proposals, to avoid an underflow error. Check if `_getMinimumThresholdPercentage() >= Maths.WAD`, and revert if that's the case.

*There is 1 instance of this issue.*

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

105: 		        if (uint256(totalTokensRequested) > _getSliceOfTreasury(Maths.WAD - _getMinimumThresholdPercentage())) revert InvalidProposal();

```
[ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L105](https://github.com/code-423n4/2023-05-ajna/blob/a51de1f0119a8175a5656a2ff9d48bbbcb4436e7/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L105)

---

### [L-02] Wasteful/redundant operations when calculating the votes squared

The power of `n` with an exponent of 2 is always positive, regardless of `n` (it can be either positive or negative), so calculating the absolute value is wasteful (`Maths.abs`)

*There is 1 instance of this issue.*

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

849: 		            votesCastSumSquared_ += Maths.wpow(SafeCast.toUint256(Maths.abs(votesCast_[i].votesUsed)), 2);

```
[ajna-grants/src/grants/base/StandardFunding.sol#L849](https://github.com/code-423n4/2023-05-ajna/blob/a51de1f0119a8175a5656a2ff9d48bbbcb4436e7/ajna-grants/src/gr

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

105: 		        if (uint256(totalTokensRequested) > _getSliceOfTreasury(Maths.WAD - _getMinimumThresholdPercentage())) revert InvalidProposal();

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

849: 		            votesCastSumSquared_ += Maths.wpow(SafeCast.toUint256(Maths.abs(votesCast_[i].votesUsed)), 2);

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

105: 		        if (uint256(totalTokensRequested) > _getSliceOfTreasury(Maths.WAD - _getMinimumThresholdPercentage())) revert InvalidProposal();

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

849: 		            votesCastSumSquared_ += Maths.wpow(SafeCast.toUint256(Maths.abs(votesCast_[i].votesUsed)), 2);


Diana G

## G-01 Increments can 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

Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of under- or overflow leading to widespread use of libraries that introduce additional checks.

Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, thus making the use of these libraries unnecessary.

To obtain the previous behaviour, an unchecked block can be used. It saves **30-40 gas** per loop.

_There are 5 instances of this issue_

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

```
File: contracts/smart-contract-wallet/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++) {
128: for (uint256 a = 0; a < opasLen; a++) {
134: for (uint256 i = 0; i < opslen; i++) {
```

---------------

## G-02 x += y costs more gas than x = x + y for state variables (same applies for x -= y , x = x - y ; x \*=y ; x /= y)

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

_There are 40 instances of this issue_

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

```
File: contracts/smart-contract-wallet/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();
```

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-walle

// Code block 1 (unknown):
File: contracts/smart-contract-wallet/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++) {
128: for (uint256 a = 0; a < opasLen; a++) {
134: for (uint256 i = 0; i < opslen; i++) {

// Code block 2 (unknown):
File: contracts/smart-contract-wallet/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();


hunter_w3b G

## [G-01] Add unchecked{} for subtraction where the operands can't underflow becasue of previous IF-STATEMENT

**Issue Description**\
The subtraction operation `uint256 remainingSALT = claimedSALT - amountToSendToTeam;` does not have an unchecked block, even though underflow is not possible due to the previous check `if ( claimedSALT == 0 )` return. Adding an unchecked block helps avoid unnecessary checks.

**Proposed Optimization**\
Add an unchecked block around the subtraction:

```solidity
unchecked {
  uint256 remainingSALT = claimedSALT - amountToSendToTeam;
}
```

**Estimated Gas Savings**\
Adding an unchecked block for a subtraction that cannot underflow due to a previous check saves approximately 3 gas per subtraction. With many such subtractions occurring across interactions, the total gas savings could be significant.

**Attachments**

- **Code Snippets**

```solidity
File: src/dao/DAO.sol

337		if ( claimedSALT == 0 ) // @audit  Because of this check can't overflow   remainingSALT

345		uint256 remainingSALT = claimedSALT - amountToSendToTeam;
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/DAO.sol#L337

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

110		if ( addedAmountA < maxAmountA )
111			tokenA.safeTransfer( msg.sender, maxAmountA - addedAmountA );

113		if ( addedAmountB < maxAmountB )
114			tokenB.safeTransfer( msg.sender, maxAmountB - addedAmountB );
```

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

```solidity
File: src/pools/PoolMath.sol

161		if ( maximumMSB > 80 )
162			shift = maximumMSB - 80;
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolMath.sol#L161

```solidity
File: src/price_feed/PriceAggregator.sol

101		if ( x > y )
102			return x - y;
103
104		return y - x;
105		}
```

https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/PriceAggregator.sol#L101

```solidity
File: src/rewards/RewardsConfig.sol

40            if (rewardsEmitterDaily

// Code block 1 (solidity):
unchecked {
  uint256 remainingSALT = claimedSALT - amountToSendToTeam;
}

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

337		if ( claimedSALT == 0 ) // @audit  Because of this check can't overflow   remainingSALT

345		uint256 remainingSALT = claimedSALT - amountToSendToTeam;

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

110		if ( addedAmountA < maxAmountA )
111			tokenA.safeTransfer( msg.sender, maxAmountA - addedAmountA );

113		if ( addedAmountB < maxAmountB )
114			tokenB.safeTransfer( msg.sender, maxAmountB - addedAmountB );

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

161		if ( maximumMSB > 80 )
162			shift = maximumMSB - 80;

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

101		if ( x > y )
102			return x - y;
103
104		return y - x;
105		}

unchecked {
  uint256 remainingSALT = claimedSALT - amountToSendToTeam;
}

File: src/dao/DAO.sol

337		if ( claimedSALT == 0 ) // @audit  Because of this check can't overflow   remainingSALT

345		uint256 remainingSALT = claimedSALT - amountToSendToTeam;

File: src/staking/Liquidity.sol

110		if ( addedAmountA < maxAmountA )
111			tokenA.safeTransfer( msg.sender, maxAmountA - addedAmountA );

113		if ( addedAmountB < maxAmountB )
114			tokenB.safeTransfer( msg.sender, maxAmountB - addedAmountB );

File: src/pools/PoolMath.sol

161		if ( maximumMSB > 80 )
162			shift = maximumMSB - 80;

File: src/price_feed/PriceAggregator.sol

101		if ( x > y )
102			return x - y;
103
104		return y - x;
105		}

DAO.sol#L337, Liquidity.sol#L110, PoolMath.sol#L161, PriceAggregator.sol#L101

PoolMath.sol, Liquidity.sol, PriceAggregator.sol, DAO.sol


Shahen Q

## Make only the _borrower or _positionManager to call the `permitPositionManagerApproval()` function.

- So in the `permitPositionManagerApproval()` function, It's to grant approval for a positionManager, Suppose the (_borrower,_positionManager) calls the `permitPositionManagerApproval()` function with the parameters, Anyone who watches the mempool could frontrun the function call, Ofcourse this is not a medium or any critical issue but when someone else frontruns the function call, the nonce will be incremented and when the actual (_borrower,_positionManager) call gets processed it'll return him the error `"BorrowerOperations: Invalid signature"`. I mean he might not know the approval was granted to the positionManager already because of the frontrun call. As i have mentioned this is not a big issue but its better to have a require in the function that only the _borrower or _positionManager address can call so no one else will be able to mess as said above.

```
require(msg.sender == _borrower); // or _positionManager
```

https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/BorrowerOperations.sol#L706

// Code block 1 (unknown):
require(msg.sender == _borrower); // or _positionManager


popeye Analysis

| Serial No. | Topic                                            |
|------------|--------------------------------------------------|
| 01         | Overview of Revert Lend                          |
| 02         | Scope                                            |
| 03         | Architecture view (Sequence Diagram)                      |
| 04         | Protocol Roles                                   |
| 05         | Approach Taken in auditing Revert Lend           |
| 06         | Contract Analysis                                |
| 07         | Codebase Quality (Table)                         |
| 08         | Centralization Risks                             |
| 09         | Systematic Risks                                 |
| 10         | Architectural Improvement                        |
| 11         | Time spent                                       |

# Overview of Revert Lend
Revert Lend is a decentralized lending protocol designed specifically for Automated Market Maker (AMM) Liquidity Providers (LPs) on Uniswap v3. The protocol allows LPs to collateralize their Uniswap v3 LP positions, represented as NFTs, and obtain loans in a specified ERC-20 token. The key feature of Revert Lend is that it enables LPs to maintain control over their capital in the Uniswap v3 pools while simultaneously using their positions as collateral for loans.

`Collateralization of Uniswap v3 LP Positions:` LPs can deposit their Uniswap v3 LP position NFTs into the protocol's Collateralized Debt Position (CDP) vaults. Each LP position is treated as a separate collateral, allowing for granular risk management and loan accounting.

`Borrowing and Loan Management:` LPs can borrow tokens from the lending pool by collateralizing their LP positions. Loans are subject to a minimum collateral ratio, determined by the collateral factors of the underlying tokens in the LP position. Interest rates for loans are dynamically adjusted based on the supply and demand within the lending pool.

`Flexi

# Protocol Roles:

Revert Lend protocol involves several key roles/actors, each with specific responsibilities and interactions within the system.

**Liquidity Providers (LPs):**
   - LPs are Uniswap v3 liquidity providers who supply liquidity to Uniswap v3 pools and receive LP position NFTs in return.
   - In the context of Revert Lend, LPs can collateralize their LP position NFTs to borrow tokens from the lending pool.
   - LPs retain control over their collateralized positions and can manage them by adjusting liquidity, collecting fees, or changing the position's price range.
     ```solidity
     // LP deposits LP position NFT into the CDP vault
     function create(uint256 tokenId, address recipient) external {
         nonfungiblePositionManager.safeTransferFrom(msg.sender, address(this), tokenId, abi.encode(recipient));
     }
     ```

**Borrowers:**
   - Borrowers are users who borrow tokens from the Revert Lend lending pool by collateralizing their LP position NFTs.
   - They are subject to maintaining a minimum collateral ratio based on the collateral factors of the underlying tokens in their LP positions.
   - Borrowers can borrow up to a certain percentage of their collateral value and are obligated to repay the borrowed amount along with accrued interest.
     ```solidity
     // Borrower borrows tokens against the collateralized position
     function borrow(uint256 tokenId, uint256 amount) external {
         require(positionOwner[tokenId] == msg.sender, "Not position owner");
         require(isPositionHealthy(tokenId, amount), "Insufficient collateral");

         loanAmount[tokenId] += amount;
         IERC20(borrowToken).safeTransfer(msg.sender, amount);
     }
     ```

**Lenders:**
   - Lenders are users who supply tokens to the Revert Lend lending pool to earn interest.
   - They receive rTokens (e.g., rUSDC) in exchange for their supplied assets, representing their share of the lending pool.
   - Lenders can redeem their rTokens to w

**Key function's functionality:**

a. `create(uint256 tokenId, address recipient)`:
This function creates a new collateralized position by transferring an approved Uniswap V3 LP position (NFT) to the vault. The `tokenId` represents the LP position, and the `recipient` is the address that will receive the position in the vault. The function adds the token to the owner's list of owned tokens and initializes a new loan with zero debt shares.


8olidity Q

## The maxSlippage should be limited

The administrator can set the value of `maxSlippage` through the `setMaxSlippage()` function, but there is no validation here. If `maxSlippage` is greater than `10 ** 18`, it will cause an overflow and make it impossible to  `withdraw()`. This problem exists in several contracts.

```
uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;

```

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

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

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

```

Suggestion

```
function setMaxSlippage(uint256 _slippage) external onlyOwner {
    require(_slippage <= 10 ** 18);
    maxSlippage = _slippage;
}

```

## Code and comments do not match

In the `safeETH::adjustWeight()` function, the function should update the value corresponding to `_derivativeIndex` in the weights array and calculate the new `totalWeight` value.

However, the comment for the function is

```
/**
  @notice - Adds new derivative to the index fund
	.....
*/

```

This comment is the same as the comment in the `addDerivative()` function.

Suggestion

Revise the comment.

// Code block 1 (unknown):
uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;

// Code block 2 (unknown):
contracts/SafEth/derivatives/Reth.sol:
  57      */
  58:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
  59          maxSlippage = _slippage;

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

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

// Code block 3 (unknown):
function setMaxSlippage(uint256 _slippage) external onlyOwner {
    require(_slippage <= 10 ** 18);
    maxSlippage = _slippage;
}

// Code block 4 (unknown):
/**
  @notice - Adds new derivative to the index fund
	.....
*/


supersizer0x Q

###### An attacker can grief users  with large amounts of funds
An attacker can grief hard by depositing funds before a call is made making the
example:
price = 100 eth = 50 rseth
attacker deposits 25 eth
price =100 eth = 35 rseth
There is no slippage check so it can harm users.
```solidity
 rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
```
###### If there are some delegators that are not contracts it will revert
The sponsor says that there will be delegators to transfer the funds to the strategy.
If an admin adds an eoa  to the [delegators array](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L81C5-L84C101) it will revert
```solidity
    uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);
```
###### Their can be an issue with a user transferring x token but getting the price for x,y,b tokens
Since the rsETh price is computed using all the supported assets, if some of them are overpriced or underpriced it will affect how many eth are in the contract which can cause txs to fail when the price rises or when it lowers more funds can be deposited.
###### comment is wrong the function is called by Admin not the [manager](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L160)
```solidity
/// @notice add new node delegator contract addresses/// @dev only callable by LRT manager/// @param nodeDelegatorContracts Array of NodeDelegator contract addresses function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {
```
###### Their is no check for the heartbeat of the oracle which can return stale prices
Most of the supported tokens, upda

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

// Code block 2 (solidity):
uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);

// Code block 3 (solidity):
/// @notice add new node delegator contract addresses/// @dev only callable by LRT manager/// @param nodeDelegatorContracts Array of NodeDelegator contract addresses function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {

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

uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);

/// @notice add new node delegator contract addresses/// @dev only callable by LRT manager/// @param nodeDelegatorContracts Array of NodeDelegator contract addresses function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {


Kaysoft Q

## [L-01] REMOVE UNSED IMPORT `./Dependencies/console.sol` in `BorrowerOperations.sol`
console.sol is imported in `BorrowerOperations.sol` and it is not used.
File:
- [Ethos-Core/contracts/BorrowerOperations.sol#L15](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/BorrowerOperations.sol#L15)

#### Recommended Mitigation Steps
Consider removing unused import of console.sol in `BorrowerOperations.sol`

## [L-02] UNUSED VARIABLES.
Remove all unused variables.
Files:
- [Ethos-Core/contracts/StabilityPool.sol#L384](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L384)
- [Ethos-Core/contracts/StabilityPool.sol#L339](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/StabilityPool.sol#L339)

## [L-03] USE LATEST COMPILER STABLE VERSION
The EThos-Core project uses the Solidity version 0.6.11 while the Ethos-Vault version uses the version 0.80. It is best practice to use the latest Solidity stable version as there will have been some security bug fixes and updates.
see: https://swcregistry.io/docs/SWC-102

Files: All files

#### Recommended Mitigation Step
Consider using latest Solidity stable compiler version 0.8.17 for all the contracts.

## [L-04] AVOID FLOATING PRAGMA
Consider locking compiler versions by using `pragma solidity 0.8.17;` instead of `pragma solidity ^0.8.0;` in order not to deploy the contract with a different compiler version that is used to test the contract thereby introducing compiler bugs.
see: https://swcregistry.io/docs/SWC-103
Files:
All files in the Ethos-Vault project

#### Recommended Mitigation Steps
Consider locking the pragma version by replacing `pragma solidity ^0.8.0;` with `pragma solidity 0.8.17;`

## [L-05] ENABLING SOLIDITY COMPILER OPTIMIZATION CAN BE BUGGY
The compiler optimization is enabled in the project since it is set to true.

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    compilers: [
      {
        version: "0.8.11",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200,
          },
        },
      },
    ],
  },

 /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }


RaymondFam G

## Combining for loops in `buy()` of PrivatePool
Like it has been implemented in [`sell()`](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L328-L351), consider combining the two [for loops](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L242) involving [`if (payRoyalties)`](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L271) in [`buy()`](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211-L289) to save gas both on function calls and contract size.

## Identical arithmetic operation can be cached
In PrivatePool, the two identical arithmetic operations of `buy()` can be cached into a local variable to save gas on function call as follows:

[File: PrivatePool.sol#L229-L237](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L229-L237)

```diff
+        trimmedNetInputAmount = netInputAmount - feeAmount - protocolFeeAmount;

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

        // ~~~ Interactions ~~~ //

        // calculate the sale price (assume it's the same for each NFT even if weights differ)
-        uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length;
+        uint256 salePrice = trimmedNetInputAmount / tokenIds.length;
        uint256 royaltyFeeAmount = 0;
```
## Unneeded `flashFeeToken()`
`baseToken` is already evidently used in [line 635](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L635) and [line 651](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L651) of `flashLoan()`. Additionally, `baseToken` comes with a free [public getter](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L82). Hence, consider r

## Unneeded `flashFeeToken()`
`baseToken` is already evidently used in [line 635](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L635) and [line 651](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L651) of `flashLoan()`. Additionally, `baseToken` comes with a free [public getter](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L82). Hence, consider removing the unneeded [`flashFeeToken()`](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L754-L757) to reduce the contract size.

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

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

[File: PrivatePool.sol#L742-L746](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L742-L746)

## += and -= cost more gas
`+=` and `-=` generally cost 22 more gas than writing out the assigned equation explicitly. The amount of gas wasted can be quite sizable when repeatedly operated in a loop.

For example, the `+=` instance below may be refactored as follows:

[File: PrivatePool.sol#L247](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L247)

// Code block 1 (diff):
+        trimmedNetInputAmount = netInputAmount - feeAmount - protocolFeeAmount;

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

        // ~~~ Interactions ~~~ //

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

PrivatePool.sol#L328-L351, PrivatePool.sol#L242, PrivatePool.sol#L271, PrivatePool.sol#L211-L289, PrivatePool.sol#L229-L237, PrivatePool.sol#L635, PrivatePool.sol#L651, PrivatePool.sol#L82, PrivatePool.sol#L754-L757, PrivatePool.sol#L742-L746, PrivatePool.sol#L247


castle_chain G

# Gas optimization report
# DestinationBridge

## 1) unnecessary check inside the for loop that waste gas can be optimized .
the function `setThresholds()` uses for loop to assign the thresholds to the amounts , the for loop always check
```
  for (uint256 i = 0; i < amounts.length; ++i) {
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }
```
, and this condition will be true only once in the loop , so it can be executed outside of the loop to avoid unnecessary check and save much gas .
**the optimized code should be**
```diff
  function setThresholds(
    string calldata srcChain,
    uint256[] calldata amounts,
    uint256[] calldata numOfApprovers
  ) external onlyOwner {
    if (amounts.length != numOfApprovers.length) {
      revert ArrayLengthMismatch();
    }
    delete chainToThresholds[srcChain];

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

```
**code snippet**

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

## 2) read from the storage and make a copy of the struct to the memory before the check will waste gas in case the check did not pass .

the function `_mintIfThresholdMet()` read from the storage to get the `Transaction` struct corresponding to the `txnHash` and then create a copy on t

**code snippet**

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

## 2) read from the storage and make a copy of the struct to the memory before the check will waste gas in case the check did not pass .

the function `_mintIfThresholdMet()` read from the storage to get the `Transaction` struct corresponding to the `txnHash` and then create a copy on the memory , then checks if the threshold is met or not  , in case of the threshold has not been met yet ,the function will not use the copy of the struct , so it is considered wasting gas .

// Code block 1 (unknown):
for (uint256 i = 0; i < amounts.length; ++i) {
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }

// Code block 2 (diff):
function setThresholds(
    string calldata srcChain,
    uint256[] calldata amounts,
    uint256[] calldata numOfApprovers
  ) external onlyOwner {
    if (amounts.length != numOfApprovers.length) {
      revert ArrayLengthMismatch();
    }
    delete chainToThresholds[srcChain];

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


lanyi2023 Q

```
function vote( bool voteStartExchangeYes, bytes calldata signature ) external nonReentrant
```
After block.timestamp >= completionTimestamp, you can continue to call the vote function to vote

// Code block 1 (unknown):
function vote( bool voteStartExchangeYes, bytes calldata signature ) external nonReentrant