BRONZEDISC Q

## QA
---

### Function Visibility [1]

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

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

```solidity
// receive function should come before all the other functions
246:    receive() external payable {}
```

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

```solidity
// public functions coming after external ones
44:    function name() public pure returns (string memory) {
111:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
122:    function balance() public view returns (uint256) {

// receive function should come before all the other functions
126:    receive() external payable {}
```

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

```solidity
// public functions coming after external ones
50:    function name() public pure returns (string memory) {
211:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
221:    function balance() public view returns (uint256) {

// external functions coming after private functions
107:    function withdraw(uint256 amount) external onlyOwner {
156:    function deposit() external payable onlyOwner returns (uint256) {

// receive function should come before all the other functions
244:    receive() external payable {}
```

---

### natSpec missing [2]

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

```solidity
21:    event ChangeMinAmount(uint256 indexed minAmount);
22:    event ChangeMaxAmount(uint256 indexed maxAmount

// Code block 1 (solidity):
// receive function should come before all the other functions
246:    receive() external payable {}

// Code block 2 (solidity):
// public functions coming after external ones
44:    function name() public pure returns (string memory) {
111:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
122:    function balance() public view returns (uint256) {

// receive function should come before all the other functions
126:    receive() external payable {}

// Code block 3 (solidity):
// public functions coming after external ones
50:    function name() public pure returns (string memory) {
211:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
221:    function balance() public view returns (uint256) {

// external functions coming after private functions
107:    function withdraw(uint256 amount) external onlyOwner {
156:    function deposit() external payable onlyOwner returns (uint256) {

// receive function should come before all the other functions
244:    receive() external payable {}

// receive function should come before all the other functions
246:    receive() external payable {}

// public functions coming after external ones
44:    function name() public pure returns (string memory) {
111:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
122:    function balance() public view returns (uint256) {

// receive function should come before all the other functions
126:    receive() external payable {}

// public functions coming after external ones
50:    function name() public pure returns (string memory) {
211:    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
221:    function balance() public view returns (uint256) {

// external functions coming after private functions
107:    function withdraw(uint256 amount) external onlyOwner {
156:    function deposit() external payable onlyOwner returns (uint256) {

// receive function should come before all the other functions
244:    receive() external payable {}


0x73696d616f Q

# [L-01] Ownership transferral should be implemented in 2 steps
## SmartAccount.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109

## Description

The function `setOwner(args...)` should be done in a 2 step process.

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

## Recommended Mitigation Steps
Implement the 2 step process, something like this:
```
    function setPendingOwner(address _newOwner) external mixedAuth {
        require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
        pendingOwner = _newOwner;
    }

    function acceptOwnership() external {
        require(msg.sender == pendingOwner, "not pending owner");
        address oldOwner = owner;
        owner = pendingOwner;
        pendingOwner = address(0);
        emit EOAChanged(address(this), oldOwner, owner);
    }
```

# [N-01]  SmartAccountCreated event not emitted when using deployWallet
## SmartAccountFactory
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L53
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33

## Description
The event is missing in deployWallet

```
    function deployWallet(address _owner, address _entryPoint, address _handler) public returns(address proxy){
        bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
        // solhint-disable-next-line no-inline-assembly
        assembly {
            proxy := create(0x0, add(0x20, deploymentData), mload(deploymentData))
        }
        BaseSmartAccount(prox

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

// Code block 2 (unknown):
function setPendingOwner(address _newOwner) external mixedAuth {
        require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
        pendingOwner = _newOwner;
    }

    function acceptOwnership() external {
        require(msg.sender == pendingOwner, "not pending owner");
        address oldOwner = owner;
        owner = pendingOwner;
        pendingOwner = address(0);
        emit EOAChanged(address(this), oldOwner, owner);
    }


AlexCzm Q

### L-01 `attributeProbabilities` are set twice in AiArenaHelper constructor;
Remove `addAttributeProbabilities()` [function call](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L45) from `AiArenaHelper`  constructor.

```solidity
    constructor(uint8[][] memory probabilities) {
        _ownerAddress = msg.sender;

        // Initialize the probabilities for each attribute
        addAttributeProbabilities(0, probabilities);// @audit-qa remove this function call

        uint256 attributesLength = attributes.length;
        for (uint8 i = 0; i < attributesLength; i++) {
            attributeProbabilities[0][attributes[i]] = probabilities[i];// because you are setting attr prob here
            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
        }
    }
```

### L-02 `MINTER_ROLE` can mint back any burned NRN.
Neuron contract [expose](https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L163-L165) externally the `burn` function, allowing any NRN holder to destroy tokens they hold. The maximum total supply should be decreased by the amount of burned tokens.
Additionally, in `mint` function use `<=` instead `<` to better reflect what `MAX_SUPPLY` means.

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

### L-03 Once staked, a fighter is considered staked even if its `amountStaked` balance == 0.
To allow transferring a previously staked fighter, a player must call `unstakeNRN` even if `amountStaked` for that fighter is 0.
Consider improving the player UX and read the `stakedBalance` directly.

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L286

### L-04 Create an Enum with all possible battle results
Currently magic numbers 0, 1, and 2 are used as battle results.
Consider creating and u

    enum battleResult {
        invalidResult,
        won,
        tie,
        lost
    }

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L440

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L505-L513

### L-05 Allows only voltage spender to consume energy
Currently any addresses can call `spendVoltage` to reduce its energy.
Even if unlikely, this can open the door for abuses. Consider keeping only selected addresses to spend voltage

// Code block 1 (solidity):
constructor(uint8[][] memory probabilities) {
        _ownerAddress = msg.sender;

        // Initialize the probabilities for each attribute
        addAttributeProbabilities(0, probabilities);// @audit-qa remove this function call

        uint256 attributesLength = attributes.length;
        for (uint8 i = 0; i < attributesLength; i++) {
            attributeProbabilities[0][attributes[i]] = probabilities[i];// because you are setting attr prob here
            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
        }
    }

AiArenaHelper.sol#L45, Neuron.sol#L163-L165, Neuron.sol#L156, RankedBattle.sol#L286, RankedBattle.sol#L440, RankedBattle.sol#L505-L513


JCK G

## Gas Optimizations

| Number | Issue | Instances | Total gas saved |
|--------|-------|-----------|-----------------|
|[G-01]|  Pack structs by putting data types that can fit together next to each other |  8  | 16000 |
|[G-02]|  Possible Optimization 1 |  8  |  |
|[G-03]|  Possible Optimization 2 |  3  |  |
|[G-04]|  Possible Optimizations in ARCDVestingVault. |  3  |  |
|[G-05]|  Possible Optimizations |  1  |  |
|[G-06]|  Possible Optimization in ActivePool.sol |  2  |  |
|[G-07]|  Possible Optimization in accept() function  |  1  |  |
|[G-08]|  Possible Optimizations |  12  |  |
|[G-09]|  Don’t cache calls that are only used once |  1  |  |
|[G-10]|  public functions not called by the contract should be declared external instead |  26  |  |
|[G-11]|  Do not calculate constants |  8  |  |
|[G-12]|  State variables which are not modified within functions should be set as constant or immutable for values set at deployment |  11  | 110000 |
|[G-13]|  Use assembly in place of abi.decode to extract calldata values more efficiently |  4  |  |
|[G-14]|  Use assembly to emit an event |  88  | 3344 |
|[G-15]|  Use hardcoded address instead of address(this) |  25  |  |
|[G-16]|  Use assembly to write address storage values |  6  | 444 |
|[G-17]|  Use uint256(1)/uint256(2) instead for true and false boolean states |  22  | 376200 |
|[G-18]|  Use assembly to validate msg.sender |  19  | 228 |
|[G-19]|  Use assembly for loops |  8  |  |
|[G-20]|  abi.encode() is less efficient than abi.encodepacked() |  4  |  |
|[G-21]|  Using delete statement can save gas |  3  |  |
|[G-22]|  Use nested if/require statements instead of && |  19  | 570 |
|[G-23]|  Counting down in for statements is more gas efficient |  13  |  |
|[G-24]|  Create immutable variable to avoid an external call |  3  |  |
|[G-25]|  Avoid Unnecessary Public Variables |  14  | 308000 |

## [G-01] Pack structs by putting data types that can fit together next to each other

As the solidity EVM works with 32 bytes,

file: main/packages/contracts/contracts/BorrowerOperations.sol

97   struct MoveTokensParams {
        address user;
        uint256 collSharesChange;
        uint256 collAddUnderlying; // ONLY for isCollIncrease=true
        bool isCollIncrease;
        uint256 netDebtChange;
        bool isDebtIncrease;
    }

file: main/packages/contracts/contracts/LeverageMacroBase.sol

268   struct SwapOperation {
        // Swap Data
        address tokenForSwap;
        address addressForApprove;
        uint256 exactApproveAmount;
        address addressForSwap;
        bytes calldataForSwap;
        SwapCheck[] swapChecks; // Empty to skip
    }

322   struct AdjustCdpOperation {
        bytes32 _cdpId;
        uint256 _stEthBalanceDecrease;
        uint256 _EBTCChange;
        bool _isDebtIncrease;
        bytes32 _upperHint;
        bytes32 _lowerHint;
        uint256 _stEthBalanceIncrease;
    }


bshramin G

# GR1 - Fail fast(I)
In the eth router contract we should change this check:
```solidity
if (block.timestamp > deadline && deadline != 0) {
    revert DeadlinePassed();
}
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L101

to this:
```solidity
if (deadline != 0 && block.timestamp > deadline) {
    revert DeadlinePassed();
}
```

With this change we will consume the same amount of gas if the deadline is set but we will consume less gas if the deadline is equal to zero.

# GR3 - Fail fast(II)
In the `buy` function of the private pool contract, we should move this check to the beginning of the function.
```solidity
if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L225

# GR2 - Fail fast(III)
There is a check in the `Initialize` function:
```solidity
if (_feeRate > 5_000) revert FeeRateTooHigh();
```
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L172

This can be moved to the beginning of the `create` function in the `Factory` contract.
This way we will prevent spending a lot of gas for cloning the contract and calling initialize when the check fails.

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

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

// Code block 3 (solidity):
if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();

// Code block 4 (solidity):
if (_feeRate > 5_000) revert FeeRateTooHigh();

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

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

if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();

if (_feeRate > 5_000) revert FeeRateTooHigh();


0xhacksmithh G

### [Gas-01] Should Use ```require()``` Instead of ```assert()```
when assert() executed, it revert all state variable change and consume all remaining gas
when require() executed, it reverts all state variable change and return all remaining gas to caller

From gas saving perspective its recommended to use require() instead of assert()

*Instances(2)*
```solidity
File: Proxy.sol

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L16
```
```solidity
File: common/Singleton.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol#L13
```

### [Gas-02] ```onlyOwner``` functions could make as ```payable```
*Instances(10)*
```solidity
File: SmartAccount.sol

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

// Code block 1 (unknown):
when assert() executed, it revert all state variable change and consume all remaining gas
when require() executed, it reverts all state variable change and return all remaining gas to caller

From gas saving perspective its recommended to use require() instead of assert()

*Instances(2)*

// Code block 2 (unknown):
### [Gas-02]

// Code block 3 (unknown):
*Instances(10)*

Proxy.sol#L16, Singleton.sol#L13, SmartAccount.sol#L449-L453, SmartAccount.sol#L455-L458, SmartAccount.sol#L460-L463, SmartAccount.sol#L465-L473, SmartAccount.sol#L536-L538, BasePaymaster.sol#L67, BasePaymaster.sol#L75, BasePaymaster.sol#L90

SmartAccount.sol, Proxy.sol, BasePaymaster.sol, Singleton.sol


dec3ntraliz3d Q

## QA Report

## N-01

Remove unnecessary type casting in the constructor parameters of ReaperVaultV2.sol

## Summary

In ReaperVaultV2.sol, the constructor takes several input parameters, including _name and _symbol, which are of type string. In the current implementation, these parameters are being unnecessarily cast to string in the constructor declaration, which has no effect on the code's functionality and readability.

Therefore, it is recommended to remove the unnecessary type casting in the constructor parameters of ReaperVaultV2.sol.

[Link to the code on Github](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Vault/contracts/ReaperVaultV2.sol#L111-L119)

After making this change, the constructor should look like this:

```solidity
constructor(
    address _token,
    string memory _name,
    string memory _symbol,
    uint256 _tvlCap,
    address _treasury,
    address[] memory _strategists,
    address[] memory _multisigRoles
) ERC20(_name, _symbol) {
    // rest of the constructor code
}

```

## N-02

Use the `emit` keyword for events to improve code clarity.

## Summary

In older versions of Solidity, it was not mandatory to use the `emit` keyword when triggering events. However, this has changed in newer versions of Solidity, and it is now required. We recommend using the `emit` keyword in the following functions.

[Link to the code on Github](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/ActivePool.sol#L190-L202)

Current `increaseLUSDDebt` function:
```
    function increaseLUSDDebt(address _collateral, uint _amount) external override {
        _requireValidCollateralAddress(_collateral);
        _requireCallerIsBOorTroveM();
        LUSDDebt[_collateral] = LUSDDebt[_collateral].add(_amount);
        ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
    }
```
change to :
```solidity
    function increaseLUSDDebt(address _col

// Code block 1 (solidity):
constructor(
    address _token,
    string memory _name,
    string memory _symbol,
    uint256 _tvlCap,
    address _treasury,
    address[] memory _strategists,
    address[] memory _multisigRoles
) ERC20(_name, _symbol) {
    // rest of the constructor code
}

// Code block 2 (unknown):
function increaseLUSDDebt(address _collateral, uint _amount) external override {
        _requireValidCollateralAddress(_collateral);
        _requireCallerIsBOorTroveM();
        LUSDDebt[_collateral] = LUSDDebt[_collateral].add(_amount);
        ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
    }


petrichor G

# Summary

## Gas Optimization

| No     | Issue   | Instances |
|------- | ------- | -------   |
| [G-01] | Structs can be packed into fewer storage slots | 1  |  -   |
| [G-02] | Use hardcode address instead address(this)     | 4  |  -   |
| [G-03] | Do not calculate constants | 4 | - |
| [G-04] | Use bitmaps to save gas| 4 | - |
| [G-05] | Using delete statement can save gas| 1 | - |
| [G-06] | A modifier used only once and not being inherited should be inlined to save gas| 1 | - |
| [G-07] | Access mappings directly rather than using accessor functions | 4   | - |
| [G-08] | Use double if statements instead of && | 4  | - |
| [G-09] | Make 3 event parameters indexed when possible | 2  | -  |
| [G-10] | public functions to external | 1 | - |
| [G-11] | >= costs less gas than >  | 3 | - |
| [G-12] | It costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied | 22 | -|
| [G-13] | abi.encode() is less efficient than abi.encodePacked()| 5 | - |
| [G-14] | Use != 0 instead of > 0 for unsigned integer comparison | 1 | - |
| [G-15] | Use assembly to check for address(0)| 1 | - |
| [G-16] | Use constants instead of type(uintx).max | 1 | - |
| [G-17] | Usage of "UINTS", "INTS" smaller than 32 Bytes (256 bits) results in Increased Gas Consumption.| 4 | - |
| [G-18] | Multiple accesses of a mapping/array should use a local variable cache| 9 | - |
| [G-19] | internal functions only called once can be inlined to save gas | 11  | - |
| [G-20] | Multiple Address/id Mappings Can Be Combined Into A Single Mapping Of An Address/id To A Struct, Where Appropriate | 8 | - |
| [G-21] | Avoid contract existence checks by using low level calls| 1 | - |
| [G-22] | Use a more recent version of solidity | 1 | - |
| [G-23] | Use Modifiers Instead of Functions To Save Gas| 6 | - |
| [G-24] | State variables should be cached in stack variables rather than re-reading them from storage| 10 | -  |
| [G-25] | Using storage instead of mem

110  struct QuarterlyDistribution {
        uint24  id;
        uint48  startBlock;
        uint48  endBlock;
        uint128 fundsAvailable;
        uint256 fundingVotePowerCast;
        bytes32 fundedSlateHash;
    }

File: /ajna-core/src/RewardsManager.sol
250   IERC721(address(positionManager)).transferFrom(msg.sender, address(this), tokenId_);
## [G-4] Use bitmaps to save gas


Brenzee Q

# Summary

## Low Risk issues

|                                                                                | Issue                                                                                | Instances |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | --------- |
| [L-01](#l-01-privatepooldeposit-function-can-be-used-by-anyone-but-only-owner-can-withdraw-the-funds) | `PrivatePool.deposit` function can be used by anyone, but only owner can withdraw the funds | 1         |
| [L-02](#l-02-array-lengths-are-not-compared-to-each-other)                                            | Array lengths are not compared to each other                                                | 4         |
| [L-03](#l-03-basetoken-with-3-or-less-decimals-will-make-privatepoolchange-unusable)                  | ERC20 tokens with 3 or less decimals will make `PrivatePool.change`                         | 1         |
| [L-04](#l-04-wrong-nft-address-can-be-passed-in-some-of-ethrouter-functions)                          | Wrong `nft` address can be passed in some of `EthRouter` functions                          | 4         |
| [L-05](#l-05-changefee-cannot-be-updated-after-initalizing-privatepool)                               | `changeFee` cannot updated be after initalizing `PrivatePool`                               | 1         |
| [L-06](#l-06-privatepoolflashfee-returns-changefee-value)                                             | `PrivatePool.flashFee` returns `changeFee` value                                            | 1         |
| [L-07](#l-07-zero-values-are-not-checked)                                                             | Zero values are not checked                                                                 | 7         |
| [L-08](#l-08-privatepoolbuy-and-privatepoolsell-functions-a

function deposit(uint256[] calldata tokenIds, uint256 baseTokenAmount) public payable onlyOwner

    function sumWeightsAndValidateProof(
        uint256[] memory tokenIds,
        uint256[] memory tokenWeights,
        MerkleMultiProof memory proof
    ) public view returns (uint256) {
        require(inputTokenIds.length == inputTokenWeights.length, "Arrays are not equal length");

        // if the merkle root is not set then set the weight of each nft to be 1e18
        if (merkleRoot == bytes32(0)) {
            return tokenIds.length * 1e18;
        }

        uint256 sum;
        bytes32[] memory leafs = new bytes32[](tokenIds.length);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            // create the leaf for the merkle proof
            leafs[i] = keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i], tokenWeights[i]))));

            // sum each token weight
            sum += tokenWeights[i];
        }

        // validate that the weights are valid against the merkle proof
        if (!MerkleProofLib.verifyMultiProof(proof.proof, merkleRoot, leafs, proof.flags)) {
            revert InvalidMerkleProof();
        }

        return sum;
    }


YY Q

# The calculation of derivativeAmount in unstake function which could potentially lead to an integer overflow, which can cause unexpected behavior and loss of funds.

##  Affected Code
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L108-L129

```
uint256 derivativeAmount = (derivatives[i].balance() * _safEthAmount) / safEthTotalSupply;
```

## Description and Impact
The multiplication of `derivatives[i].balance()` and _safEthAmount may result in a number that exceeds the maximum or minimum value.

For example, if the user wants to `unstake` 10 token, at this time, the `_safEthAmount` should be 10.
If the total supply of SAFETH is 1,000,000, then the line of code
`uint256 derivativeAmount = (derivatives[i].balance() * _safEthAmount) / safEthTotalSupply;` would result in derivativeAmount being calculated as `derivatives[i].balance() * 10 / 1,000,000`.

If `derivatives[i].balance()` is very large, such as 2^256 - 1 (the maximum value of a uint256), then the multiplication may result in an integer overflow, and then the `derivativeAmount` to become 0.

### Fix
Use SafeMath Library
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol

// Code block 1 (unknown):
uint256 derivativeAmount = (derivatives[i].balance() * _safEthAmount) / safEthTotalSupply;


Kaysoft G

## [GAS-01] x += y/x -= y costs more gas than x = x + y/x = x - y for state variables

Files: https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230L231

```
virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);
```

## [GAS-02] USE `assembly` to write address storage values

files: https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L129-L131

```diff
function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {
-       privatePoolMetadata = _privatePoolMetadata; //@audit centeralization. use timelock.
+ assembly{
+ sstore(privateMetadata.slot, _privatePoolMetadata);
+ }
    }

```

## [GAS-03] SET CONSTRUCTORS TO `payable` to save 13 gas each on deployment.
Setting the constructor payable removes the initial check opcodes of msg.value == 0 and saves 13 gas on deployment without security risk.

Files: https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L53
- https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L90
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L143

// Code block 1 (unknown):
virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
        virtualNftReserves -= uint128(weightSum);

// Code block 2 (diff):
function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner {
-       privatePoolMetadata = _privatePoolMetadata; //@audit centeralization. use timelock.
+ assembly{
+ sstore(privateMetadata.slot, _privatePoolMetadata);
+ }
    }

PrivatePool.sol#L230, Factory.sol#L129-L131, Factory.sol#L53, EthRouter.sol#L90, PrivatePool.sol#L143


ro1sharkm Q

## [QA-1] Misleading comments
Comments are meant to  describe the intent of the code block. However, in multiple parts of the codebase, comments are not consistent with the code that is written or do not add any value to the reader. These are some examples:

In the contract `positionManager.sol` The comment associated with the positions mapping appears to be misleading.The comment suggests that the mapping maps a token ID to an ajna pool address, in reality, the mapping actually maps a token ID to another mapping that maps an asset ID to a Position struct.
```
    /// @dev Mapping of `token id => ajna pool address` for which token was minted.
    mapping(uint256 => mapping(uint256 => Position)) internal positions;
```

The comment in the `memorisePosition` function of `positionManager.sol` states that the function will revert if the position token to burn has `liquidity LiquidityNotRemoved() `. However, the function does not implement such a revert and it is unclear what the intended behavior of the comment on the functionality.
```
     *  @dev    === Revert on ===
     *  @dev    positions token to burn has liquidity `LiquidityNotRemoved()`
```

Consider correcting the comments to not mislead users or developers

## [QA-2] Publicly Callable `memorializePositions()` Function Allows Unauthorized memorization of User Positions
`memorializePositions()` function in `positionManager.sol` allows any caller to modify position information of any user. This is because the function does not include any ownership check on the provided TokenID.Any user can guess and update a position that they should not have access to. While the downside is that the user must know both the TokenID and position indexes, it is possible for a malicious user to guess the position index and the TokenID which is  a predictable value.

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/PositionManager.sol#L170-L216

// Code block 1 (unknown):
The comment in the `memorisePosition` function of `positionManager.sol` states that the function will revert if the position token to burn has `liquidity LiquidityNotRemoved() `. However, the function does not implement such a revert and it is unclear what the intended behavior of the comment on the functionality.


oyc_109 Q

## [L-01] Use of Block.timestamp

Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.

```
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::83 => * @return deadline the last block timestamp this operation is valid, or zero if it is valid indefinitely.
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::84 => *      Note that the validation code cannot use block.timestamp (or block.number) directly.
```

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

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert

```
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::550 => receive() external payable {}
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::540 => receive() external payable {}
```

## [L-03] abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). Unless there is a compelling reason, abi.encode should be preferred. If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

```
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::347 => _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
2023-01

// Code block 1 (unknown):
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::83 => * @return deadline the last block timestamp this operation is valid, or zero if it is valid indefinitely.
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol::84 => *      Note that the validation code cannot use block.timestamp (or block.number) directly.

// Code block 2 (unknown):
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol::550 => receive() external payable {}
2023-01-biconomy/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol::540 => receive() external payable {}


Matin Q

### Low-Risk Issues

|  | Issue | Instances |
| --- | --- | --- |
| [L‑01] | `indexed` keyword for reference type variables such as `string` in events may lead to data loss. | 1 |

Total: 1 instance over 1 issue

Note: The above tables were created, considering the automatic findings and thus, those are not included.

---

## Low-Risk Issues

### [L‑01] **`indexed` keyword for reference type variables such as `string` in events may lead to data loss**

Summary:

when the ```indexed``` keyword is used for reference typed variables such as string, it will return the hash of the mentioned string.
Thus, the event which is supposed to inform all of the applications subscribed to its emitting transaction (e.g. front-end of the DApp), would get a meaningless and obscure 32 bytes that correspond to keccak256 of an encoded string. For more information about the `indexed` events, one can [check here](https://docs.soliditylang.org/en/v0.8.17/abi-spec.html?highlight=indexed#events).

*There are 2 instances of this issue:*

```solidity
  event DestinationChainContractAddressSet(
    string indexed destinationChain,
    address contractAddress
  );
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L183C1-L186C5

// Code block 1 (solidity):
event DestinationChainContractAddressSet(
    string indexed destinationChain,
    address contractAddress
  );


lsaudit Q

# [QA-01]  `updatePriceFeedFor()` emits an event even when the priceFeed has not been changed

[File: ChainlinkPriceOracle.sol](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/oracles/ChainlinkPriceOracle.sol#L45-L49)
```
    function updatePriceFeedFor(address asset, address priceFeed) external onlyLRTManager onlySupportedAsset(asset) {
        UtilLib.checkNonZeroAddress(priceFeed);
        assetPriceFeed[asset] = priceFeed;
        emit AssetPriceFeedUpdate(asset, priceFeed);
    }
```

Function `updatePriceFeedFor()` does not verify if provided `priceFeed` is not already set in `assetPriceFeed[asset]`.
If `onlyLTRManager` provides `priceFeed` which was set before (`assetPriceFeed[asset] == priceFeed`), function will still emit an event.
Emitting this event may be very misleading to the end user - thus he/she may think that the price was updated, even though, it wasn't.

The same issue occurs in `LRTOracle.sol`:

[File: LRTOracle.sol](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L88-L99)
```
 function updatePriceOracleFor(
        address asset,
        address priceOracle
    )
        external
        onlyLRTManager
        onlySupportedAsset(asset)
    {
        UtilLib.checkNonZeroAddress(priceOracle);
        assetPriceOracle[asset] = priceOracle;
        emit AssetPriceOracleUpdate(asset, priceOracle);
    }
```

It's a good practice to perform additional check which verifies if the value is really being updated:

```
if (assetPriceFeed[asset] == priceFeed) revert ValueAlreadyInUse();
```

You can verify how it's done in `LRConfig.sol`. For example here:

[File: LRTConfig.sol](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L118)
```
 if (assetStrategy[asset] == strategy) {
            revert ValueAlreadyInUse();
    }
```

# [QA-02] Override `renounceRole()` in `RSETH.sol`

`RSETH` inherit

// Code block 1 (unknown):
function updatePriceFeedFor(address asset, address priceFeed) external onlyLRTManager onlySupportedAsset(asset) {
        UtilLib.checkNonZeroAddress(priceFeed);
        assetPriceFeed[asset] = priceFeed;
        emit AssetPriceFeedUpdate(asset, priceFeed);
    }

// Code block 2 (unknown):
function updatePriceOracleFor(
        address asset,
        address priceOracle
    )
        external
        onlyLRTManager
        onlySupportedAsset(asset)
    {
        UtilLib.checkNonZeroAddress(priceOracle);
        assetPriceOracle[asset] = priceOracle;
        emit AssetPriceOracleUpdate(asset, priceOracle);
    }

// Code block 3 (unknown):
if (assetPriceFeed[asset] == priceFeed) revert ValueAlreadyInUse();

// Code block 4 (unknown):
if (assetStrategy[asset] == strategy) {
            revert ValueAlreadyInUse();
    }


0xVolcano G

# Gas report

## Table of contents

- [Gas report](#gas-report)
  - [Table of contents](#table-of-contents)
    - [In accordance to the sponsors requirements, this report only focuses on issues that save more than 100 Gas](#in-accordance-to-the-sponsors-requirements-this-report-only-focuses-on-issues-that-save-more-than-100-gas)
  - [Pack `lastUpkeepTimeEmissions` and `lastUpkeepTimeRewardsEmitters` together by reducing their size to `uint128` (Saves 1 SLOT: 2.1K Gas)](#pack-lastupkeeptimeemissions-and-lastupkeeptimerewardsemitters-together-by-reducing-their-size-to-uint128-saves-1-slot-21k-gas)
  - [Pack the following by reducing their size(Save 3 SLOTS: 6.3K Gas)](#pack-the-following-by-reducing-their-sizesave-3-slots-63k-gas)
  - [Pack `minUnstakeWeeks,maxUnstakeWeeks,minUnstakePercent` by reducing the sizes(Save 2 SLOTs: 4.2K Gas)](#pack-minunstakeweeksmaxunstakeweeksminunstakepercent-by-reducing-the-sizessave-2-slots-42k-gas)
  - [Pack `rewardPercentForCallingLiquidation` with `maxRewardValueForCallingLiquidation`  by reducing the size(Save 1 SLOT: 2.1K Gas)](#pack-rewardpercentforcallingliquidation-with-maxrewardvalueforcallingliquidation--by-reducing-the-sizesave-1-slot-21k-gas)
  - [Pack `minimumCollateralValueForBorrowing` with `initialCollateralRatioPercent`(Save 1 SLOT: 2.1K Gas)](#pack-minimumcollateralvalueforborrowing-with-initialcollateralratiopercentsave-1-slot-21k-gas)
  - [Declare immutables for `exchangeConfig.wbtc()` and `exchangeConfig.weth()`(Save 2351 Gas on average)](#declare-immutables-for-exchangeconfigwbtc-and-exchangeconfigwethsave-2351-gas-on-average)
  - [Reference the immutable variable `salt` instead of making the external call again(Save 546 Gas on average)](#reference-the-immutable-variable-salt-instead-of-making-the-external-call-againsave-546-gas-on-average)
  - [Use the already defined immutable variable(save 595 Gas on average)](#use-the-already-defined-immutable-variablesave-595-gas-on-average)
  - [Use the imutable variable de

File: /src/Upkeep.sol
63:        uint256 public lastUpkeepTimeEmissions;
64:        uint256 public lastUpkeepTimeRewardsEmitters;

86:                lastUpkeepTimeEmissions = block.timestamp;
87:                lastUpkeepTimeRewardsEmitters = block.timestamp;


btk Q

| Total Low issues |
|------------------|

| Risk   | Issues Details                                                                                | Number        |
|--------|-------------------------------------------------------------------------------------------------------------|---------------|
| [L-01] | No Storage Gap for Upgradeable contracts                                                                    | 4             |
| [L-02] | Loss of precision due to rounding                                                                           | 11            |
| [L-03] | Lack of `nonReentrant` modifier                                                                             | 1             |
| [L-04] | Missing Event for initialize                                                                                | 4             |
| [L-05] | `owner` can renounce while system is paused                                                                 | 1             |
| [L-06] | Unused `receive()` Function Will Lock Ether In Contract                                                     | 1             |
| [L-07] | Use a more recent version of OpenZeppelin dependencies                                                      | 1             |
| [L-08] | Value is not validated to be different than the existing one                                                | 5             |
| [L-09] | Add a timelock to critical functions                                                                        | 8             |
| [L-10] | Lock pragmas to specific compiler version                                                                   | 4             |
| [L-11] | Use `uint256` instead `uint`                                                                                | 16            |
| [L-12] | Inconsistent check between Reth.Deposit() and WstEth.deposit(), SfrxEth.deposit()                           | 2             |
| [L-13] | Critical changes should use-

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;

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


Raihan G

# gas

 ### Note: last 6 types ([G-18],[G-19],[G-20],[G-21],[G-22],[G-23]) that the automated report missed.

# summary
|      | issue | instance |
|------|-------|----------|
|[G-01]|Use calldata instead of memory for function arguments that do not get mutated|3|
|[G-02]|Avoid contract existence checks by using low level calls|93|
|[G-03]|Can Make The Variable Outside The Loop To Save Gas |7|
|[G-04]|Make 3 event parameters indexed when possible|10|
|[G-05]|Amounts should be checked for 0 before calling a transfer|13|
|[G-06]|Do not calculate constants|2|
|[G-07]|Use hardcode address instead address(this)|10|
|[G-08]|Use Assembly To Check For address(0)|19|
|[G-09]|Use constants instead of type(uintx).max|17|
|[G-10]|Use ERC721A instead ERC721|3|
|[G-11]|Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement|1|
|[G-12]|Use assembly for math (add, sub, mul, div)|4|
|[G-13]|Use assembly to perform efficient back-to-back calls|6|
|[G-14]|Empty blocks should be removed to save gas|1|
|[G-15]|Avoid emitting storage values|10|
|[G-16]|Use Modifiers Instead of Functions To Save Gas|2|
|[G-17]|Gas savings can be achieved by changing the model for assigning value to the structure (260 gas)|8|
|[G-18]|Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate|1|
|[G-19]|Using storage instead of memory for structs/arrays saves gas|3|
|[G-20]|State variables should be cached in stack variables rather than re-reading them from storage|54|
|[G-21]|internal functions only called once can be inlined to save gas|4|
|[G-22]|Use assembly for small keccak256 hashes, in order to save gas|1|
|[G-23]|>= costs less gas than >|5|

## [G-01] Use calldata instead of memory for function arguments that do not get mutated
Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed

File: core/RdpxV2Core.sol
765       uint256[] memory optionIds

File: perp-vault/PerpetualAtlanticVault.sol
316   uint256[] memory optionIds

406   uint256[] memory strikes


ayden G

1.The way to save on gas costs with a for lop
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L71
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L113
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L140
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L147
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L171
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L191

```solidity
+    for (uint256 i = 0; i < exCallData.length;) {
+      unchecked {
+        ++i;
+      }
    }
}
```

2.Prepending zero-checking can reduce the number of checks and save more gas
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L138#L155
```solidity
    function rebalanceToWeights() external onlyOwner {
        uint256 ethAmountBefore = address(this).balance;
        for (uint i = 0; i < derivativeCount; i++) {
            if (derivatives[i].balance() > 0)
                derivatives[i].withdraw(derivatives[i].balance());
        }
        uint256 ethAmountAfter = address(this).balance;
        uint256 ethAmountToRebalance = ethAmountAfter - ethAmountBefore;
   +    if(ethAmountToRebalance == 0){
   +        emit Rebalanced();
   +        return;
   +    }

        for (uint i = 0; i < derivativeCount; i++) {
   -        if (weights[i] == 0 || ethAmountToRebalance == 0) continue;
   +        if (weights[i] == 0) continue;
            uint256 ethAmount = (ethAmountToRebalance * weights[i]) /
                totalWeight;
            // Price will change due to slippage
            derivatives[i].deposit{value: ethAmount}();
        }
        emit Rebalanced();
    }
```

3.Delete Unused function parameter
https://github.com/code-423n4/2023-03-asy

// Code block 1 (solidity):
+    for (uint256 i = 0; i < exCallData.length;) {
+      unchecked {
+        ++i;
+      }
    }
}

// Code block 2 (solidity):
function rebalanceToWeights() external onlyOwner {
        uint256 ethAmountBefore = address(this).balance;
        for (uint i = 0; i < derivativeCount; i++) {
            if (derivatives[i].balance() > 0)
                derivatives[i].withdraw(derivatives[i].balance());
        }
        uint256 ethAmountAfter = address(this).balance;
        uint256 ethAmountToRebalance = ethAmountAfter - ethAmountBefore;
   +    if(ethAmountToRebalance == 0){
   +        emit Rebalanced();
   +        return;
   +    }

        for (uint i = 0; i < derivativeCount; i++) {
   -        if (weights[i] == 0 || ethAmountToRebalance == 0) continue;
   +        if (weights[i] == 0) continue;
            uint256 ethAmount = (ethAmountToRebalance * weights[i]) /
                totalWeight;
            // Price will change due to slippage
            derivatives[i].deposit{value: ethAmount}();
        }
        emit Rebalanced();
    }

+    for (uint256 i = 0; i < exCallData.length;) {
+      unchecked {
+        ++i;
+      }
    }
}

function rebalanceToWeights() external onlyOwner {
        uint256 ethAmountBefore = address(this).balance;
        for (uint i = 0; i < derivativeCount; i++) {
            if (derivatives[i].balance() > 0)
                derivatives[i].withdraw(derivatives[i].balance());
        }
        uint256 ethAmountAfter = address(this).balance;
        uint256 ethAmountToRebalance = ethAmountAfter - ethAmountBefore;
   +    if(ethAmountToRebalance == 0){
   +        emit Rebalanced();
   +        return;
   +    }

        for (uint i = 0; i < derivativeCount; i++) {
   -        if (weights[i] == 0 || ethAmountToRebalance == 0) continue;
   +        if (weights[i] == 0) continue;
            uint256 ethAmount = (ethAmountToRebalance * weights[i]) /
                totalWeight;
            // Price will change due to slippage
            derivatives[i].deposit{value: ethAmount}();
        }
        emit Rebalanced();
    }

SafEth.sol#L71, SafEth.sol#L84, SafEth.sol#L113, SafEth.sol#L140, SafEth.sol#L147, SafEth.sol#L171, SafEth.sol#L191, SafEth.sol#L138


adeolu Q

## Unused argument in ethPerDerivative function in SfrxEth and WstEth contracts
 The ethPerDerivative function in SfrxEth and WstEth contracts accepts `uint256 _amount` argument which is unused in the logic of the function. It is not needed, it can be removed.

- **Proof of Concept**
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L86
```
    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);
    }
```

In this snippet from the ethPerDerivative function in WstEth.sol, it can be seen that `_amount` is unused.

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L111

```
    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        uint256 frxAmount = IsFrxEth(SFRX_ETH_ADDRESS).convertToAssets(
            10 ** 18
        );
        return ((10 ** 18 * frxAmount) /
            IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).price_oracle());
    }
```
In this snippet from the ethPerDerivative function in SfrxEth.sol, it can be seen that `_amount` is unused.

- **Tools Used**
  VS Code
- **Recommended Mitigation Steps**
Remove the unused argument `_amount` from the code.

// Code block 1 (unknown):
function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);
    }

// Code block 2 (unknown):
function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        uint256 frxAmount = IsFrxEth(SFRX_ETH_ADDRESS).convertToAssets(
            10 ** 18
        );
        return ((10 ** 18 * frxAmount) /
            IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).price_oracle());
    }


Aymen0909 Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1 | Possible loss of `msg.value` when calling the `flashloan` function | Low | 1 |
| 2 | slippage protection should be included in `sell` function | Low | 1 |
| 3 | No zero address check on `royaltyRecipient` in EthRouter | Low | 2 |
| 4 | `protocolFeeRate` should have a maximum upper bound | Low | 1 |
| 5 | Immutable state variables lack zero address checks | Low | 4 |
| 6 | Event should be emitted in setters | NC | 3 |
| 7 | Avoid floating pragma where possible | NC |  |
| 8 | `public` functions not called by the contract should be declared `external` instead | NC | 20 |

## Findings

### 1- Possible loss of `msg.value` when calling the `flashloan` function :

#### Risk : Low

When the `flashloan` function is called by a user it allows both the ETH and ERC20 tokens payments, so when the `baseToken` is an ERC20 token the user will pay with ERC20 tokens but if he also sends ETH in the form of `msg.value` in the same transaction (by accident), this ETH fund will be lost as the `flashloan` function doesn't verify that `msg.value == 0` when baseToken is an ERC20 tokens.

The impact of this is that those ETH funds sent by accident will be locked in the `PrivatePool` contract and the user won't be able to get them back.

The issue occurs because the `flashloan` function just checks that `msg.value >= fee` in case  `baseToken == address(0)` but does not that `msg.value == 0` when `baseToken != address(0)`, as it can be seen in the code below :

File: PrivatePool.sol [Line 635](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L635)
```
if (baseToken == address(0) && msg.value < fee) revert InvalidEthAmount();
```

#### Mitigation
To avoid this issue the check in the `flashloan` function should verify that `msg.value == 0` when `baseToken != address(0)`, thus the check [Line 635](https://github.c

if ((baseToken == address(0) && msg.value < fee) || (baseToken != address(0) && msg.value > 0)) revert InvalidEthAmount();

// Code block 1 (unknown):
if (baseToken == address(0) && msg.value < fee) revert InvalidEthAmount();


14si2o_Flint Q

## [L-01] Valid collateralValueLimitFactorX32 value will cause a revert

In the `_updateAndCheckCollateral` function, there are 2 checks to make sure that the `collateralValueLimitFactorX32` does not exceed `type(uint32).max`. This is because this maximal value represents a collateral value limit of 100%.

However in the actual checks, `<` is used instead of `<=`. Meaning a valid value of 100% (type(uint32).max) would cause a revert.

Even though setting a value of 100% is very unlikely, it remains a valid input and thus should not cause a revert.

```diff
                if (
-                    collateralValueLimitFactorX32 < type(uint32).max
+                    collateralValueLimitFactorX32 <= type(uint32).max
                        && _convertToAssets(tokenConfigs[token0].totalDebtShares, debtExchangeRateX96, Math.Rounding.Up) > lentAssets * collateralValueLimitFactorX32 / Q32
                ) {
                    revert CollateralValueLimit();
                }
                collateralValueLimitFactorX32 = tokenConfigs[token1].collateralValueLimitFactorX32;
                if (
-                    collateralValueLimitFactorX32 < type(uint32).max
+                    collateralValueLimitFactorX32 <= type(uint32).max
                        && _convertToAssets(tokenConfigs[token1].totalDebtShares, debtExchangeRateX96, Math.Rounding.Up)
                            > lentAssets * collateralValueLimitFactorX32 / Q32
                ) {
                    revert CollateralValueLimit();
                }
```

## [L-02] No slippage protection in Vault:liquidate

In the `Vault` `liquidate` function, the internal function `_sendPositionValue` is called which calls `nonfungiblePositionManager.decreaseLiquidity` with 100% slippage tolerance.
Meaning MEV bots will take advantage and the liquidity returned will be either extremely small or non-existing.

This should a High finding, but it is mitigated by another finding. The `_sendPositionValue` does no

## [L-02] No slippage protection in Vault:liquidate

In the `Vault` `liquidate` function, the internal function `_sendPositionValue` is called which calls `nonfungiblePositionManager.decreaseLiquidity` with 100% slippage tolerance.
Meaning MEV bots will take advantage and the liquidity returned will be either extremely small or non-existing.

This should a High finding, but it is mitigated by another finding. The `_sendPositionValue` does not return the decreased liquidity + fees, but only the fees.
Which means in 90%+ of the cases, the `amount0` and `amount1` returned will be smaller than `params.amount0Min` and `params.amount1Min` and thus cause a `SlippageError` revert.

## [L-03] Inconsistent Lending rounding in view functions

The `vaultInfo` function calculates the total lent by Rounding Up `_convertToAssets`.


avik_saikat G

# array.length inside loop
## Vulnerability details
Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.

You save `3 gas` by not reading array.length - `3 gas` per instance - `27 gas` saved

## Lines of code
### Total 23
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L106
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L115
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L116
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L134
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L159
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L161
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L182
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L183
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L239
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L261
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L265
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L284
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L119
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L236
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L238
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L272
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L329
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L335
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L441
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L446
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L49

# Default assignment
## Vulnerability details

Uninitialized variables are assigned with the types default value.

Explicitly initializing a variable with it's default value costs unnecessary gas.

## Lines of code
### Total 21
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L106
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L116
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L134
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L159
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L161
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L183
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L239
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L261
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L265
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L284
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/Factory.sol#L119
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L237
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L238
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L272
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L328
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L329
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L441
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L446
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L496
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L518
- https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L673

## Impact
Declaring `uint256 i = 0;` means doing an `MSTORE` of the value

EthRouter.sol#L106, EthRouter.sol#L115, EthRouter.sol#L116, EthRouter.sol#L134, EthRouter.sol#L159, EthRouter.sol#L161, EthRouter.sol#L182, EthRouter.sol#L183, EthRouter.sol#L239, EthRouter.sol#L261, EthRouter.sol#L265, EthRouter.sol#L284, Factory.sol#L119, PrivatePool.sol#L236, PrivatePool.sol#L238, PrivatePool.sol#L272, PrivatePool.sol#L329, PrivatePool.sol#L335, PrivatePool.sol#L441, PrivatePool.sol#L446, PrivatePool.sol#L49, PrivatePool.sol#L237, PrivatePool.sol#L328, PrivatePool.sol#L496, PrivatePool.sol#L518, PrivatePool.sol#L673


albahaca G

# gas

# summary

|      | issue | instance |
|------|-------|----------|
|[G-01]| Use Assembly To Check For address(0)|6|
|[G-02]| Avoid contract existence checks by using low level calls |2|
|[G-03]| Use do while loops instead of for loops|2|
|[G-04]| Functions guaranteed to revert when called by normal users can be marked payable|35|
|[G-05]| Avoid emitting storage values |2|
|[G-06]| Use assembly to perform efficient back-to-back calls|2|

## [G-01] Use Assembly To Check For address(0)
it's generally more gas-efficient to use assembly to check for a zero address (address(0)) than to use the == operator.

The reason for this is that the == operator generates additional instructions in the EVM bytecode, which can increase the gas cost of your contract. By using assembly, you can perform the zero address check more efficiently and reduce the overall gas cost of your contract.

Here's an example of how you can use assembly to check for a zero address:

```
contract MyContract {
    function isZeroAddress(address addr) public pure returns (bool) {
        uint256 addrInt = uint256(addr);

        assembly {
            // Load the zero address into memory
            let zero := mload(0x00)

            // Compare the address to the zero address
            let isZero := eq(addrInt, zero)

            // Return the result
            mstore(0x00, isZero)
            return(0, 0x20)
        }
    }
}
```
In the above example, we have a function isZeroAddress that takes an address as input and returns a boolean value indicating whether the address is equal to the zero address. Inside the function, we convert the address to an integer using uint256(addr), and then use assembly to compare the integer to the zero address.

By using assembly to perform the zero address check, we can make our code more gas-efficient and reduce the overall cost of our contract. It's important to note that assembly can be more difficult to read and maintain t

File:  contracts/usdy/rUSDY.sol
490     require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");

491     require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

519     require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");

520     require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

547     require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");

579     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

642     if (from != address(0)) {

649     if (to != address(0)) {

// Code block 1 (unknown):
contract MyContract {
    function isZeroAddress(address addr) public pure returns (bool) {
        uint256 addrInt = uint256(addr);

        assembly {
            // Load the zero address into memory
            let zero := mload(0x00)

            // Compare the address to the zero address
            let isZero := eq(addrInt, zero)

            // Return the result
            mstore(0x00, isZero)
            return(0, 0x20)
        }
    }
}


hunter_w3b G

# Gas Optimization

# Summary

| Number | Optimization Details                                                                | Context |
| :----: | :---------------------------------------------------------------------------------- | :-----: |
| [G-01] | Avoid contract `existence` checks by using low level calls                          |    1    |
| [G-02] | Using fixed bytes is cheaper than using `string`                                    |    2    |
| [G-03] | Expressions for `constant` values such as a call to keccak256(), should use immutable rather than constant                                                                                |    7    |
| [G-04] | Using `calldata` instead of `memory` for read-only arguments                        |    7    |
| [G-05] | Before some functions, we should `check` some variables for possible gas save       |    8    |
| [G-06] | `LybraPeUSDVaultBase::LybraPeUSDVaultBase()` function not called by the contract should be removed to save deployment gas                                                                                |    1    |
| [G-07] | Duplicated `require()` checks should be refactored to a modifier or function        |   18    |
| [G-08] | Use constants instead of `type(uintx).max`                                          |    1    |
| [G-09] | Use double `require` instead of using &&                                            |    3    |
| [G-10] | A `modifier` used only once should be inlined to save gas                           |    3    |
| [G-11] | Refactor event to avoid `emitting` data that is already present in transaction data |   26    |
| [G-12] | Use hardcode address instead `address(this)`                                        |   38    |
| [G-13] | Use Assembly To Check For `address(0)`                                              |   16    |
| [G-14] | When possible, use assembly instead of `unchecked{++i}`                             |    2    |
| [G-15] | Use nested if and,

File: contracts/lybra/miner/ProtocolRewardsPool.sol

172        amount = IEUSD(configurator.getEUSDAddress()).getMintedEUSDByShares(earned(user));

232            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);

file: lybra/token/EUSD.sol

99    function name() public pure returns (string memory) {
100        return "eUSD";
101    }
102
103    /**
104     * @return the symbol of the token, usually a shorter version of the
105     * name.
106     */
107    function symbol() public pure returns (string memory) {
108        return "eUSD";
109    }


hunter_w3b Q

## [L-01] Deposit limits can be updated by the manager role, but there is no check to prevent reducing limits. This could lock user assets

The addNewSupportedAsset function allows the manager role to update the deposit limit amount for any supported asset. However, there is no check to prevent reducing this limit to a value below what users have already deposited.

```solidity
File: src/LRTConfig.sol

73    function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(LRTConstants.MANAGER) {
74        _addNewSupportedAsset(asset, depositLimit);
75    }
76
77    /// @dev private function to add a new supported asset
78    /// @param asset Asset address
79    /// @param depositLimit Deposit limit for the asset
80    function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
81        UtilLib.checkNonZeroAddress(asset);
82        if (isSupportedAsset[asset]) {
83            revert AssetAlreadySupported();
84        }
85        isSupportedAsset[asset] = true;
86        supportedAssetList.push(asset);
87        depositLimitByAsset[asset] = depositLimit;
88        emit AddedNewSupportedAsset(asset, depositLimit);
89    }
```

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

This could potentially lock funds if a limit is reduced after a user has deposited more than the new lower limit. An attacker with manager role privileges could maliciously lock funds in this way. The deposit limit should always be increased or maintained, never decreased, to avoid locking user funds.

## [L-02] Strategies can be updated by any admin, but there is no logic to handle existing funds. Strategy changes could lose user asset

The updateAssetStrategy function allows any admin to change the strategy address for how an asset is managed. However, there is no logic defined in the contract for how existing deposited funds should be handled during a strategy change. Without proper handling, funds could be lost or inaccessibl

File: src/LRTConfig.sol

109    function updateAssetStrategy(
110        address asset,
111        address strategy
112    )
113        external
114        onlyRole(DEFAULT_ADMIN_ROLE)
115        onlySupportedAsset(asset)
116    {
117        UtilLib.checkNonZeroAddress(strategy);
118       if (assetStrategy[asset] == strategy) {
119            revert ValueAlreadyInUse();
120        }
121        assetStrategy[asset] = strategy;
122    }

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

73    function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(LRTConstants.MANAGER) {
74        _addNewSupportedAsset(asset, depositLimit);
75    }
76
77    /// @dev private function to add a new supported asset
78    /// @param asset Asset address
79    /// @param depositLimit Deposit limit for the asset
80    function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
81        UtilLib.checkNonZeroAddress(asset);
82        if (isSupportedAsset[asset]) {
83            revert AssetAlreadySupported();
84        }
85        isSupportedAsset[asset] = true;
86        supportedAssetList.push(asset);
87        depositLimitByAsset[asset] = depositLimit;
88        emit AddedNewSupportedAsset(asset, depositLimit);
89    }


alexzoid G

## [G-01] Storage Variable Accessed Multiple Times

The `derivativeCount` storage variable is accessed multiple times within for-loops, which can lead to inefficient gas usage:

- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L71
```solidity
for (uint i = 0; i < derivativeCount; i++)
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84
```solidity
for (uint i = 0; i < derivativeCount; i++) {
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L113
```solidity
for (uint256 i = 0; i < derivativeCount; i++) {
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L140
```solidity
for (uint i = 0; i < derivativeCount; i++) {
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L147
```solidity
for (uint i = 0; i < derivativeCount; i++) {
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L171
```solidity
for (uint256 i = 0; i < derivativeCount; i++)
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L191
```solidity
for (uint256 i = 0; i < derivativeCount; i++)
```
- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L186-L188
```solidity
derivatives[derivativeCount] = IDerivative(_contractAddress);
weights[derivativeCount] = _weight;
derivativeCount++;
```

Additionally, the `derivatives[i]` and `weights[i]` storage variables are accessed multiple times:

- https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L73-L74
```solidity
// Getting underlying value in terms of ETH for each derivative
for (uint i = 0; i < derivativeCount; i++)
    underlyingValue +=
        (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
            derivatives[i].balance()) /
        10 ** 18;
```
- https://github.com/code-423n4/2023-03-a

// Code block 1 (solidity):
for (uint i = 0; i < derivativeCount; i++)

// Code block 2 (solidity):
for (uint i = 0; i < derivativeCount; i++) {

// Code block 3 (solidity):
for (uint256 i = 0; i < derivativeCount; i++) {

// Code block 4 (solidity):
for (uint i = 0; i < derivativeCount; i++) {

// Code block 5 (solidity):
for (uint i = 0; i < derivativeCount; i++) {

// Code block 6 (solidity):
for (uint256 i = 0; i < derivativeCount; i++)

// Code block 7 (solidity):
for (uint256 i = 0; i < derivativeCount; i++)

// Code block 8 (solidity):
derivatives[derivativeCount] = IDerivative(_contractAddress);
weights[derivativeCount] = _weight;
derivativeCount++;

// Code block 9 (solidity):
// Getting underlying value in terms of ETH for each derivative
for (uint i = 0; i < derivativeCount; i++)
    underlyingValue +=
        (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
            derivatives[i].balance()) /
        10 ** 18;

for (uint i = 0; i < derivativeCount; i++)

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

for (uint256 i = 0; i < derivativeCount; i++) {

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

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

for (uint256 i = 0; i < derivativeCount; i++)

for (uint256 i = 0; i < derivativeCount; i++)

derivatives[derivativeCount] = IDerivative(_contractAddress);
weights[derivativeCount] = _weight;
derivativeCount++;

// Getting underlying value in terms of ETH for each derivative
for (uint i = 0; i < derivativeCount; i++)
    underlyingValue +=
        (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
            derivatives[i].balance()) /
        10 ** 18;

SafEth.sol#L71, SafEth.sol#L84, SafEth.sol#L113, SafEth.sol#L140, SafEth.sol#L147, SafEth.sol#L171, SafEth.sol#L191, SafEth.sol#L186-L188, SafEth.sol#L73-L74


digitizeworx Analysis

## Overview

Kelp DAO aims to provide higher yield opportunities for staked ETH assets like rETH by "restaking" them into liquidity strategies. The core components are:

**LRTConfig** - Stores configuration and contract addresses. Controls access.

**LRTDepositPool** - Receives user deposits and mints rsETH tokens.

**NodeDelegator** - Receives assets from DepositPool. Deposits into EigenLayer strategies.

**LRTOracle** - Fetches LST token prices from Chainlink.

**RSETH** - ERC20 receipt token minted when users deposit.

### Architecture

Kelp has a modular architecture with well-defined contract responsibilities:

- LRTConfig stores all shared configuration and controls access

- LRTDepositPool is the main user entry point to deposit assets

- Funds flow from DepositPool into NodeDelegators

- NodeDelegators deposit into yield generating EigenLayer strategies

- LRTOracle provides price feeds to calculate rsETH mint amounts

This separation of concerns makes the system flexible and upgradable.

## Analysis

### Access Control

- Access control done well with admin and manager roles in LRTConfig.

- Modifiers like `onlyLRTAdmin` prevent privilege escalation.

- No ways found to manipulate roles or bypass restrictions.

### Funds Safety

- Users can only deposit into Kelp, no withdrawals. Mitigates mismatched receipt risk.

- Admin roles have no access to user funds. Prevents loss due to compromised admin keys.

- Use of pausable, reentrancy guard, and deposit limits provide security.

**[deposit function](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L119-L144)**

LRTDepositPool controls user deposits:

```solidity
function deposit(uint amount) external {

  // Transfer asset from user

  // Mint rsETH

  // Emit deposit event

}

// No withdraw function!
```

Admin roles defined in LRTConfig:

```solidity
// ADMIN_ROLE
// MANAGER_ROLE
```

**Analysis**

- LRTDepositPool has no withdraw funct

// Code block 1 (solidity):
function deposit(uint amount) external {

  // Transfer asset from user

  // Mint rsETH

  // Emit deposit event

}

// No withdraw function!


evmboi32 Q

# Incorrect ```GLOBAL_BUDGET_CONSTRAINT```
If we look at the whitepaper it states that ```GLOBAL_BUDGET_CONSTRAINT``` should be set to 2%.
> On a quarterly basis, up to 2% of the treasury (30% of the AJNA token supply on launch) is
distributed to facilitate growth of the Ajna system.

The actual implementation has this value set to 3%

StandardFunding.sol#27
```solidity
    uint256 internal constant GLOBAL_BUDGET_CONSTRAINT = 0.03 * 1e18;
```

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/StandardFunding.sol#L27

// Code block 1 (unknown):
If we look at the whitepaper it states that

// Code block 2 (solidity):
uint256 internal constant GLOBAL_BUDGET_CONSTRAINT = 0.03 * 1e18;


KupiaSec Q

## [L-01] function `MergingPool.pickWinner()` has no event
It is recommended to emit events on important functions

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

```solidity
    function pickWinner(uint256[] calldata winners) external {
        require(isAdmin[msg.sender]);
        require(winners.length == winnersPerPeriod, "Incorrect number of winners");
        require(!isSelectionComplete[roundId], "Winners are already selected");
        uint256 winnersLength = winners.length;
        address[] memory currentWinnerAddresses = new address[](winnersLength);
        for (uint256 i = 0; i < winnersLength; i++) {
            currentWinnerAddresses[i] = _fighterFarmInstance.ownerOf(winners[i]);
            totalPoints -= fighterPoints[winners[i]];
            fighterPoints[winners[i]] = 0;
        }
        winnerAddresses[roundId] = currentWinnerAddresses;
        isSelectionComplete[roundId] = true;
        roundId += 1;
    }
```

## [L-02] function `FighterFarm.redeemMintPass()` doesn't check the length of the `iconsTypes`.

All inputs must have the same length, and there is a check but the `iconTypes` is missing in the check.

```solidity
    function redeemMintPass(
        uint256[] calldata mintpassIdsToBurn,
        uint8[] calldata fighterTypes,
        uint8[] calldata iconsTypes,
        string[] calldata mintPassDnas,
        string[] calldata modelHashes,
        string[] calldata modelTypes
    )
        external
    {
        require(
            mintpassIdsToBurn.length == mintPassDnas.length &&
            mintPassDnas.length == fighterTypes.length &&
            fighterTypes.length == modelHashes.length &&
            modelHashes.length == modelTypes.length
        );
        for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) {
            require(msg.sender == _mintpassInstance.ownerOf(mintpassIdsToBurn[i]));
            _mintpassInstance.burn(mintpassIdsToBurn[i]

    function redeemMintPass(
        uint256[] calldata mintpassIdsToBurn,
        uint8[] calldata fighterTypes,
        uint8[] calldata iconsTypes,
        string[] calldata mintPassDnas,
        string[] calldata modelHashes,
        string[] calldata modelTypes
    )
        external
    {
        require(
            mintpassIdsToBurn.length == mintPassDnas.length &&
            mintPassDnas.length == fighterTypes.length &&
            fighterTypes.length == modelHashes.length &&
            modelHashes.length == modelTypes.length
        );
        for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) {
            require(msg.sender == _mintpassInstance.ownerOf(mintpassIdsToBurn[i]));
            _mintpassInstance.burn(mintpassIdsToBurn[i]);
            _createNewFighter(
                msg.sender,
                uint256(keccak256(abi.encode(mintPassDnas[i]))),
                modelHashes[i],
                modelTypes[i],
                fighterTypes[i],
                iconsTypes[i],
                [uint256(100), uint256(100)]
            );
        }
    }

// Code block 1 (solidity):
function pickWinner(uint256[] calldata winners) external {
        require(isAdmin[msg.sender]);
        require(winners.length == winnersPerPeriod, "Incorrect number of winners");
        require(!isSelectionComplete[roundId], "Winners are already selected");
        uint256 winnersLength = winners.length;
        address[] memory currentWinnerAddresses = new address[](winnersLength);
        for (uint256 i = 0; i < winnersLength; i++) {
            currentWinnerAddresses[i] = _fighterFarmInstance.ownerOf(winners[i]);
            totalPoints -= fighterPoints[winners[i]];
            fighterPoints[winners[i]] = 0;
        }
        winnerAddresses[roundId] = currentWinnerAddresses;
        isSelectionComplete[roundId] = true;
        roundId += 1;
    }


c3phas Q

### Table of Contents
## QA Findings
- [QA Findings](#qa-findings)
- [Upgradeable contract missing a `__gap` storage variable](#upgradeable-contract-missing-a-__gap-storage-variable)
- [Avoid Naming collisions, totalSupply should be renamed](#avoid-naming-collisions-totalsupply-should-be-renamed)
- [Unused local variables should be removed](#unused-local-variables-should-be-removed)
- [Constants should be defined rather than using magic numbers](#constants-should-be-defined-rather-than-using-magic-numbers)
- [Lock pragmas to specific compiler version](#lock-pragmas-to-specific-compiler-version)
- [Natspec is incomplete](#natspec-is-incomplete)
- [Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. `10**18`)](#use-scientific-notation-eg-1e18-rather-than-exponentiation-eg-1018)
- [Lack of event emission on setters](#lack-of-event-emission-on-setters)
- [Lack of consistenty with using uint vs uint256](#lack-of-consistenty-with-using-uint-vs-uint256)
- [Code Structure Deviates From Best-Practice](#code-structure-deviates-from-best-practice)
- [Import declarations should import specific identifiers, rather than the whole file](#import-declarations-should-import-specific-identifiers-rather-than-the-whole-file)

## Upgradeable contract missing a `__gap` storage variable
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L15-L20
```solidity
File: /contracts/SafEth/SafEth.sol
15: contract SafEth is
16:    Initializable,
17:    ERC20Upgradeable,
18:    OwnableUpgradeable,
19:    SafEthStorage
20:{
```

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L12
```solidity
File: /contracts/SafEth/derivatives/WstEth.sol
12:    contract WstEth is IDerivative, Initializable, OwnableUpgradeable {
```

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/deriv

// Code block 1 (solidity):
File: /contracts/SafEth/SafEth.sol
15: contract SafEth is
16:    Initializable,
17:    ERC20Upgradeable,
18:    OwnableUpgradeable,
19:    SafEthStorage
20:{

// Code block 2 (solidity):
File: /contracts/SafEth/derivatives/WstEth.sol
12:    contract WstEth is IDerivative, Initializable, OwnableUpgradeable {

File: /contracts/SafEth/SafEth.sol
15: contract SafEth is
16:    Initializable,
17:    ERC20Upgradeable,
18:    OwnableUpgradeable,
19:    SafEthStorage
20:{

File: /contracts/SafEth/derivatives/WstEth.sol
12:    contract WstEth is IDerivative, Initializable, OwnableUpgradeable {


foxb868 Analysis

 Based on reviewing the Salty.IO codebase and architecture, the main objectives of the project appear to be:

1. To build a decentralized exchange (DEX) with zero swap fees and automatic yield generation.

The core Pools.sol contract implements an automated market maker (AMM) model and arbitrage functionality to generate yield from swap transactions. This aligns incentives by pairing user swaps with arbitrage profits.

2. To offer a native stablecoin (USDS) collateralized by WBTC/WETH.

The USDS.sol stablecoin allows borrowing against WBTC/WETH collateral. The collateral model and incentive structure around USDS aims to maintain its 1:1 peg with USD.

3. To decentralize control of the platform as much as possible.

Salty.IO is governed by a DAO.sol contract where SALT holders vote on protocol changes. No special admin roles or privileges exist. This ensures the platform lacks centralized points of failure.

4. To bootstrap sustainable liquidity and usage of the DEX.

Incentive mechanisms focus on rewarding liquidity providers with emissions and protocol revenue share. Adoption incentives are structured to make Salty.IO self-sustaining. So in summary - zero fee swaps, yield from arbitrage, USDS stablecoin, control decentralization via the DAO, and bootstrapping sustainable platform usage appear to be the main goals behind this project.

## Architecture

Salty.IO divides functionality into contracts reflecting the separation of concerns

```diff
┌─────────────────────────────────┐
│                                 │
│            DAO.sol              │
│       Governance Logic          │
│                                 │
└─────────────────┬───────────────┘
                  │
┌─────────────────┴───────────────┐
│            Pools.sol            │
│         AMM + Arbitrage         │
└─────────────────┬───────────────┘
                  │
┌─────────────────┴───┬────────────┐
│       Rewards.sol      │         │
│    Incentives and      │         │
│      Distribu

**Trust Minimization**

- DAO contract is the only privileged role in Salty - avoiding single points of compromise.
- USDS design depends on collateral health without system ability to liquidate assets in black swan events.

- Failure to attract liquidity makes architecture unsustainable.

**Sustainability Incentives**

Salty rewards liquidity with protocol fees:


MaNcHaSsS Q

`totalWethDelegated` is not updated when user withdraws delegated WETH from contract.

```
function withdraw(
    uint256 delegateId
  ) external returns (uint256 amountWithdrawn) {
    _whenNotPaused();
    _validate(delegateId < delegates.length, 14);
    Delegate storage delegate = delegates[delegateId];
    _validate(delegate.owner == msg.sender, 9);

    amountWithdrawn = delegate.amount - delegate.activeCollateral;
    _validate(amountWithdrawn > 0, 15);
    delegate.amount = delegate.activeCollateral;

    IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn);

    emit LogDelegateWithdraw(delegateId, amountWithdrawn);
  }
```

// Code block 1 (unknown):
function withdraw(
    uint256 delegateId
  ) external returns (uint256 amountWithdrawn) {
    _whenNotPaused();
    _validate(delegateId < delegates.length, 14);
    Delegate storage delegate = delegates[delegateId];
    _validate(delegate.owner == msg.sender, 9);

    amountWithdrawn = delegate.amount - delegate.activeCollateral;
    _validate(amountWithdrawn > 0, 15);
    delegate.amount = delegate.activeCollateral;

    IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn);

    emit LogDelegateWithdraw(delegateId, amountWithdrawn);
  }


0xblackskull Q

### If `totalRewardX64` set to 0 then there is no way to set new value
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/AutoCompound.sol#L243-L247
```
function setReward(uint64 _totalRewardX64) external onlyOwner {
        //@audit-qa
        require(_totalRewardX64 <= totalRewardX64, ">totalRewardX64");
        totalRewardX64 = _totalRewardX64;
        emit RewardUpdated(msg.sender, _totalRewardX64);
    }
```
Mitigation:  add require statement
```
require(_totalRewardX64 != 0);
```

### Need emit event in `else` block
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L1150-L1165
```
function _updateGlobalInterest()
        internal
        returns (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96)
    {
        // only needs to be updated once per block (when needed)
        if (block.timestamp > lastExchangeRateUpdate) {
            (newDebtExchangeRateX96, newLendExchangeRateX96) = _calculateGlobalInterest();
            lastDebtExchangeRateX96 = newDebtExchangeRateX96;
            lastLendExchangeRateX96 = newLendExchangeRateX96;
            lastExchangeRateUpdate = block.timestamp;
            emit ExchangeRateUpdate(newDebtExchangeRateX96, newLendExchangeRateX96);
        } else {
            newDebtExchangeRateX96 = lastDebtExchangeRateX96;
            newLendExchangeRateX96 = lastLendExchangeRateX96;
            //@audit missing event, ExchangeRateUpdate(newDebtExchangeRateX96, newLendExchangeRateX96)
        }
    }
```

### operator address is not EOA checked
According to Additional Context on C4 page, `Operators (which are EOA used by bots to call actions in Automator contracts)`
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/automators/Automator.sol#L69-L72
```
function setOperator(address _operator, bool _active) public onlyOwner {
        emit OperatorChanged(_operator, _active);
        operators[_operator] = _active;
    }
```

### should check tokens length must

// Code block 1 (unknown):
function setReward(uint64 _totalRewardX64) external onlyOwner {
        //@audit-qa
        require(_totalRewardX64 <= totalRewardX64, ">totalRewardX64");
        totalRewardX64 = _totalRewardX64;
        emit RewardUpdated(msg.sender, _totalRewardX64);
    }

// Code block 2 (unknown):
require(_totalRewardX64 != 0);

// Code block 3 (unknown):
function _updateGlobalInterest()
        internal
        returns (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96)
    {
        // only needs to be updated once per block (when needed)
        if (block.timestamp > lastExchangeRateUpdate) {
            (newDebtExchangeRateX96, newLendExchangeRateX96) = _calculateGlobalInterest();
            lastDebtExchangeRateX96 = newDebtExchangeRateX96;
            lastLendExchangeRateX96 = newLendExchangeRateX96;
            lastExchangeRateUpdate = block.timestamp;
            emit ExchangeRateUpdate(newDebtExchangeRateX96, newLendExchangeRateX96);
        } else {
            newDebtExchangeRateX96 = lastDebtExchangeRateX96;
            newLendExchangeRateX96 = lastLendExchangeRateX96;
            //@audit missing event, ExchangeRateUpdate(newDebtExchangeRateX96, newLendExchangeRateX96)
        }
    }

// Code block 4 (unknown):
function setOperator(address _operator, bool _active) public onlyOwner {
        emit OperatorChanged(_operator, _active);
        operators[_operator] = _active;
    }


0xMosh Q

# L-01 :  Have a nonreentrant modifier in `emergencyWithdraw` function
The `emergencyWithdraw` function in RpdxDecayingBonds.sol deals with eth transfers to an externel account . Exteranal calls with may reenter to the contract before state changes properly .  However , having a nonreentrant modifier is always preferred .
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/decaying-bonds/RdpxDecayingBonds.sol#L89
# L-02: `RDPXV2CORE_ROLE` is not set in the constructor in ` PerpetualAtlanticVault.sol ` .
`RDPXV2CORE_ROLE` has some crucial functionality in  ` PerpetualAtlanticVault.sol ` contract . Having it set in the constructor should be done while deploying the contracts . However , it is not set in the constuctor.
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L113

# L-03 : `_convertToAssets ` should be public .
The function `_convertToAssets ` is to check the  amount of assets for a number of shares . This is a view function . However , this should be Public function in my opinion cause users may have to check the return amount  of assets for their corresponding shares .
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L218

# NC-01 :Lack of documentation in the code
 The whole codebase lacks documentation . This issue makes it difficult to understand the code's purpose, functionality, and usage, which can lead to confusion, errors, and challenges when maintaining or extending the codebase. Proper documentation is crucial for code readability, collaboration, and long-term maintainability.

# NC-02 : Function naming is not accurate of `decreaseAmount` function in RpdxDecayingBonds.sol
The function is intended to decrease the amount of `rdpxAmount ` .  However ,  the function can be used to increase the `rdpxAmount ` of a bo

function decreaseAmount(
    uint256 bondId,
    uint256 amount
  ) public onlyRole(RDPXV2CORE_ROLE) {
    _whenNotPaused();
    bonds[bondId].rdpxAmount = amount;
  }


Bason Q

## Low and Non-Critical Issues Summary
| Number |Issues Details |
|:--:|:-------|
|[NC-01]| Use latest Solidity version
|[NC-02]| Use stable pragma statement
|[NC-03]| Use named imports instead of plain `import FILE.SOL`
|[NC-04]| Constants should be defined rather than using magic numbers
|[NC-05]| You can use named parameters in mapping types
|[L-01]| Missing events for critical parameter changes
|[L-02]| Possible division by 0 if contracts are configured improperly
|[L-03]| Possible to pass address 0 in functions that pass ownership

***

## [NC-01] Use latest Solidity version

Solidity pragma versioning should be upgraded to latest available version - 0.8.19

***

## |[NC-02]| Use stable pragma statement

Using a floating pragma statement `^0.8.13` is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode.

## |[NC-03]| Use named imports instead of plain `import FILE.SOL`

**Recommendation:**
`import {contract1, interface1} from "filename.sol";

## |[NC-04]| Constants should be defined rather than using magic numbers

SafEth.sol
minAmount = 5 * 10 ** 17;
maxAmount = 200 * 10 ** 18;
preDepositPrice = 10 ** 18;
underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;
preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply
uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
                depositAmount
            ) * depositAmount) / 10 ** 18;

Reth.sol
Define "RocketPool" as a private constant and return it in the method
function name() public pure returns (string memory) {
        return "RocketPool";
    }
***
uint rethPerEth = (10 ** 36) / poolPrice();
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
                ((10 ** 18 - maxSlippage))) / 10 ** 18);
(poolPrice() * 10 ** 18) / (10 ** 18);
factory.getPool(rocketTokenRETHA

Reth.sol
Create a custom event DepositSuccessful(indexed address depositor)

WstEth.sol
Create a custom event DepositSuccessful(indexed address depositor)
function deposit(
uint256 wstEthAmount = wstEthBalancePost - wstEthBalancePre;
emit DepositSuccessful(WST_ETH)


BRONZEDISC Q

## QA
---

### Layout Order [1]

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

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

```solidity
// event coming after constructor
24:    event SmartAccountCreated(address indexed _proxy, address indexed _implementation, address indexed _owner, string version, uint256 _index);
```

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

```solidity
// events is coming after functions
17:    event CalledFrom(address sender);

// state vars are coming after functions
22:    mapping(uint256 => uint256) public xxx;
23:    uint256 public offset;
```

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

```solidity
//  structs should come before functions
145:    struct MemoryUserOp {
156:    struct UserOpInfo {
```

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

```solidity
// these state variables should come before events
16:    address internal constant SENTINEL_MODULES = address(0x1);
18:    mapping(address => address) internal modules;
```

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

```solidity
// modifiers should come before functions
10:    modifier authorized() {
```

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

```solidity
// events should come after state variables
7:   event ButtonPushed(address pusher, uint256 pushes);
```

---

### Function Visibility [2]

- Order of Functions: Ordering helps readers identify which functions they can call and to find the con

// Code block 1 (solidity):
// event coming after constructor
24:    event SmartAccountCreated(address indexed _proxy, address indexed _implementation, address indexed _owner, string version, uint256 _index);

// Code block 2 (solidity):
// events is coming after functions
17:    event CalledFrom(address sender);

// state vars are coming after functions
22:    mapping(uint256 => uint256) public xxx;
23:    uint256 public offset;

// Code block 3 (solidity):
//  structs should come before functions
145:    struct MemoryUserOp {
156:    struct UserOpInfo {

// Code block 4 (solidity):
// these state variables should come before events
16:    address internal constant SENTINEL_MODULES = address(0x1);
18:    mapping(address => address) internal modules;

// Code block 5 (solidity):
// modifiers should come before functions
10:    modifier authorized() {

// Code block 6 (solidity):
// events should come after state variables
7:   event ButtonPushed(address pusher, uint256 pushes);

// event coming after constructor
24:    event SmartAccountCreated(address indexed _proxy, address indexed _implementation, address indexed _owner, string version, uint256 _index);

// events is coming after functions
17:    event CalledFrom(address sender);

// state vars are coming after functions
22:    mapping(uint256 => uint256) public xxx;
23:    uint256 public offset;

//  structs should come before functions
145:    struct MemoryUserOp {
156:    struct UserOpInfo {

// these state variables should come before events
16:    address internal constant SENTINEL_MODULES = address(0x1);
18:    mapping(address => address) internal modules;

// modifiers should come before functions
10:    modifier authorized() {

// events should come after state variables
7:   event ButtonPushed(address pusher, uint256 pushes);

SmartAccountFactory.sol, TestCounter.sol, EntryPoint.sol, ModuleManager.sol, SelfAuthorized.sol, Button.sol

SelfAuthorized.sol, TestCounter.sol, Button.sol, SmartAccountFactory.sol, EntryPoint.sol, ModuleManager.sol