Kaysoft Q

## [L-1] Consider using 2 step ownership transfer

The Neuron.sol#transferOwnership() function transfers ownership in a single step. This can cause the ownership of the contract to be easily lost when a wrong address is mistakenly entered.

With 2 step ownership transfer, the above mistake can be recovered from because the new owner send a confirmation trasnaction to accept ownership while its pending.

there 7 instances of these
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L85C5-L88C6
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L61C5-L64C6
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L120
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L108
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L89
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L167
- https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/VoltageManager.sol#L64

```
File: Neuron.sol
function transferOwnership(address newOwnerAddress) external {
        require(msg.sender == _ownerAddress);
        _ownerAddress = newOwnerAddress;
    }
```

Recommendation:
Consider using Openzeppelin's Ownable2step library.

## [L-2] Consider having functions that can also remove `minter`, `staker` and `spender`

The `addMinter`, `addStaker` and `addSpender` function are used to grant roles to some contracts. `owner` should be able to also `revoke` roles.

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L93C5-L112C6

Recommendation:
Consider creating functions that allows revoking the above roles.

## [L-3] FighterFa

/// @notice Adds a new address that is allowed to stake fighters on behalf of users.
    /// @dev Only the owner address is authorized to call this function.
    /// @param newStaker The address of the new staker
    function addStaker(address newStaker) external {
        require(msg.sender == _ownerAddress);
        hasStakerRole[newStaker] = true;
    }

// Code block 1 (unknown):
File: Neuron.sol
function transferOwnership(address newOwnerAddress) external {
        require(msg.sender == _ownerAddress);
        _ownerAddress = newOwnerAddress;
    }

Neuron.sol#L85-L5, AiArenaHelper.sol#L61-L5, FighterFarm.sol#L120, GameItems.sol#L108, MergingPool.sol#L89, RankedBattle.sol#L167, VoltageManager.sol#L64, Neuron.sol#L93-L5

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


matrix_0wl G

## Gas Optimizations

|        | Issue                                                                                |
| ------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| GAS-1  | USE `SELFBALANCE()` INSTEAD OF `ADDRESS(THIS).BALANCE`                                                                                |
| GAS-2  | `<X> += <Y>`/`<X> -= <Y>` COSTS MORE GAS THAN `<X> = <X> + <Y>`/`<X> = <X> - <Y>` FOR STATE VARIABLES                                               |
| GAS-3  | `ABI.ENCODE()` IS LESS EFFICIENT THAN `ABI.ENCODEPACKED()`                                                                                |
| GAS-4  | BEFORE SOME FUNCTIONS, WE SHOULD CHECK SOME VARIABLES FOR POSSIBLE GAS SAVE                                                                         |
| GAS-5  | SETTING THE CONSTRUCTOR TO PAYABLE                                                                                |
| GAS-6  | DIV BY 0                                                                                |
| GAS-7  | DOS WITH BLOCK GAS LIMIT                                                                                |
| GAS-8  | USE FUNCTION INSTEAD OF MODIFIERS                                                                                |
| GAS-9  | INSTEAD OF CALCULATING A STATEVAR WITH KECCAK256() EVERY TIME THE CONTRACT IS MADE PRE CALCULATE THEM BEFORE AND ONLY GIVE THE RESULT TO A CONSTANT |
| GAS-10 | CONSTANT VALUES SUCH AS A CALL TO KECCAK256(), SHOULD USE IMMUTABLE RATHER THAN CONSTANT                                                            |
| GAS-11 | KECCAK256() SHOULD ONLY NEED TO BE

File: src/EthRouter.sol

141:         if (address(this).balance > 0) {

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

203:         if (address(this).balance < minOutputAmount) {

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

290:         if (address(this).balance > 0) {

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

File: src/PrivatePool.sol

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

231:         virtualNftReserves -= uint128(weightSum);

247:                 royaltyFeeAmount += royaltyFee;

252:         netInputAmount += royaltyFeeAmount;

323:         virtualBaseTokenReserves -= uint128(netOutputAmount + protocolFeeAmount + feeAmount);

324:         virtualNftReserves += uint128(weightSum);

341:                 royaltyFeeAmount += royaltyFee;

355:         netOutputAmount -= royaltyFeeAmount;

678:             sum += tokenWeights[i];


Daniel526 Q

## A. Possible underflow when decreasing system Debt:
[Link](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/ActivePool.sol#L207-L214)
If `_amount` is greater than `systemDebt` and not properly handled elsewhere in the code, it could lead to an underflow. Underflows can potentially be exploited to manipulate balances and disrupt the functioning of the contract.
## Impact:
Incorrect accounting of system debt could distort the protocol's financial health and risk assessments. It may provide an inaccurate representation of the protocol's ability to manage and cover its debt, potentially leading to suboptimal decisions by protocol users or administrators.
## Mitigation:
```solidity
function decreaseSystemDebt(uint256 _amount) external override {
    _requireCallerIsBOorCdpM();

    require(_amount <= systemDebt, "ActivePool: Amount exceeds systemDebt");

    uint256 cachedSystemDebt = systemDebt - _amount;

    systemDebt = cachedSystemDebt;
    emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
}
```
## B. Unverified Collateral Token Transfer in 'flashLoan' Function
[Link](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/ActivePool.sol#L261-L310)
The `flashLoan` function allows borrowers to borrow stETH tokens through a flash loan. In this function, `collateral` tokens are transferred to the `receiver` address using the `collateral.transfer(address(receiver), amount);` statement. However, the code does not verify whether the transfer was successful. If the transfer fails for any reason, such as an out-of-gas condition or insufficient token balance, the contract will proceed with the remaining operations, potentially leading to unexpected behavior or loss of funds.
## Impact:
 If the collateral token transfer to the `receiver` address fails, it may result in the loss of funds for the flash loan borrower. This can lead to unexpecte

// ...
// Use a require statement to check the success of the transfer
require(collateral.transfer(address(receiver), amount), "ActivePool: Transfer failed");
// ...

// Code block 1 (solidity):
function decreaseSystemDebt(uint256 _amount) external override {
    _requireCallerIsBOorCdpM();

    require(_amount <= systemDebt, "ActivePool: Amount exceeds systemDebt");

    uint256 cachedSystemDebt = systemDebt - _amount;

    systemDebt = cachedSystemDebt;
    emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
}


3agle Q

### Issues Template

|Letter |Name|Description|
|--|-------|-------|
| NC |  Non-critical | Non risky findings |
| R  | Refactor | Changing the code |

| Total Found Issues | 4 |
|:--:|:--:|

### Non-Critical Template

| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | Missing checks | 2 |

### Refactor Template

| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [R-01] | Documentation Mismatch | 1 |
| [R-02] | Misleading comments | 2 |

### [L-01] Missing checks
- According to the comment, the `amount` should be higher than 0. But there is no check to ensure this
- https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L137
```solidity
    /**
     * @notice Burn the amount of EUSD and payback the amount of minted EUSD
     * Emits a `Burn` event.
     * Requirements:
     * - `onBehalfOf` cannot be the zero address.
     * - `amount` Must be higher than 0.
     * @dev Calling the internal`_repay`function.
     */
    function burn(address onBehalfOf, uint256 amount) external {
        require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
        require(amount>0,"ZERO_AMOUNT"); //Add this check
        _repay(msg.sender, onBehalfOf, amount);
    }
```
- In `LybraEUSDVaultBase.sol`, the check to ensure that "individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000" is missing.
- https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L118-L130
```solidity

    /**
     * @notice The mint amount number of EUSD is minted to the address
     * Emits a `Mint` event.
     *
     * Requirements:
     * - `onBehalfOf` cannot be the zero address.
     * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
     */

    function mint(address onBehalfOf, uint256 amount) external {
        require(onBehalfOf != address(0), "MINT_TO_THE_ZERO_ADDRESS")

    /**
     * @notice The mint amount number of EUSD is minted to the address
     * Emits a `Mint` event.
     *
     * Requirements:
     * - `onBehalfOf` cannot be the zero address.
     * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
     */

    function mint(address onBehalfOf, uint256 amount) external {
        require(onBehalfOf != address(0), "MINT_TO_THE_ZERO_ADDRESS");
        require(amount > 0, "ZERO_MINT");
        _mintEUSD(msg.sender, onBehalfOf, amount, getAssetPrice());
    }

// Code block 1 (solidity):
/**
     * @notice Burn the amount of EUSD and payback the amount of minted EUSD
     * Emits a `Burn` event.
     * Requirements:
     * - `onBehalfOf` cannot be the zero address.
     * - `amount` Must be higher than 0.
     * @dev Calling the internal`_repay`function.
     */
    function burn(address onBehalfOf, uint256 amount) external {
        require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
        require(amount>0,"ZERO_AMOUNT"); //Add this check
        _repay(msg.sender, onBehalfOf, amount);
    }


p0wd3r Q

# The liquidateTroves function in TroveManager does not verify if the Trove is active.

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

Compared to the `liquidate` function, the `liquidateTroves` function is also an external function and does not check if the Trove is active, which could lead to unexpected issues.

```
function liquidate(address _borrower, address _collateral) external override {
        _requireTroveIsActive(_borrower, _collateral);
        ...
}
```

# Add checkContract for RedemptionHelper

Use `checkContract` to verify the correctness of the `setAddresses` function in RedemptionHelper, just like other contracts such as ActivePool.

// Code block 1 (unknown):
function liquidate(address _borrower, address _collateral) external override {
        _requireTroveIsActive(_borrower, _collateral);
        ...
}


Bauchibred Q

# Amphora Protocol QA Report

## **Table of Contents**

| Identifier | Issue                                                                              |
| ---------- | ---------------------------------------------------------------------------------- |
| QA-01      | Prevent underflows in the recovery of stranded tokens                              |
| QA-02      | The `if (_answer <= 0)` check for chainlink return is no longer needed             |
| QA-03      | A different oracle should be used to price stETH                                   |
| QA-04      | Unnecessary dual type casting adds to code complexity                              |
| QA-05      | WUSDA.sol: `mintFor()` should not be allowed to mint to the zero address           |
| QA-06      | Unnecessary calculation due to lack of check for decimal precision                 |
| QA-07      | `_migrateCollateralsFrom()` could be made more effective                           |
| QA-08      | `TriCrypto2Oracle::_get()` should be made more similar to its Vyper implementation |
| QA-09      | Unsafe typecasts                                                                   |
| QA-10      | Functions including boundaries should implement sanity checks                      |
| QA-11      | No protection against division/multiplication by zero                              |
| QA-12      | Fix typos                                                                          |
| QA-13      | Underflow in `withdraw()` and `withdrawTo()` not protected against                 |
| QA-14      | More measures should be taken to take care of any stranded tokens in the contract  |
| QA-15      | Lack of consistency in `_mint()` and `_burn()` functions                           |

## QA-01 - Prevent underflows in the recovery of stranded tokens

### Vulnerability Details

The current implementation of the contract includes a function, `recoverDust`, which is designed to manage the risk of tokens accidentally being sent

function recoverDust(address _to) external onlyOwner {
    uint256 _amount = sUSD.balanceOf(address(this)) - reserveAmount;
    require(sUSD.transfer(_to, _amount), "Transfer failed");
}

function recoverDust(address _to) external onlyOwner {
    uint256 balance = sUSD.balanceOf(address(this));
    require(balance > reserveAmount, "Insufficient balance to recover dust");
    uint256 _amount = balance - reserveAmount;
    require(sUSD.transfer(_to, _amount), "Transfer failed");
}


Raihan G

# Gas Optamization
  ## Note:  The last 5 types were not found completely by the bots {30,31,32,33,34,35}
   I found that it was missed by the bots
# SUMMRY
|      |    ISSUE     |  INSTANCE   |
|------|--------------|-------------|
|[G-01]| Can Make The Variable Outside The Loop To Save Gas |13|
|[G-02]| Use do while loops instead of for loops|1|
|[G-03]| Use assembly to write address storage values|9|
|[G-04]| Use nested if statements instead of &&|7|
|[G-05]| Make 3 event parameters indexed when possible|30|
|[G-06]| Use assembly to perform efficient back-to-back calls|2|
|[G-07]| public functions to external|9|
|[G-08]| Expressions for constant values such as a call to keccak256(), should use immutable rather than constant|5|
|[G-09]| internal functions not called by the contract should be removed to save deployment gas|3|
|[G-10]| Amounts should be checked for 0 before calling a transfer|7|
|[G-11]| Do not calculate constants|8|
|[G-12]| State variables can be packed to use fewer storage slots|1|
|[G-13]| Gas savings can be achieved by changing the model for assigning value to the structure |1|
|[G-14]| Use != 0 instead of > 0 for unsigned integer comparison|8|
|[G-15]| Use hardcode address instead address(this)|8|
|[G-16]| use Mappings Instead of Arrays|2|
|[G-17]| Duplicated require()/if() checks should be refactored to a modifier or function|5|
|[G-18]| Not using the named return variables when a function returns, wastes deployment gas|6|
|[G-19]| Unnecessary computation|14|
|[G-20]| keccak256() should only need to be called on a specific string literal once|4|
|[G-21]| array[index] += amount is cheaper than array[index] = array[index] + amount (or related variants)|7|
|[G-22]| Avoid emitting storage values|16|
|[G-23]| Use uint256(1)/uint256(2) instead for true and false boolean states|6|
|[G-24]| Using XOR (^) and OR (|) bitwise equivalents|8|
|[G-25]| When possible, use assembly instead of unchecked{++i}|10|
|[G-26]| Use assembly for math (add, sub, mul,

contract MyContract {
    function sum(uint256[] memory values) public pure returns (uint256) {
        uint256 total = 0;

        for (uint256 i = 0; i < values.length; i++) {
            total += values[i];
        }

        return total;
    }
}

File:  core/solidity/contracts/core/Vault.sol
177   uint256 _crvReward = _rewardsContract.earned(address(this));

187   uint256 _extraRewards = _rewardsContract.extraRewardsLength();

209   (uint256 _takenCVX, uint256 _takenCRV, uint256 _claimableAmph) =
          _amphClaimer.claimable(address(this), this.id(), _totalCvxReward, _totalCrvReward);

257   uint256 _earnedReward = _virtualReward.earned(address(this));


bdmcbri Q

# QA Report

## Low Findings
### L01 - Loop conditions in `_descendList` and `_ascendList` contain incorrect termination condition
The first loop condition when [ascending](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/SortedCdps.sol#L642) and [descending](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/SortedCdps.sol#L619) the sorted cdp list is unnecessary or incorrect, but it is unclear that it causes any problems

I believe this is the correct logic:

```diff
diff --git a/packages/contracts/contracts/SortedCdps.sol b/packages/contracts/contracts/SortedCdps.sol
index 0fd34e7..f37cdc4 100644
--- a/packages/contracts/contracts/SortedCdps.sol
+++ b/packages/contracts/contracts/SortedCdps.sol
@@ -626,7 +626,7 @@ contract SortedCdps is ISortedCdps {
         bytes32 nextId = data.nodes[prevId].nextId;

         // Descend the list until we reach the end or until we find a valid insert position
-        while (prevId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
+        while (nextId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
             prevId = data.nodes[prevId].nextId;
             nextId = data.nodes[prevId].nextId;
         }
@@ -649,7 +649,7 @@ contract SortedCdps is ISortedCdps {
         bytes32 prevId = data.nodes[nextId].prevId;

         // Ascend the list until we reach the end or until we find a valid insertion point
-        while (nextId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
+        while (prevId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
             nextId = data.nodes[nextId].prevId;
             prevId = data.nodes[nextId].prevId;
         }
```

However, removing the first condition also is perfectly valid as the necessary logic for loop termination is redundantly captured in `_validInsertPosition`.  The following logic also

However, removing the first condition also is perfectly valid as the necessary logic for loop termination is redundantly captured in `_validInsertPosition`.  The following logic also works:

If this is not correct, the test cases do not reflect the need for this condition, as all three instances pass the tests.
## Non-Critical Findings

### NC01 - Unnecessary containment check for EnumerableSet

In `RolesAuthority.sol` there are a couple checks to see if an EnumerableSet contains an item before adding it to the set. This is unnecessary as `add` in EnumerableSet will also check for containment.

[L116](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Dependencies/RolesAuthority.sol#L116) in `setRoleCapability`:

// Code block 1 (diff):
diff --git a/packages/contracts/contracts/SortedCdps.sol b/packages/contracts/contracts/SortedCdps.sol
index 0fd34e7..f37cdc4 100644
--- a/packages/contracts/contracts/SortedCdps.sol
+++ b/packages/contracts/contracts/SortedCdps.sol
@@ -626,7 +626,7 @@ contract SortedCdps is ISortedCdps {
         bytes32 nextId = data.nodes[prevId].nextId;

         // Descend the list until we reach the end or until we find a valid insert position
-        while (prevId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
+        while (nextId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
             prevId = data.nodes[prevId].nextId;
             nextId = data.nodes[prevId].nextId;
         }
@@ -649,7 +649,7 @@ contract SortedCdps is ISortedCdps {
         bytes32 prevId = data.nodes[nextId].prevId;

         // Ascend the list until we reach the end or until we find a valid insertion point
-        while (nextId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
+        while (prevId != dummyId && !_validInsertPosition(_NICR, prevId, nextId)) {
             nextId = data.nodes[nextId].prevId;
             prevId = data.nodes[nextId].prevId;
         }


ro Q

1. The entry point address must be a contract, currently, there is no input validation. You can provide an EOA as the entry point.

Instances:

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

Recommendation:
Check that the entry point has code.

2. Update the EIP1271 implementation.

    Notice: The current EIP1271 is forked from gnosis safe. But the implementation is not eip1271 compliant, because gnosis safe implemented it
   prior to the eip completeness.
   There are a few changes to be made:
     1: Update the magic value. The current magic value is:

   ``` solidity
     bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
    ```
    But the compliant magic value for the eip is: 0x1626ba7e

    2: Update the "isValidSignature" parameters.
        In the validation signature, the interface for isValidSignature is:

       function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);

      But the correct compliant interface is:

      function isValidSignature(
    bytes32 _hash,
    bytes memory _signature)
    public
    view
    returns (bytes4 magicValue);

3. Incorrect "validateUserOp" implementation.
    If an invalid signature is provided in "validateUserOp", we should return an invalid message instead of reverting.
   As stated from the eip: "SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch."

  The current implementation just reverts if the signatures don't belong to the owner:
   https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L506

  ```solidity
        require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");
```
   Recommenda

 function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash, address)
    internal override virtual returns (uint256 sigTimeRange) {
        bytes32 hash = userOpHash.toEthSignedMessageHash();
        if (owner != hash.recover(userOp.signature))
            return SIG_VALIDATION_FAILED;
        return 0;
    }

// Code block 1 (solidity):
require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");


RaymondFam Q

## Timelock for `adjustWeight()` and `addDerivative()`
Changes made via `adjustWeight()` and `addDerivative()` are sensitive transactions that may go against users` original portfolio plan with Asymmetry Finance. As such, users of the system should have assurances about the behavior of the changed action(s) they’re about to take.

Here are two typical secnarios that could transpire:
- A user decides to stake 100 ETH after seeing a placing of weights to his desire the system has on the the three derivatives, i.e. Reth, SfrxEth and WstEth. His call happens to be inadvertently front run by [`adjustWeight()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L165-L175) or [`addDerivative()`](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L182-L195) with his ETH now diversified to a different portfolio of staked derivatives.
- The same situation could also happen later, if not front run.

Consider implementing a time lock by making the weight and derivative parameter changes require two steps with a mandatory time window between them. The first step merely broadcasts to users that a particular change is coming, and the second step commits that change after a suitable waiting period. This allows users that do not accept the change to withdraw their positions in time.

## Zero address check
Zero address check for `_contractAddress` should be implemented in `addDerivative()`. It is more crucial than having a zero value check for `_weight` because the former, if there is any any error committed, can never be undone or deleted since we are dealing here with a mapping that has no function implemented to update or make changes to it. If this were to happen, `stake()` would have to forever be dealing with this obsolete mapping variable in its for loops.

## Modularity on import usages
For cleaner Solidity code in conjunction with the rule of modularity and modular programming, use named imports with

## Parameterized address variables
Addresses should be parameterized and initialized instead of being declared as constants.

Here are the contract instances entailed:

[File: Reth.sol#L20-L27](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L20-L27)
[File: SfrxEth.sol#L15-L21](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L15-L21)
[File: WstEth.sol#L13-L18](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L13-L18)

## Unspecific compiler version pragma
For some source-units the compiler version pragma is very unspecific, i.e. ^0.8.13. While this often makes sense for libraries to allow them to be included with multiple different versions of an application, it may be a security risk for the actual application implementation itself. A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up actually checking a different EVM compilation that is ultimately deployed on the blockchain.

Avoid floating pragmas where possible. It is highly recommend pinning a concrete compiler version (latest without security issues) in at least the top-level “deployed” contracts to make it unambiguous which compiler version is being used. Rule of thumb: a flattened source-unit should have at least one non-floating concrete solidity compiler version pragma.

## Use a more recent version of solidity
The protocol adopts version 0.8.13 on writing contracts. For better security, it is best practice to use the latest Solidity version, 0.8.17.

Security fix list in the versions can be found in the link below:

https://github.com/ethereum/solidity/blob/develop/Changelog.md

## Devoid of system documentation and complete technical specification
A system’s design specification and supporting documentation should be almost as important as the system’s implementation itself.

## No storage gap for upgradeable contracts
Consider adding a storage gap at the end of an upgradeable contract, just in case it would entail some child contracts in the future. This would ensure no shifting down of storage in the inheritance chain.

SafEth.sol#L165-L175, SafEth.sol#L182-L195, Reth.sol#L20-L27, SfrxEth.sol#L15-L21, WstEth.sol#L13-L18

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


Nikki Q

1. Incorrect POP operation of the value `reserveTokens` in the function `removeAssetFromtokenReserves`.
Link:
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L287

Summary:
```solidity
    // remove the asset from the mapping
    reservesIndex[_assetSymbol] = 0;

    // add new index for the last element
    reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();
```
This would always delete the last symbol of `reserveTokens` in the array, irrespective of the `_assetSymbol` provided to delete.

POC:
```solidity
  function testPopBugPoc() public {
    // rdpxV2Core.addAssetTotokenReserves(address(rdpx), "RDPX");     - 1
    // rdpxV2Core.addAssetTotokenReserves(address(weth), "WETH");     - 2
    // rdpxV2Core.addAssetTotokenReserves(address(dpxETH), "DPXETH"); - 3
    assertEq(rdpxV2Core.getReserveAssetLength(), 4);
    assertEq(rdpxV2Core.getReserveTokensLength(), 3);

    rdpxV2Core.removeAssetFromtokenReserves("WETH");
    uint len = rdpxV2Core.getReserveTokensLength();
    string memory tokens_ = rdpxV2Core.reserveTokens(len - 1);
    assertNotEq(tokens_, "DPXETH");
    assertEq(tokens_, "WETH");
  }
```

Recommondations:
```
  function testPopBugPoc() public {
    rdpxV2Core.addAssetTotokenReserves(address(rdpx), "RDPX");     - 1
    rdpxV2Core.addAssetTotokenReserves(address(weth), "WETH");     - 2
    rdpxV2Core.addAssetTotokenReserves(address(dpxETH), "DPXETH"); - 3
    assertEq(rdpxV2Core.getReserveAssetLength(), 4);
    assertEq(rdpxV2Core.getReserveTokensLength(), 3);

    rdpxV2Core.removeAssetFromtokenReserves("WETH");
    uint len = rdpxV2Core.getReserveTokensLength();
    string memory tokens_ = rdpxV2Core.reserveTokens(len - 1);

    assertNotEq(tokens_, "DPXETH");
    assertEq(tokens_, "WETH");
  }
``

// Code block 1 (solidity):
// remove the asset from the mapping
    reservesIndex[_assetSymbol] = 0;

    // add new index for the last element
    reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();

// Code block 2 (solidity):
function testPopBugPoc() public {
    // rdpxV2Core.addAssetTotokenReserves(address(rdpx), "RDPX");     - 1
    // rdpxV2Core.addAssetTotokenReserves(address(weth), "WETH");     - 2
    // rdpxV2Core.addAssetTotokenReserves(address(dpxETH), "DPXETH"); - 3
    assertEq(rdpxV2Core.getReserveAssetLength(), 4);
    assertEq(rdpxV2Core.getReserveTokensLength(), 3);

    rdpxV2Core.removeAssetFromtokenReserves("WETH");
    uint len = rdpxV2Core.getReserveTokensLength();
    string memory tokens_ = rdpxV2Core.reserveTokens(len - 1);
    assertNotEq(tokens_, "DPXETH");
    assertEq(tokens_, "WETH");
  }

// remove the asset from the mapping
    reservesIndex[_assetSymbol] = 0;

    // add new index for the last element
    reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();

function testPopBugPoc() public {
    // rdpxV2Core.addAssetTotokenReserves(address(rdpx), "RDPX");     - 1
    // rdpxV2Core.addAssetTotokenReserves(address(weth), "WETH");     - 2
    // rdpxV2Core.addAssetTotokenReserves(address(dpxETH), "DPXETH"); - 3
    assertEq(rdpxV2Core.getReserveAssetLength(), 4);
    assertEq(rdpxV2Core.getReserveTokensLength(), 3);

    rdpxV2Core.removeAssetFromtokenReserves("WETH");
    uint len = rdpxV2Core.getReserveTokensLength();
    string memory tokens_ = rdpxV2Core.reserveTokens(len - 1);
    assertNotEq(tokens_, "DPXETH");
    assertEq(tokens_, "WETH");
  }


InAllHonesty G

## [G-01] `Unauthorized` check in `V3Vault::borrow` should be moved right below the `bool isTransformMode` declaration line to save gas in case of failure.

The function below performs 2 internal functions calls (`_updateGlobalInterest` and `_resetDailyDebtIncreaseLimit`) and a SLOAD before executing the `if (!isTransformMode && tokenOwner[tokenId] != msg.sender && address(this) != msg.sender)` check:
```
function borrow(uint256 tokenId, uint256 assets) external override {
        bool isTransformMode =
            transformedTokenId > 0 && transformedTokenId == tokenId && transformerAllowList[msg.sender];

        (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96) = _updateGlobalInterest();

        _resetDailyDebtIncreaseLimit(newLendExchangeRateX96, false);

        Loan storage loan = loans[tokenId];

        // if not in transfer mode - must be called from owner or the vault itself
@>        if (!isTransformMode && tokenOwner[tokenId] != msg.sender && address(this) != msg.sender) {
@>            revert Unauthorized();
        }
```

That check could be moved right after [Line 552](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L552) to save gas in case of failure.

Please paste the following test into `V3Vault.t.sol`:
```
    function testBorrowUnauthorized() external {
        // 0 borrow loan
        uint256 amount = 1e6;
        _setupBasicLoan(false);

        (,, uint256 collateralValue,,) = vault.loanInfo(TEST_NFT);

        vm.assume(amount <= collateralValue * 100);

        uint256 debtLimit = vault.globalDebtLimit();
        uint256 increaseLimit = vault.dailyDebtIncreaseLimitMin();

        if (amount > debtLimit) {
            vm.expectRevert(IErrors.GlobalDebtLimit.selector);
        } else if (amount > increaseLimit) {
            vm.expectRevert(IErrors.DailyDebtIncreaseLimit.selector);
        } else if (amount > collateralValue) {
            vm.expectRevert(IErrors.

    function testBorrowUnauthorized() external {
        // 0 borrow loan
        uint256 amount = 1e6;
        _setupBasicLoan(false);

        (,, uint256 collateralValue,,) = vault.loanInfo(TEST_NFT);

        vm.assume(amount <= collateralValue * 100);

        uint256 debtLimit = vault.globalDebtLimit();
        uint256 increaseLimit = vault.dailyDebtIncreaseLimitMin();

        if (amount > debtLimit) {
            vm.expectRevert(IErrors.GlobalDebtLimit.selector);
        } else if (amount > increaseLimit) {
            vm.expectRevert(IErrors.DailyDebtIncreaseLimit.selector);
        } else if (amount > collateralValue) {
            vm.expectRevert(IErrors.CollateralFail.selector);
        }

        vm.prank(TEST_NFT_ACCOUNT);
        vm.expectRevert(IErrors.Unauthorized.selector);
        vault.borrow(0, amount); //NFT id hardcoded to 0 to make the function fail
    }

// Code block 1 (unknown):
function borrow(uint256 tokenId, uint256 assets) external override {
        bool isTransformMode =
            transformedTokenId > 0 && transformedTokenId == tokenId && transformerAllowList[msg.sender];

        (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96) = _updateGlobalInterest();

        _resetDailyDebtIncreaseLimit(newLendExchangeRateX96, false);

        Loan storage loan = loans[tokenId];

        // if not in transfer mode - must be called from owner or the vault itself
@>        if (!isTransformMode && tokenOwner[tokenId] != msg.sender && address(this) != msg.sender) {
@>            revert Unauthorized();
        }


alpha Q

1. Use braces around operators with uncertain precedence
In RolesAuthority::canCall we see the following code
```
File: package/contracts/contracts/Dependencies/RolesAuthority.sol
function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) public view virtual override returns (bool) {
return bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
```

In Solidity the & operator will be executed before != but this might not always be clear and might be different in other languages. I suggest adding braces around getUserRoles[user] & getRolesWithCapability[target][functionSig] to clarify operator precedence.

2. More check for save storage
In the RolesAuthority::setRoleCapability method, a require condition should be added at the beginning to ensure that the functionSig is not invoked for the CapabilityFlag.Burned and CapabilityFlag.Public cases:

```
File: package/contracts/contracts/Dependencies/RolesAuthority.sol
function setRoleCapability(
        uint8 role,
        address target,
        bytes4 functionSig,
        bool enabled
    ) public virtual requiresAuth {
require(capabilityFlag[target][functionSig] == CapabilityFlag.None,"xxx");
```
This condition serves to protect the owner from setting roles as burned or public, thus preventing unnecessary storage wastage.

3. `transferOwnership` should be two step process
```
File: package/contracts/contracts/Dependencies/Auth.sol
function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
```
The function transferOwnership() transfer ownership to a new address. In case a wrong address is supplied
ownership is inaccessible.
Consider implementing a two step ownership transfer.

4. Strengthening Checks in the _initializeAuthority Method for Enhanced Reliability
```
File: package/contracts/contracts/Dependencies/AuthNoOwner.sol
function _initializeAuthorit

// Code block 1 (unknown):
File: package/contracts/contracts/Dependencies/RolesAuthority.sol
function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) public view virtual override returns (bool) {
return bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];

// Code block 2 (unknown):
File: package/contracts/contracts/Dependencies/RolesAuthority.sol
function setRoleCapability(
        uint8 role,
        address target,
        bytes4 functionSig,
        bool enabled
    ) public virtual requiresAuth {
require(capabilityFlag[target][functionSig] == CapabilityFlag.None,"xxx");

// Code block 3 (unknown):
File: package/contracts/contracts/Dependencies/Auth.sol
function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }


0xpoor4ever Q

Incorrect comment, The correct comment should be: "Mapping of tokenId to fighter points."
[MergingPool.sol#L50](https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/MergingPool.sol#L50)
```
    /// @notice Mapping of address to fighter points.
    mapping(uint256 => uint256) public fighterPoints;
```

// Code block 1 (unknown):
/// @notice Mapping of address to fighter points.
    mapping(uint256 => uint256) public fighterPoints;


adriro Q

# Report

## Summary

### Low Issues

Total of **7 issues**:

|ID|Issue|
|:--:|:---|
| [L-1](#l-1-supported-assets-cannot-be-removed) | Supported assets cannot be removed |
| [L-2](#l-2-duplicate-access-list-in-rseth-contract) | Duplicate access list in RSETH contract |
| [L-3](#l-3-admin-could-get-locked-out-when-updating-lrtconfig) | Admin could get locked out when updating LRTConfig |
| [L-4](#l-4-missing-storage-gap-in-lrtconfigrolechecker) | Missing storage gap in LRTConfigRoleChecker |
| [L-5](#l-5-no-decimal-normalization-in-price-feeds) | No decimal normalization in price feeds |
| [L-6](#l-6-potential-denial-of-service-due-to-unbounded-gas-usage-in-gettotalassetdeposits) | Potential denial of service due to unbounded gas usage in `getTotalAssetDeposits()` |
| [L-7](#l-7-potential-overflow-in-getassetcurrentlimit) | Potential overflow in `getAssetCurrentLimit()` |

### Non Critical Issues

Total of **2 issues**:

|ID|Issue|
|:--:|:---|
| [NC-1](#nc-1-add-an-initializer-to-the-lrtconfigrolechecker-contract) | Add an initializer to the LRTConfigRoleChecker contract |
| [NC-2](#nc-2-use-uups-over-transparentproxy) | Use UUPS over TransparentProxy |

## Low Issues

### [L-1] Supported assets cannot be removed

Configured assets in the LRTConfig contract can be added by calling `addNewSupportedAsset()`, but there is no current way of removing these assets if it is needed later.

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L73-L75

```solidity
73:     function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(LRTConstants.MANAGER) {
74:         _addNewSupportedAsset(asset, depositLimit);
75:     }
```

Consider adding a function to let admins or managers to eventually remove an asset from the supported list.

### [L-2] Duplicate access list in RSETH contract

The LRTConfig acts as an access list through the protocol, contracts inherit from L

47:     function updateLRTConfig(address lrtConfigAddr) external virtual onlyLRTAdmin {
48:         UtilLib.checkNonZeroAddress(lrtConfigAddr);
49:         lrtConfig = ILRTConfig(lrtConfigAddr);
50:         emit UpdatedLRTConfig(lrtConfigAddr);
51:     }

// Code block 1 (solidity):
73:     function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(LRTConstants.MANAGER) {
74:         _addNewSupportedAsset(asset, depositLimit);
75:     }


holyhansss_kr Q

## no event on critical events

```solidity
function setPrivatePoolMetadata()
function setPrivatePoolImplementation()
function setProtocolFeeRate()
```

## use safeTransfer and safeTransferFrom on ERC20 transfer

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

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

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

## User is able to create pool with 0 NFT
User is able to create private pool without sending any NFT.

// Code block 1 (solidity):
function setPrivatePoolMetadata()
function setPrivatePoolImplementation()
function setProtocolFeeRate()


ladboy233 Q

# ReentrancyGuardUpgradeable is not initalized in the init function of the SmartAccount

### Line of Code

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

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

### Vulnerability and recommended fix

The SmartAccount inherits from ReentrancyGuardUpgradeable

```solidity
contract SmartAccount is
     Singleton,
     BaseSmartAccount,
     IERC165,
     ModuleManager,
     SignatureDecoder,
     SecuredTokenTransfer,
     ISignatureValidatorConstants,
     FallbackManager,
     Initializable,
     ReentrancyGuardUpgradeable
    {
```

However, in the init function of the SmartAccount, the ReentrancyGuardUpgradeable is not initalized.

```solidity
    // init
    // Initialize / Setup
    // Used to setup
    // i. owner ii. entry point address iii. handler
    function init(address _owner, address _entryPointAddress, address _handler) public override initializer {
        require(owner == address(0), "Already initialized");
        require(address(_entryPoint) == address(0), "Already initialized");
        require(_owner != address(0),"Invalid owner");
        require(_entryPointAddress != address(0), "Invalid Entrypoint");
        require(_handler != address(0), "Invalid Entrypoint");
        owner = _owner;
        _entryPoint =  IEntryPoint(payable(_entryPointAddress));
        if (_handler != address(0)) internalSetFallbackHandler(_handler);
        setupModules(address(0), bytes(""));
    }
```

If in fact, if the SmartAccount is not upgradeable, no need to use the ReentrancyGuardUpgradeable, just use the regular reentrancy guard should be sufficient, otherwise, the recommended fix is init the ReentrancyGuardUpgradeable inside the init function

https://github.com/OpenZeppelin/openzeppelin-contrac

// Code block 1 (solidity):
contract SmartAccount is
     Singleton,
     BaseSmartAccount,
     IERC165,
     ModuleManager,
     SignatureDecoder,
     SecuredTokenTransfer,
     ISignatureValidatorConstants,
     FallbackManager,
     Initializable,
     ReentrancyGuardUpgradeable
    {

// Code block 2 (solidity):
// init
    // Initialize / Setup
    // Used to setup
    // i. owner ii. entry point address iii. handler
    function init(address _owner, address _entryPointAddress, address _handler) public override initializer {
        require(owner == address(0), "Already initialized");
        require(address(_entryPoint) == address(0), "Already initialized");
        require(_owner != address(0),"Invalid owner");
        require(_entryPointAddress != address(0), "Invalid Entrypoint");
        require(_handler != address(0), "Invalid Entrypoint");
        owner = _owner;
        _entryPoint =  IEntryPoint(payable(_entryPointAddress));
        if (_handler != address(0)) internalSetFallbackHandler(_handler);
        setupModules(address(0), bytes(""));
    }

contract SmartAccount is
     Singleton,
     BaseSmartAccount,
     IERC165,
     ModuleManager,
     SignatureDecoder,
     SecuredTokenTransfer,
     ISignatureValidatorConstants,
     FallbackManager,
     Initializable,
     ReentrancyGuardUpgradeable
    {

// init
    // Initialize / Setup
    // Used to setup
    // i. owner ii. entry point address iii. handler
    function init(address _owner, address _entryPointAddress, address _handler) public override initializer {
        require(owner == address(0), "Already initialized");
        require(address(_entryPoint) == address(0), "Already initialized");
        require(_owner != address(0),"Invalid owner");
        require(_entryPointAddress != address(0), "Invalid Entrypoint");
        require(_handler != address(0), "Invalid Entrypoint");
        owner = _owner;
        _entryPoint =  IEntryPoint(payable(_entryPointAddress));
        if (_handler != address(0)) internalSetFallbackHandler(_handler);
        setupModules(address(0), bytes(""));
    }


pipidu83 Q

# Low Risk Issues

## 1. ShareBurnt event is emitted with two identical inputs.

```preRebaseTokenAmount``` and ```postRebaseTokenAmount``` calculations will always return the same value in the ```_burnShares``` function as they take the same input parameter ```_sharesAmount``` and they share an identical timestamp (used in ```oracle.getPrice()```), while the range was not changed.

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

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

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

uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

totalShares -= _sharesAmount;

shares[_account] = accountShares - _sharesAmount;

uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

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

return totalShares;

// Notice: we're not emitting a Transfer event to the zero address here since shares burn
// works by redistributing the amount of tokens corresponding to the burned shares between
// all other token holders. The total supply of the token doesn't change as the result.
// This is equivalent to performing a send from `address` to each other token holder address,
// but we cannot reflect this as it would require sending an unbounded number of events.

// We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
}
```

Given [getRUSDYByShares](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L397) definition below:

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

And the getPrice() function in [RWADynamicOracle.sol](https://github.com/code-423n4/2023-09-

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

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

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

uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

totalShares -= _sharesAmount;

shares[_account] = accountShares - _sharesAmount;

uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

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

return totalShares;

// Notice: we're not emitting a Transfer event to the zero address here since shares burn
// works by redistributing the amount of tokens corresponding to the burned shares between
// all other token holders. The total supply of the token doesn't change as the result.
// This is equivalent to performing a send from `address` to each other token holder address,
// but we cannot reflect this as it would require sending an unbounded number of events.

// We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
}

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

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

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

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

uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

totalShares -= _sharesAmount;

shares[_account] = accountShares - _sharesAmount;

uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

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

return totalShares;

// Notice: we're not emitting a Transfer event to the zero address here since shares burn
// works by redistributing the amount of tokens corresponding to the burned shares between
// all other token holders. The total supply of the token doesn't change as the result.
// This is equivalent to performing a send from `address` to each other token holder address,
// but we cannot reflect this as it would require sending an unbounded number of events.

// We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
}

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


SY_S G

## Summary

### Gas Optimization

no |Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] |Mappings not used externally/internally can be marked private  |  |17|--|
| [G-02] |Redundant state variable getters |  |15|--|
| [G-03] |Use named returns for local variables of pure/view functions where it is possible |  ||--|
| [G-04] |Using PRIVATE rather than PUBLIC FOR Constants/Immutable, Saves Gas|  |8|--|
| [G-05] |Before transfer of  some functions, we should check some variables for possible gas save |  |2|--|
| [G-06] |Require() or revert() statements that check input arguments should be at the top of the function |  |3|--|
| [G-07] |Optimize Address Storage Value Management with `assembly` |  |17|--|
| [G-08] |Use assembly to emit events |  |19|--|
| [G-09] |Use byte32 in place of string|  |21|--|
| [G-10] |Divisions which do not divide by -X cannot overflow or overflow so such operations can be unchecked to save gas |  |8|--|
| [G-11] |Do not calculate constants |  |3|--|
| [G-12] |Stack variable cost less while used in emiting event|  |8|--|
| [G-13] | `internal` functions only called once can be inlined to save gas |  |2|--|
| [G-14] |Use shift Left instead of multiplication if possible to save gas |  |3|--|

## Gas Optimizations

## [G-1] Mappings not used externally/internally can be marked private
in this case, since all mappings are declared as public, they can be accessed and modified by any external contract or account. If you do not need this level of accessibility, it's recommended to mark them as private to restrict their usage to within the contract where they are defined. This can help prevent unintended or malicious manipulation of the mapping data from external sources.

```solidity
file:/src/AiArenaHelper.sol
 30   mapping(uint256 => mapping(string => uint8[])) public attributeProbabilities;
 33   mapping(string => uint8) public attributeToDnaDivisor;

```
https://github.com/code-423n4/2024-02-ai-arena/blob/f2952187a8afc44ee6adc2876965

File: /src/FighterFarm.sol
  76  mapping(uint256 => bool) public fighterStaked;

  79   mapping(uint256 => uint8) public numRerolls;

  82  mapping(address => bool) public hasStakerRole;

  85   mapping(uint8 => uint8) public numElements;

  88  mapping(address => mapping(uint8 => uint8)) public nftsClaimed;

  91  mapping(uint256 => uint32) public numTrained;

// Code block 1 (solidity):
file:/src/AiArenaHelper.sol
 30   mapping(uint256 => mapping(string => uint8[])) public attributeProbabilities;
 33   mapping(string => uint8) public attributeToDnaDivisor;


ballx G

# c4udit Report

## Files analyzed
- contracts/smart-contract-wallet/BaseSmartAccount.sol
- contracts/smart-contract-wallet/Proxy.sol
- contracts/smart-contract-wallet/SmartAccount.sol
- contracts/smart-contract-wallet/SmartAccountFactory.sol
- contracts/smart-contract-wallet/SmartAccountNoAuth.sol
- contracts/smart-contract-wallet/aa-4337/core/BaseAccount.sol
- contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol
- contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol
- contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol
- contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IAggregator.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol
- contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol
- contracts/smart-contract-wallet/aa-4337/samples/DepositPaymaster.sol
- contracts/smart-contract-wallet/aa-4337/samples/IOracle.sol
- contracts/smart-contract-wallet/aa-4337/samples/SimpleAccount.sol
- contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountFactory.sol
- contracts/smart-contract-wallet/aa-4337/samples/SimpleAccountForTokens.sol
- contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccount.sol
- contracts/smart-contract-wallet/aa-4337/samples/TestAggregatedAccountFactory.sol
- contracts/smart-contract-wallet/aa-4337/samples/TestSignatureAggregator.sol
- contracts/smart-contract-wallet/aa-4337/samples/TokenPaymaster.sol
- contracts/smart-contract-wallet/aa-4337/samples/VerifyingPaymaster.sol
- contracts/smart-contract-wallet/aa-4337/test/TestCounter.sol
- contracts/smart-contract-wallet/aa-4337/test/TestExpirePaymaster.sol
- contracts/smart-contrac

contracts/smart-contract-wallet/SmartAccount.sol::234 => uint256 payment = 0;
contracts/smart-contract-wallet/SmartAccount.sol::310 => uint256 i = 0;
contracts/smart-contract-wallet/SmartAccount.sol::468 => for (uint i = 0; i < dest.length;) {
contracts/smart-contract-wallet/SmartAccountNoAuth.sol::234 => uint256 payment = 0;
contracts/smart-contract-wallet/SmartAccountNoAuth.sol::305 => uint256 i = 0;
contracts/smart-contract-wallet/SmartAccountNoAuth.sol::458 => for (uint i = 0; i < dest.length;) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::74 => for (uint256 i = 0; i < opslen; i++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::78 => uint256 collected = 0;
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::80 => for (uint256 i = 0; i < opslen; i++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::99 => uint256 totalOps = 0;
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::100 => for (uint256 i = 0; i < opasLen; i++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::106 => uint256 opIndex = 0;
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::107 => for (uint256 a = 0; a < opasLen; a++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::112 => for (uint256 i = 0; i < opslen; i++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::126 => uint256 collected = 0;
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::128 => for (uint256 a = 0; a < opasLen; a++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::134 => for (uint256 i = 0; i < opslen; i++) {
contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol::313 => uint256 missingAccountFunds = 0;
contracts/smart-contract-wallet/aa-4337/samples/SimpleAccount.sol::71 => for (uint256 i = 0; i < dest.length; i++) {
contracts/smart-contract-wallet/aa-4337/samples/TestSignatureAggregator.sol::18 => uint sum = 0;
contracts/smart-contract-wallet/aa-4337/samples/TestSignatureAggregato

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


Rolezn Q

## QA Summary<a name="QA Summary">

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#LOW‑1) | IERC20 `approve()` Is Deprecated | 2 |
| [LOW‑2](#LOW‑2) | Consider the case where `totalsupply` is 0 | 1 |
| [LOW‑3](#LOW‑3) | Do not allow fees to be set to `100%` | 2 |
| [LOW‑4](#LOW‑4) | External calls in an un-bounded `for-`loop may result in a DOS | 2 |
| [LOW‑5](#LOW‑5) | Hardcoded String that is repeatedly used can be replaced with a constant | 4 |
| [LOW‑6](#LOW‑6) | Lack of checks-effects-interactions | 2 |
| [LOW‑7](#LOW‑7) | Minting tokens to the zero address should be avoided | 1 |
| [LOW‑8](#LOW‑8) | The Contract Should `approve(0)` First | 2 |
| [LOW‑9](#LOW‑9) | Contracts are not using their OZ Upgradeable counterparts | 23 |
| [LOW‑10](#LOW‑10) | There should be an upper limit for flash loan settings | 2 |
| [LOW‑11](#LOW‑11) | Remove unused code | 8 |
| [LOW‑12](#LOW‑12) | Unbounded loop | 1 |
| [LOW‑13](#LOW‑13) | Upgrade OpenZeppelin Contract Dependency | 1 |
| [LOW‑14](#LOW‑14) | Use `safeTransferOwnership` instead of `transferOwnership` function | 4 |
| [LOW‑15](#LOW‑15) | `_creditTo` call should avoid setting to 0 `amount` and to zero `address` | 1 |

Total: 56 contexts over 15 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#NC‑1) | No need for `== true` or `== false` checks | 1 |
| [NC‑2](#NC‑2) | Critical Changes Should Use Two-step Procedure | 33 |
| [NC‑3](#NC‑3) | `block.timestamp` is already used when emitting events, no need to input timestamp | 36 |
| [NC‑4](#NC‑4) | Immutables can be named using the same convention | 18 |
| [NC‑5](#NC‑5) | Initial value check is missing in Set Functions | 31 |
| [NC‑6](#NC‑6) | Omissi


forkforkdog Q

## Adding reserves with a large disproportion can lead to subsequent *k* manipulation due to rounding errors
**Github:**  [Liquidity.sol:l146](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/staking/Liquidity.sol#L146)

### Vulnerability details:
Calling depositLiquidityAndIncreaseShare() with values that differs more than 1e26,  for example 1000 and 10e26, for reserve0 and reserve1 will lead to the situation when subsequent calls of depositLiquidityAndIncreaseShare() with values up to 10e23 for both reserve0 and reserve1 will add 10e23 liquidity to reserve1 and 0 to reserve0.

This allows attacker influence manipulate k value by adding liquidity only to reserve1 in the loop.

Subsequent swaps will be calculated with manipulated k.

### POC
```javascript
function testMultipleInteractions() public {
    vm.startPrank(address(collateralAndLiquidity));

    IERC20 token0 = new TestERC20("TEST", 18);
    IERC20 token1 = new TestERC20("TEST", 18);
    vm.stopPrank();

    vm.prank(address(dao));
    poolsConfig.whitelistPool(pools, token0, token1);

    vm.startPrank(address(collateralAndLiquidity));

    token0.transfer(
        alice,
        token0.balanceOf(address(collateralAndLiquidity))
    );

    token1.transfer(
        alice,
        token1.balanceOf(address(collateralAndLiquidity))
    );

    vm.stopPrank();

    vm.startPrank(alice);
    token0.approve(address(collateralAndLiquidity), type(uint256).max);
    token1.approve(address(collateralAndLiquidity), type(uint256).max);
    token0.approve(address(pools), type(uint256).max);
    token1.approve(address(pools), type(uint256).max);
    vm.stopPrank();

    vm.startPrank(bob);
    token0.approve(address(collateralAndLiquidity), type(uint256).max);
    token1.approve(address(collateralAndLiquidity), type(uint256).max);
    token0.approve(address(pools), type(uint256).max);
    token1.approve(address(pools), type(uint256).max);
    vm.stopPrank();

    bytes32

function testMultipleInteractions() public {
    vm.startPrank(address(collateralAndLiquidity));

    IERC20 token0 = new TestERC20("TEST", 18);
    IERC20 token1 = new TestERC20("TEST", 18);
    vm.stopPrank();

    vm.prank(address(dao));
    poolsConfig.whitelistPool(pools, token0, token1);

    vm.startPrank(address(collateralAndLiquidity));

    token0.transfer(
        alice,
        token0.balanceOf(address(collateralAndLiquidity))
    );

    token1.transfer(
        alice,
        token1.balanceOf(address(collateralAndLiquidity))
    );

    vm.stopPrank();

    vm.startPrank(alice);
    token0.approve(address(collateralAndLiquidity), type(uint256).max);
    token1.approve(address(collateralAndLiquidity), type(uint256).max);
    token0.approve(address(pools), type(uint256).max);
    token1.approve(address(pools), type(uint256).max);
    vm.stopPrank();

    vm.startPrank(bob);
    token0.approve(address(collateralAndLiquidity), type(uint256).max);
    token1.approve(address(collateralAndLiquidity), type(uint256).max);
    token0.approve(address(pools), type(uint256).max);
    token1.approve(address(pools), type(uint256).max);
    vm.stopPrank();

    bytes32 poolID = PoolUtils._poolID(token0, token1);
    {
        vm.startPrank(alice);

        token0.transfer(bob, 10 ** 27);
        token1.transfer(bob, 10 ** 27);

        collateralAndLiquidity.depositLiquidityAndIncreaseShare(
            token0,
            token1,
            1000,
            10 ** 26, //should be >=  10 ** 26
            0,
            block.timestamp,
            false
        );

        vm.warp(block.timestamp + 1 hours);
        vm.stopPrank();
        (uint reserve0middle, uint reserve1middle) = pools.getPoolReserves(
            token0,
            token1
        );

        console2.log(
            "1. k is ",
            (reserve0middle * reserve1middle + 5 * 10 ** 21) / 10 ** 22
        );

        vm.startPrank(bob);
        for (uint i; i < 100; ++i) {
            collat


hl_ Q

# Table of contents

- [N-01] Insufficient test coverage
- [N-02] Use `require` instead of `assert`
- [N-03] Values such as a call to keccak256() should used to `immutable` rather than `constant`
- [N-04] `0 address` check for `asset`
- [N-05] Use a more recent version of solidity
- [N-06] Avoid the use of sensitive terms in favour of neutral ones
- [N-07] Non-library/interface files should use fixed compiler versions, not floating ones
- [N-08] Best practice is to prevent signature malleability
- [N-09] Import exact functions
- [L-01] `_account` not checked for address(0)
- [L-02] `_OathAmount` not checked for 0 amount

## [N-01] Insufficient test coverage
Testing all functions is best practice in terms of security criteria. Noted the current overall line coverage percentage provided by tests is 93. Due to its capacity, test coverage is expected to be 100%.

## [N-02] Use `require` instead of `assert`

`assert` should not be used except for tests; `require` should be used.

Prior to Solidity 0.8.0, pressing a confirm consumes the remainder of the process’s available gas instead of returning it, as request()/revert() did.

`assert()` and `reqire()` -

The big difference between the two is that the assert()function when false, uses up all the remaining gas and reverts all the changes made.

Meanwhile, a require() function when false, also reverts back all the changes made to the contract but does refund all the remaining gas fees we offered to pay.This is the most common Solidity function used by developers for debugging and error handling.

For example:

```solidity
LUSDToken.sol:
311     function _transfer(address sender, address recipient, uint256 amount) internal {
312:    assert(sender != address(0));
313:    assert(recipient != address(0));
```

## [N-03] Values such as a call to keccak256() should used to `immutable` rather than `constant`

`constant` and `immutable` variables should each be used in their appropriate contexts: Constants should be used for li

ReaperVaultV2.sol (L73 - 76):
    bytes32 public constant DEPOSITOR = keccak256("DEPOSITOR");
    bytes32 public constant STRATEGIST = keccak256("STRATEGIST");
    bytes32 public constant GUARDIAN = keccak256("GUARDIAN");
    bytes32 public constant ADMIN = keccak256("ADMIN");

// Code block 1 (solidity):
LUSDToken.sol:
311     function _transfer(address sender, address recipient, uint256 amount) internal {
312:    assert(sender != address(0));
313:    assert(recipient != address(0));


btk Q

| Issues Summarize|
|-----------------|

| Risk    | Issues Details                                                                                |
|---------|---------------------------------------------------------------------------------------------------------------|
| [QA-01] | Owner may delete all the pools unintentionally                                                                |
| [QA-02] | Loss of precision due to division before multiplication                                                       |

### [QA-01] Owner may delete all the pools unintentionally

#### Impact

The [`EUSDMiningIncentives.setPools()`](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L93-L98) function has a bad design where all pools can be unintentionally deleted. This can render the contract unusable if the owner adds a single pool.

```solidity
    function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }
```

The current implementation overwrites the existing pool list with the new input, causing an unintentional deletion of all pools except the ones provided as arguments.

#### Tools Used

Manual Review

#### Recommended Mitigation Steps

We rcommend updating the function as follow:

```solidity
    function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
            pools.push(_pools[i]);
        }
    }
```

### [QA-02] Loss of precision due to division before multiplication

#### Impact

`collateralAmount` incurs a precision loss due to division before multiplication. As a result, users will receive less collateral than they are entitled to.

#### Proof of Concept

```solidity
        uint256 collateralAmount = (

// Code block 1 (solidity):
function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }

// Code block 2 (solidity):
function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
            pools.push(_pools[i]);
        }
    }

function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        pools = _pools;
    }

function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
            pools.push(_pools[i]);
        }
    }


0xdaydream G

# Details

| Number | Optimization Details | Instances | Total Gas Saved |
|---|---|---|---|
| [G-01] | With assembly, `.call (bool success)` transfer can be done gas-optimized | 5 | 594 |
| [G-02] | Functions guaranteed to revert when callled by normal users can be marked `payable` | 9 | 192 |
| [G-03] | Setting the _constructor_ to `payable` | 4 | - |
| [G-04] | Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 2 | - |
| [G-05] | Use a more recent version of solidity | 4 | - |
| [G-06] | Optimize names to save gas | - | - |

Total: 24 instances over 6 issues with **786 gas** saved

All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions.

## [G-01] With assembly, `.call (bool success)` transfer can be done gas-optimized

`return` data `(bool success,)` has to be stored due to EVM architecture, but in a usage like below, 'out' and 'outsize' values are given (0,0), this storage disappears and gas optimization is provided.

https://twitter.com/pashovkrum/status/1607024043718316032?t=xs30iD6ORWtE2bTTYsCFIQ&s=19

5 instances - 4 files:

```diff
contracts/SafEth/SafEth.sol#L124:
-         (bool sent, ) = address(msg.sender).call{value: ethAmountToWithdraw}(
-             ""
-         );
+        bool sent;
+        address sender = msg.sender;
+        // solhint-disable-next-line
+        assembly {
+            sent := call(gas(), sender, ethAmountToWithdraw, 0, 0, 0, 0)
+        }
```
[SafEth.sol#L124](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L124)

```solidity
contracts/SafEth/derivatives/Reth.sol#L110:
110:        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
111:            ""
112:        );
```
[Reth.sol#L110](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L110)

```solidity
contracts/SafEth/d

// Code block 1 (diff):
contracts/SafEth/SafEth.sol#L124:
-         (bool sent, ) = address(msg.sender).call{value: ethAmountToWithdraw}(
-             ""
-         );
+        bool sent;
+        address sender = msg.sender;
+        // solhint-disable-next-line
+        assembly {
+            sent := call(gas(), sender, ethAmountToWithdraw, 0, 0, 0, 0)
+        }

// Code block 2 (solidity):
contracts/SafEth/derivatives/Reth.sol#L110:
110:        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
111:            ""
112:        );


ABA Q

# QA Report for Ethos Reserve contest

## Overview
During the audit, 7 low and 14 non-critical issues were found.

### Low Risk Issues

Total: 11 instances over 7 issues

|#|Issue|Instances|
|-|:-|:-:|
| [L-01] | `hasPendingRewards` only checks for pending collateral rewards and not for pending LUSD Debt Rewards | 1 |
| [L-02] | Fallback price oracle Tellor can be easily manipulated | 1 |
| [L-03] | Local variables or function arguments declared as `storage` without actual need | 5 |
| [L-04] | `setHarvestSteps` accepts an empty array which simply deletes the steps | 1 |
| [L-05] | `updateStrategyFeeBPS` should have differentiating behavior when `emergencyShutdown` is set | 1 |
| [L-06] | Ambiguous implementation of `setWithdrawalQueue` may lead to unforeseen issues | 1 |
| [L-07] | There is no way to completely remove a strategy from the ReaperVault | 1 |

### Non-critical Issues

Total: 29 instances over 14 issues

|#|Issue|Instances|
|-|:-|:-:|
|[NC-01]| `updateCollateralRatios` accepts redundant updating with the same values | 1 |
|[NC-02]| Not enough tests for calculating collateral surplus in a sub-case of recovery mode | 6 |
|[NC-03]| Use "type(uint256).max" instead of "uint256(-1)" | 1 |
|[NC-04]| Inconsistent event emission syntax used | 2 |
|[NC-05]| Incomplete require error messages | 2 |
|[NC-06]| Unused code | 1 |
|[NC-07]| Missing events for critical parameter changes | 3 |
|[NC-08]| `_requireValidMaxFeePercentage` can be rewritten to optimize calculations | 1 |
|[NC-09]| `_cascadingAccessRoles` can be pure instead of view | 2 |
|[NC-10]| Dead Comments | 1 |
|[NC-11]| Inconsistent shares calculation across code base | 2 |
|[NC-12]| Open TODOs | 3 |
|[NC-13]| Already declared variable not reused | 2 |
|[NC-14]| Functions do not respect camelCase naming convention | 2 |

## Low Risk Issues (7)
#
### [L-01] `hasPendingRewards` only checks for pending collateral rewards and not for pending LUSD Debt Rewards
##### Description

`applyPendingRewards` f

    function applyPendingRewards(address _borrower, address _collateral) external override {
        _requireCallerIsBorrowerOperationsOrRedemptionHelper();
        return _applyPendingRewards(activePool, defaultPool, _borrower, _collateral);
    }

    // Add the borrowers's coll and debt rewards earned from redistributions, to their Trove
    function _applyPendingRewards(IActivePool _activePool, IDefaultPool _defaultPool, address _borrower, address _collateral) internal {
        if (hasPendingRewards(_borrower, _collateral)) {

return (rewardSnapshots[_borrower][_collateral].collAmount < L_Collateral[_collateral]);


itzr Q

# Issue 1

## `TIMELOCK` role is not assigned to any address/account

In `GovernanceTimelock::constructor`, roles and role admins are granted, yet the `TIMELOCK` role is not assigned to any address.

```
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {
   _setRoleAdmin(DAO, GOV);
   _setRoleAdmin(TIMELOCK, GOV);
   _setRoleAdmin(ADMIN, GOV);
   _grantRole(DAO, address(this));
   _grantRole(DAO, msg.sender);
   _grantRole(GOV, msg.sender);
}
```
Failure to grant the `TIMELOCK` role to any address means that the protocol is not upgradable via DAO Governance, since `LybraGovernance::_execute` requires: `(GovernanceTimelock.checkOnlyRole(keccak256("TIMELOCK"), msg.sender), "not authorized");`

This is not a critical bug since the team can grant roles at a future date, however on deployment `LybraGovernance::_execute` will not run.

It is recommended to grant the address of `AdminTimelock` with the `TIMELOCK` role within the `GovernanceTimelock::constructor` and open source the deployment script.

# Issue 2

## Both `msg.sender` and `address(this)` control the DAO

The v2 docs claim that `The Lybra protocol is governed by esLBR token holders`, and in `LybraConfigurator` it's stated that the `DAO` is `A time-locked contract initiated by esLBR voting, with a minimum effective period of 14 days. After the vote is passed, only the developer can execute the action`. However, in `GovernanceTimelock::constructor`, `msg.sender` is assigned with the `DAO` role and given full administrative rights over role assignment via `GOV`. So long as this is the case, the protocol remains 100% in the control of the core team, not the DAO.

```
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {
   _setRoleAdmin(DAO, GOV);
   _setRoleAdmin(TIMELOCK, GOV);
   _setRoleAdmin

// Code block 1 (unknown):
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {
   _setRoleAdmin(DAO, GOV);
   _setRoleAdmin(TIMELOCK, GOV);
   _setRoleAdmin(ADMIN, GOV);
   _grantRole(DAO, address(this));
   _grantRole(DAO, msg.sender);
   _grantRole(GOV, msg.sender);
}


Madalad Q

# QA Report

## Low Risk
| |Issue|Instances|
|:-:|:-|:-:|
|[[L-01]](#l-01-potential-dos-if-constantimmutable-address-becomes-blacklisted)|Potential DOS if `constant`/`immutable` address becomes blacklisted|1|
|[[L-02]](#l-02-chainlink-aggregators-return-the-incorrect-price-if-it-drops-below-minanswer)|Chainlink aggregators return the incorrect price if it drops below `minAnswer`|3|
|[[L-03]](#l-03-fee-on-transferrebasing-tokens-not-properly-accounted-for)|Fee on transfer/rebasing tokens not properly accounted for|3|
|[[L-04]](#l-04-user-facing-functions-should-have-address0-checks)|User facing functions should have `address(0)` checks|38|
|[[L-05]](#l-05-missing-address0-checks-when-assigning-to-address-state-variables)|Missing `address(0)` checks when assigning to `address` state variables|1|
|[[L-06]](#l-06-approval-may-revert-if-current-allowance-is-non-zero)|Approval may revert if current allowance is non-zero|1|
|[[L-07]](#l-07-use-require-instead-of-assert)|Use `require` instead of `assert`|1|
|[[L-08]](#l-08-missing-contract-existence-check-for-yul-call)|Missing contract existence check for yul `call`|3|
|[[L-09]](#l-09-lack-of-two-step-update-for-critical-functions)|Lack of two-step update for critical functions|17|
|[[L-10]](#l-10-contract-deployment-is-vulnerable-to-block-re-orgs)|Contract deployment is vulnerable to block re-orgs|1|
|[[L-11]](#l-11-division-before-multiplications-leads-to-precision-loss)|Division before multiplications leads to precision loss|1|
|[[L-12]](#l-12-numbers-downcast-to-address-may-result-in-collisions)|Numbers downcast to `address` may result in collisions|1|
|[[L-13]](#l-13-decimals-is-not-part-of-the-erc20-standard)|`decimals()` is not part of the ERC20 standard|4|
|[[L-14]](#l-14-erc20-may-revert-on-zero-transfer)|ERC20 may revert on zero transfer|7|
|[[L-15]](#l-15-soliditys-ecrecover-is-vulnerable-to-signature-malleability)|Solidity's `ecrecover` is vulnerable to signature malleability|2|
|[[L-16]](#l-16-consider-bounding

File: packages/contracts/contracts/CollSurplusPool.sol

148:         IERC20(token).safeTransfer(feeRecipientAddress, amount);

File: packages/contracts/contracts/PriceFeed.sol

634:         try ETH_BTC_CL_FEED.latestRoundData() returns (
635:             uint80 roundId,
636:             int256 answer,
637:             uint256,
638:             /* startedAt */
639:             uint256 timestamp,
640:             uint80 /* answeredInRound */
641:         ) {

650:         try STETH_ETH_CL_FEED.latestRoundData() returns (
651:             uint80 roundId,
652:             int256 answer,
653:             uint256,
654:             /* startedAt */
655:             uint256 timestamp,
656:             uint80 /* answeredInRound */
657:         ) {


Walter G

## NodeDelegator
1) IERC20(token) in line 60(https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/NodeDelegator.sol#L60) could be implemented directly inside the lines without creating temp variable,following the standard of other functions like ``maxApproveToEigenStrategyManager``,in this way every call of the ``depositAssetIntoStrategy`` will save 2 gas by re-writing the depositAssetIntoStrategy function like this:
```
    function depositAssetIntoStrategy(address asset)
        external
        override
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
        onlyLRTManager
    {
        address strategy = lrtConfig.assetStrategy(asset);
        address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

        uint256 balance = IERC20(asset).balanceOf(address(this));

        emit AssetDepositIntoStrategy(asset, strategy, balance);

        IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), IERC20(asset), balance);
    }
```

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

        uint256 balance = IERC20(asset).balanceOf(address(this));

        emit AssetDepositIntoStrategy(asset, strategy, balance);

        IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), IERC20(asset), balance);
    }


Raihan G

# Gas optimization

### Note: The Last types 5 Not Find by Bots completly ([G-33],[G-34],[G-35],[G-36],[G-37])

## Summary
|      | Issue    |  Instance  |
|------|----------|------------|
|[G‑01]| Use calldata instead of memory  | 14 |
|[G-02]| Can Make The Variable Outside The Loop To Save Gas | 3 |
|[G-03]| Access mappings directly rather than using accessor functions | 4 |
|[G-04]| Use assembly to write address storage values | 1 |
|[G-05]| A modifier used only once and not being inherited should be inlined to save gas | 1 |
|[G-06]|Use double if statements instead of && | 4 |
|[G-07]| Make 3 event parameters indexed when possible | 6 |
|[G-08]| Sort Solidity operations using short-circuit mode | 10 |
|[G-09]| public functions to external | 1 |
|[G-10]| Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 2 |
|[G-11]| Unnecessary computation | 2 |
|[G-12]| >= costs less gas than > | 4 |
|[G-13]| internal functions not called by the contract should be removed to save deployment gas | 6 |
|[G-14]| Initialize variables with no default value | 24 |
|[G-15]| Amounts should be checked for 0 before calling a transfer | 1 |
|[G-16]| abi.encode() is less efficient than abi.encodePacked() | 7 |
|[G-17]| Using bools for storage incurs overhead | 6 |
|[G-18]| Do not calculate constants | 6 |
|[G-19]|Delete variables that you don’t need | 6 |
|[G-20]|Do not shrink Variables | 7 |
|[G-21]|  Change public state variable visibility to private | 4 |
|[G-22]| State variables can be packed to use fewer storage slots | 1 |
|[G-23]| With assembly, .call (bool success) transfer can be done gas-optimized | 1 |
|[G-24]|Use != 0 instead of > 0 for unsigned integer comparison | 1 |
|[G-25]| Use hardcode address instead address(this) | 6 |
|[G‑26]| Structs can be packed into fewer storage slots | 1 |
|[G-27]|use Mappings Instead of Arrays | 12 |
|[G-28]| Use Assembly To Check For address(0) | 1 |
|[G-29]| Use constants instead of type

File: /ajna-core/src/RewardsManager.sol
135   function moveStakedLiquidity(
        uint256 tokenId_,
        uint256[] memory fromBuckets_,
        uint256[] memory toBuckets_,
        uint256 expiry_
    ) external nonReentrant override {

File: /ajna-grants/src/grants/GrantFund.so
22     function hashProposal(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory calldatas_,
        bytes32 descriptionHash_
    ) external pure override returns (uint256 proposalId_) {


0xta G

# Gas Optimization

# Summary

| Number | Gas Optimization                                                                                | Context |
| :----: | :------------------------------------------------------------------------------------------------------- | :-----: |
| [G-01] | Shorten arrays with inline assembly                                                                      |    2    |
| [G-02] | It is sometimes cheaper to cache calldata                                                                |    1    |
| [G-03] | Mark initializer functions as payable to save deployment gas                                             |    6    |
| [G-04] | Using mappings instead of arrays to avoid length checks                                                  |    2    |
| [G-05] | Use do while  loops instead of for loops                                                                 |    4    |
| [G-06] | Using Storage instead of memory for structs/arrays saves gas                                             |    2    |
| [G-07] | Don't make variables public unless necessary                                                             |   14    |
| [G-08] | Avoid contract existence checks by using low level calls                                                 |   22    |
| [G-09] | Use hardcoded addresses instead of address(this)                                                         |    6    |
| [G-10] | Use constants instead of type(uintx).max                                                                 |    1    |
| [G-11] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant |    9    |

## [G-01] Shorten arrays with inline assembly

```solidity
File: src/NodeDelegator.sol

106        assets = new address[](strategiesLength);

107        assetBalances = new uint256[](strategiesLength);
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L106-L107

## [G-02] I

File: src/LRTDepositPool.sol

162    function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin {

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

106        assets = new address[](strategiesLength);

107        assetBalances = new uint256[](strategiesLength);


SY_S G

## Summary

### Gas Optimization

no | Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] |Require()/Revert() string longer than 32 bytes cost extra gas | |10| |
| [G-02] |Bytes constants are more efficient than String constants | |6| |
| [G-03] |Require() or revert() statements that check input arguments should be at the top of the function | |7| |
| [G-04] |Using PRIVATE rather than PUBLIC FOR Constants/Immutable, Saves Gas | |6| |
| [G-05] |Before transfer of  some functions, we should check some variables for possible gas save | |2| |
| [G-06] |Duplicated require()/if() checks should be refactored to a modifier or function  | |1| |
| [G-07] | keccak256() should only need to be called on a specific string literal once | |1| |
| [G-08] |Use constants instead of type(uintx).max  | |8| |
| [G-09] |Optimize Names to save Gas | |5| |
| [G-10] |Avoid contract existence checks by using low level calls | |13+| |
| [G-11] |SPLITTING  REQUIRE() STATEMENTS THAT USE  && SAVES GAS  | |4| |
| [G-12] |USE A MORE RECENT VERSION OF SOLIDITY  | |All file +39| |
| [G-13] |Internal functions only called once can be inlined to save gas | |6| |
| [G-14] |A modifier used only once and not being inherited should be inlined to save gas  | |3| |
| [G-15] | Do not calculate constant  | |1| |
| [G-16] |abi.encode() is less efficient than  abi.encodepacked()  | |1| |
| [G-17] | Setting the constructor to payable | |4| |
| [G-18] |Using storage instead of memory for structs/arrays saves gas  | |5| |
| [G-19] |State varaibles only set in the Constructor should be declared Immutable | |2| |
| [G-20] |Don't compare boolean expressions to boolean literals | |1| |

## Gas Optimizations
## [G-1] Require()/Revert() string longer than 32 bytes cost extra gas

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

when these strings get longer than 32 bytes, they cost more gas
Use shorter require()/revert() strings

```solidity
file: /contracts/con

file: /contracts/contracts/ActivePool.sol

137        require(cachedSystemCollShares >= _shares, "ActivePool: Insufficient collateral shares");

162        require(cachedSystemCollShares >= _shares, "ActivePool: Insufficient collateral shares");

220        require(
            msg.sender == borrowerOperationsAddress,
            "ActivePool: Caller is not BorrowerOperations"
223        );

228        require(
            msg.sender == borrowerOperationsAddress || msg.sender == cdpManagerAddress,
            "ActivePool: Caller is neither BorrowerOperations nor CdpManager"
231        );

377        require(amount <= balance, "ActivePool: Attempt to sweep more than balance");

file: /contracts/contracts/BorrowerOperations.sol

145        require(locked == OPEN, "BorrowerOperations: Reentrancy in nonReentrant call");

        require(
            ReentrancyGuard(address(cdpManager)).locked() == OPEN,
            "CdpManager: Reentrancy in nonReentrant call"
        );


0xAleko Q

1. https://github.com/code-423n4/2024-02-ai-arena/blob/1d18d1298729e443e14fea08149c77182a65da32/src/MergingPool.sol#L206

points array length is 1. If user input maxId more than 1, function will be reverted: Index out of bound.

PoC:
```
function testRevertGetFighterPoints() public {
        // owner mints a fighter by claiming
        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_ownerAddress);

        address owner = _fighterFarmContract.ownerOf(0);
        assertEq(owner, _ownerAddress);

        // rankedeBattle contract adds points
        vm.startPrank(address(_rankedBattleContract));
        _mergingPoolContract.addPoints(0, 100);
        _mergingPoolContract.addPoints(1, 100);
        _mergingPoolContract.addPoints(2, 100);
        vm.stopPrank();

        assertEq(_mergingPoolContract.totalPoints(), 300);

        // getFighterPoints for owners fighter
        uint256[] memory fighterPoints = _mergingPoolContract.getFighterPoints(3);
        assertEq(fighterPoints.length >= 1 && fighterPoints[0] == 100, true);
    }
```

Recommendation:
`-uint256[] memory points = new uint256[](1);`
`+uint256[] memory points = new uint256[](maxId);`

// Code block 1 (unknown):
function testRevertGetFighterPoints() public {
        // owner mints a fighter by claiming
        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_ownerAddress);

        address owner = _fighterFarmContract.ownerOf(0);
        assertEq(owner, _ownerAddress);

        // rankedeBattle contract adds points
        vm.startPrank(address(_rankedBattleContract));
        _mergingPoolContract.addPoints(0, 100);
        _mergingPoolContract.addPoints(1, 100);
        _mergingPoolContract.addPoints(2, 100);
        vm.stopPrank();

        assertEq(_mergingPoolContract.totalPoints(), 300);

        // getFighterPoints for owners fighter
        uint256[] memory fighterPoints = _mergingPoolContract.getFighterPoints(3);
        assertEq(fighterPoints.length >= 1 && fighterPoints[0] == 100, true);
    }


Sathish9098 G

# GAS OPTIMIZATION

The final gas calculations are determined based on the ``OPCODEs`` and the ``sample tests``. The ``OPCODEs`` are the basic instructions that are used to execute code in Solidity. The sample test is a piece of code that is used to test the gas consumption of a function.

The gas costs of each ``OPCODE`` are defined in the Solidity documentation. The gas costs of the sample test can be calculated by ``running`` the ``test`` and ``measuring`` the ``amount of gas`` that is used.

- [Struct can be packed in fewer slots - Saves ``4000 GAS``](#g-1-struct-can-be-packed-in-fewer-slots)
  - [``owner``,``expiry`` can be packed within same slot : Saves ``2000 GAS``, ``1 SLOT``](#ownerexpiry-can-be-packed-within-same-slot--saves-2000-gas-1-slot)
  - [``_amount0Min`` and ``_amount1Min`` can be packed to same slot : Saves ``2000 GAS`` , ``1 SLOT``](#_amount0min-and-_amount1min-can-be-packed-to-same-slot--saves-2000-gas--1-slot)

- [The result of a function calls should be cached rather than re-calling the function - Saves ``27912 GAS``](#g-2-the-result-of-a-function-calls-should-be-cached-rather-than-re-calling-the-function)
  - [``getEthPrice()``,``getDpxEthPrice()``, ``getRdpxPrice()`` functions should be cached : Saves ``300 GAS``](#getethpricegetdpxethprice-getrdpxprice-functions-should-be-cached--saves-300-gas)
  - [``nextFundingPaymentTimestamp()`` function should be cached : Saves ``27612 GAS``, ``9 Instances``](#nextfundingpaymenttimestamp-function-should-be-cached--saves-27612-gas-9-instances)
- [Using ``calldata`` to optimize gas costs for ``Read-Only`` external function arguments - Saves ``1420 GAS`` ](#g-3-using-calldata-to-optimize-gas-costs-for-read-only-external-function-arguments)
  - [``_assetSymbol``, ``_assetSymbol``, and ``optionIds``can be declared in ``calldata`` instead of ``memory`` : Saves ``852 GAS``, ``3 Instances``](#_assetsymbol-_assetsymbol-and-optionidscan-be-declared-in-calldata-instead-of-memory--saves-852-gas-3-instances)
  - [

### ``_amount0Min`` and ``_amount1Min`` can be packed to same slot : Saves ``2000 GAS`` , ``1 SLOT``

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L50-L60

``_amount0Min`` and ``_amount1Min`` store the minimum allowed values, which are not very large. Therefore, a ``uint128`` is more than enough to store these values accurately.

##

## [G-2] The result of a function calls should be cached rather than re-calling the function

External calls are expensive

### ``getEthPrice()``,``getDpxEthPrice()``, ``getRdpxPrice()`` functions should be cached : Saves ``300 GAS``

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L546-L549