PNS Q

# Low Risk

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

```solidity
File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }
```
```solidity
File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }
```
```solidity
File: src/PrivatePool.sol
175:         baseToken = _baseToken;
176:         nft = _nft;
```

## Consider `isContract` checks in constructors
Several addresses are assigned in the contract constructors and assigned to immutable variables. A successful deployment is sensitive to these addresses being assigned correctly for the current network, and that addresses were specified in the correct order. Consider adding checks, as aggressively as possible for the use case, to help ensure the deployment configuration is correct.
`.isContract()` is referring to the [OZ Address library](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L40) or similar implementation.

```solidity
File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }
```
```solidity
File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }
```

# Non-Critical

## `public` functions not called by the contract should be declared `external` instead
```solidity
File: src/EthRouter.sol
99:     function buy(Buy[] calldata buys, uint256 deadline, bool payRoyalties) public payable {
...
152:     function sell(Sell[] calldata sells, uint256 minOutputAmount, uin

// Code block 1 (solidity):
File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }

// Code block 2 (solidity):
File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }

// Code block 3 (solidity):
File: src/PrivatePool.sol
175:         baseToken = _baseToken;
176:         nft = _nft;

// Code block 4 (solidity):
File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }

// Code block 5 (solidity):
File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }

File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }

File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }

File: src/PrivatePool.sol
175:         baseToken = _baseToken;
176:         nft = _nft;

File: src/EthRouter.sol
90:     constructor(address _royaltyRegistry) {
91:         royaltyRegistry = _royaltyRegistry;
92:     }

File: src/PrivatePool.sol
143:     constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) {
144:         factory = payable(_factory);
145:         royaltyRegistry = _royaltyRegistry;
146:         stolenNftOracle = _stolenNftOracle;
147:     }


BRONZEDISC Q

## QA
---

### Layout Order [1]

- The best-practices for layout within a contract is the following order: state variables, events, modifiers, constructor and functions.

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol

```solidity
72:    modifier updateReward(address _account) {
```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol

```solidity
// modifier should come before constructor
178:    modifier updateReward(address account) {
```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/stakerewardV2pool.sol

```solidity
// modifier coming after constructor
56:    modifier updateReward(address _account) {
```

---

### Function Visibility [2]

- Order of Functions: Ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. Functions should be grouped according to their visibility and ordered: constructor, receive function (if exists), fallback function (if exists), public, external, internal, private. Within a grouping, place the view and pure functions last.

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol

```solidity
// internal function coming before some public and external ones
173:    function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
192:    function _repay(address _provider, address _onBehalfOf, uint256 _amount) internal virtual {
212:    function _withdraw(address _provider, address _onBehalfOf, uint256 _amount) internal {
225:    function _checkHealth(address user, uint256 price) internal view {
230:    function _updateFee(address user) internal {
237:    function _newFee(address user) internal view returns (uint256) {
244:    function _etherPrice() internal returns (uint256) {

// public functions coming after internal and external ones

// Code block 1 (solidity):
72:    modifier updateReward(address _account) {

// Code block 2 (solidity):
// modifier should come before constructor
178:    modifier updateReward(address account) {

// Code block 3 (solidity):
// modifier coming after constructor
56:    modifier updateReward(address _account) {

72:    modifier updateReward(address _account) {

// modifier should come before constructor
178:    modifier updateReward(address account) {

// modifier coming after constructor
56:    modifier updateReward(address _account) {

EUSDMiningIncentives.sol, ProtocolRewardsPool.sol, stakerewardV2pool.sol, LybraPeUSDVaultBase.sol

EUSDMiningIncentives.sol, LybraPeUSDVaultBase.sol, ProtocolRewardsPool.sol, stakerewardV2pool.sol


IllIllI Q

## Summary

### Low Risk Issues
| |Issue|Instances|
|-|:-|:-:|
| [L‑01] | Upgradeable contract not initialized | 1 |
| [L‑02] | Not all parent contracts are upgradeable | 4 |
| [L‑03] | Loss of precision | 1 |
| [L‑04] | Signatures vulnerable | 1 |
| [L‑05] | Empty `receive()`/`payable fallback()` function does not authorize requests | 1 |
| [L‑06] | `require()` should be used instead of `assert()` | 2 |
| [L‑07] | Open TODOs | 1 |

Total: 11 instances over 7 issues

### Non-critical Issues
| |Issue|Instances|
|-|:-|:-:|
| [N‑01] | Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions | 1 |
| [N‑02] | Import declarations should import specific identifiers, rather than the whole file | 54 |
| [N‑03] | Missing `initializer` modifier on constructor | 1 |
| [N‑04] | Unused file | 2 |
| [N‑05] | Adding a `return` statement when the function defines a named return variable, is redundant | 1 |
| [N‑06] | `override` function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings | 3 |
| [N‑07] | Non-assembly method available | 2 |
| [N‑08] | `constant`s should be defined rather than using magic numbers | 192 |
| [N‑09] | Missing event and or timelock for critical parameter change | 2 |
| [N‑10] | Events that mark critical parameter changes should contain both the old and the new value | 3 |
| [N‑11] | Use a more recent version of solidity | 5 |
| [N‑12] | Use scientific notation (e.g. `1e18`) rather than exponentiation (e.g. `10**18`) | 13 |
| [N‑13] | Constant redefined elsewhere | 2 |
| [N‑14] | Inconsistent spacing in comments | 30 |
| [N‑15] | Lines are too long | 12 |
| [N‑16] | Non-library/interface files should use fixed compiler versions, not floating ones | 2 |
| [N&#x

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

/// @audit missing __ReentrancyGuard_init()
18:   contract SmartAccount is

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

/// @audit ModuleManager
/// @audit SignatureDecoder
/// @audit SecuredTokenTransfer
/// @audit FallbackManager
18:   contract SmartAccount is


0xblackskull Q

### `_setupRole` is deprecated in favor of `_grantRole` in `Neuron.sol`
- addMinter()
- addStaker()
- addSpender()

### No check for 0 or 1 in FighterFarm::incrementGeneration
```
/// @param fighterType Type of fighter either 0 or 1 (champion or dendroid).
```

```solidity
function incrementGeneration(uint8 fighterType) external returns (uint8) {
        require(msg.sender == _ownerAddress);
        generation[fighterType] += 1;
        maxRerollsAllowed[fighterType] += 1;
        return generation[fighterType];
    }
```
But there is no check for fighterType would be 0 or 1, owner can trigger it by using any value. So add require check for fighterType is either 0 or 1
```
require(fighterType == 0 || fighterType == 1);
```

// Code block 1 (unknown):
/// @param fighterType Type of fighter either 0 or 1 (champion or dendroid).

// Code block 2 (solidity):
function incrementGeneration(uint8 fighterType) external returns (uint8) {
        require(msg.sender == _ownerAddress);
        generation[fighterType] += 1;
        maxRerollsAllowed[fighterType] += 1;
        return generation[fighterType];
    }

// Code block 3 (unknown):
require(fighterType == 0 || fighterType == 1);


cheatc0d3 Q

#### `transferSystemCollShares` function can be more optimized

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

```solidity
function transferSystemCollShares(address _account, uint256 _shares) public override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}
```

#### `transferSystemCollSharesAndLiquidatorReward` function can be more optimized

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

```solidity
function transferSystemCollSharesAndLiquidatorReward(
    address _account,
    uint256 _shares,
    uint256 _liquidatorRewardShares
) external override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}
```

#### `allocateSystemCollSharesToFeeRecipient` function can be more optimized

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

```solidity
function allocateSystemCollSharesToFeeRecipient(uint256 _shares) external override {
    require(_shares > 0, "Zero shares");
    _requireCallerIsCdpManager();
    // Rest of the function code
}
```

#### `increaseSystemDebt` function can be more optimized

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

```solidity
function increaseSystemDebt(uint256 _amount) external override {
    require(_amount > 0, "Zero increase amount");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}
```

#### `decreaseSystemDebt` function can be more optimized

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

```solidity
function decreaseSystemDebt(uint256 _amount) external override {
    require(_amount > 0, "Zero d

// Code block 1 (solidity):
function transferSystemCollShares(address _account, uint256 _shares) public override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

// Code block 2 (solidity):
function transferSystemCollSharesAndLiquidatorReward(
    address _account,
    uint256 _shares,
    uint256 _liquidatorRewardShares
) external override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

// Code block 3 (solidity):
function allocateSystemCollSharesToFeeRecipient(uint256 _shares) external override {
    require(_shares > 0, "Zero shares");
    _requireCallerIsCdpManager();
    // Rest of the function code
}

// Code block 4 (solidity):
function increaseSystemDebt(uint256 _amount) external override {
    require(_amount > 0, "Zero increase amount");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

function transferSystemCollShares(address _account, uint256 _shares) public override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

function transferSystemCollSharesAndLiquidatorReward(
    address _account,
    uint256 _shares,
    uint256 _liquidatorRewardShares
) external override {
    require(_account != address(0), "Zero account address");
    require(_shares > 0, "Zero shares");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

function allocateSystemCollSharesToFeeRecipient(uint256 _shares) external override {
    require(_shares > 0, "Zero shares");
    _requireCallerIsCdpManager();
    // Rest of the function code
}

function increaseSystemDebt(uint256 _amount) external override {
    require(_amount > 0, "Zero increase amount");
    _requireCallerIsBOorCdpM();
    // Rest of the function code
}

ActivePool.sol#L100, ActivePool.sol#L129, ActivePool.sol#L157, ActivePool.sol#L194, ActivePool.sol#L207


Ignite Q

# Low Issues

|  |	Issues Details | Instances |
|---|---|---|
| [Low-1] | Loss of Precision due to rounding  | 1 |
| [Low-2] | Function Parameters Without Bounds | 1 |
| [Low-3] | Missing deadline checks in `swapExactInputSingleHop()` function | 1 |
| [Low-4] | Lack of zero address checks | 4 |
| [Low-5] | Pragma Float | All Contracts |

## [Low-1] Loss of Precision due to rounding
In the `stake` function, the ethAmount is calculated by `(msg.value * weight) / totalWeight` at line 88. Due to a potential loss of precision when rounding, there is a possibility that some wei may remain in the `SafEth` contract.

For instance, consider the following initial state:
`msg.value` = 200;
`weights[0]` = 10;
`weights[1]` = 20;
`weights[2]` = 30;
`totalWeight` = 60;

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84-L88
```solidity=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;
```

In this scenario, the `ethAmount` values would be `33`, `66`, and `99` for the three iterations of the loop, respectively. Therefore, there would be small wei left in the `SafETH` contract since `200 - (33 + 66 + 99) = 2` (without slippage).

### Recommendation
Transfer the remaining wei back to the sender.

Otherwise, use a different mechanism to allocate funds, instead of calculating `ethAmount` based on weights, the contract could use a fixed price for each derivative or allow users to specify the exact amount of ETH they want to allocate to each derivative.

## [Low-2] Function Parameters Without Bounds
The function can be called with any input value for those parameters, regardless of whether they are valid or reasonable. This can lead to unexpected behavior or even security vulnerabilities if the function does not properly validate or sanitize its input parameters.
F

In this scenario, the `ethAmount` values would be `33`, `66`, and `99` for the three iterations of the loop, respectively. Therefore, there would be small wei left in the `SafETH` contract since `200 - (33 + 66 + 99) = 2` (without slippage).

### Recommendation
Transfer the remaining wei back to the sender.

Otherwise, use a different mechanism to allocate funds, instead of calculating `ethAmount` based on weights, the contract could use a fixed price for each derivative or allow users to specify the exact amount of ETH they want to allocate to each derivative.

## [Low-2] Function Parameters Without Bounds
The function can be called with any input value for those parameters, regardless of whether they are valid or reasonable. This can lead to unexpected behavior or even security vulnerabilities if the function does not properly validate or sanitize its input parameters.
For example:

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

### Recommendation
Define limits or constraints on function parameters to prevent unintended consequences.

## [Low-3] Missing deadline checks in `swapExactInputSingleHop()` function

The `swapExactInputSingleHop()` function lacks a deadline for its actions that execute swaps on the `deposit()` function. This missing feature can result in pending transactions being executed later, potentially leading to outdated slippage.

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


arialblack14 G

# GAS OPTIMIZATION REPORT ⛽

## [G-1] `require()`/`revert()` strings longer than 32 bytes cost extras gas.

### Description
Each extra chunk of bytes past the original 32 incurs an MSTORE which costs 3 gas

### ✅ Recommendation
Consider the following require statement:
```Solidity
// condition is boolean
// str is a string
require(condition, str)
```
The string str is split into 32-byte sized chunks and then stored in `memory` using `mstore`, then the `memory` offsets are provided to `revert(offset, length)`. For chunks shorter than 32 bytes, and for low --optimize-runs value (usually even the default value of 200), instead of `push32 val`, where `val` is the 32 byte hexadecimal representation of the string with 0 padding on the least significant bits, the solidity compiler replaces it by `shl(value, short-value))`. Where `short-value` does not have any 0 padding. This saves the total bytes in the deploy code and therefore saves deploy time cost, at the expense of extra 6 gas during runtime. This means that shorter revert strings saves deploy time costs of the contract. Note that this kind of saving is not relevant for high values of --optimize-runs as `push32 value` will not be replaced by a `shl(..., ...)` equivalent by the Solidity compiler.
Going back, each 32 byte chunk of the string requires an extra `mstore`. That is, additional cost for `mstore`, `memory` expansion costs, as well as stack operations. Note that, this runtime cost is only relevant when the revert condition is met.
Overall, shorter revert strings can save deploy time as well as runtime costs.

### 🔍 Findings:
| | |
|---|---|
|2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77|[require(msg.sender == owner, "Smart Account:: Sender is not authorized");](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77 )|
|2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L110|[r

for (uint i = 0; i < length; i++) {
// do something that doesn't change the value of i
}

// Code block 1 (Solidity):
// condition is boolean
// str is a string
require(condition, str)


yongskiws G

### Gas Optimizations Issues List
| Number |Issues Details|Instance|
|:--:|:-------|:--:|
|[G-01]| Using unchecked blocks to save gas | 9 |
|[G-02]| Use calldata instead of memory for function parameters | 4 |
|[G-03]| Use hardcode address instead address(this) | 1 |
|[G-04]| Sort Solidity operations using short-circuit mode | 8 |
|[G-05]| Using unchecked blocks to save gas - Increments in for loop can be unchecked ( save 30-40 gas per loop iteration) | 1 |
|[G-06]| Using storage instead of memory for structs/arrays saves gas | 10 |
|[G-07]| screeningVote: requests[proposal_.distributionId] should be cached in local storage | 1 |
|[G-08]| Using immutable for uint & address for save gas | - |
|[G-09]| internal functions not called by the contract should be removed to save deployment gas | 6 |
|[G-10]| x = x + y is cheaper than x += y; | 3 |
|[G-11]| Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 7 |
|[G-12]| Using > 0 costs more gas than != 0 when used on a uint in a require() statement | 7 |
|[G-13]| >= costs less gas than > | 1 |
|[G-14]| Change public function visibility to external | 1 |

## [G-01] Using unchecked blocks to save gas

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block
https://github.com/ethereum/solidity/issues/10695

Gas Saved For Using unchecked blocks to save gas

The above should be modified to:

9 results - 3 files:

``` diff

ajna\ExtraordinaryFunding.sol
172:         return
173:             // succeeded if proposal's votes received doesn't exceed the minimum threshold required
174:             (votesReceived >= tokensRequested_ + _getSliceOfNonTreasury(minThresholdPercentage))
175:             &&
176:             // succeeded if tokens requested are available for claiming from the treasury
177:

## [G-02] Use calldata instead of memory for function parameters

In some cases, having function arguments in calldata instead of memory is more optimal.

Consider the following generic example:

In the above example, the dynamic array arr has the storage location memory. When the function gets called externally, the array values are kept in calldata and copied to memory during ABI decoding (using the opcode calldataload and mstore). And during the for loop, arr[i] accesses the value in memory using a mload. However, for the above example this is inefficient. Consider the following snippet instead:


0xrafaelnicolau Q

# [L-01] Insufficient input validation let's users mint a free bond
If a user calls the `bondWithDelegate` function with empty arrays for both `_amounts` and `_delegateIds`, unintendedly, a Bond with zero amount will be minted to the user.

## PoC
```solidity
contract Exploit is Setup {
    function testBondWithDelegateEmptyArrays() external {
        // create empty arrays to pass as arguments
        uint256[] memory _amounts = new uint256[](0);
        uint256[] memory _delegateIds = new uint256[](0);

        // calls bondWithDelegate
        rdpxV2Core.bondWithDelegate(address(this), _amounts, _delegateIds, 0);

        // assert that the user was able to mint a bond (although the bond has 0 amount).
        assertEq(rdpxV2Bond.balanceOf(address(this)), 1);
    }
}
```

## Recommended Mitigation
Since the function `bondWithDelegate` already checks the length of `_amounts` and `_delegateIds` arrays are the same, consider adding a check to ensure that one of the arrays has a non-zero length.

```solidity
function bondWithDelegate(
    address _to,
    uint256[] memory _amounts,
    uint256[] memory _delegateIds,
    uint256 rdpxBondId
  ) public returns (uint256 receiptTokenAmount, uint256[] memory) {
    _whenNotPaused();
    // Validate amount
    _validate(_amounts.length == _delegateIds.length, 3);
+   _validate(_amounts.length != 0, 3);

    ...
  }
```

// Code block 1 (solidity):
contract Exploit is Setup {
    function testBondWithDelegateEmptyArrays() external {
        // create empty arrays to pass as arguments
        uint256[] memory _amounts = new uint256[](0);
        uint256[] memory _delegateIds = new uint256[](0);

        // calls bondWithDelegate
        rdpxV2Core.bondWithDelegate(address(this), _amounts, _delegateIds, 0);

        // assert that the user was able to mint a bond (although the bond has 0 amount).
        assertEq(rdpxV2Bond.balanceOf(address(this)), 1);
    }
}

// Code block 2 (solidity):
function bondWithDelegate(
    address _to,
    uint256[] memory _amounts,
    uint256[] memory _delegateIds,
    uint256 rdpxBondId
  ) public returns (uint256 receiptTokenAmount, uint256[] memory) {
    _whenNotPaused();
    // Validate amount
    _validate(_amounts.length == _delegateIds.length, 3);
+   _validate(_amounts.length != 0, 3);

    ...
  }

contract Exploit is Setup {
    function testBondWithDelegateEmptyArrays() external {
        // create empty arrays to pass as arguments
        uint256[] memory _amounts = new uint256[](0);
        uint256[] memory _delegateIds = new uint256[](0);

        // calls bondWithDelegate
        rdpxV2Core.bondWithDelegate(address(this), _amounts, _delegateIds, 0);

        // assert that the user was able to mint a bond (although the bond has 0 amount).
        assertEq(rdpxV2Bond.balanceOf(address(this)), 1);
    }
}

function bondWithDelegate(
    address _to,
    uint256[] memory _amounts,
    uint256[] memory _delegateIds,
    uint256 rdpxBondId
  ) public returns (uint256 receiptTokenAmount, uint256[] memory) {
    _whenNotPaused();
    // Validate amount
    _validate(_amounts.length == _delegateIds.length, 3);
+   _validate(_amounts.length != 0, 3);

    ...
  }


CodingNameKiki Q

### Issues Template
| Letter | Name | Description |
|:--:|:-------:|:-------:|
| L  | Low risk | Potential risk |
| NC |  Non-critical | Non risky findings |
| R  | Refactor | Changing the code |
| O | Ordinary | Often found issues |

| Total Found Issues | 33 |
|:--:|:--:|

### Low Risk Template
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | Unnecessary KYC check on the payer | 1 |
| [L-02] | Wrong if statement in the function `_acceptAdmin` | 1 |
| [L-03] | Token transfer to address(0) should be avoided | 1 |
| [L-04] | The maximum fee of 10_000 isn't allowed in the function `setMintFee` | 1 |
| [L-05] | Giving KYC status to address(0) should be forbidden | 1 |
| [L-06] | Redeem limit shouldn't be set below the `currentMintAmount` | 1 |
| [L-07] | `completeRedemptions` is vulnerable to admin mistakes | 1 |

| Total Low Risk Issues | 7 |
|:--:|:--:|

### Non-Critical Template
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [N-01] | Mandatory checks for extra safety | 3 |
| [N-02] | Constructor lacks address(0) check | 5 |
| [N-03] | Unrealistic if statement | 9 |
| [N-04] | Missing check to ensure epoch duration isn't set as zero seconds | 1 |
| [N-05] | Upgradeable contract is missing a __gap[50] storage variable | 2 |
| [N-06] | Create your own import names instead of using the regular ones | 19 |
| [N-07] | Initialize function does not use the initializer modifier | 2 |
| [N-08] | Use delete to clear variables instead of zero assignment | 2 |
| [N-09] | Confusing function comment on `setMinimumDepositAmount` | 1 |
| [N-10] | Unnecessary if statement in `_checkAndUpdateRedeemLimit` | 1 |
| [N-11] | Pause/Unpause functions should emit event to notify users | 2 |
| [N-12] | Two KYC checks made on the same redeemers | 1 |
| [N-13] | Unused constructor | 2 |
| [N-14] | NatSpec is incomplete in the pausing functions | 2 |

| Total Non-Critical Issues | 14 |
|:--:|:--:|

### Refactor Issues Template
| Count | Explanation | Instances |
|

contracts/lending/tokens/cCash/CCash.sol

121:  function repayBorrowBehalf(
122:    address borrower,
123:    uint repayAmount
124:  ) external override returns (uint) {
125:   repayBorrowBehalfInternal(borrower, repayAmount);
126:    return NO_ERROR;
127:  }

contracts/lending/tokens/cCash/CTokenCash.sol

767:  function repayBorrowFresh(
768:    address payer,
769:    address borrower,
770:    uint repayAmount
771:  ) internal returns (uint) {
772:    /* Revert if not KYC'd */
773:    require(_getKYCStatus(payer), "Payer not KYC'd");
774:    require(_getKYCStatus(borrower), "Borrower not KYC'd");

function _acceptAdmin() external override returns (uint) {
    // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
    if (msg.sender != pendingAdmin || msg.sender == address(0)) {
      revert AcceptAdminPendingAdminCheck();
    }

    // Save current values for inclusion in log
    address oldAdmin = admin;
    address oldPendingAdmin = pendingAdmin;

    // Store admin with value pendingAdmin
    admin = pendingAdmin;

    // Clear the pending value
    pendingAdmin = payable(address(0));

    emit NewAdmin(oldAdmin, admin);
    emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);

    return NO_ERROR;
  }


Breeje Q

 QA Report

## Low Risk Issues
| Count | Explanation |
|:--:|:-------|
| [L-01] | No checks to confirm that the gas paid by user is enough |
| [L-02] | In case `DestinationBridge` is paused before `SourceBridge`, User can lose funds |
| [L-03] | No incentive for approvers to approve can lead to issues in long term |
| [L-04] | Possible Reorg situation in `rUSDYFactory` |

| Total Low Risk Issues | 4 |
|:--:|:--:|

### [L-01] No checks to confirm that the gas paid by user is enough

## Proof of Concept

In `SourceBridge` contract, When any user calls `burnAndCallAxelar`:

1. `TOKEN`s are burned from caller.
2. Gas Fees passed by user is paid through `payNativeGasForContractCall`.
3. `callContract` call is made to transfer the token cross chain.

```solidity

  function burnAndCallAxelar(
    uint256 amount,
    string calldata destinationChain
  ) external payable whenNotPaused {
    string memory destContract = destChainToContractAddr[destinationChain];

    if (bytes(destContract).length == 0) {
      revert DestinationNotSupported();
    }

    if (msg.value == 0) {
      revert GasFeeTooLow();
    }

    TOKEN.burnFrom(msg.sender, amount);

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

    _payGasAndCallContract(destinationChain, destContract, payload);
  }

  function _payGasAndCallContract(
    string calldata destinationChain,
    string memory destContract,
    bytes memory payload
  ) private {
    GAS_RECEIVER.payNativeGasForContractCall{value: msg.value}(
      address(this),
      destinationChain,
      destContract,
      payload,
      msg.sender
    );

    AXELAR_GATEWAY.callContract(destinationChain, destContract, payload);
  }

```

Here, there is no validation that the Gas Fees paid by user is sufficient to transfer the token cross chain or not.

## Impact

There are transaction won't be successful at the destination chain because of lack of gas fees paid. User might need to manually call

## Recommended

File: rUSDYFactory.sol

  function deployrUSDY(
    address blocklist,
    address allowlist,
    address sanctionsList,
    address usdy,
    address oracle
  ) external onlyGuardian returns (address, address, address) {
    rUSDYImplementation = new rUSDY();
    rUSDYProxyAdmin = new ProxyAdmin();
    rUSDYProxy = new TokenProxy(
      address(rUSDYImplementation),
      address(rUSDYProxyAdmin),
      ""
    );

// Code block 1 (solidity):
function burnAndCallAxelar(
    uint256 amount,
    string calldata destinationChain
  ) external payable whenNotPaused {
    string memory destContract = destChainToContractAddr[destinationChain];

    if (bytes(destContract).length == 0) {
      revert DestinationNotSupported();
    }

    if (msg.value == 0) {
      revert GasFeeTooLow();
    }

    TOKEN.burnFrom(msg.sender, amount);

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

    _payGasAndCallContract(destinationChain, destContract, payload);
  }

  function _payGasAndCallContract(
    string calldata destinationChain,
    string memory destContract,
    bytes memory payload
  ) private {
    GAS_RECEIVER.payNativeGasForContractCall{value: msg.value}(
      address(this),
      destinationChain,
      destContract,
      payload,
      msg.sender
    );

    AXELAR_GATEWAY.callContract(destinationChain, destContract, payload);
  }


hunter_w3b Q

## [L-01] DO NOT PERFORM `ARITHMETIC` OPERATION INSIDE THE emit OF AN event

It is not recommended to perform arithmetic operations inside the emit of an event. The emits are placed to log the function results for the use of the off-chain tools and not to perform arithmetic operations. The arithmetic opeations should be performed outside the emit and only the result should be logged and emitted in an event for the use of off-chain tools.

```solidity
File: lybra/pools/LybraWbETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
31        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWbETHVault.sol#L31

```solidity
File: lybra/pools/LybraRETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
38        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraRETHVault.sol#L38

```solidity
File: lybra/miner/ProtocolRewardsPool.sol

/// @audit  reward - eUSDShare
203                    emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);

```

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L203

## [L-02] CONDUCT THE `MULTIPLICATION` BEFORE `DIVISON` WHEN PERFORMING ARITHMETIC OPERATIONS TO AVOID ROUNDING ERROR.

When arithmetic operations are performed in solidity, it is recommended to perform the multiplicatons before division. This is because the division results are rounded down to the neares interger value in solidity. which introduces rounding error. And if the multiplication is followed, it will inflate the rounding error to a much larger error which could be detrimental to the functionality of the protocol.

```solidity
File: l

// Code block 1 (solidity):
File: lybra/pools/LybraWbETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
31        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

// Code block 2 (solidity):
File: lybra/pools/LybraRETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
38        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

// Code block 3 (solidity):
File: lybra/miner/ProtocolRewardsPool.sol

/// @audit  reward - eUSDShare
203                    emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);

File: lybra/pools/LybraWbETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
31        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

File: lybra/pools/LybraRETHVault.sol

/// @audit   balance - preBalance don't perfrom arithmetic
38        emit DepositEther(msg.sender, address(collateralAsset), msg.value,balance - preBalance, block.timestamp);

File: lybra/miner/ProtocolRewardsPool.sol

/// @audit  reward - eUSDShare
203                    emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);


J4X Q

# Low

## [L-01] Reserves Above DUST Incorrectly Enforced
[Pools Line 270](https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L271)

**Issue Description:**

In the Salty protocol's `_adjustReservesForSwap()` function, the intention, as per the accompanying comment ("// Make sure that the reserves after swap are above DUST"), is to ensure that the reserves always remain above a certain threshold defined by the `DUST` constant. However, the current implementation of this check uses `>=` (greater than or equal to), which leads to an off-by-one error. The actual requirement, as intended, is for the reserves to be strictly greater than `DUST`.

Current implementation:
```solidity
require( (reserve0 >= PoolUtils.DUST) && (reserve1 >= PoolUtils.DUST), "Insufficient reserves after swap");
```

This condition allows the reserves to be exactly equal to `DUST`, which contradicts the intended logic of keeping them above `DUST`.

**Recommended Mitigation Steps**

To align the implementation with the intended behavior, the `require` statement should be adjusted to enforce that reserves are strictly greater than `DUST`. This change will ensure that the reserves are maintained above the minimal threshold, as originally planned.

Proposed change:
```solidity
require( (reserve0 > PoolUtils.DUST) && (reserve1 > PoolUtils.DUST), "Insufficient reserves after swap");
```

By modifying the condition to `>` (greater than), the function will correctly enforce that the reserves are maintained above the `DUST` level, thus adhering to the protocol's intended safety and operational standards.

## [L-02] Liquidity can still be below DUST after a call to `addLiquidity()`
[Pools Line 146-147](https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L146-L147)

**Issue Description:**

n the Salty protocol's `addLiquidity()` function, there's an enforcement to ensure the maximum liquidity added by a user is above the `PoolUtils.DUST` threshold. However, a pote

// Code block 1 (solidity):
require( (reserve0 >= PoolUtils.DUST) && (reserve1 >= PoolUtils.DUST), "Insufficient reserves after swap");

// Code block 2 (solidity):
require( (reserve0 > PoolUtils.DUST) && (reserve1 > PoolUtils.DUST), "Insufficient reserves after swap");

require( (reserve0 >= PoolUtils.DUST) && (reserve1 >= PoolUtils.DUST), "Insufficient reserves after swap");

require( (reserve0 > PoolUtils.DUST) && (reserve1 > PoolUtils.DUST), "Insufficient reserves after swap");


0x1f8b Q

- [Low](#low)
    - [**1. Use abstract for base contracts**](#1-use-abstract-for-base-contracts)
    - [**2. Mixing and Outdated compiler**](#2-mixing-and-outdated-compiler)
    - [**3. Wrong logic in paymasterContext**](#3-wrong-logic-in-paymastercontext)
    - [**4. Lack of checks**](#4-lack-of-checks)
    - [**5. Centralization risks**](#5-centralization-risks)
    - [**6. Poxy isues**](#6-poxy-isues)
    - [**7. Integer overflow**](#7-integer-overflow)
    - [**8. Loss of tokens**](#8-loss-of-tokens)
- [Non critical](#non-critical)
    - [**9. Open TODO**](#9-open-todo)
    - [**10. Wrong comment**](#10-wrong-comment)
    - [**11. Lack of ACK during owner change**](#11-lack-of-ack-during-owner-change)
    - [**12. Unsafe low level call**](#12-unsafe-low-level-call)
    - [**13. Wong naming**](#13-wong-naming)
    - [**14. Avoid hardcoded values**](#14-avoid-hardcoded-values)

# Low

## **1. Use `abstract` for base contracts**

`abstract` contracts are contracts that have at least one function without its implementation. **An instance of an abstract cannot be created.**

**Reference:**

- https://docs.soliditylang.org/en/v0.6.2/contracts.html#abstract-contracts

**Affected source code:**

- [Enum.sol:5](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/Enum.sol#L5)
- [MultiSend.sol:9](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L9)
- [MultiSendCallOnly.sol:8](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L8)
- [Executor.sol:7](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L7)
- [FallbackManager.sol:8](https://github.com/code-423n4

    /**
     * @dev Encodes the paymaster context: sender, token, rate, and fee
     */
    function paymasterContext(
        UserOperation calldata op,
        PaymasterData memory data
    ) internal pure returns (bytes memory context) {
        return abi.encode(data.paymasterId);
    }

Enum.sol#L5, MultiSend.sol#L9, MultiSendCallOnly.sol#L8, Executor.sol#L7

Enum.sol, Executor.sol, MultiSendCallOnly.sol, MultiSend.sol


bin2chen Q

L-01:EthRouter#buy/sell does not determine whether royaltyRecipient is address(0)
If `getRoyalty()` returns royaltyFee>0, but `royaltyRecipient==address(0)` this will transfer the funds to address(0)
Suggest adding judgment:

```solidity
    function buy(Buy[] calldata buys, uint256 deadline, bool payRoyalties) public payable {
...
                if (payRoyalties) {
                    uint256 salePrice = inputAmount / buys[i].tokenIds.length;
                    for (uint256 j = 0; j < buys[i].tokenIds.length; j++) {
                        // get the royalty fee and recipient
                        (uint256 royaltyFee, address royaltyRecipient) =
                            getRoyalty(buys[i].nft, buys[i].tokenIds[j], salePrice);

-                       if (royaltyFee > 0) {
+                       if (royaltyFee > 0 && royaltyRecipient!=address(0)) {
                            // transfer the royalty fee to the royalty recipient
                            royaltyRecipient.safeTransferETH(royaltyFee);
                        }
                    }
                }
```

L-02:setProtocolFeeRate() does not limit the maximum value, there is a risk of malicious amplification of the fee
It is recommended that this should not exceed, for example, 5%

```solidity
contract Factory is ERC721, Owned {
...
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
+       if (_protocolFeeRate > 5_000) revert FeeRateTooHigh();
        protocolFeeRate = _protocolFeeRate;
    }
```

// Code block 1 (solidity):
function buy(Buy[] calldata buys, uint256 deadline, bool payRoyalties) public payable {
...
                if (payRoyalties) {
                    uint256 salePrice = inputAmount / buys[i].tokenIds.length;
                    for (uint256 j = 0; j < buys[i].tokenIds.length; j++) {
                        // get the royalty fee and recipient
                        (uint256 royaltyFee, address royaltyRecipient) =
                            getRoyalty(buys[i].nft, buys[i].tokenIds[j], salePrice);

-                       if (royaltyFee > 0) {
+                       if (royaltyFee > 0 && royaltyRecipient!=address(0)) {
                            // transfer the royalty fee to the royalty recipient
                            royaltyRecipient.safeTransferETH(royaltyFee);
                        }
                    }
                }

// Code block 2 (solidity):
contract Factory is ERC721, Owned {
...
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
+       if (_protocolFeeRate > 5_000) revert FeeRateTooHigh();
        protocolFeeRate = _protocolFeeRate;
    }

function buy(Buy[] calldata buys, uint256 deadline, bool payRoyalties) public payable {
...
                if (payRoyalties) {
                    uint256 salePrice = inputAmount / buys[i].tokenIds.length;
                    for (uint256 j = 0; j < buys[i].tokenIds.length; j++) {
                        // get the royalty fee and recipient
                        (uint256 royaltyFee, address royaltyRecipient) =
                            getRoyalty(buys[i].nft, buys[i].tokenIds[j], salePrice);

-                       if (royaltyFee > 0) {
+                       if (royaltyFee > 0 && royaltyRecipient!=address(0)) {
                            // transfer the royalty fee to the royalty recipient
                            royaltyRecipient.safeTransferETH(royaltyFee);
                        }
                    }
                }

contract Factory is ERC721, Owned {
...
    function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner {
+       if (_protocolFeeRate > 5_000) revert FeeRateTooHigh();
        protocolFeeRate = _protocolFeeRate;
    }


catellatech Q

### Issues
| Letter | Name | Description |
|:--:|:-------:|:-------:|
| L  | Low risk | Potential risk |
| NC |  Non-critical | Non risky findings |
| R  | Refactor | Changing the code |
| O | Ordinary | Often found issues |

| Total Found Issues | 21 |
|:--:|:--:|

### Low Risk
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | NO USE OF UPGRADEABLE IERC20 | 3 |
| [L-02] | CRITICAL CHANGES SHOULD USE TWO-STEP PROCEDURE | 4 |
| [L-03] | MISSING EVENTS FOR ONLY FUNCTIONS THAT CHANGE CRITICAL PARAMETERS | 6 |
| [L-04] | THE FUNCTIONS OF THE PROTOCOL ARE SERIOUSLY COMPROMISED | 4 |
| [L-05] | IN THE EMITS, INCLUDE THE OLD AND NEW VALUES OF THE UPDATED PARAMETERS TO TRACK THE CHANGES MADE | 3 |

| Total Low Risk Issues | 5 | Total Instances | 19 |
|:--:|:--:|:--:|--:|

### Non-Critical
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [N-01] | FUNCTION WRITING THAT DOES NOT COMPLY WITH THE SOLIDITY STYLE GUIDE | All contracts |
| [N-02] | USE A SINGLE FILE FOR ALL SYSTEM-WIDE CONSTANTS | 3 |
| [N-03] | NON-LIBRARY/INTERFACE FILES SHOULD USE FIXED COMPILER VERSIONS, NOT FLOATING ONES | all contracts |
| [N-04] | USE A MORE RECENT VERSION OF SOLIDITY | All contracts |
| [N-05] | CREATE YOUR OWN IMPORT NAMES INSTEAD OF USING THE REGULAR ONES | All contracts |
| [N-06] | USE SCIENTIFIC NOTATION (E.G. 1E18) RATHER THAN EXPONENTIATION (E.G. 10**18) | 11 |
| [N-07] | USE OF BYTES.CONCAT() INSTEAD OF ABI.ENCODEPACKED() | 6 |
| [N-08] | NEED FUZZING TEST |  |
| [N-09] | NATSPEC IS INCOMPLETE | 6 |

| Total Non-Critical Issues | 9 | Total Instances | 17 |
|:--:|:--:|:--:|--:|

### Refactor Issues
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [R-01] | USE CUSTOM ERRORS INSTEAD OF REVERT STRINGS TO SAVE GAS | All contracts |
| [R-02] | DO NOT PRE-DECLARE VARIABLE WITH DEFAULT VALUES | 8 |
| [R-03] | SHORTHAND WAY TO WRITE IF/ELSE STATEMENT | 2 |

| Total Refactor Issues | 3 | Total Instances | 10 |
|:--:|:--:|:--:|--:|

### Ordinary Issues

## [L-02] CRITICAL CHANGES SHOULD USE TWO-STEP PROCEDURE
Handle ownership transfers with two steps and two transactions. First, allow the current owner to propose a new owner address. Second, allow the proposed owner (and only the proposed owner) to accept ownership, and update the contract owner internally

### PROOF OF CONCEPT

### MITIGATION
Implement zero address check and consider implementing a two step process where the owner nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.

## [L-03] MISSING EVENTS FOR ONLY FUNCTIONS THAT CHANGE CRITICAL PARAMETERS
The afunctions that change critical parameters should emit events. Events allow capturing the changed parameters so that off-chain tools/interfaces can register such changes with timelocks that allow users to evaluate them and consider if they would like to engage/exit based on how they perceive the changes as affecting the trustworthiness of the protocol or profitability of the implemented financial services. The alternative of directly querying on-chain contract state for such changes is not considered practical for most users/usages.

missing events and timelocks do not promote transparency and if such changes immediately affect users' perception of fairness or trustworthiness, they could exit the protocol causing a reduction in liquidity which could negatively impact protocol TVL and reputation.
### PROOF OF CONCEPT


SAQ G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] | Can make the variable outside the loop to save gas | 1 | - |
| [G-02] | Don't initialize variables with default value | 12 | - |
| [G-03] | Using storage instead of memory for structs/arrays saves gas | 3 | - |
| [G-04] | Duplicated require()/if() checks should be refactored to a modifier or function | 1 | - |
| [G-05] |  Use constants instead of type(uintx).max | 9 | - |
| [G-06] | Use selfbalance() instead of address(this).balance | 1 | - |
| [G-07] | Using ERC721A instead of ERC721 for more gas-efficient  | 3 | - |
| [G-08] | Use hardcode address instead address(this) | 4 | - |
| [G-09] | Avoid fetching a low-level call's return data by using assembly | 2 | - |
| [G-10] | Using assembly to check for zero can save gas | 2 | - |
| [G-11] | Pre-increment and pre-decrement are cheaper than + 1 ,- 1 | 2 | - |
| [G-12] | address(this) should be cached | 10 | - |

## Gas Optimizations

## [G-01]  Can make the variable outside the loop to save gas

```solidity
file: /src/automators/Automator.sol

112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));

```
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/automators/Automator.sol#L112C1-L112C74

## [G-02]  Don't initialize variables with default value

```solidity
file: /src/V3Vault.so

118    uint32 public reserveFactorX32 = 0;

124    uint256 public debtSharesTotal = 0;

127    uint256 public lastExchangeRateUpdate = 0;

131    uint256 public globalDebtLimit = 0;

132    uint256 public globalLendLimit = 0;

135    uint256 public minLoanSize = 0;

138    uint256 public dailyLendIncreaseLimitMin = 0;
139    uint256 public dailyLendIncreaseLimitLeft = 0;
140    uint256 public dailyLendIncreaseLimitLastReset = 0;

143    uint256 public dailyDebtIncreaseLimitMin = 0;
144    uint256 public dailyDebtIncreaseLimitLeft = 0;
145    uint256 public dailyDebtIncreaseLimitLastReset = 0;

```
https://github.c

// Code block 1 (solidity):
file: /src/automators/Automator.sol

112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));

// Code block 2 (solidity):
file: /src/V3Vault.so

118    uint32 public reserveFactorX32 = 0;

124    uint256 public debtSharesTotal = 0;

127    uint256 public lastExchangeRateUpdate = 0;

131    uint256 public globalDebtLimit = 0;

132    uint256 public globalLendLimit = 0;

135    uint256 public minLoanSize = 0;

138    uint256 public dailyLendIncreaseLimitMin = 0;
139    uint256 public dailyLendIncreaseLimitLeft = 0;
140    uint256 public dailyLendIncreaseLimitLastReset = 0;

143    uint256 public dailyDebtIncreaseLimitMin = 0;
144    uint256 public dailyDebtIncreaseLimitLeft = 0;
145    uint256 public dailyDebtIncreaseLimitLastReset = 0;

file: /src/automators/Automator.sol

112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));

file: /src/V3Vault.so

118    uint32 public reserveFactorX32 = 0;

124    uint256 public debtSharesTotal = 0;

127    uint256 public lastExchangeRateUpdate = 0;

131    uint256 public globalDebtLimit = 0;

132    uint256 public globalLendLimit = 0;

135    uint256 public minLoanSize = 0;

138    uint256 public dailyLendIncreaseLimitMin = 0;
139    uint256 public dailyLendIncreaseLimitLeft = 0;
140    uint256 public dailyLendIncreaseLimitLastReset = 0;

143    uint256 public dailyDebtIncreaseLimitMin = 0;
144    uint256 public dailyDebtIncreaseLimitLeft = 0;
145    uint256 public dailyDebtIncreaseLimitLastReset = 0;


Sathish9098 G

## GAS OPTIMIZATIONS

| Gas Count     | Issues | Instances | Gas Saved|
| --------------- | --------------- | --------------- | --------------- |
| [G-1] | Structs can be packed into fewer storage slots | 2 | 40000 |
| [G-2] | Optimize names to save gas | 38 | - |
| [G-3] | Functions guaranteed to revert when called by normal users can be marked payable | 10 | 210 |
| [G-4] | Setting the constructor to payable | 2 | 26 |
| [G-5] | Use assembly to write address storage values | 8 | - |
| [G-6] | Use nested if and, avoid multiple check combinations | 9 | 81 |
| [G-7] | Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 7 | 196 |
| [G-8] | Empty blocks should be removed or emit something | 4 | - |
| [G-9] | Use assembly to check for address(0) | 15 | 90 |
| [G-10] | ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops | 18 | 2520 |
| [G-11] | Amounts should be checked for 0 before calling a transfer functions | 16 | - |
| [G-12] | Use 3 indexed parameters rule for events to save gas | 11 | - |
| [G-13] | public functions to external | 23 | 345 |
| [G-14] | Avoid contract existence checks by using low level calls | 30 | 3000 |
| [G-15] | Duplicated require()/if() checks should be refactored to a modifier or function | 12 | - |
| [G-16] | <x> += <y> costs more gas than <x> = <x> + <y> for state variables (SAME FOR -=) | 4 | 452 |
| [G-17] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement | 2 | 440 |
| [G-18] | Don't declare variables inside the loops  | 3 | - |
| [G-19] | The condition check should be top of the functions  | 1 | - |
| [G-20] | Cheaper input validations should come before expensive operations | 1 | - |
| [G-21] | Sort Solidity operations using short-circuit mode | 8 | - |
| [G-22] | Multiplication/division by 2 should use bit shifting | 3 | - |
| [G-23] | abi.encode() is less

FILE: 2023-04-caviar/src/EthRouter.sol

/// @audit Variable ordering with 6 slots instead of the current 7 :
///  address(20):pool,address(20):nft,bool(1):isPublicPool,uint256(dynamic):tokenIds,uint256(dynamic):tokenWeights, PrivatePool(32):proof,uint256(32):baseTokenAmount
 48:  struct Buy {
 49:       address payable pool;
 50:       address nft;
+55:       bool isPublicPool;
 51:       uint256[] tokenIds;
 52:       uint256[] tokenWeights;
 53:       PrivatePool.MerkleMultiProof proof;
 54:       uint256 baseTokenAmount;
-55:       bool isPublicPool;
 56:    }

/// @audit Variable ordering with 7 slots instead of the current 8 :
///
 58: struct Sell {
 59:       address payable pool;
 60:       address nft;
+65:       bool isPublicPool;
 61:       uint256[] tokenIds;
 62:       uint256[] tokenWeights;
 63:       PrivatePool.MerkleMultiProof proof;
 64:       IStolenNftOracle.Message[] stolenNftProofs;
-65:       bool isPublicPool;
 66:       bytes32[][] publicPoolProofs;
 67:   }


mojito_auditor Q

# Summary

| Id | Title |
| --- | --- |
| L-1 | Function `_mul()` does not work with Solidity 0.8 |
| L-2 | Incorrect naming variable in `unwrap()` function |
| L-3 | Pre and post rebase amount will always be equal |

# L-1. Function `_mul()` does not work with Solidity 0.8

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L405

## Detail
In `RWADynamicOracle` contract, the function `_mul()` implemented overflow check to ensure the result of `x * y` does not overflow. However, this check does not work with Solidity 0.8 because if there is overflow, Solidity 0.8 will automatically revert when doing `z = x * y`.
```solidity
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
  // @audit Solidity 0.8 will revert when doing `x * y`
  require(y == 0 || (z = x * y) / y == x);
}
```
https://docs.soliditylang.org/en/v0.8.19/080-breaking-changes.html#silent-changes-of-the-semantics

## Recommendation
Solidity 0.8 has already checked for overflow so no need for this function.

# L-2. Incorrect naming variable in `unwrap()` function

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

## Detail
In the function `unwrap()`, it calculated the amount of shares from amount of rUSDY. But the result is named `usdyAmount` instead of `sharesAmount`. This is confusing and should be correct to make the code more readable.

```solidity
function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
  require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");

  // @audit Should be `shareAmount` instead of `usdyAmount`
  uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
  if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
  _burnShares(msg.sender, usdyAmount);
  usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
  emit TokensBurnt(msg.sender, _rUSDYAmount);
}
```

## Recommendation
Change th

// Code block 1 (solidity):
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
  // @audit Solidity 0.8 will revert when doing `x * y`
  require(y == 0 || (z = x * y) / y == x);
}

// Code block 2 (solidity):
function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
  require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");

  // @audit Should be `shareAmount` instead of `usdyAmount`
  uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
  if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
  _burnShares(msg.sender, usdyAmount);
  usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
  emit TokensBurnt(msg.sender, _rUSDYAmount);
}

function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
  // @audit Solidity 0.8 will revert when doing `x * y`
  require(y == 0 || (z = x * y) / y == x);
}

function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
  require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");

  // @audit Should be `shareAmount` instead of `usdyAmount`
  uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
  if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
  _burnShares(msg.sender, usdyAmount);
  usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
  emit TokensBurnt(msg.sender, _rUSDYAmount);
}


Stormreckson Q

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/RSETH.sol#L14-L20

1- contract does inherit it's own interface

`RSETH.SOL` is not inheriting `IRSETH.SOL`

Contract implementations should inherit their interfaces. Extending an interface ensures that all function signatures are correct, and catches mistakes introduced by typos

```
contract RSETH is
    Initializable,
    LRTConfigRoleChecker,
    ERC20Upgradeable,
    PausableUpgradeable,
    AccessControlUpgradeable
```
##FIX

```
contract RSETH is IRSETH
    Initializable,
    LRTConfigRoleChecker,
    ERC20Upgradeable,
    PausableUpgradeable,
    AccessControlUpgradeable
```

2- Upgradable Tokens can break the contract behavior.

Some tokens (e.g. USDC, USDT) are upgradable, allowing the token owners to make arbitrary modifications to the logic of the token at any point in time.

A change to the token semantics can break any smart contract that depends on the past behaviour.
Consider introducing logic that will freeze interactions with the token in question if an upgrade is detected.

For example TUSD adapter used by MakerDao ( https://github.com/makerdao/dss-deploy/blob/7394f6555daf5747686a1b29b2f46c6b2c64b061/src/join.sol#L321)

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

New supported asset contract address might not be ERC20-Compatible.

it is good practice to validate whether a new asset address is an ERC20-compatible contract before adding it as a supported asset. This validation helps ensure that only valid `ERC20` tokens are added to the supported asset list.

To check if an address corresponds to an `ERC20` contract, you can utilize the `ERC20` interface's ` totalSupply() ` function. If the  `totalSupply()`  function exists and returns a value without throwing an exception, it indicates that the address is an `ERC20` contract. Here's an example of how you can perform the check:

 `

// Code block 1 (unknown):
contract RSETH is
    Initializable,
    LRTConfigRoleChecker,
    ERC20Upgradeable,
    PausableUpgradeable,
    AccessControlUpgradeable

// Code block 2 (unknown):
contract RSETH is IRSETH
    Initializable,
    LRTConfigRoleChecker,
    ERC20Upgradeable,
    PausableUpgradeable,
    AccessControlUpgradeable


kinda_very_good Analysis

### [H-1] The first user to deposit could set a bad ratio

**Description:** Depositing and collateral addition does not rely on price returned by aggregator meaning the first depositer would determine the price ratio of the pool

**Impact:** The protocol would be griefed

**Recommended Mitigation:** The initial addition of liquidity especially in the important pools (weth,wbtc and dai) should be made off a price feed

### [H-2] setInitialFeeds does not use cool down

**Description:** PriceAggregator::setInitialFeeds allows the owner to set the priceFeeds before the cooldown period is over

**Impact:** owner could set bad feed

**Recommended Mitigation:** PriceAggregator::setInitialFeeds should only be callable once

### [L-1] Aggregate price could revert on correct price

**Description:** AggregatePrice::_aggregate would return zero if the two closest prices are exactly the same

**Impact:** Users could be griefed

```
if (  (_absoluteDifference(priceA, priceB) * 100000) / averagePrice > maximumPriceFeedPercentDifferenceTimes1000 )

if the two prices are exactly the same their absolute difference would be zero

```

**Recommended Mitigation:** A check should be included to check if the two prices are exactly the same and then one of the prices could be returned

### [I-1] Named mappings should be used

### Time spent:
22 hours

// Code block 1 (unknown):
if (  (_absoluteDifference(priceA, priceB) * 100000) / averagePrice > maximumPriceFeedPercentDifferenceTimes1000 )

if the two prices are exactly the same their absolute difference would be zero


Secureverse G

### [Gas-01] ```onlyOwner``` functions Should make as ```payable```, Those function which reverts when called by normal user should make as payable
*Instances(10)*
```solidity
File: SmartAccount.sol

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L449-L453
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L455-L458
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L460-L463
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L465-L473
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L536-L538
```
```solidity
File: paymasters/BasePaymaster.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L67
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L75
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L90
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L99
```
```solidity
File: paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L65
```

### [Gas-02] ``uncheck``` Arithmatic Operation that would never be ````Underflow/Overflow```
*Instances(1)*
```solidity
File: base/ModuleManager.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L124
```

### [Gas-

SmartAccount.sol#L449-L453, SmartAccount.sol#L455-L458, SmartAccount.sol#L460-L463, SmartAccount.sol#L465-L473, SmartAccount.sol#L536-L538, BasePaymaster.sol#L67, BasePaymaster.sol#L75, BasePaymaster.sol#L90, BasePaymaster.sol#L99, VerifyingSingletonPaymaster.sol#L65, ModuleManager.sol#L124

ModuleManager.sol, SmartAccount.sol, VerifyingSingletonPaymaster.sol, BasePaymaster.sol


adeolu Q

# no need for "<= 0" check on uint256 value

**Explanation**
since value is uint256, its lowest value by default is 0, so the check should be "== 0" not "<= 0"

**Proof Of Concept**
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/governance/AmphoraProtocolToken.sol#L14
```
  constructor(
    address _account,
    uint256 _initialSupply
  ) ERC20('Amphora Protocol', 'AMPH') ERC20Permit('Amphora Protocol') {
    if (_account == address(0)) revert AmphoraProtocolToken_InvalidAddress();
    if (_initialSupply <= 0) revert AmphoraProtocolToken_InvalidSupply();

    _mint(_account, _initialSupply);
  }
```

**Recommended Mitigation**

change <= to ==
```
  constructor(
    address _account,
    uint256 _initialSupply
  ) ERC20('Amphora Protocol', 'AMPH') ERC20Permit('Amphora Protocol') {
    if (_account == address(0)) revert AmphoraProtocolToken_InvalidAddress();
    if (_initialSupply == 0) revert AmphoraProtocolToken_InvalidSupply();

    _mint(_account, _initialSupply);
  }
```

// Code block 1 (unknown):
constructor(
    address _account,
    uint256 _initialSupply
  ) ERC20('Amphora Protocol', 'AMPH') ERC20Permit('Amphora Protocol') {
    if (_account == address(0)) revert AmphoraProtocolToken_InvalidAddress();
    if (_initialSupply <= 0) revert AmphoraProtocolToken_InvalidSupply();

    _mint(_account, _initialSupply);
  }

// Code block 2 (unknown):
constructor(
    address _account,
    uint256 _initialSupply
  ) ERC20('Amphora Protocol', 'AMPH') ERC20Permit('Amphora Protocol') {
    if (_account == address(0)) revert AmphoraProtocolToken_InvalidAddress();
    if (_initialSupply == 0) revert AmphoraProtocolToken_InvalidSupply();

    _mint(_account, _initialSupply);
  }


PENGUN Q

## sETH2 does not have a chainlink price feed.

The new token sETH2 supported by EigenLayer does not have a chainlink price feed and therefore cannot be used in the current implementation.

> Eigenlayer supports stETH, rETH and cbETH as of today. But it intends to add more LSTs in the future through its governance. KelpDao also wants to add support for tokens that Eigenlayer will support in the future.

https://discord.com/channels/810916927919620096/1171865604114882600/1172948255412342824

According to the team, they are trying to support assets that Eigenlayer wants to support. To do so, it is necessary to use a price feed other than chainlink.

## Contract info cannot be changed once it is set.

```solidity
function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (contractMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        contractMap[key] = val;
        emit SetContract(key, val);
    }

```

contractMap is used to store contract addresses like LRTOracle.
However, once set in _setContract, it cannot be updated.

It is recommended to allow updates for already set keys, so they can be used in situations where contract addresses need to be updated.

## pause does nothing.

LRTOracle has a pause function.

```solidity
/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager { //@audit pause nothing
        _pause();
    }

```

However, it does not use modifiers like whenNotPaused that prevent certain actions during a pause, so essentially the pause does nothing.

It is recommended to take measures to prevent major functions like getRSETHPrice from being used during a pause.

## The return value of depositIntoStrategy must be stored.

```solidity
function depositIntoStrategy(
        IStrategy strategy,
        IERC20 token,
        uint256 amount
    ) external onlyWhenNotPaused(PAUSED_DEPOSITS) nonReentrant returns (uint256 shares) {

// Code block 1 (solidity):
function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (contractMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        contractMap[key] = val;
        emit SetContract(key, val);
    }

// Code block 2 (solidity):
/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager { //@audit pause nothing
        _pause();
    }

function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);
        if (contractMap[key] == val) {
            revert ValueAlreadyInUse();
        }
        contractMap[key] = val;
        emit SetContract(key, val);
    }

/// @dev Triggers stopped state. Contract must not be paused.
    function pause() external onlyLRTManager { //@audit pause nothing
        _pause();
    }


ybansal2403 G

| Number | Optimization Details                                                                                | Instances |
| :----: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: |
| [G-01] | Use assembly to check for the zero address                                                                                |     4     |
| [G-02] | State variables access within a loop                                                                                |     2     |
| [G-03] | Use of memory instead of storage for struct/array state variables                                                                                |     3     |
| [G-04] | Avoid contract existence checks by using low level calls                                                                                |     9     |
| [G-05] | Using ternary operator instead of if else saves gas                                                                                |     5     |
| [G-06] | Cache state variables with stack variables                                                                                |     18     |
| [G-07] | Do not calculate constants variables                                                                                |     5     |
| [G-08] | The use of a logical AND in place of double if is slightly less gas efficient in instances where there isn't a corresponding else statement for the given if statement |     4     |
| [G-09] | Using pre instead of post increments/decrements

File: core/solidity/contracts/core/VaultController.sol
112: if (address(_oldVaultController) != address(0)) _migrateCollateralsFrom(_oldVaultController, _tokenAddresses);

File: core/solidity/contracts/core/Vault.sol
207: if (address(_amphClaimer) != address(0)) {

269: if (address(_amphClaimer) != address(0)) {


Shubham Q

## Non-Critical

| |Issue|Instances|
|-|:-|:-:|
| [N-1](#N-1) | 10 ** 18 can be changed to 1e18 for better readability  | 20 |
| [N-2](#N-2) | Unlocked compiler version  | 4 |

## [N-1] 10 ** 18 can be changed to 1e18 for better readability

10 ** 18 can be changed to 1e18 to avoid unnecessary arithmetic operation, better readability and might save gas.

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

173:         uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
174:         ((10 ** 18 - maxSlippage))) / 10 ** 18);

214:         RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
215:         else return (poolPrice() * 10 ** 18) / (10 ** 18);
```

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

74:         uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
75:         (10 ** 18 - maxSlippage)) / 10 ** 18;

113:        10 ** 18
115:        return ((10 ** 18 * frxAmount) /
```

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol

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

87:        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);
```

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol

55:        maxAmount = 200 * 10 ** 18;
75:        10 ** 18;
80:        preDepositPrice = 10 ** 18;
81:        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
94:        ) * depositAmount) / 10 ** 18;
98:        uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
```
## [N-02] Unlocked compiler version

Pragma version for most contracts is ^0.7.5. Keep the pragma compiler version locked to a specific version as a best practice.
Preferably the one everything has been tested most with.

There are 4 instances of this case.

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

// Code block 1 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

173:         uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
174:         ((10 ** 18 - maxSlippage))) / 10 ** 18);

214:         RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
215:         else return (poolPrice() * 10 ** 18) / (10 ** 18);

// Code block 2 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

74:         uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
75:         (10 ** 18 - maxSlippage)) / 10 ** 18;

113:        10 ** 18
115:        return ((10 ** 18 * frxAmount) /

// Code block 3 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol

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

87:        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);

// Code block 4 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol

55:        maxAmount = 200 * 10 ** 18;
75:        10 ** 18;
80:        preDepositPrice = 10 ** 18;
81:        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
94:        ) * depositAmount) / 10 ** 18;
98:        uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;

File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

173:         uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
174:         ((10 ** 18 - maxSlippage))) / 10 ** 18);

214:         RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
215:         else return (poolPrice() * 10 ** 18) / (10 ** 18);

File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

74:         uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
75:         (10 ** 18 - maxSlippage)) / 10 ** 18;

113:        10 ** 18
115:        return ((10 ** 18 * frxAmount) /

File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol

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

87:        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);

File: 2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol

55:        maxAmount = 200 * 10 ** 18;
75:        10 ** 18;
80:        preDepositPrice = 10 ** 18;
81:        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
94:        ) * depositAmount) / 10 ** 18;
98:        uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;


DavidGiladi Q

### Low Issues
|Title|Issue|Instances|
|-|:-|:-:|
|[L-1] Require Should Used Instead of Assert | [Require Should Used Instead of Assert](#require-should-used-instead-of-assert) | 1 |
|[L-2] Burn functions must be protected with a modifier | [Burn functions must be protected with a modifier](#burn-functions-must-be-protected-with-a-modifier) | 2 |
|[L-3] Casting block.timestamp can reduce the lifespan of a contract | [Casting block.timestamp can reduce the lifespan of a contract](#casting-blocktimestamp-can-reduce-the-lifespan-of-a-contract) | 1 |
|[L-4] Code does not follow the best practice of check-effects-interaction | [Code does not follow the best practice of check-effects-interaction](#code-does-not-follow-the-best-practice-of-check-effects-interaction) | 11 |
|[L-5] Divide before multiply | [Divide before multiply](#divide-before-multiply) | 4 |
|[L-6] Some tokens may not consider type(uint256).max as an infinite approval | [Some tokens may not consider type(uint256).max as an infinite approval](#some-tokens-may-not-consider-typeuint256max-as-an-infinite-approval) | 6 |
|[L-7] ERC20 Approve Call is Not Safe | [ERC20 Approve Call is Not Safe](#erc20-approve-call-is-not-safe) | 6 |
|[L-8] Empty Uncontrolled Ether Flow in receive()/payable fallback() | [Empty Uncontrolled Ether Flow in receive()/payable fallback()](#empty-uncontrolled-ether-flow-in-receivepayable-fallback) | 1 |
|[L-9] Local variable shadowing | [Local variable shadowing](#local-variable-shadowing) | 1 |
|[L-10] Loss of Precision in Division by Large Numbers | [Loss of Precision in Division by Large Numbers](#loss-of-precision-in-division-by-large-numbers) | 9 |
|[L-11] Missing zero address validation in constructor | [Missing zero address validation in constructor](#missing-zero-address-validation-in-constructor) | 42 |
|[L-12] External calls in an un-bounded for-loop may result in a DOS | [External calls in an un-bounded for-loop may result in a DOS](#external-calls-in-an-un-bounded-for-loop-

File: contracts/LiquidationLibrary.sol

564    assert(toLiquidator < _totalColToSend)

File: contracts/EBTCToken.sol

95    function burn(address _account, uint256 _amount) external override


Raihan Q

## [L-01] _safeMint() should be used rather than _mint() wherever possible
_mint() is [discouraged](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d4d8d2ed9798cc3383912a23b5e8d5cb602f7d4b/contracts/token/ERC721/ERC721.sol#L271) in favor of _safeMint() which ensures that the recipient is either an EOA or implements IERC721Receiver. Both open [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d4d8d2ed9798cc3383912a23b5e8d5cb602f7d4b/contracts/token/ERC721/ERC721.sol#L238-L250) and [solmate](https://github.com/Rari-Capital/solmate/blob/4eaf6b68202e36f67cab379768ac6be304c8ebde/src/tokens/ERC721.sol#L180) have versions of this function so that NFTs aren’t lost if they’re minted to contracts that cannot transfer them back out.

```solidity
File:   src/RSETH.sol
48     _mint(to, amount);
```
https://github.com/code-423n4/2023-11-kelp/blob/main/src/RSETH.sol#L48

## [L-02] Use `safetransfer` Instead Of `transfer`
It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.
For example, Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert.

```solidity
File:  src/LRTDepositPool.sol
194   if (!IERC20(asset).transfer(nodeDelegator, amount)) {
```
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L194

```solidity
File:  src/NodeDelegator.sol
86  if (!IERC20(asset).transfer(lrtDepositPool, amount)) {
```
https://github.

// Code block 1 (solidity):
File:   src/RSETH.sol
48     _mint(to, amount);

// Code block 2 (solidity):
File:  src/LRTDepositPool.sol
194   if (!IERC20(asset).transfer(nodeDelegator, amount)) {

// Code block 3 (solidity):
File:  src/NodeDelegator.sol
86  if (!IERC20(asset).transfer(lrtDepositPool, amount)) {

File:   src/RSETH.sol
48     _mint(to, amount);

File:  src/LRTDepositPool.sol
194   if (!IERC20(asset).transfer(nodeDelegator, amount)) {

File:  src/NodeDelegator.sol
86  if (!IERC20(asset).transfer(lrtDepositPool, amount)) {

ERC721.sol#L271, ERC721.sol#L238-L250, ERC721.sol#L180, RSETH.sol#L48, LRTDepositPool.sol#L194


ABAIKUNANBAEV Q

## Finding Summary

| ID | Severity | Description |
| - | - | :-: |
| [L-01](#l-01-Missing-checks-for-zero-amount-transfers-in-univ3liquidityamo.sol) | Low | Missing checks for zero amount transfers in `UniV2LiquidityAmo.sol` and `UniV3LiquidityAmo.sol`
| [L-02](#l-02-Typo-in-the-comments) | Low | Typo in the comments
| [L-03](#l-03-safeerc20-and-safetransferlib-libraries-can-be-used-interchangeably) | Low | `SafeERC20` and `SafeTransferLib` libraries can be used interchangeably
| [L-04](#l-04-there-is-no-need-for-two-slippage-variables-in-relpcontract.sol) | Low | There is no need for two slippage variables in `ReLPContract.sol`
| [L-05](#l-05-unchecked-return-data-when-making-arbitrary-calls-to-the-to-address) | Low | Unchecked return data when making arbitrary calls to the `to` address
| [L-06](#l-06-erc721burnable-is-initialized-but-its-functionality-is-never-used-in-rpdxdecayingbonds.sol) | Low | ERC721Burnable is initialized but its functionality is never used in RpdxDecayingBonds.sol
| [L-07](#l-07-admin-is-a-single-point-of-failure-when-calling-emergencywithdraw-due-to-unspecified-to-address) | Low | Admin is a single point of failure when calling `emergencyWithdraw()` due to unspecified `to` address

## [L-01] Missing checks for zero amount transfers in `UniV2LiquidityAmo.sol` and `UniV3LiquidityAmo.sol`

In `UniV2LiquidityAmo.sol` and `UniV3LiquiidtyAmo.sol`, there is no check whether `tokenABalance` and `tokenBBalance` being sent to the `RpdxV2Core` via `safeTransfer()` are not zero. Therefore, there can be a situation where there are zero amount transfers:

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/amo/UniV2LiquidityAmo.sol#L168-175
```
  IERC20WithBurn(addresses.tokenA).safeTransfer(
      addresses.rdpxV2Core,
      tokenABalance
    );
    IERC20WithBurn(addresses.tokenB).safeTransfer(
      addresses.rdpxV2Core,
      tokenBBalance
    );
```

### Recommendation

Consider adding the following checks:

```
require(tokenABalan

require(tokenABalance > 0)
require(tokenBBalance > 0)

// Code block 1 (unknown):
IERC20WithBurn(addresses.tokenA).safeTransfer(
      addresses.rdpxV2Core,
      tokenABalance
    );
    IERC20WithBurn(addresses.tokenB).safeTransfer(
      addresses.rdpxV2Core,
      tokenBBalance
    );


dharma09 G

SALTY GAS OPTIMIZATIONS
--
INTRODUCTION
--
Highlighted below are optimizations exclusively targeting state-mutating functions and view/pure functions invoked by state-mutating functions. In the discussion that follows, only runtime gas is emphasized, given its inevitable dominance over deployment gas costs throughout the protocol's lifetime.

Please be aware that some code snippets may be shortened to conserve space, and certain code snippets may include `@audit` tags in comments to facilitate issue explanations."
- [Gas report](#gas-report)
  - [Table Of Contents](#table-of-contents)
  - [ Only Make SLOADS when necessary](#g-01-only-make-sloads-when-necessary)
  - [Directly modifies the value in the storage location without reading the current value first](#g-02-directly-modifies-the-value-in-the-storage-location-without-reading-the-current-value-first)
  - [Cache state variable in memory before emiting event](#g-03-cache-state-variable-in-memory-before-emiting-event)
  - [Caching the length of the array can be a good optimization strategy](#g-04-caching-the-length-of-the-array-can-be-a-good-optimization-strategy)
  - [When variables are declared with the storage keyword caching any fields that need to be re-read in stack variables Saves gas](#g-05-when-variables-are-declared-with-the-storage-keyword-caching-any-fields-that-need-to-be-re-read-in-stack-variables-saves-gas)
  - [`ballot` is only used once, no need to cache `proposals.ballotForID(ballotID)`](#g-06-ballot-is-only-used-once-no-need-to-cache-proposalsballotforidballotid)
  - [Use uint8 for Constants and we can save 1 slods by pack with state variable](#g-07-use-uint8-for-constants-and-we-can-save-1-slods-by-pack-with-state-variable)
  - [Move lesser gas costing require checks to the top](#g-08-move-lesser-gas-costing-require-checks-to-the-top)
  - [Add unchecked blocks for subtractions where the operands cannot underflow](#g-09-add-unchecked-blocks-for-subtractions-where-the-operands-cannot-underflow)

File: src/staking/StakingRewards.sol
57: function _increaseUserShare( address wallet, bytes32 poolID, uint256 increaseShareAmount, bool useCooldown ) internal
		{
		require( poolsConfig.isWhitelisted( poolID ), "Invalid pool" );
		require( increaseShareAmount != 0, "Cannot increase zero share" );

		UserShareInfo storage user = _userShareInfo[wallet][poolID]; //@audit cache vaiable when its use write down after below cheack

		if ( useCooldown )
		if ( msg.sender != address(exchangeConfig.dao()) ) // DAO doesn't use the cooldown
			{
			require( block.timestamp >= user.cooldownExpiration, "Must wait for the cooldown to expire" );

			// Update the cooldown expiration for future transactions
			user.cooldownExpiration = block.timestamp + stakingConfig.modificationCooldown();
			}

		uint256 existingTotalShares = totalShares[poolID];

### [G-02] Directly modifies the value in the storage location without reading the current value first
**Details:**
 using pre-increment `(++i)` instead of the addition assignment `(i += 1)` can sometimes result in more efficient code. By doing this we can avoid state variable read as a result we can save gas.
**Proof of Code:**
- [AccessManager.sol#L40](https://github.com/code-423n4/2024-01-salty/blob/main/src/AccessManager.sol#L40C1-L46C7)


No12Samurai Q

# QA Report

## Summary

This QA report highlights two issues related to event parameter inconsistency and incorrect order in the Lybra protocol contracts. The first issue involves the `ClaimReward` event emission in the `ProtocolRewardsPool.sol` contract, where incorrect parameters are used. The second issue is about the `WithdrawAsset` event emission in the `LybraPeUSDVaultBase.sol` contract, where the parameter order is inconsistent between the event definition and its usage. Both issues can lead to confusion and misinterpretation of the event data.

## Issue 1: Incorrect Parameters in `ProtocolRewardsPool.getReward()` Function Event Emission

In the `ProtocolRewardsPool.sol` contract, the `getReward()` function at [L190-L218](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L190-L218) emits the `ClaimReward` event at [L214](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L214) with incorrect parameters. The event emission lacks essential reward information, such as the address of `token` and the `tokenAmount`. The following event is emitted at [L214](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L214):

```solidity
ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(0), 0, block.timestamp);
```

The event is defined at [L46](https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L46) with the following parameters:

```solidity
event ClaimReward(address indexed user, uint256 eUSDAmount, address token, uint256 tokenAmount, uint256 time);
```

To resolve this issue, the event emission line in the `getReward()` function should be modified to include the correct parameters. The recommended modification is a

// Code block 1 (solidity):
ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(0), 0, block.timestamp);

// Code block 2 (solidity):
event ClaimReward(address indexed user, uint256 eUSDAmount, address token, uint256 tokenAmount, uint256 time);

ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(0), 0, block.timestamp);

event ClaimReward(address indexed user, uint256 eUSDAmount, address token, uint256 tokenAmount, uint256 time);


Musaka Q

# Table of findings

|          |                                                                                |
|----------|------------------------------------------------------------------------------------|
| **L-01** | `executeFlashloan` does not check if funds are bigger or equal after the execution |
| **L-02** | EUSD can be transferred even when paused.                                          |

# Findings

**[LOW-01]** `executeFlashloan` does not check if funds are **>=** after the execution.

In [`PeUSDMainnetStableVision`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/PeUSDMainnetStableVision.sol), there is an [`executeFlashloan`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/PeUSDMainnetStableVision.sol#L129-L139) function that, as the name suggests, is used for users to make flash loans. However, best practices are not followed in this function. It pulls funds from the receiver and does not check if the funds are greater than or equal to the amount before the flash loan.
Add a check for if the funds are more or equal to the ones before the flash loan.
```jsx
    function executeFlashloan(FlashBorrower receiver, uint256 eusdAmount, bytes calldata data) public payable {
        uint256 shareAmount = EUSD.getSharesByMintedEUSD(eusdAmount);
+       uint sharesBefore = balanceOf(address(this); //balance of on EUSD gets the shares of an account
        EUSD.transferShares(address(receiver), shareAmount);
        receiver.onFlashLoan(shareAmount, data);//audot LOW cuz it does not check balances before and after
        bool success = EUSD.transferFrom(address(receiver), address(this), EUSD.getMintedEUSDByShares(shareAmount));
        require(success, "TF");
+       uint sharesAfter = balanceOf(address(this);
+       require(sharesBefore <= sharesAfter,"FL failed");

        uint256 burnShare = getFee(shareAmount);
        EUSD.burnShares(address(receiver), burnShare);
        emit Flashloaned

**[LOW-02]** EUSD can be transferred even when paused.

In the [comments](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/EUSD.sol#L222), the developers state that a transfers and increase or decrease on allowances  should not be possible if the contract is paused. However, the [function](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/EUSD.sol#L226) lacks the modifier for pausing transfers. Here's an example of the code:

PeUSDMainnetStableVision.sol, PeUSDMainnetStableVision.sol#L129-L139, EUSD.sol#L222, EUSD.sol#L226


Breeje Q

# QA Report

## Low Risk Issues
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | `pragma experimental ABIEncoderV2` Used is deprecated | 1 |
| [L-02] | Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions | 1 |
| [L-03] | Use of `ecrecover` can lead to signature mallebility vulnerability | 1 |

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

## Non-Critical Issues
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [N-01] | Function state mutability can be restricted to pure | 2 |
| [N-02] | Variable name should be in CamelCase | 1 |
| [N-03] | No Error Message provided for `require` | 3 |
| [N-04] | Spelling Errors in Natspec | 1 |
| [N-05] | Recommended to use 2 step while Updating Critical addresses | 2 |

| Total Non-Critical Issues | 5 |
|:--:|:--:|

### [L-01] `pragma experimental ABIEncoderV2` Used is deprecated

## Description

`pragma experimental ABIEncoderV2` Used is deprecated. Should use `pragma abicoder v2` instead which supports more types than v1 and performs more sanity checks on the inputs.

ABI coder v2 is activated by default in Solidity Version ^0.8.0. So it is already Enabled without explictly enabling it.

## Reference

* [Breaking Changes in Solidity ^0.8.0](https://github.com/ethereum/solidity/blob/69411436139acf5dbcfc5828446f18b9fcfee32c/docs/080-breaking-changes.rst#silent-changes-of-the-semantics)

## Recommendation Mitigation Step

Remove pragma experimental ABIEncoderV2 from the following code instance.

*Instance (1):*
```solidity
File: DistributionTypes.sol

2:    pragma experimental ABIEncoderV2;

```
[Link to code](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/libraries/DistributionTypes.sol#L2)

### [L-02] Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions

See [this](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps) link for a descript

File: ReaperBaseStrategyv4.sol

14:   abstract contract ReaperBaseStrategyv4 is

// Code block 1 (solidity):
File: DistributionTypes.sol

2:    pragma experimental ABIEncoderV2;


0xweb3boy Q

# [L-1] - ClaimNRN will revert when everybody send their 100% points to the Merging Pool

```solidity
function claimNRN() external {
        require(numRoundsClaimed[msg.sender] < roundId, "Already claimed NRNs for this period");
        uint256 claimableNRN = 0;
        uint256 nrnDistribution;
        uint32 lowerBound = numRoundsClaimed[msg.sender];
        for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) {
            nrnDistribution = getNrnDistribution(currentRound);
            claimableNRN += (
                accumulatedPointsPerAddress[msg.sender][currentRound] * nrnDistribution
            ) / totalAccumulatedPoints[currentRound];
            numRoundsClaimed[msg.sender] += 1;
        }
        if (claimableNRN > 0) {
            amountClaimed[msg.sender] += claimableNRN;
            _neuronInstance.mint(msg.sender, claimableNRN);
            emit Claimed(msg.sender, claimableNRN);
        }
    }
```

The above function will revert when everyone decide to send their 100% points to the MergingPool since `mergingPortion`  matters in `_addResultPoints`

```solidity
if (battleResult == 0) {
            /// If the user won the match

            /// If the user has no NRNs at risk, then they can earn points
            if (stakeAtRisk == 0) {
                points = stakingFactor[tokenId] * eloFactor;
            }

            /// Divert a portion of the points to the merging pool
            uint256 mergingPoints = (points * mergingPortion) / 100;
            points -= mergingPoints;
            _mergingPoolInstance.addPoints(tokenId, mergingPoints);
```

If user won a match and his mergingPortion is 100%, all of his points are sent to mergingPool.And the line `points -= mergingPoints`, points will be 0 in that case and no points are added to the rankedBattle accounting here:

```solidity
accumulatedPointsPerFighter[tokenId][roundId] += points;
            accumulatedPointsPerAddress[fighterOwner][roundId] += points;

// Code block 1 (solidity):
function claimNRN() external {
        require(numRoundsClaimed[msg.sender] < roundId, "Already claimed NRNs for this period");
        uint256 claimableNRN = 0;
        uint256 nrnDistribution;
        uint32 lowerBound = numRoundsClaimed[msg.sender];
        for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) {
            nrnDistribution = getNrnDistribution(currentRound);
            claimableNRN += (
                accumulatedPointsPerAddress[msg.sender][currentRound] * nrnDistribution
            ) / totalAccumulatedPoints[currentRound];
            numRoundsClaimed[msg.sender] += 1;
        }
        if (claimableNRN > 0) {
            amountClaimed[msg.sender] += claimableNRN;
            _neuronInstance.mint(msg.sender, claimableNRN);
            emit Claimed(msg.sender, claimableNRN);
        }
    }

// Code block 2 (solidity):
if (battleResult == 0) {
            /// If the user won the match

            /// If the user has no NRNs at risk, then they can earn points
            if (stakeAtRisk == 0) {
                points = stakingFactor[tokenId] * eloFactor;
            }

            /// Divert a portion of the points to the merging pool
            uint256 mergingPoints = (points * mergingPortion) / 100;
            points -= mergingPoints;
            _mergingPoolInstance.addPoints(tokenId, mergingPoints);

function claimNRN() external {
        require(numRoundsClaimed[msg.sender] < roundId, "Already claimed NRNs for this period");
        uint256 claimableNRN = 0;
        uint256 nrnDistribution;
        uint32 lowerBound = numRoundsClaimed[msg.sender];
        for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) {
            nrnDistribution = getNrnDistribution(currentRound);
            claimableNRN += (
                accumulatedPointsPerAddress[msg.sender][currentRound] * nrnDistribution
            ) / totalAccumulatedPoints[currentRound];
            numRoundsClaimed[msg.sender] += 1;
        }
        if (claimableNRN > 0) {
            amountClaimed[msg.sender] += claimableNRN;
            _neuronInstance.mint(msg.sender, claimableNRN);
            emit Claimed(msg.sender, claimableNRN);
        }
    }

if (battleResult == 0) {
            /// If the user won the match

            /// If the user has no NRNs at risk, then they can earn points
            if (stakeAtRisk == 0) {
                points = stakingFactor[tokenId] * eloFactor;
            }

            /// Divert a portion of the points to the merging pool
            uint256 mergingPoints = (points * mergingPortion) / 100;
            points -= mergingPoints;
            _mergingPoolInstance.addPoints(tokenId, mergingPoints);