memforvik Q

The function _withdrawLiquidityAndClaim not followed Checks effects interactions, may lead to a read-only reentrancy attacks

```
src/staking/Liquidity.sol

	// Withdraw specified liquidity, decrease the user's liquidity share and claim any pending rewards.
    function _withdrawLiquidityAndClaim( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToWithdraw, uint256 minReclaimedA, uint256 minReclaimedB ) internal returns (uint256 reclaimedA, uint256 reclaimedB)
		{
		bytes32 poolID = PoolUtils._poolID( tokenA, tokenB );
		require( userShareForPool(msg.sender, poolID) >= liquidityToWithdraw, "Cannot withdraw more than existing user share" );

		// Remove the amount of liquidity specified by the user.
		// The liquidity in the pool is currently owned by this contract. (external call)
		(reclaimedA, reclaimedB) = pools.removeLiquidity( tokenA, tokenB, liquidityToWithdraw, minReclaimedA, minReclaimedB, totalShares[poolID] );

		// Transfer the reclaimed tokens to the user
		tokenA.safeTransfer( msg.sender, reclaimedA );
		tokenB.safeTransfer( msg.sender, reclaimedB );

		// Reduce the user's liquidity share for the specified pool so that they receive less rewards.
		// Cooldown is specified to prevent reward hunting (ie - quickly depositing and withdrawing large amounts of liquidity to snipe rewards)
		// This call will send any pending SALT rewards to msg.sender as well.
		_decreaseUserShare( msg.sender, poolID, liquidityToWithdraw, true );

		emit LiquidityWithdrawn(msg.sender, address(tokenA), address(tokenB), reclaimedA, reclaimedB, liquidityToWithdraw);
		}

```

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/staking/Liquidity.sol#L137

The code logic does not meet CEI, Since erc777 is fully compatible with erc20, if finalized an erc777 token to whitelist token, Malicious users can potentially profit from read-only reentrancy attacks

// Code block 1 (unknown):
src/staking/Liquidity.sol

	// Withdraw specified liquidity, decrease the user's liquidity share and claim any pending rewards.
    function _withdrawLiquidityAndClaim( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToWithdraw, uint256 minReclaimedA, uint256 minReclaimedB ) internal returns (uint256 reclaimedA, uint256 reclaimedB)
		{
		bytes32 poolID = PoolUtils._poolID( tokenA, tokenB );
		require( userShareForPool(msg.sender, poolID) >= liquidityToWithdraw, "Cannot withdraw more than existing user share" );

		// Remove the amount of liquidity specified by the user.
		// The liquidity in the pool is currently owned by this contract. (external call)
		(reclaimedA, reclaimedB) = pools.removeLiquidity( tokenA, tokenB, liquidityToWithdraw, minReclaimedA, minReclaimedB, totalShares[poolID] );

		// Transfer the reclaimed tokens to the user
		tokenA.safeTransfer( msg.sender, reclaimedA );
		tokenB.safeTransfer( msg.sender, reclaimedB );

		// Reduce the user's liquidity share for the specified pool so that they receive less rewards.
		// Cooldown is specified to prevent reward hunting (ie - quickly depositing and withdrawing large amounts of liquidity to snipe rewards)
		// This call will send any pending SALT rewards to msg.sender as well.
		_decreaseUserShare( msg.sender, poolID, liquidityToWithdraw, true );

		emit LiquidityWithdrawn(msg.sender, address(tokenA), address(tokenB), reclaimedA, reclaimedB, liquidityToWithdraw);
		}


0xkazim Q

# Findings Summary

| ID     | Title                                                            | Severity |
| ------ | ---------------------------------------------------------------- | -------- |
| [L-01] | Gas griefing/theft is possible on unsafe external call           | low      |
| [L-02] | event missed in `setMaxSlippage()` function may cause fund loose | low      |

# [L-01] contracts should inherit their interfacese

## Description

return data (bool success,) has to be stored due to EVM architecture, if in a usage like below, ‘out’ and ‘outsize’ values are given (0,0) . Thus, this storage disappears and may come from external contracts a possible Gas griefing/theft problem is avoided

## context

There are 5 instances of the topic.

```solidity

file: contracts/SafEth/SafEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: ethAmountToWithdraw}(
            ""
        );
        require(sent, "Failed to send Ether");
```

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

```solidity
file: contracts/SafEth/derivatives/Reth.sol

 (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
    require(sent, "Failed to send Ether");
```

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

```solidity
file: contracts/SafEth/derivatives/SfrxEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
        require(sent, "Failed to send Ether");
```

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

```solidity
file: contracts/SafEth/derivatives/WstEth.sol

 (bool sent, ) = address(msg.sender).call{value: address(this).balance}(

// Code block 1 (solidity):
file: contracts/SafEth/SafEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: ethAmountToWithdraw}(
            ""
        );
        require(sent, "Failed to send Ether");

// Code block 2 (solidity):
file: contracts/SafEth/derivatives/Reth.sol

 (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
    require(sent, "Failed to send Ether");

// Code block 3 (solidity):
file: contracts/SafEth/derivatives/SfrxEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
        require(sent, "Failed to send Ether");

file: contracts/SafEth/SafEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: ethAmountToWithdraw}(
            ""
        );
        require(sent, "Failed to send Ether");

file: contracts/SafEth/derivatives/Reth.sol

 (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
    require(sent, "Failed to send Ether");

file: contracts/SafEth/derivatives/SfrxEth.sol

 // solhint-disable-next-line
        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
        require(sent, "Failed to send Ether");


Tomio G

1.
Title: Using bytes constant is more gas efficient

Reference: [Here](https://ethereum.stackexchange.com/questions/3795/why-do-solidity-examples-use-bytes32-type-instead-of-string)

Proof of Concept:
[DefaultCallbackHandler.sol#L12-L13](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L12-L13)
[SmartAccountFactory.sol#L11](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L11)
[SmartAccount.sol#L36](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L36)

Recommended Mitigation Steps:
Change it to `bytes(1..32) constant`
________________________________________________________________________

2.
Title: Struct optimization

Proof of Concept:
[PaymasterHelpers.sol#L13-L16](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L13-L16)

Recommended Mitigation Steps:
Use elementary types or a user-defined type instead can save gas
________________________________________________________________________

3.
Title: Reduce the size of error messages (Long revert Strings)

Impact:
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

Proof of Concept:
[SmartAccount.sol#L77](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77)
[SmartAccount.sol#L110](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L110)
[SmartAccount.sol#L128](https://github.com/code-423n4/2023-01-biconomy/blob/main/s

function getChainId() public view returns (uint256 id) { //@audit-info: set here

	require(module != address(0), "BSA101");
	require(module != SENTINEL_MODULES, "BSA101");

DefaultCallbackHandler.sol#L12-L13, SmartAccountFactory.sol#L11, SmartAccount.sol#L36, PaymasterHelpers.sol#L13-L16, SmartAccount.sol#L77, SmartAccount.sol#L110

DefaultCallbackHandler.sol, SmartAccount.sol, PaymasterHelpers.sol, SmartAccountFactory.sol


2997ms Q

## Summary

## Low Risk Issues

## [LOW-01] Better to set a up limit for ```totalLQTYStaked```

```
if (totalLQTYStaked > 0) {LUSDFeePerLQTYStaked = _LUSDFee.mul(DECIMAL_PRECISION).div(totalLQTYStaked);}
```

totalLQTYStaked is divided in multiple places but there is no up limit for this value. If this value becomes really large one day, all the divisions will become meaningless because all the results will be 0.

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L191

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L181

## Non Critical Issues

###  [NC-01]Open TODOs

An open TODO is present. It is recommended to avoid open TODOs as they may indicate programming errors that still need to be fixed.

**Proof Of Concept**

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/StabilityPool.sol#L335

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/StabilityPool.sol#L380

**Recommended Mitigation Steps**
Resolve these TODOs

### [NC-02]Solidity versions should be consistent

Most of solidity versions in the files are ```pragma solidity 0.6.11;```, but in ReaperStrategyGranarySupplyOnly.sol and ReaperVaultERC4626.sol
, the value is ```pragma solidity ^0.8.0;```

**Proof Of Concept**
```
pragma solidity ^0.8.0
```
https://github.com/code-423n4/2023-02-ethos/blob/34ba1e7dd2d259f06f9d5fa13f5d96be7829ff34/Ethos-Vault/contracts/ReaperVaultERC4626.sol#L3

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/ActivePool.sol#L3

**Recommended Mitigation Steps**
Make the versions consistent

// Code block 1 (unknown):
totalLQTYStaked is divided in multiple places but there is no up limit for this value. If this value becomes really large one day, all the divisions will become meaningless because all the results will be 0.

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L191

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L181

## Non Critical Issues

###  [NC-01]Open TODOs

An open TODO is present. It is recommended to avoid open TODOs as they may indicate programming errors that still need to be fixed.

**Proof Of Concept**

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/StabilityPool.sol#L335

https://github.com/code-423n4/2023-02-ethos/blob/891252b407033489140de63ea502b053d5dc860c/Ethos-Core/contracts/StabilityPool.sol#L380

**Recommended Mitigation Steps**
Resolve these TODOs

### [NC-02]Solidity versions should be consistent

Most of solidity versions in the files are

// Code block 2 (unknown):
**Proof Of Concept**

LQTYStaking.sol#L191, LQTYStaking.sol#L181, StabilityPool.sol#L335, StabilityPool.sol#L380, ReaperVaultERC4626.sol#L3, ActivePool.sol#L3

LQTYStaking.sol, ActivePool.sol, ReaperVaultERC4626.sol, StabilityPool.sol


juancito Q

# QA Report

## Summary

### Low Issues

|       | Issue                                               | Instances |
| ----- | :-------------------------------------------------- | :-------: |
| [L-1] | Missing checks for `address(0)` in withdraw methods |     7     |
| [L-2] | Unresolved `TODO` and `review` comments             |    12     |
| [L-3] | Unspecific compiler version pragma                  |    11     |
| [L-4] | Missing event emit on `deployWallet`                |     1     |

### Non Critical Issues

|        | Issue                                                       | Instances |
| ------ | :---------------------------------------------------------- | :-------: |
| [NC-1] | Commented code lines                                        |    12     |
| [NC-2] | Test coverage improvement                                   |    18     |
| [NC-3] | Misleading error messages                                   |     2     |
| [NC-4] | Typos                                                       |     4     |
| [NC-5] | Constants should be defined rather than using magic numbers |     1     |

## Low Issues

### [L-1] Missing checks for `address(0)` in withdraw methods

Consider checking for `address(0)` to prevent any loss of funds.

_Instances 7_:

```solidity
File: contracts/smart-contract-wallet/SmartAccount.sol

537:    entryPoint().withdrawTo(withdrawAddress, amount);

```

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

```solidity
File: contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

68:     entryPoint.withdrawTo(withdrawAddress, amount);

100:    entryPoint.withdrawStake(withdrawAddress);

```

[Link to code](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol)

```solidity
File: contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

106:    (bool success,) = wi

// Code block 1 (solidity):
File: contracts/smart-contract-wallet/SmartAccount.sol

537:    entryPoint().withdrawTo(withdrawAddress, amount);

// Code block 2 (solidity):
File: contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

68:     entryPoint.withdrawTo(withdrawAddress, amount);

100:    entryPoint.withdrawStake(withdrawAddress);

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

537:    entryPoint().withdrawTo(withdrawAddress, amount);

File: contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

68:     entryPoint.withdrawTo(withdrawAddress, amount);

100:    entryPoint.withdrawStake(withdrawAddress);


MalfurionWhitehat G

# 1. Remove duplicate validation of address zero in `SmartAccount.init`

```diff
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..cc30425 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -168,10 +168,10 @@ contract SmartAccount is
         require(address(_entryPoint) == address(0), "Already initialized");
         require(_owner != address(0),"Invalid owner");
         require(_entryPointAddress != address(0), "Invalid Entrypoint");
-        require(_handler != address(0), "Invalid Entrypoint");
+        require(_handler != address(0), "Invalid Fallback Handler");
         owner = _owner;
         _entryPoint =  IEntryPoint(payable(_entryPointAddress));
-        if (_handler != address(0)) internalSetFallbackHandler(_handler);
+        internalSetFallbackHandler(_handler);
         setupModules(address(0), bytes(""));
     }

```

# 2. Remove unnecessary owner validation on `SmartAccount.execute` and `SmartAccount.executeBatch` as these functions are already behind an `onlyOwner` modifier

```diff
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..67a2d5f 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -458,12 +458,10 @@ contract SmartAccount is
     }

     function execute(address dest, uint value, bytes calldata func) external onlyOwner{
-        _requireFromEntryPointOrOwner();
         _call(dest, value, func);
     }

     function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{
-        _requireFromEntryPointOrOwner();
         require(dest.length == func.length, "wrong array lengths");
         for (uint i = 0; i < de

// Code block 1 (diff):
diff --git a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
index c4f69a8..cc30425 100644
--- a/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
+++ b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol
@@ -168,10 +168,10 @@ contract SmartAccount is
         require(address(_entryPoint) == address(0), "Already initialized");
         require(_owner != address(0),"Invalid owner");
         require(_entryPointAddress != address(0), "Invalid Entrypoint");
-        require(_handler != address(0), "Invalid Entrypoint");
+        require(_handler != address(0), "Invalid Fallback Handler");
         owner = _owner;
         _entryPoint =  IEntryPoint(payable(_entryPointAddress));
-        if (_handler != address(0)) internalSetFallbackHandler(_handler);
+        internalSetFallbackHandler(_handler);
         setupModules(address(0), bytes(""));
     }


debo G

## [G-01] Do not initialize variables with default value
Description
Uninitialized variables are assigned with the types default value.

Explicitly initializing a variable with it's default value costs unnecessary gas.
```txt
2023-09-ondo/contracts/bridge/DestinationBridge.sol::134 => for (uint256 i = 0; i < thresholds.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::160 => for (uint256 i = 0; i < t.approvers.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::264 => for (uint256 i = 0; i < amounts.length; ++i) {
2023-09-ondo/contracts/bridge/SourceBridge.sol::166 => for (uint256 i = 0; i < exCallData.length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::77 => for (uint256 i = 0; i < length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::113 => for (uint256 i = 0; i < length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::129 => for (uint256 i = 0; i < length + 1; ++i) {
2023-09-ondo/contracts/usdy/rUSDYFactory.sol::130 => for (uint256 i = 0; i < exCallData.length; ++i) {
```
## [G-02] Cache array length outside of loop
Description
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.
```txt
2023-09-ondo/contracts/bridge/DestinationBridge.sol::134 => for (uint256 i = 0; i < thresholds.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::159 => if (t.approvers.length > 0) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::160 => for (uint256 i = 0; i < t.approvers.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::179 => if (t.numberOfApprovalsNeeded <= t.approvers.length) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::260 => if (amounts.length != numOfApprovers.length) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::264 => for (uint256 i = 0; i < amounts.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::362 => return txnToThresholdSe

## [G-03] Use not equal to 0 instead of Greater than 0 for unsigned integer comparison
Description
When dealing with unsigned integer types, comparisons with != 0 are cheaper than with > 0.

// Code block 1 (txt):
2023-09-ondo/contracts/bridge/DestinationBridge.sol::134 => for (uint256 i = 0; i < thresholds.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::160 => for (uint256 i = 0; i < t.approvers.length; ++i) {
2023-09-ondo/contracts/bridge/DestinationBridge.sol::264 => for (uint256 i = 0; i < amounts.length; ++i) {
2023-09-ondo/contracts/bridge/SourceBridge.sol::166 => for (uint256 i = 0; i < exCallData.length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::77 => for (uint256 i = 0; i < length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::113 => for (uint256 i = 0; i < length; ++i) {
2023-09-ondo/contracts/rwaOracles/RWADynamicOracle.sol::129 => for (uint256 i = 0; i < length + 1; ++i) {
2023-09-ondo/contracts/usdy/rUSDYFactory.sol::130 => for (uint256 i = 0; i < exCallData.length; ++i) {


dharma09 G

### [G-01] Use Immutable instead of constant for `keccak256`

Constant expressions are [re-calculated each time it is in use]([https://github.com/ethereum/solidity/issues/9232](https://github.com/ethereum/solidity/issues/9232)), costing an extra `97` gas than a constant every time they are called.

Instances include:

Mark these as  `immutable`  instead of  `constant`

*There are 14 instances of this issue:*

**Affected source code:**

```solidity
/CashManager.sol
122: bytes32 public constant MANAGER_ADMIN = keccak256("MANAGER_ADMIN");
123: bytes32 public constant PAUSER_ADMIN = keccak256("PAUSER_ADMIN");
124: bytes32 public constant SETTER_ADMIN = keccak256("SETTER_ADMIN");

/[KYCRegistry.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L31)
31: bytes32 public constant _APPROVAL_TYPEHASH =keccak256("KYCApproval(uint256 kycRequirementGroup,address user,uint256 deadline)");
36: bytes32 public constant REGISTRY_ADMIN = keccak256("REGISTRY_ADMIN");

/Cash.sol
22: bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");

/CashKYCSender.sol
25: bytes32 public constant KYC_CONFIGURER_ROLE = keccak256("KYC_CONFIGURER_ROLE");

/CashKYCSenderReceiver.sol
26: bytes32 public constant KYC_CONFIGURER_ROLE = keccak256("KYC_CONFIGURER_ROLE");

/CashFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

/CashKYCSenderFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

/CashKYCSenderReceiverFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
```

### [G-02] Use constants if you already know the permanent value

Use constants if you already know the permanent value. it get directly embedded in bytecode, saving `SLOAD`

*There are 3 instances of t

/CashManager.sol
58: uint256 public minimumDepositAmount = 10_000;
68: uint256 public exchangeRateDeltaLimit = 100;

/OndoPriceOracleV2.sol
77: uint256 public maxChainlinkOracleTimeDelay = 90000;

// Code block 1 (solidity):
/CashManager.sol
122: bytes32 public constant MANAGER_ADMIN = keccak256("MANAGER_ADMIN");
123: bytes32 public constant PAUSER_ADMIN = keccak256("PAUSER_ADMIN");
124: bytes32 public constant SETTER_ADMIN = keccak256("SETTER_ADMIN");

/[KYCRegistry.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L31)
31: bytes32 public constant _APPROVAL_TYPEHASH =keccak256("KYCApproval(uint256 kycRequirementGroup,address user,uint256 deadline)");
36: bytes32 public constant REGISTRY_ADMIN = keccak256("REGISTRY_ADMIN");

/Cash.sol
22: bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");

/CashKYCSender.sol
25: bytes32 public constant KYC_CONFIGURER_ROLE = keccak256("KYC_CONFIGURER_ROLE");

/CashKYCSenderReceiver.sol
26: bytes32 public constant KYC_CONFIGURER_ROLE = keccak256("KYC_CONFIGURER_ROLE");

/CashFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

/CashKYCSenderFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

/CashKYCSenderReceiverFactory.sol
44: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
45: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");


descharre Q

# Summary
## Low Risk
|ID     | Finding| Instances |
|:----: | :---           |   :----:         |
|1       | Other contract addresses can only be set once | 1 |
|2       | Everyone can call initialize() function| 4 |
|3      | type(uint).max is not equal to -1 | 1 |

## Non critical
|ID     | Finding| Instances |
|:----: | :---           |   :----:         |
|1       | Don't use keccack256 in constants | 8 |
| 2      | Require() or revert() statement that check input arguments should be at the top of the function| 5 |
| 3      | Similar contracts| 4 |
| 4      |Use modifier instead of duplicate require| 5 |
| 5     |Multiple address mappings can be combined into a single mapping of an address to a struct| 1 |
| 6      |Miscellaneous| 1 |

# Details
## Low Risk
## 1 Other contract addresses can only be set once
Other contract addresses are immutable so they can only be set once in the constructor. There is no other setter method for this.

This makes it so there is no room for error and can lead to contract reverts and redeployment.

A solution for this is to make a setter method which only the owner can call. So if the contract address is wrong it can always be reset.

[CashManager.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L163-L164)
```solidity
L163:   collateral = IERC20(_collateral);
L164:   cash = Cash(_cash);
```
## 2 Everyone can call initialize() function
When the contract is not initialized, the initialize() function can be called by anybody. This can be an issue because roles will be set in the __ERC20PresetMinterPauser_init function.
- [CashKYCSender](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol)
- [CashKYCSenderReceiver](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSenderReceiver.sol)
- [CCash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCash.sol)
- [CErc20.sol](https://github.com

    /* Fail if repayAmount = -1 */
    if (repayAmount == type(uint).max) {
      revert LiquidateCloseAmountIsUintMax();
    }

// Code block 1 (solidity):
L163:   collateral = IERC20(_collateral);
L164:   cash = Cash(_cash);

CashManager.sol#L163-L164, CashKYCSender.sol, CashKYCSenderReceiver.sol, CCash.sol

CashManager.sol, CashKYCSenderReceiver.sol, CCash.sol, CashKYCSender.sol


sm stack G

## Gas Optimizations
### [G-1] Expand the range of unchecked in `VaultController::getCollateralInfo`

Apply the following git diff:

```diff
diff --git a/core/solidity/contracts/core/VaultController.sol b/core/solidity/contracts/core/VaultController.sol
index 95e3009..be6fa2b 100644
--- a/core/solidity/contracts/core/VaultController.sol
+++ b/core/solidity/contracts/core/VaultController.sol
@@ -235,12 +235,13 @@ contract VaultController is Pausable, IVaultController, ExponentialNoError, Owna
     uint256 _enabledTokensLength = enabledTokens.length;
     _end = _enabledTokensLength < _end ? _enabledTokensLength : _end;

-    _collateralsInfo = new CollateralInfo[](_end - _start);
-
+    unchecked {
+      _collateralsInfo = new CollateralInfo[](_end - _start);
+    }
+
     for (uint256 _i = _start; _i < _end;) {
-      _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
-
       unchecked {
+        _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
         ++_i;
       }
     }

```

### [G-2] Add unchecked to `Vault::modifyLiability`

Apply the following git diff:

```diff
diff --git a/core/solidity/contracts/core/Vault.sol b/core/solidity/contracts/core/Vault.sol
index 62bc9ad..5e76e89 100644
--- a/core/solidity/contracts/core/Vault.sol
+++ b/core/solidity/contracts/core/Vault.sol
@@ -311,7 +311,9 @@ contract Vault is IVault, Context {
     } else {
       // require statement only valid for repayment
       if (baseLiability < _baseAmount) revert Vault_RepayTooMuch();
-      baseLiability -= _baseAmount;
+      unchecked {
+        baseLiability -= _baseAmount;
+      }
     }
     _newLiability = baseLiability;
   }

```

// Code block 1 (diff):
diff --git a/core/solidity/contracts/core/VaultController.sol b/core/solidity/contracts/core/VaultController.sol
index 95e3009..be6fa2b 100644
--- a/core/solidity/contracts/core/VaultController.sol
+++ b/core/solidity/contracts/core/VaultController.sol
@@ -235,12 +235,13 @@ contract VaultController is Pausable, IVaultController, ExponentialNoError, Owna
     uint256 _enabledTokensLength = enabledTokens.length;
     _end = _enabledTokensLength < _end ? _enabledTokensLength : _end;

-    _collateralsInfo = new CollateralInfo[](_end - _start);
-
+    unchecked {
+      _collateralsInfo = new CollateralInfo[](_end - _start);
+    }
+
     for (uint256 _i = _start; _i < _end;) {
-      _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
-
       unchecked {
+        _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
         ++_i;
       }
     }

// Code block 2 (diff):
diff --git a/core/solidity/contracts/core/Vault.sol b/core/solidity/contracts/core/Vault.sol
index 62bc9ad..5e76e89 100644
--- a/core/solidity/contracts/core/Vault.sol
+++ b/core/solidity/contracts/core/Vault.sol
@@ -311,7 +311,9 @@ contract Vault is IVault, Context {
     } else {
       // require statement only valid for repayment
       if (baseLiability < _baseAmount) revert Vault_RepayTooMuch();
-      baseLiability -= _baseAmount;
+      unchecked {
+        baseLiability -= _baseAmount;
+      }
     }
     _newLiability = baseLiability;
   }

diff --git a/core/solidity/contracts/core/VaultController.sol b/core/solidity/contracts/core/VaultController.sol
index 95e3009..be6fa2b 100644
--- a/core/solidity/contracts/core/VaultController.sol
+++ b/core/solidity/contracts/core/VaultController.sol
@@ -235,12 +235,13 @@ contract VaultController is Pausable, IVaultController, ExponentialNoError, Owna
     uint256 _enabledTokensLength = enabledTokens.length;
     _end = _enabledTokensLength < _end ? _enabledTokensLength : _end;

-    _collateralsInfo = new CollateralInfo[](_end - _start);
-
+    unchecked {
+      _collateralsInfo = new CollateralInfo[](_end - _start);
+    }
+
     for (uint256 _i = _start; _i < _end;) {
-      _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
-
       unchecked {
+        _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
         ++_i;
       }
     }

diff --git a/core/solidity/contracts/core/Vault.sol b/core/solidity/contracts/core/Vault.sol
index 62bc9ad..5e76e89 100644
--- a/core/solidity/contracts/core/Vault.sol
+++ b/core/solidity/contracts/core/Vault.sol
@@ -311,7 +311,9 @@ contract Vault is IVault, Context {
     } else {
       // require statement only valid for repayment
       if (baseLiability < _baseAmount) revert Vault_RepayTooMuch();
-      baseLiability -= _baseAmount;
+      unchecked {
+        baseLiability -= _baseAmount;
+      }
     }
     _newLiability = baseLiability;
   }


btk Q

# Protocol Overview

The Caviar Private Pools protocol is a decentralized platform on Ethereum that allows users to create and join private investment pools. The smart contract manages the funds within the pool, ensures compliance with the specified rules, and distributes rewards to members. The protocol democratizes investment opportunities, promotes decentralization, and fosters innovation in the investment industry.

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

| Risk   | Issues Details                                                                                | Number        |
|--------|-----------------------------------------------------------------------------------------------------------|---------------|
| [L-01] | Critical changes should use-two step procedure                                                            | 1             |
| [L-02] | No Storage Gap for Upgradeable contracts                                                                  | 1             |
| [L-03] | Loss of precision due to rounding                                                                         | 1             |
| [L-04] | Lack of `nonReentrant` modifier                                                                           | 4             |
| [L-05] | Integer overflow by unsafe casting                                                                        | 4             |
| [L-06] | Add `address(0)` check for the critical changes                                                           | 2             |
| [L-07] | Array lengths not checked                                                                                | 4             |
| [L-08] | Unused `receive()` Function Will Lock Ether In Contract                                                   | 1             |
| [L-09] | Misleading comment                                                                                | 1             |
| [L-10] | Fees are not capped

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }

  /**
   * @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;


erebus G

# First
[Here](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/USDA.sol#L259) the function `donation` makes the math to share the donation between all the `USDA` holders. However, if it get to be more than `MAX_SUPPLY`, the `_totalSupply` is set to `MAX_SUPPLY` and it will be doing unnecessary instructions in the `_gonsPerFragment` calculation for the following calls to donation from within the contract (if `_totalSupply` is not reduced). The optimization lies lies in adding an `if (_totalSupply == MAX_SUPPLY) return;` so that it does not spend gas doing redundant instructions when the supply has reached the limit.

# Second
It is more efficient, for `++X` and `X++` to do `X += 1`. See the next test in foundry to show that it saves 8 gas and 2 gas respectively:

```
pragma solidity ^0.8.13;

import "forge-std/Test.sol";

contract POC is Test {
    uint256 private a;

    function setUp() public {
        a = 0;
    }

    function testA() public {
        a++;
    }

    function testB() public {
        a += 1;
    }

    function testC() public {
        ++a;
    }
}
```

Results:

- testA -> 22397
- testB -> 22389
- testC -> 22391

Consider using the `X += 1` version everywhere. The RE can be `[^ \+]*\+\+` for the occurrences of `X++` and `\+\+[^ \)]*` for the occurrences of `++X`. The same applies to other arithmetic operations.

# Third
Consider making [MAX_SUPPLY](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/utils/UFragments.sol#L70C18-L70C28) constant, which saves storage read/writes (ssread/sstore costs A LOT of gas) from being done within the contract's logic (and saves more opcodes, which reduces its size). The same goes to many others like [name](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/utils/UFragments.sol#L76) and [symbol](https://githu

// Code block 1 (unknown):
pragma solidity ^0.8.13;

import "forge-std/Test.sol";

contract POC is Test {
    uint256 private a;

    function setUp() public {
        a = 0;
    }

    function testA() public {
        a++;
    }

    function testB() public {
        a += 1;
    }

    function testC() public {
        ++a;
    }
}


IllIllI Q

## Summary

### Low Risk Issues
| |Issue|Instances|
|-|:-|:-:|
| [L‑01] | Upgradeable contract not initialized | 1 |
| [L‑02] | Loss of precision | 1 |
| [L‑03] | Owner can renounce while system is paused | 1 |
| [L‑04] | `require()` should be used instead of `assert()` | 3 |

Total: 6 instances over 4 issues

### Non-critical Issues
| |Issue|Instances|
|-|:-|:-:|
| [N‑01] | Upgradeable contract is missing a `__gap[50]` storage variable to allow for new storage variables in later versions | 4 |
| [N‑02] | Import declarations should import specific identifiers, rather than the whole file | 45 |
| [N‑03] | Duplicate import statements | 1 |
| [N‑04] | The `nonReentrant` `modifier` should occur before all other modifiers | 3 |
| [N‑05] | Adding a `return` statement when the function defines a named return variable, is redundant | 1 |
| [N‑06] | `constant`s should be defined rather than using magic numbers | 3 |
| [N‑07] | Numeric values having to do with time should use time units for readability | 1 |
| [N‑08] | Events that mark critical parameter changes should contain both the old and the new value | 2 |
| [N‑09] | Expressions for constant values such as a call to `keccak256()`, should use `immutable` rather than `constant` | 14 |
| [N‑10] | Constant redefined elsewhere | 7 |
| [N‑11] | Typos | 5 |
| [N‑12] | Constructor visibility is ignored | 3 |
| [N‑13] | File is missing NatSpec | 1 |
| [N‑14] | NatSpec is incomplete | 16 |
| [N‑15] | Consider using `delete` rather than assigning zero to clear values | 3 |
| [N‑16] | Contracts should have full test coverage | 1 |
| [N‑17] | Large or complicated code bases should implement fuzzing tests | 1 |
| [N‑18] | Function ordering does not follow the Solidity style guide | 8 |
| [N‑19] | Contract does not follow the Solidity style guide's sug

File: contracts/cash/token/Cash.sol

/// @audit missing __ERC20PresetMinterPauser_init()
21:   contract Cash is ERC20PresetMinterPauserUpgradeable {

File: contracts/cash/CashManager.sol

755         uint256 collateralAmountDue = (amountToDist * cashAmountReturned) /
756:          quantityBurned;


0xbrett8571 Analysis

Executive Summary
-----------------

This report presents the findings from an in-depth security review of the Revert Lend smart contracts. The goal was to evaluate the codebase for potential vulnerabilities, centralization risks, architecture issues, and overall code quality that could negatively impact the protocol and its users.

The review uncovered several areas of concern, including centralization risks, potential reentrancy issues, signature replay vulnerabilities, and risks around unchecked user input and admin configuration abilities. While the codebase is generally well-structured, architectural improvements around access control and input validation are recommended.

Overall, the identified vulnerabilities pose a material risk to protocol and user funds if not addressed. Detailed descriptions of the findings along with code snippets and recommendations are provided in the following sections.

Scope and Approach
------------------

The security review covered the entire smart contract codebase for the Revert Lend protocol, consisting of the core contracts (V3Vault, V3Oracle, InterestRateModel), transformer contracts (AutoCompound, AutoRange, LeverageTransformer, V3Utils), and supporting utility contracts (Swapper, FlashloanLiquidator).

The approach taken was a combination of automated scanning using static analysis tools and manual code inspection. Particular focus was given to evaluating the codebase for common smart contract pitfalls, correctness of the implementation against the specified logic, proper access controls, and centralization/admin risks.

Architecture Review
-------------------

Revert Lend follows a typical design pattern for lending protocols, with the core V3Vault contract holding user deposits and collateral, while interacting with various transformer contracts to enable LP position management.

Some key architectural observations and risks:

### Vault and Transformer Interaction

The protocol allows "transformer" contracts to be wh

function transform(uint256 tokenId, address transformer, bytes calldata data)
    external
    override
    returns (uint256 newTokenId)
{
    if (tokenId == 0 || !transformerAllowList[transformer]) {
        revert TransformNotAllowed();
    }
    // ...
    (bool success,) = transformer.call(data);
    // ...
}

function setTokenConfig(
    address token,
    AggregatorV3Interface feed,
    uint32 maxFeedAge,
    IUniswapV3Pool pool,
    uint32 twapSeconds,
    Mode mode,
    uint16 maxDifference
) external onlyOwner {
    // ...
    feedConfigs[token] = config;
    // ...
}


0xWagmi Q

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

### Low Risk

| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | If a user sends ETH direcly to ETH router it could be stolen by another  user  | 1 |
| [L-02] | Allowing  anyOne to reenter the `function create` in factory.sol may be problamatic   | 1 |
| [L-03] |  Use openzeppelin re-entrancyGuard     | 8 |
| [L-04] |  Use openzeppelin upgradable proxy  contracts   | 1 |
| [L-05] |  execute function in privatePool  is  a risky implementation    | 1 |

### Non-Critical
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [N-01] | Floating Pragma | 5 |

| Total Found Issues | 17 |
|:--:|:--:|

### [L-01]   If a user send ETH directly to ETH router it could be stolen by another  user
---

copy the below code to test folder run  `forge test --match-contract  TestUser  -vvvv`
Lets say,
1 .Deloyer  creates a private Pool  ;
2 . Alice sends ETH to router contract directly it triggers the receive function now 1 ETH is in router contract
3 .  Bob create  a nft called fake just mint a  1 fake nft  token id 0  to address(privatePool)
4 . Bob just call sell function  with 0 ether ;
Then   bobs contract will recieve alice's 1 ether ;
This could be a medium sevierity but I think users are not supposed to directly send ether to
the router address but a user can accidently send eth to the ETH router   via receive()   ;

```js
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "./Fixture.sol";

contract Fake is ERC721 {
    constructor() ERC721("FAKE", "FK") {}
    function mint(address to, uint256 id) public {
        _mint(to, id);
    }

    function tokenURI(
        uint256
    ) public view virtual override returns (string memory) {
        return "";
    }
}

contract TestUser is Fixture {
    //users

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "./Fixture.sol";

contract Fake is ERC721 {
    constructor() ERC721("FAKE", "FK") {}
    function mint(address to, uint256 id) public {
        _mint(to, id);
    }

    function tokenURI(
        uint256
    ) public view virtual override returns (string memory) {
        return "";
    }
}

contract TestUser is Fixture {
    //users
    address alice;
    address bob;
    address deployer;
    ///pool
    Fake fake;
    PrivatePool public privatePool;
    EthRouter.Buy[] public buys;
    uint256 public totalTokens = 0;
    uint256 public maxInputAmount = 0;

    function setUp() public {
        alice = makeAddr("Alice");
        bob = makeAddr("Bob");
        deployer = makeAddr("Deployer");
        fake = new Fake();
        vm.deal(address(this), 0 ether);
        vm.deal(address(alice), 1 ether);
        vm.deal(address(bob), 0 ether);
        vm.deal(address(deployer), 1 ether);
        vm.label(address(this), "ThisContract");
        vm.startPrank(deployer);
        _addBuy();
        vm.stopPrank();
        for (uint256 i = 0; i < buys.length; i++) {
            maxInputAmount += buys[i].baseTokenAmount;
        }
    }

    function _addBuy() internal {
        uint256[] memory empty = new uint256[](0);
        privatePool = factory.create{value: 1e18}(
            address(0),
            address(milady),
            10e18,
            10e18,
            200,
            100,
            bytes32(0),
            true,
            false,
            bytes32(address(this).balance), // random between each call to _addBuy
            empty,
            1e18
        );

        uint256[] memory tokenIds = new uint256[](1);
        for (uint256 i = 0; i < 1; i++) {
            milady.mint(address(privatePool), i + totalTokens);
            tokenIds[i] = i + totalTokens;
        }

        totalTokens += 1;
        (uint256 baseTokenAmount, , ) = privatePool.buyQuote(
            tok

### [L-2]   Allowing  anyOne to reenter the `function create` in factory.sol may be problamatic

In function create   we have ,


cryptothemex Q

### [L-01] Local variable shadowing

Shadowing of other variables using local variables.  **Recom** Rename the local variables that shadow another component.

*There are 21 instance(s) of this issue:*

```solidity
File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault._repay(uint256,uint256,bool,bytes).owner (L#1001) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1001:         address owner = tokenOwner[tokenId];
```

*GitHub* : [1001-1001](https://github.com/code-423n4/2024-03-revert-lend/src/V3Vault.sol#L1001-L1001)

```solidity
File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault._cleanupLoan(uint256,uint256,uint256,address).owner (L#1077) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1077:     function _cleanupLoan(uint256 tokenId, uint256 debtExchangeRateX96, uint256 lendExchangeRateX96, address owner)
```

*GitHub* : [1077-1077](https://github.com/code-423n4/2024-03-revert-lend/src/V3Vault.sol#L1077-L1077)

```solidity
File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault.transform(uint256,address,bytes).owner (L#531) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 531:         address owner = nonfungiblePositionManager.ownerOf(tokenId);
```

*GitHub* : [531-531](https://github.com/code-423n4/2024-03-revert-lend/src/V3Vault.sol#L531-L531)

```solidity
File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault.loanCount(address).owner (L#264) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 264:     function loanCount(address owner) external view override returns (uint256) {
```

*GitHub* : [264-264](https://github.com/code

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

/// @audit ******************* Issue Detail *******************
V3Vault._repay(uint256,uint256,bool,bytes).owner (L#1001) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1001:         address owner = tokenOwner[tokenId];

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

/// @audit ******************* Issue Detail *******************
V3Vault._cleanupLoan(uint256,uint256,uint256,address).owner (L#1077) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1077:     function _cleanupLoan(uint256 tokenId, uint256 debtExchangeRateX96, uint256 lendExchangeRateX96, address owner)

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

/// @audit ******************* Issue Detail *******************
V3Vault.transform(uint256,address,bytes).owner (L#531) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 531:         address owner = nonfungiblePositionManager.ownerOf(tokenId);

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

/// @audit ******************* Issue Detail *******************
V3Vault.loanCount(address).owner (L#264) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 264:     function loanCount(address owner) external view override returns (uint256) {

File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault._repay(uint256,uint256,bool,bytes).owner (L#1001) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1001:         address owner = tokenOwner[tokenId];

File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault._cleanupLoan(uint256,uint256,uint256,address).owner (L#1077) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
1077:     function _cleanupLoan(uint256 tokenId, uint256 debtExchangeRateX96, uint256 lendExchangeRateX96, address owner)

File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault.transform(uint256,address,bytes).owner (L#531) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 531:         address owner = nonfungiblePositionManager.ownerOf(tokenId);

File: src/V3Vault.sol

/// @audit ******************* Issue Detail *******************
V3Vault.loanCount(address).owner (L#264) shadows:
	- Ownable.owner() (L#43-45) (function)

/// @audit ****************** Affected Code *******************
 264:     function loanCount(address owner) external view override returns (uint256) {


Lavishq Q

## G1 - require statement can be chained using && operator

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L65 and L66 can be chained in a single require() statement as such `require(msg.value >= minAmount && msg.value <= maxAmount, "invalid amount");` (error could be a custom error or even something other than `invalid amount`) so that when the line executes most of the times it saves gas and does not check second case.
The reason for adding minAmount first is because assuming the maxAmount is 200 ETH and there will be only rare cases that a person exceeds the maxAmount threashold but there is high possibility that many people will send less value.

Also, it is recommended to have the check and perform the interaction of this specific case of front end side in case the amount is entered by the user is less than minAmount then ask him to enter more and vice versa.

## G2 - for loop can be chained in one for loop

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L63
for loop can be chained in a single for loop saving loads of gas
the only things i changed is optimized variables which store default values again and merged both the for loop in one

Code :

```solidity
function stake() external payable {
  require(pauseStaking == false, "staking is paused");
  require(msg.value >= minAmount && msg.value <= maxAmount, "invalid amount"); // This is taken from G1 of my report

  uint256 underlyingValue;
  uint256 totalStakeValueEth; // total amount of derivatives worth of ETH in system

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

    uint256 weight = weights[i];
    IDerivative derivative = derivatives[i];
    if (weight == 0) continue;
    uint256 ethAmount = (msg.value * weight) / totalWeight

function stake() external payable {
  require(pauseStaking == false, "staking is paused");
  require(msg.value >= minAmount && msg.value <= maxAmount, "invalid amount"); // This is taken from G1 of my report

  uint256 underlyingValue;
  uint256 totalStakeValueEth; // total amount of derivatives worth of ETH in system

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

    uint256 weight = weights[i];
    IDerivative derivative = derivatives[i];
    if (weight == 0) continue;
    uint256 ethAmount = (msg.value * weight) / totalWeight;

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

  uint256 totalSupply = totalSupply();
  uint256 preDepositPrice; // Price of safETH in regards to ETH
  if (totalSupply == 0)
    preDepositPrice = 10 ** 18; // initializes with a price of 1
  else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

  // mintAmount represents a percentage of the total assets in the system
  uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
  _mint(msg.sender, mintAmount);
  emit Staked(msg.sender, msg.value, mintAmount);
}


0xnev Q

### Issues Template
| Letter | Name | Description |
|:--:|:-------:|:-------:|
| L  | Low risk | Potential risk |
| NC |  Non-critical | Non risky findings |
| R  | Refactor | Code changes |

| Total Found Issues | 14 |
|:--:|:--:|

### Low Risk Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [L-01] | `getPositionInfo` may return wrong information for positions not deleted after calling `moveLiquidity` | 1 |
| [L-02] | Lack of access control for `PositionManager.mint` | 4 |

| Total Low Risk Issues | 2 |
|:--:|:--:|

### Non-Critical Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [N-01] | Users may not receive deserved reward when `RewardsManager.sol` contract has low AJNA balance | 1 |
| [N-02] | Lender can unexpectedly claim rewards from all existing positions in buckets when `RewardsManager.movingStakedLiquidity ` is called instead of just specified buckets where postions is moved | 1 |
| [N-03] | User can still memorialize and stake positions to NFT at buckets after they have already move position | 1 |
| [N-04] | Consider using `block.timestamp` instead of `block.number` | 1 |

| Total Non-Critical Issues | 4 |
|:--:|:--:|

### Refactor Issues Template
| Count | Title | Instances |
|:--:|:-------|:--:|
| [R-01] | Confusing mapping name | 1 |
| [R-02] | Repeated owner checks can be refactored into a modifer | 2 |
| [R-03] | Use `WAD` constant declared |  |
| [R-04] | Do not need to use `+=` operator, `=` can be used instead | 1 |
| [R-05] | `else if` block in `Maths.wsqrt()` can be removed | 1 |
| [R-06] | `RewardsManager._getBurnEpochsClaimed` can be refactored | 1 |
| [R-07] | Use constant instead of immutable for `ajnaTokenAddress` in `Funding.sol` | 1 |
| [R-08] | Implement `StandardFunding._setNewDistributionId()` in `StandardFunding.startNewDistributionPeriod()` directly | 1 |

| Total Refactor Issues | 8 |
|:--:|:--:|

### [L-01] `PostionManager.getPositionInfo` may return wrong information for positions not deleted after cal

function getPositionInfo(
    uint256 tokenId_,
    uint256 index_
) external view override returns (uint256, uint256) {
    return (
        positions[tokenId_][index_].lps,
        positions[tokenId_][index_].depositTime
    );
}

function mint(
    MintParams calldata params_
) external override nonReentrant returns (uint256 tokenId_) {
    tokenId_ = _nextId++;

    // revert if the address is not a valid Ajna pool
    if (!_isAjnaPool(params_.pool, params_.poolSubsetHash)) revert NotAjnaPool();

    // record which pool the tokenId was minted in
    poolKey[tokenId_] = params_.pool;

    _mint(params_.recipient, tokenId_);

    emit Mint(params_.recipient, params_.pool, tokenId_);
}


Sathish9098 G

# GAS OPTIMIZATIONS

##

## [G-1] Optimizing storage usage with packed structs

### Saves ``8000 GAS`` , ``4 SLOT``

Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct.

Subsequent reads as well as writes have smaller gas savings.

### [G-1.1] user,isCollIncrease,isDebtIncrease can be packed with same slot : Saves ``2 SLOT`` , ``4000 GAS``

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

```diff
FILE: Breadcrumbs2023-10-badger/packages/contracts/contracts/BorrowerOperations.sol

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

```

### [G-1.2]  roundEthBtcId , roundStEthEthId , success can be packed with same slot : Saves 1 SLOT , 2000 GAS

https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Interfaces/IPriceFeed.sol#L17-L24

```diff
FILE: 2023-10-badger/packages/contracts/contracts/Interfaces/IPriceFeed.sol

 struct ChainlinkResponse {
        uint80 roundEthBtcId;
        uint80 roundStEthEthId;
+        bool success;
        uint256 answer;
        uint256 timestampEthBtc;
        uint256 timestampStEthEth;
-        bool success;
    }

```

### [G-1.3]  timestamp , success can be packed with same slot : Saves ``1 SLOT`` , ``2000 GAS``

https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/Interfaces/IPriceFeed.sol#L26-L30

```diff
FILE: 2023-10-badger/packages/contracts/contracts/Interfaces/IPriceFeed.sol

  struct FallbackResponse {
        uint256 answer;
-        uint256 timestamp;
+        uint248 timestamp;
        bool success;

// Code block 1 (diff):
FILE: Breadcrumbs2023-10-badger/packages/contracts/contracts/BorrowerOperations.sol

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

// Code block 2 (diff):
FILE: 2023-10-badger/packages/contracts/contracts/Interfaces/IPriceFeed.sol

 struct ChainlinkResponse {
        uint80 roundEthBtcId;
        uint80 roundStEthEthId;
+        bool success;
        uint256 answer;
        uint256 timestampEthBtc;
        uint256 timestampStEthEth;
-        bool success;
    }

FILE: Breadcrumbs2023-10-badger/packages/contracts/contracts/BorrowerOperations.sol

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

FILE: 2023-10-badger/packages/contracts/contracts/Interfaces/IPriceFeed.sol

 struct ChainlinkResponse {
        uint80 roundEthBtcId;
        uint80 roundStEthEthId;
+        bool success;
        uint256 answer;
        uint256 timestampEthBtc;
        uint256 timestampStEthEth;
-        bool success;
    }


atharvasama G

# Gas Optimizations list

| Number | Details                                                                                | Instances |
| ------ | ------------------------------------------------------------------------------------------------- | --------- |
| 1      | ```x += y```/```x -= y``` COSTS MORE GAS THAN ```x = x + y```/```x = x - y``` FOR STATE VARIABLES | 3         |
| 2      | CAN DECLARE VARIABLE OUTSIDE LOOP TO SAVE GAS                                                     | 7         |
| 3      | PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD                   | 8         |
| 4      | USE ```bytes32``` INSTEAD OF ```string``` WHEREVER POSSIBLE                                       | 1         |
| 5      | OPTIMIZE NAMES TO SAVE GAS                                                                        | 4         |
| 6      | BEFORE SOME FUNCTIONS, WE SHOULD CHECK SOME VARIABLES FOR POSSIBLE GAS SAVE                       | 9         |
| 7      | CACHE STORAGE VALUES IN MEMORY TO MINIMIZE SLOADS                                                 | 8         |
| 8      | SHOULD USE LOCAL ARGUMENTS INSTEAD OF STATE VARIABLE                                              | 4         |
| 9      | CACHE ARRAY LENGTH OUTSIDE OF LOOP                                                                | 7         |

# Gas Optimizations
## [G-01]  ```x += y```/```x -= y``` COSTS MORE GAS THAN ```x = x + y```/```x = x - y``` FOR STATE VARIABLES
Using the addition operator instead of plus-equals saves some gas for state variables.

[contracts/SafEth/SafEth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol)
```js
72:             underlyingValue +=
73:                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74:                     derivatives[i].balance()) /
75:                 10 ** 18;

172:             localTotalWeight += weights[i];

192:             localTotalWeig

72:             underlyingValue +=
73:                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
74:                     derivatives[i].balance()) /
75:                 10 ** 18;

172:             localTotalWeight += weights[i];

192:             localTotalWeight += weights[i];

85:             uint256 weight = weights[i];

86:             IDerivative derivative = derivatives[i];

88:             uint256 ethAmount = (msg.value * weight) / totalWeight;

91:             uint256 depositAmount = derivative.deposit{value: ethAmount}();
92:             uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
93:                 depositAmount
94:             ) * depositAmount) / 10 ** 18;
95:             totalStakeValueEth += derivativeReceivedEthValue;

115:             uint256 derivativeAmount = (derivatives[i].balance() *

149:             uint256 ethAmount = (ethAmountToRebalance * weights[i]) /


MohammedRizwan G

## Summary

### Gas Optimizations
| |Issue|Instances| |
|-|:-|:-:|:-:|
| [G‑01] | require() or revert() statements that check input arguments should be at the top of the function | 2 |
| [G‑02] | Splitting require() statements that use && saves gas | 4 |
| [G‑03] | [Ethos-Core contracts] ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow  | 11 |
| [G‑04] | Avoid compound assignment operator in state variables | 21 |
| [G‑05] | Use require instead of assert | 17 |
| [G‑06] | Use mapping instead of arrays | - |
| [G‑07] | Use a safe and secure smart contract libraries | - |
| [G‑08] | Use a more recent version of solidity | 8 |

## Gas Optimizations
### [G‑01]  require() or revert() statements that check input arguments should be at the top of the function
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.

In CollateralConfig.initialize( ) function, below checks should be at top which will check and verify the conditions and prevent any gas wastage and function failue.
There are 2 instances of this issue.

```solidity
File: Ethos-Core/contracts/CollateralConfig.sol

66            require(_MCRs[i] >= MIN_ALLOWED_MCR, "MCR below allowed minimum");

---

69            require(_CCRs[i] >= MIN_ALLOWED_CCR, "CCR below allowed minimum");

```
https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/CollateralConfig.sol#L66
https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/CollateralConfig.sol#L69

### [G‑02]  Splitting require() statements that use && saves gas
&& Operator takes more gas, by splitting it in require() it Sav

File: Ethos-Core/contracts/BorrowerOperations.sol

653         require(_maxFeePercentage >= BORROWING_FEE_FLOOR && _maxFeePercentage <= DECIMAL_PRECISION,

// Code block 1 (solidity):
File: Ethos-Core/contracts/CollateralConfig.sol

66            require(_MCRs[i] >= MIN_ALLOWED_MCR, "MCR below allowed minimum");

---

69            require(_CCRs[i] >= MIN_ALLOWED_CCR, "CCR below allowed minimum");


joss G

Use custom solidity errors instead of require statements to save gas on string allocation.

Ex:  Instead of this
`require(someBool, "Error Message");`

use this
```
error ErrorMessage(); // declare error

// logically same as require.  Reverts state to before function was called.
// Saves gas on string allocation.
if(!someBool){
  revert ErrorMessage();
}
```

Saves ~50 gas each time the error triggers (https://gist.github.com/IllIllI000/ad1bd0d29a0101b25e57c293b4b0c746)
Also saves gas on deployment.

Some examples of `require` statements that can be gas optimized:
 - Ethos-Vault/contracts/ReaperVaultERC4626.sol line 270
 - Ethos-Vault/contracts/ReaperVaultV2.sol lines 150-156, 180, 181, 193, 197, 261, 267, 321, 322, 324, 364, 404, 436, 497, 568, 619, 629, 639
 - Ethos-Vault/contracts/ReaperBaseStrategyv4.sol lines 81, 96-98, 191

// Code block 1 (unknown):
error ErrorMessage(); // declare error

// logically same as require.  Reverts state to before function was called.
// Saves gas on string allocation.
if(!someBool){
  revert ErrorMessage();
}


report

---
sponsor: "eBTC Protocol"
slug: "2023-10-badger"
date: "2024-02-13"
title: "Badger eBTC Audit + Certora Formal Verification Competition"
findings: "https://github.com/code-423n4/2023-10-badger-findings/issues"
contest: 300
---

# Overview

## About C4

Code4rena (C4) is an open organization consisting of security researchers, auditors, developers, and individuals with domain expertise in smart contracts.

A C4 audit is an event in which community participants, referred to as Wardens, review, audit, or analyze smart contract logic in exchange for a bounty provided by sponsoring projects.

During the audit outlined in this document, C4 conducted an analysis of the Badger eBTC smart contract system written in Solidity. The audit took place between October 24—November 14 2023.

Alongside the C4 audit, a formal verification competition was held in partnership with Certora. The summary of the [formal verification competition](#formal-verification-competition) is appended below the audit report.

## Wardens

95 Wardens contributed reports to the Badger eBTC Audit + Certora Formal Verification Competition:

  1. [Stormy](https://code4rena.com/@Stormy)
  2. [TrungOre](https://code4rena.com/@TrungOre)
  3. [0xBeirao](https://code4rena.com/@0xBeirao)
  4. [ether\_sky](https://code4rena.com/@ether_sky)
  5. [shaka](https://code4rena.com/@shaka)
  6. [0xRobocop](https://code4rena.com/@0xRobocop)
  7. [SpicyMeatball](https://code4rena.com/@SpicyMeatball)
  8. [rvierdiiev](https://code4rena.com/@rvierdiiev)
  9. [Nyx](https://code4rena.com/@Nyx)
  10. [nobody2018](https://code4rena.com/@nobody2018)
  11. [BARW](https://code4rena.com/@BARW) ([BenRai](https://code4rena.com/@BenRai) and [albertwh1te](https://code4rena.com/@albertwh1te))
  12. [DavidGiladi](https://code4rena.com/@DavidGiladi)
  13. [hunter\_w3b](https://code4rena.com/@hunter_w3b)
  14. [hihen](https://code4rena.com/@hihen)
  15. [cheatc0d3](https://code4rena.com/@cheatc0d3)
  16. [zabihullahazadzoi](https://code4re

    /// @notice Deploys a new macro for you
    function deployNewMacro() external returns (address) {
        return deployNewMacro(msg.sender);
    }

    /// @notice Deploys a new macro for an owner, only they can operate the macro
    function deployNewMacro(address _owner) public returns (address) {
        address addy = address(
            new LeverageMacroReference(
                borrowerOperations,
                activePool,
                cdpManager,
                ebtcToken,
                stETH,
                sortedCdps,
                _owner
            )
        );

        emit DeployNewMacro(_owner, addy);

        return addy;
    }


lsaudit G

# [G-01] Using bools for storage incurs overhead
Booleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled. Use uint256(0) and uint256(1) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD.

```
./CdpManagerStorage.sol:143:    bool public redemptionsPaused;
./ERC3156FlashLender.sol:15:    bool public flashLoansPaused;
./LeverageMacroBase.sol:35:    bool internal immutable willSweep;
./AuthNoOwner.sol:14:    bool private _authorityInitialized;
```

# [G‑02] Constructors can be marked as payable to save deployment gas
Payable functions cost less gas to execute, because the compiler does not have to add extra checks to ensure that no payment is provided. A constructor can be safely marked as payable, because only the deployer would be able to pass funds, and the project itself would not pass any funds.

```
./Auth.sol:18:    constructor(address _owner, Authority _authority) {
./RolesAuthority.sol:20:    constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}
./Governor.sol:36:    constructor(address _owner) RolesAuthority(_owner, Authority(address(this))) {}
./EbtcBase.sol:52:    constructor(address _activePoolAddress, address _priceFeedAddress, address _collateralAddress) {
./SortedCdps.sol:88:    constructor(uint256 _size, address _cdpManagerAddress, address _borrowerOperationsAddress) {
./SimplifiedDiamondLike.sol:44:    constructor(address _owner) {

```

# [G‑03] Functions that revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will n

// Code block 1 (unknown):
./CdpManagerStorage.sol:143:    bool public redemptionsPaused;
./ERC3156FlashLender.sol:15:    bool public flashLoansPaused;
./LeverageMacroBase.sol:35:    bool internal immutable willSweep;
./AuthNoOwner.sol:14:    bool private _authorityInitialized;

// Code block 2 (unknown):
./Auth.sol:18:    constructor(address _owner, Authority _authority) {
./RolesAuthority.sol:20:    constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}
./Governor.sol:36:    constructor(address _owner) RolesAuthority(_owner, Authority(address(this))) {}
./EbtcBase.sol:52:    constructor(address _activePoolAddress, address _priceFeedAddress, address _collateralAddress) {
./SortedCdps.sol:88:    constructor(uint256 _size, address _cdpManagerAddress, address _borrowerOperationsAddress) {
./SimplifiedDiamondLike.sol:44:    constructor(address _owner) {


Breeje Q

# QA Report

## Low Risk Issues
| Count | Explanation | Instances |
|:--:|:-------|:--:|
| [L-01] | Possible DOS in `stake` in future | 1 |
| [L-02] | `receive` function can be removed to prevent accidental sending of Eth | 3 |

| Total Low Risk Issues | 2 |
|:--:|:--:|

### [L-01] Possible DOS in `stake` in future

`stake` method in `SafEth` uses couple of for loops bounded by the derivative count. As of now there are only 3 Derivatives, so it is fine. But in future when more derivatives will be added by owner, it can possibly lead to DOS by crossing the Block limit.

*Instance:*
```solidity
File: SafEth.sol

  function stake() external payable {
        require(pauseStaking == false, "staking is paused");
        require(msg.value >= minAmount, "amount too low");
        require(msg.value <= maxAmount, "amount too high");

        uint256 underlyingValue = 0;

        // Getting underlying value in terms of ETH for each derivative
        for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *  // @audit checkout for the Precision in Derivatives
derivatives[i].balance()) /                                                   // @audit Gas Optimization Possible
                10 ** 18;

        uint256 totalSupply = totalSupply();
        uint256 preDepositPrice; // Price of safETH in regards to ETH
        if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

        uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
        for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            // This is slightly less than ethAmount becau

File: SafEth.sol

  function stake() external payable {
        require(pauseStaking == false, "staking is paused");
        require(msg.value >= minAmount, "amount too low");
        require(msg.value <= maxAmount, "amount too high");

        uint256 underlyingValue = 0;

        // Getting underlying value in terms of ETH for each derivative
        for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *  // @audit checkout for the Precision in Derivatives
derivatives[i].balance()) /                                                   // @audit Gas Optimization Possible
                10 ** 18;

        uint256 totalSupply = totalSupply();
        uint256 preDepositPrice; // Price of safETH in regards to ETH
        if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

        uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
        for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            // This is slightly less than ethAmount because slippage
            uint256 depositAmount = derivative.deposit{value: ethAmount}();
            uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
                depositAmount
            ) * depositAmount) / 10 ** 18;
            totalStakeValueEth += derivativeReceivedEthValue;
        }
        // mintAmount represents a percentage of the total assets in the system
        uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
        _mint(msg.sender, mintAmount);
        emit Staked(msg.sender, msg.value, mintAmount);
    }


jnrlouis G

### Gas Optimization Report

## Summary

## Gas Optimizations

|  | Issue | Instances |
| --- | --- | ---|
| 1 | Setting the `constructor` to `payable` | 3 |
| 2 | Functions guaranteed to revert when normal users call can be marked `payable` | 11 |
| 3 | Use Assembly to check for `address(0)` | 21 |
| 4 | Splitting `require` and `if` statements that use `&&` saves gas | 12 |
| 5 | `<x> += <y>` Costs More Gas Than `<x> = <x> + <y>` For State Variables | 4 |

## Gas Optimizations

# [G-01] Setting the `constructor` to `payable`

Setting the `constructor` to `payable` saves about 13 gas per instance

*There are 3 instances of this issue:*

```javascript
File: src/PrivatePool.sol

143    constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle)
```

https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L143

```javascript
File: src/Factory.sol

53    constructor() ERC721("Caviar Private Pools", "POOL") Owned(msg.sender) {}

```

https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/Factory.sol#L53

```javascript
File: src/EthRouter.sol

90    constructor(address _royaltyRegistry)

```

https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/EthRouter.sol#L90

# [G-02] Functions guaranteed to revert when normal users call can be marked `payable`

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are
CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.

*There are 11 instances of this issue:*

```javascript
File:

// Code block 1 (javascript):
File: src/PrivatePool.sol

143    constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle)

// Code block 2 (javascript):
File: src/Factory.sol

53    constructor() ERC721("Caviar Private Pools", "POOL") Owned(msg.sender) {}

// Code block 3 (javascript):
File: src/EthRouter.sol

90    constructor(address _royaltyRegistry)


descharre Q

# Summary
## Low Risk
|ID     | Finding| Instances |
|:----: | :---           |   :----:         |
|L-01       |Missing 0 checks in constructor|4|
|L-02       |Unspecific compiler version pragma|-|
|L-03       |Not every year equals 365 days|1|
|L-04       | Not enough checks on withdraw and deposit function | 2 |

## Non critical
|ID     | Finding| Instances |
|:----: | :---           |   :----:         |
|N-01       | Use a newer solidity version | - |
|N-02       | Use a modifier instead of a function for prerequisites | 5 |
|N-03       | Event is missing indexed fields | 7 |
|N-04       | Make consistent use of comments in the contracts | - |
|N-05       | Use uint256 instead of uint | 1 |
|N-06       | Use scientific notation (1E18) rather than exponentation (10**18) | 2 |
|N-07       | Unused named return variables | 11 |
|N-08       | Formatting is not always correct | 3 |
|N-09       | Wrong comments | 1 |
|N-10       | Constant values such as a call to keccak256(), should be immutable rather than constant | 2 |
|N-11       | Only use assert when you check code that never should be false | 3 |
|N-12       | Functions should be grouped according to their visibility and ordered | 1 |

# Details
## Low Risk
## [L-01] Missing 0 checks in constructor
When you set addresses, it's a best practice to check for 0 addresses. This way a variable can't be accidently set to the 0 address. Roles are set in the constructor of ReaperVaultV2, without a check, ADMIN can be the 0 address.
- [ReaperVaultV2.sol#L111-L136](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L111-L136)
- [ReaperBaseStrategyv4.sol#L72-L73](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L72-L73)
- [ReaperBaseStrategyv4.sol#L82-L85](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L82-L85)
- [ReaperStrategyGranarySupplyOnly.sol#L68-L71]

- [ActivePool.sol#L313-L342](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L313-L342)
- [BorrowerOperations.sol#L524-L656](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L524-L656)
- [LUSDToken.sol#L346-L395](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L346-L395)
- [StabilityPool.sol#L848-L875](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L848-L875)
- [TroveManager.sol#L1522-L1540](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1522-L1540)
## [N-03] Event is missing indexed fields
When an event is missing indexed fields it can be hard for off-chain scripts to efficiently filter those events.
- [ActivePool.sol#L58-L67](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L58-L67)
- [ReaperVaultV2.sol#L80-L100](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L80-L100)
- [ActivePool.sol#L58-L67](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L58-L67)
- [CollateralConfig.sol#L37-L38](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L37-L38)
- [StabilityPool.sol#L232-L234](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L232-L234)
- [StabilityPool.sol#L246-L250](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L246-L250)
- [TroveManager.sol#L203-L216](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L203-L216)
## [N-04] Make consistent use of comments in the contracts
There are only a few functions that have comments. It is a good practice to use natspec comments.
In complex projects such as Defi, the interpretation of all functions and the

- [ActivePool.sol#L269](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L269): wrong spacing
- [BorrowerOperations.sol#L633](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L633)
- [TroveManager.sol#L1539](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1539)

One click on format document should fix this
## [N-09] Wrong comments
The comment says it returns a uint256 with 18 decimals. However the function uses the decimal function which returns the correct amount of decimals of a token. Not every token has 18 decimals, BTC is used in the tests and only has 8 decimals. So the function `getPricePerFullShare()` returns a uint256 with 8 decimals during testing.
[ReaperVaultV2.sol#L293](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L293)

ReaperVaultV2.sol#L111-L136, ReaperBaseStrategyv4.sol#L72-L73, ReaperBaseStrategyv4.sol#L82-L85, ActivePool.sol#L313-L342, BorrowerOperations.sol#L524-L656, LUSDToken.sol#L346-L395, StabilityPool.sol#L848-L875, TroveManager.sol#L1522-L1540, ActivePool.sol#L58-L67, ReaperVaultV2.sol#L80-L100, CollateralConfig.sol#L37-L38, StabilityPool.sol#L232-L234, StabilityPool.sol#L246-L250, TroveManager.sol#L203-L216, ActivePool.sol#L269, BorrowerOperations.sol#L633, TroveManager.sol#L1539, ReaperVaultV2.sol#L293

StabilityPool.sol, TroveManager.sol, BorrowerOperations.sol, ReaperBaseStrategyv4.sol, ReaperVaultV2.sol, LUSDToken.sol, ActivePool.sol, CollateralConfig.sol


Rolezn G

## Summary<a name="Summary">

### Gas Optimizations
| |Issue|Contexts|Estimated Gas Saved|
|-|:-|:-|:-:|
| [GAS‑1](#GAS‑1) | `abi.encode()` is less efficient than `abi.encodepacked()` | 1 | 100 |
| [GAS‑2](#GAS‑2) | State variables only set in the `constructor` should be declared immutable | 1 | - |
| [GAS‑3](#GAS‑3) | Setting the `constructor` to `payable` | 13 | 169 |
| [GAS‑4](#GAS‑4) | Do not calculate constants | 2 | - |
| [GAS‑5](#GAS‑5) | Duplicated `require()`/`revert()` Checks Should Be Refactored To A Modifier Or Function | 5 | 140 |
| [GAS‑6](#GAS‑6) | Using `delete` statement can save gas | 7 | - |
| [GAS‑7](#GAS‑7) | Use `assembly` to write address storage values | 10 | - |
| [GAS‑8](#GAS‑8) | `internal` functions only called once can be inlined to save gas | 47 | - |
| [GAS‑9](#GAS‑9) | Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate | 18 | - |
| [GAS‑10](#GAS‑10) | Optimize names to save gas | 18 | 396 |
| [GAS‑11](#GAS‑11) | `<x> += <y>` Costs More Gas Than `<x> = <x> + <y>` For State Variables | 9 | - |
| [GAS‑12](#GAS‑12) | Public Functions To External | 36 | - |
| [GAS‑13](#GAS‑13) | `require()` Should Be Used Instead Of `assert()` | 3 | - |
| [GAS‑14](#GAS‑14) | Usage of `uints`/`ints` smaller than 32 bytes (256 bits) incurs overhead | 5 | - |
| [GAS‑15](#GAS‑15) | `++i`/`i++` Should Be `unchecked{++i}`/`unchecked{i++}` When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops | 9 | 315 |
| [GAS‑16](#GAS‑16) | Using `unchecked` blocks to save gas | 16 | 2176 |
| [GAS‑17](#GAS‑17) | Use solidity version 0.8.17 to gain some gas boost | 31 | 2728 |
| [GAS‑18](#GAS‑18) | Using `storage` instead of `memory`

94: abi.encode(_APPROVAL_TYPEHASH, kycRequirementGroup, user, deadline)


Rolezn Q

## QA Summary
Low Severity Risks

### Low Risk Issues
| |Issue|Contexts|
|-|:-|:-:|
| [LOW‑1](#LOW‑1) | Consider checking that `mint` to `address(this)` is disabled | 4 |
| [LOW‑2](#LOW‑2) | Consider implementing two-step procedure for updating protocol addresses | 6 |
| [LOW‑3](#LOW‑3) | Constant decimal values | 16 |
| [LOW‑4](#LOW‑4) | Large transfers may not work with some `ERC20` tokens | 34 |
| [LOW‑5](#LOW‑5) | Minting tokens to the zero address should be avoided | 1 |
| [LOW‑6](#LOW‑6) | OpenZeppelin's `Counters.sol` is deprecated | 3 |
| [LOW‑7](#LOW‑7) | Privileged roles can change critical parameter even though `contract` is in `paused` mode | 1 |
| [LOW‑8](#LOW‑8) | Solmate's SafeTransferLib doesn't check whether the ERC20 contract exists | 1 |

Total: 66 contexts over 8 issues

### Non-critical Issues
| |Issue|Contexts|
|-|:-|:-:|
| [NC‑1](#NC‑1) | Empty `bytes` check is missing | 4 |
| [NC‑2](#NC‑2) | Overly complicated arithmetic | 1 |

Total: 5 contexts over 2 issues

## Low Risk Issues

### [LOW‑1] Consider checking that `mint` to `address(this)` is disabled

Functions allowing users to specify the receiving address should check whether the referenced address is not `address(this)`. This would prevent tokens to remain lock inside the contract.  An example of the potential for loss by leaving this open is the EOS token smart contract where more than 90,000 tokens are stuck at the contract address.

#### Proof Of Concept

```solidity
File: RdpxV2Bond.sol

37: function mint(
    address to
  ) public onlyRole(MINTER_ROLE) returns (uint256 tokenId) {
    tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    _mint(to, tokenId);
  }

```

https://github.com/code-423n4/2023-08-dopex/tree/main/contracts

File: RdpxDecayingBonds.sol

114: function mint(
    address to,
    uint256 expiry,
    uint256 rdpxAmount
  ) external onlyRole(MINTER_ROLE) {
    _whenNotPaused();
    require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
    uint256 bondId = _mintToken(to);
    bonds[bondId] = Bond(to, expiry, rdpxAmount);

    emit BondMinted(to, bondId, expiry, rdpxAmount);
  }

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

37: function mint(
    address to
  ) public onlyRole(MINTER_ROLE) returns (uint256 tokenId) {
    tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    _mint(to, tokenId);
  }


SAQ Q

## Summary

### Low Risk Issues

no | Issue |Instances|
|-|:-|:-:|
| [L-01] | Loss of precision | 3 |
| [L-02] | Consider implementing two-step procedure for updating protocol addresses | 1 |
| [L-03] | Missing checks for address(0x0) in the constructor | 15 |
| [L-04] | receive()/payable fallback() function does not authorize requests | 1 |
| [L-05] | Empty receive() function | 1 |

## Low Risk Issues

### [L-1] Loss of precision

Division by large numbers may result in the result being zero, due to solidity not supporting fractions. Consider requiring a minimum amount for the numerator to ensure that it is always larger than the denominator

```solidity
file: /contracts/contracts/LiquidationLibrary.sol

882        uint256 EBTCDebtRewardPerUnitStaked = EBTCDebtNumerator / _totalStakes;

```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/LiquidationLibrary.sol#L882

```solidity
file: contracts/contracts/CdpManagerStorage.sol

564        uint256 _feeTaken = collateral.getSharesByPooledEth(_deltaFeeSplit) / DECIMAL_PRECISION;

567        uint256 _deltaFeePerUnit = _deltaFeeSplitShare / _cachedAllStakes;

```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L564

```solidity
file: /contracts/contracts/PriceFeed.sol

801            (_scaledDecimal *
                uint256(_ethBtcAnswer) *
                uint256(_stEthEthAnswer) *
                EbtcMath.DECIMAL_PRECISION) / 10 ** (_decimalDenominator * 2);

```
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/PriceFeed.sol#L801-L804

### [L-2] Consider implementing two-step procedure for updating protocol addresses

A copy-paste error or a typo may end up bricking protocol functionality, or sending tokens to an address with no known private key. Consider implementing a two-step procedure for updating protocol addresses, where the recipient is set as pending, and must 'accept' the

// Code block 1 (solidity):
file: /contracts/contracts/LiquidationLibrary.sol

882        uint256 EBTCDebtRewardPerUnitStaked = EBTCDebtNumerator / _totalStakes;

// Code block 2 (solidity):
file: contracts/contracts/CdpManagerStorage.sol

564        uint256 _feeTaken = collateral.getSharesByPooledEth(_deltaFeeSplit) / DECIMAL_PRECISION;

567        uint256 _deltaFeePerUnit = _deltaFeeSplitShare / _cachedAllStakes;

// Code block 3 (solidity):
file: /contracts/contracts/PriceFeed.sol

801            (_scaledDecimal *
                uint256(_ethBtcAnswer) *
                uint256(_stEthEthAnswer) *
                EbtcMath.DECIMAL_PRECISION) / 10 ** (_decimalDenominator * 2);

file: /contracts/contracts/LiquidationLibrary.sol

882        uint256 EBTCDebtRewardPerUnitStaked = EBTCDebtNumerator / _totalStakes;

file: contracts/contracts/CdpManagerStorage.sol

564        uint256 _feeTaken = collateral.getSharesByPooledEth(_deltaFeeSplit) / DECIMAL_PRECISION;

567        uint256 _deltaFeePerUnit = _deltaFeeSplitShare / _cachedAllStakes;

file: /contracts/contracts/PriceFeed.sol

801            (_scaledDecimal *
                uint256(_ethBtcAnswer) *
                uint256(_stEthEthAnswer) *
                EbtcMath.DECIMAL_PRECISION) / 10 ** (_decimalDenominator * 2);


Fon Q

function nonce
```solidity
function nonce(uint256 _batchId) public view virtual override returns (uint256) {
        return nonces[_batchId];
    }
```
and function getNonce
```solidity
function getNonce(uint256 batchId)
    public view
    returns (uint256) {
        return nonces[batchId];
    }
```
do exactly the same thing, consider removing getNonce

// Code block 1 (solidity):
function nonce(uint256 _batchId) public view virtual override returns (uint256) {
        return nonces[_batchId];
    }

// Code block 2 (solidity):
function getNonce(uint256 batchId)
    public view
    returns (uint256) {
        return nonces[batchId];
    }

function nonce(uint256 _batchId) public view virtual override returns (uint256) {
        return nonces[_batchId];
    }

function getNonce(uint256 batchId)
    public view
    returns (uint256) {
        return nonces[batchId];
    }


Krace Q

## [Low] dailyInterestRate maybe wrongly set, causing zero price
`dailyInterestRate` is used to calculate the current price of USDY. Once it is set to zero, the price will become zero.
When admin sets the Range, `dailyInterestRate` should be checked to ensure it's not zero.

```
roundUpTo8(
        _rmul(
          _rpow(currentRange.dailyInterestRate, elapsedDays + 1, ONE),
          currentRange.prevRangeClosePrice
        )
      );
```

## [Info] Wrong comment in DestinationBridge.sol
`_mintIfThresholdMet` is not a public function, maybe you should update your comment.

```
  /*//////////////////////////////////////////////////////////////
                        Public Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice internal function to mint a transaction if it has passed the threshold
   *         for the number of approvers
   *
   * @param txnHash The hash of the transaction we wish to mint
   */
  function _mintIfThresholdMet(bytes32 txnHash) internal {
    bool thresholdMet = _checkThresholdMet(txnHash);
    Transaction memory txn = txnHashToTransaction[txnHash];
    if (thresholdMet) {
      _checkAndUpdateInstantMintLimit(txn.amount);
      if (!ALLOWLIST.isAllowed(txn.sender)) {
        ALLOWLIST.setAccountStatus(
          txn.sender,
          ALLOWLIST.getValidTermIndexes()[0],
          true
        );
      }
      TOKEN.mint(txn.sender, txn.amount);
      delete txnHashToTransaction[txnHash];
      emit BridgeCompleted(txn.sender, txn.amount);
    }
  }
```

// Code block 1 (unknown):
roundUpTo8(
        _rmul(
          _rpow(currentRange.dailyInterestRate, elapsedDays + 1, ONE),
          currentRange.prevRangeClosePrice
        )
      );

// Code block 2 (unknown):
/*//////////////////////////////////////////////////////////////
                        Public Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice internal function to mint a transaction if it has passed the threshold
   *         for the number of approvers
   *
   * @param txnHash The hash of the transaction we wish to mint
   */
  function _mintIfThresholdMet(bytes32 txnHash) internal {
    bool thresholdMet = _checkThresholdMet(txnHash);
    Transaction memory txn = txnHashToTransaction[txnHash];
    if (thresholdMet) {
      _checkAndUpdateInstantMintLimit(txn.amount);
      if (!ALLOWLIST.isAllowed(txn.sender)) {
        ALLOWLIST.setAccountStatus(
          txn.sender,
          ALLOWLIST.getValidTermIndexes()[0],
          true
        );
      }
      TOKEN.mint(txn.sender, txn.amount);
      delete txnHashToTransaction[txnHash];
      emit BridgeCompleted(txn.sender, txn.amount);
    }
  }


0xpanicError Q

# Low Issues

| |Issue|Instances|
|-|:-|:-:|
| [L-1](#L-1) | There should be a cap on `dailyInterestRate` | 2 |
| [L-2](#L-2) | `getPrice` can return 0 if startTime is set more than block.timestamp | 1 |
### [L-1] There should be a cap on `dailyInterestRate`
`dailyInterestRate` is a value set in range by the admin. Since it is being exponentiated by number of days, if
for any reason that value is not scaled appropriately, or interest is passed for a monthly or yearly rates instead
of daily, the resulting price would be way off the expected value.

Consider adding a `MIN_INTEREST` and `MAX_INTEREST` values to make sure the interest rate is not accidently passed
incorrectly.
``` solidity
require( dailyInterestRate <= MAX_INTEREST );
require( dailyInterestRate >= MIN_INTEREST );
```

Contracts in scope: https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol

*Instances (2)*:

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L151
```solidity
  function setRange(
    uint256 endTimestamp,
    uint256 dailyInterestRate
  ) external onlyRole(SETTER_ROLE) {}
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L186
``` solidity
function overrideRange(
    uint256 indexToModify,
    uint256 newStart,
    uint256 newEnd,
    uint256 newDailyIR,
    uint256 newPrevRangeClosePrice
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {}
```
### [L-2] `getPrice` can return 0 if startTime is set more than block.timestamp
The first rage is set in the constructor, but there is no limit as to how long in the future the range can start. This
means that if `firstRangeStart` is more than block.timestamp (during deployment) then anyone can call the public function
`getPrice` which would return 0 unless `firstRangeStart > block.timestamp` .

Since this function is crucial in determining the amount of rUSDY tokens of the user, there must be checks

// Code block 1 (unknown):
Contracts in scope: https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol

*Instances (2)*: <br>
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L151

// Code block 2 (unknown):
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L186


catellatech Q

# QA report for Kelp DAO contest by Catellatech

## [QA-01] onlyLRTManager can approve the max asset in NodeDelegator::maxApproveToEigenStrategyManager even when the contract is paused
Consider add the `whenNotPaused` modifier to the function `NodeDelegator::maxApproveToEigenStrategyManager` as well.

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

```solidity
    function maxApproveToEigenStrategyManager(address asset)
        external
        override
        onlySupportedAsset(asset)
        onlyLRTManager
    {
        address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
        IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);
    }
```
## [QA-02] Remove/resolve the comments like the following
in `LRTDepositPool::getAssetDistributionData` in line [78](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L78) the following comment it was not remove ` // Question: is here the right place to have this? Could it be in LRTConfig?`. Please remove or resolve that comment.

## [QA-03] in the `LRTConfig.sol` contract the team did not consider adding a function to remove assets
Context: The contract handles `addNewSupportedAsset` and `updateAssetStrategy` but does not account for a scenario where they have to remove an asset from the LRTConfig; in that case, it won't be possible.

## [QA-04] Inconsistent Use of NatSpec
NatSpec is a boon to all Solidity developers. Not only does it provide a structure for developers to document their code within the code itself, it encourages the practice of documenting code. When future developers read code documented with NatSpec, they are able to increase their capacity to understand, upgrade, and fix code. Without code documented with NatSpec, that capacity is hindered.

The Centrifuge codebase does have a high level of documentation with NatSpec. However there are numerous instances of function

// Code block 1 (solidity):
function maxApproveToEigenStrategyManager(address asset)
        external
        override
        onlySupportedAsset(asset)
        onlyLRTManager
    {
        address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
        IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);
    }