0x11singh99 Analysis

### RdpxV2Core.sol

It is the main contract of the system which is handling bonding and maintaining peg of `DpxEth` price with `weth` price. After analysis this contract i have some suggestions for it.

1. To maintain data of reserve assets in this Core contract. We can change it's layout to remove 1 extra mapping `reservesIndex` and 1 array `reserveTokens`. Since all three is for 1 purpose only. Avoid using dynamic arrays if possible and use mappings because search time in arrays are O(n) while in mapping it is O(1).
Use Token Symbol as key of the mapping.

```diff
   File : /contracts/core/IRdpxV2Core.sol
   //@audit this is defined inside IRdpxV2Core
        struct ReserveAsset {
           address tokenAddress;
           uint256 tokenBalance;
-          string tokenSymbol;
          }
```
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/IRdpxV2Core.sol#L43C2-L48C1

```diff
File: /contracts/core/RdpxV2Core.sol
-     ReserveAsset[] public reserveAsset;

-     string[] public reserveTokens;

-     mapping(string => uint256) public reservesIndex;

+     mapping(string => ReserveAsset) public reserveAssets;
//  One reserve Tokens addresses array can be maintained if protocol want to know which address exists but in current need it does not seem to be required.
```
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L61C1-L73C51

It will simplify the accessing of reserve assets. Because contract is using symbol as the key to search asset, so it can also be done with it. By this change same goal can be achieved with many advantages. eg: Lesser state variables in contract saves lot of gas and slots by removing 2 arrays.
No need to traverse whole array when checking this token already there or not.
Adding new reserve assets and removing old is easy and bug free.(in previous one removing asset have bug because of complex logic in `removeAssetFromtokenReserves`) hard to identify bug in them.

So change code of

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L61C1-L73C51

It will simplify the accessing of reserve assets. Because contract is using symbol as the key to search asset, so it can also be done with it. By this change same goal can be achieved with many advantages. eg: Lesser state variables in contract saves lot of gas and slots by removing 2 arrays.
No need to traverse whole array when checking this token already there or not.
Adding new reserve assets and removing old is easy and bug free.(in previous one removing asset have bug because of complex logic in `removeAssetFromtokenReserves`) hard to identify bug in them.

So change code of Core contract according to this above change.

## Some other suggestions

### 1. Always check amount is greater than zero when transferring, minting and burning tokens.

In the whole protocol all contracts in most of the places amount > 0 is not checked before calling `mint` , `burn`, `transfer`and `transferFrom`.

// Code block 1 (diff):
File : /contracts/core/IRdpxV2Core.sol
   //@audit this is defined inside IRdpxV2Core
        struct ReserveAsset {
           address tokenAddress;
           uint256 tokenBalance;
-          string tokenSymbol;
          }

// Code block 2 (diff):
File: /contracts/core/RdpxV2Core.sol
-     ReserveAsset[] public reserveAsset;

-     string[] public reserveTokens;

-     mapping(string => uint256) public reservesIndex;

+     mapping(string => ReserveAsset) public reserveAssets;
//  One reserve Tokens addresses array can be maintained if protocol want to know which address exists but in current need it does not seem to be required.

File : /contracts/core/IRdpxV2Core.sol
   //@audit this is defined inside IRdpxV2Core
        struct ReserveAsset {
           address tokenAddress;
           uint256 tokenBalance;
-          string tokenSymbol;
          }

File: /contracts/core/RdpxV2Core.sol
-     ReserveAsset[] public reserveAsset;

-     string[] public reserveTokens;

-     mapping(string => uint256) public reservesIndex;

+     mapping(string => ReserveAsset) public reserveAssets;
//  One reserve Tokens addresses array can be maintained if protocol want to know which address exists but in current need it does not seem to be required.


GalloDaSballo Q

# Executive Summary

The following QA Report is an aggregate of smaller reports divided by contract / topic

The following criteria are used for suggested severity

L - Low Severity -> Some risk

R - Refactoring -> Suggested changes for improvements

NC - Non-Critical / Informational -> Minor suggestions

# List of all findings

* 1. [Vault](#Vault)
	* 1.1. [L - Strategy doesn't have `inCaseTokensGetStuck`](#L-StrategydoesnthaveinCaseTokensGetStuck)
	* 1.2. [L - Deposit / Withdraw use FOT compatible math, but withdrawing from strategy doesn't](#L-DepositWithdrawuseFOTcompatiblemathbutwithdrawingfromstrategydoesnt)
	* 1.3. [L - Roles for Vault and Strat are set separately](#L-RolesforVaultandStrataresetseparately)
	* 1.4. [L - MaxLoss hardcoded means the contract will revert if any loss bigger than BPS happens](#L-MaxLosshardcodedmeansthecontractwillrevertifanylossbiggerthanBPShappens)
	* 1.5. [R - Can refactor to simplify](#R-Canrefactortosimplify)
	* 1.6. [R - Track stats via Ninja](#R-TrackstatsviaNinja)
	* 1.7. [R - Track harvest health via Health Check (by Yearn)](#R-TrackharvesthealthviaHealthCheckbyYearn)
	* 1.8. [R - Consider forking Yearn.Watch](#R-ConsiderforkingYearn.Watch)
	* 1.9. [R -  PERCENT_DIVISOR is more a BPS_DIVISOR](#R-PERCENT_DIVISORismoreaBPS_DIVISOR)
	* 1.10. [R - Math for issuing shares can be turned into an internal function](#R-Mathforissuingsharescanbeturnedintoaninternalfunction)
	* 1.11. [R - License may be incompatible with source code](#R-Licensemaybeincompatiblewithsourcecode)
	* 1.12. [R - Performance Fee is Paid Out to Treasury Twice](#R-PerformanceFeeisPaidOuttoTreasuryTwice)
	* 1.13. [NC - `_addLiquidityVelo` is unused](#NC-_addLiquidityVeloisunused)
	* 1.14. [L - Steps not validated](#L-Stepsnotvalidated)
	* 1.15. [R - Equivalent to aToken.balanceOf(this)](#R-EquivalenttoaToken.balanceOfthis)
	* 1.16. [R - Amount non-zero is known](#R-Amountnon-zeroisknown)
* 2. [Redemption Helper](#RedemptionHelper)
	* 2.1. [L - Griefing redemp

    function _atLeastRole(bytes32 _role) internal view {
        bytes32[] memory cascadingAccessRoles = _cascadingAccessRoles();
        uint256 numRoles = cascadingAccessRoles.length;
        bool specifiedRoleFound = false;
        bool senderHighestRoleFound = false;

        // {_role} must be found in the {cascadingAccessRoles} array.
        // Also, msg.sender's highest role index <= specified role index.
        for (uint256 i = 0; i < numRoles; i = i.uncheckedInc()) {
            // If the highest was not found and they have a role that is same or higher
            if (!senderHighestRoleFound && _hasRole(cascadingAccessRoles[i], msg.sender)) {
                senderHighestRoleFound = true;
            }
            // If we are at the this role part of the loop
            // E.g. we may or may have not found it here
            if (_role == cascadingAccessRoles[i]) {
                specifiedRoleFound = true;
                break;
            }
        }

        // require(senderHighestRoleFound)
        require(specifiedRoleFound && senderHighestRoleFound, "Unauthorized access");
    }


0x007 G

## Reduce the size of error messages (Long revert Strings)
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Cases can be found in `find . -name "*.sol" |xargs grep -n -E 'require.*".{36,}"'`

## Use multiple require statement instead of &&
`find . -name "*.sol" |xargs grep -n "require.*&&"`

## Mark constants as private instead of public
`find . -name "*.sol" |xargs grep -n 'public constant'`

## Pack structs into fewer storage slot
There are 2 cases in https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/PriceFeed.sol#L58-L71
Suggested change would be
```sol
struct ChainlinkResponse {
        uint80 roundId;
        uint8 decimals;
        bool success;
        int256 answer;
        uint256 timestamp;
    }

    struct TellorResponse {
        bool ifRetrieve;
        bool success;
        uint256 value;
        uint256 timestamp;
    }
```

// Code block 1 (sol):
struct ChainlinkResponse {
        uint80 roundId;
        uint8 decimals;
        bool success;
        int256 answer;
        uint256 timestamp;
    }

    struct TellorResponse {
        bool ifRetrieve;
        bool success;
        uint256 value;
        uint256 timestamp;
    }


K42 G

## Gas Optimization Report for [Salty.IO](https://github.com/code-423n4/2024-01-salty) by K42

- Note: I made sure these optimizations are unique in relation to the Bot Report and 4Analy3er Report.

### Possible Optimizations in [ArbitrageSearch.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/arbitrage/ArbitrageSearch.sol)

Possible Optimization 1 =
- Use immutable variables ``wbtc``, ``weth``, and ``salt`` directly in the [_arbitragePath()](https://github.com/code-423n4/2024-01-salty/blob/main/src/arbitrage/ArbitrageSearch.sol#L31C1-L58C4) function.

Code Snippet:

```solidity
function _arbitragePath(IERC20 swapTokenIn, IERC20 swapTokenOut) internal view returns (IERC20 arbToken2, IERC20 arbToken3) {
    if (address(swapTokenIn) == address(wbtc) && address(swapTokenOut) == address(weth)) {
        return (wbtc, salt);
    }
    // Other conditions using direct references to wbtc, weth, and salt
}
```

- Estimated Gas Saved = Directly referencing immutable variables can save gas by avoiding additional memory or storage operations.

Possible Optimization 2 =
- Streamline the ``return`` statement in [_rightMoreProfitable()](https://github.com/code-423n4/2024-01-salty/blob/main/src/arbitrage/ArbitrageSearch.sol#L63C2-L90C4) by directly returning the comparison result.

Code Snippet:

```solidity
function _rightMoreProfitable(...) internal pure returns (bool) {
    // ... [existing calculations]
    return int256(amountOutRight) - int256(midpoint + MIDPOINT_PRECISION) > profitMidpoint;
}
```

- Estimated Gas Saved = This approach reduces the need for an extra variable and directly returns the comparison result, saving gas on variable assignment.

Possible Optimization 3 =
- Simplify the loop control in [_bisectionSearch()](https://github.com/code-423n4/2024-01-salty/blob/main/src/arbitrage/ArbitrageSearch.sol#L101C2-L136C4) for clarity and efficiency.

Code Snippet:

```solidity
function _bisectionSearch(...) internal pure returns

// Code block 1 (solidity):
function _arbitragePath(IERC20 swapTokenIn, IERC20 swapTokenOut) internal view returns (IERC20 arbToken2, IERC20 arbToken3) {
    if (address(swapTokenIn) == address(wbtc) && address(swapTokenOut) == address(weth)) {
        return (wbtc, salt);
    }
    // Other conditions using direct references to wbtc, weth, and salt
}

// Code block 2 (solidity):
function _rightMoreProfitable(...) internal pure returns (bool) {
    // ... [existing calculations]
    return int256(amountOutRight) - int256(midpoint + MIDPOINT_PRECISION) > profitMidpoint;
}

function _arbitragePath(IERC20 swapTokenIn, IERC20 swapTokenOut) internal view returns (IERC20 arbToken2, IERC20 arbToken3) {
    if (address(swapTokenIn) == address(wbtc) && address(swapTokenOut) == address(weth)) {
        return (wbtc, salt);
    }
    // Other conditions using direct references to wbtc, weth, and salt
}

function _rightMoreProfitable(...) internal pure returns (bool) {
    // ... [existing calculations]
    return int256(amountOutRight) - int256(midpoint + MIDPOINT_PRECISION) > profitMidpoint;
}

ArbitrageSearch.sol, ArbitrageSearch.sol#L31-L1, ArbitrageSearch.sol#L63-L2, ArbitrageSearch.sol#L101-L2


Kaysoft Q

## [NC-01] `approveContractToSpend` will return wrong error message when `_amount` does not pass the validation check.
File: https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L410

- Wrong error code was supplied to the third validation in this code. The right error code that is supposed to be passed is `4` instead of `17`

```
function approveContractToSpend(
    address _token,
    address _spender,
    uint256 _amount
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_token != address(0), 17);
    _validate(_spender != address(0), 17);
@>    _validate(_amount > 0, 17);//@audit-info 17 error code wrong.
    IERC20WithBurn(_token).approve(_spender, _amount);
  }
```
```
// ERROR CODES
// E1: "Insufficient bond amount",
// E2: "Bond has expired",
// E3: "Invalid parameters",
// E4: "Invalid amount",
// E5: "Not enough ETH available from the delegate",
// E6: "Invalid bond ID",
// E7: "Bond has not matured",
// E8: "Fee cannot be more than 100",
// E9: "msg.sender is not the owner",
// E10: "Price is not above the upper peg",
// E11: "Amount greater that max permissible amount",
// E12: "Price is not lower than first lower peg",
// E13: "Amount greater than max permissible amount"
// E14: "Invalid delegate Id"
// E15: "Invalid amount"
// E16: "Funding already paid for the epoch"
// E17: "Zero address"
// E18: "Asset not found"
// E19: "Token not found"
```

#### Recommended Mitigation Steps
Replace the `17` on the `amount` validation here with `4` like this.
```
function approveContractToSpend(
    address _token,
    address _spender,
    uint256 _amount
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_token != address(0), 17);
    _validate(_spender != address(0), 17);
-   _validate(_amount > 0, 17);//@audit-info 17 error code wrong.
+   _validate(_amount > 0, 4);
    IERC20WithBurn(_token).approve(_spender, _amount);
  }
```

## [NC-02] Double access control check for `mint()`

// Code block 1 (unknown):
function approveContractToSpend(
    address _token,
    address _spender,
    uint256 _amount
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_token != address(0), 17);
    _validate(_spender != address(0), 17);
@>    _validate(_amount > 0, 17);//@audit-info 17 error code wrong.
    IERC20WithBurn(_token).approve(_spender, _amount);
  }

// Code block 2 (unknown):
// ERROR CODES
// E1: "Insufficient bond amount",
// E2: "Bond has expired",
// E3: "Invalid parameters",
// E4: "Invalid amount",
// E5: "Not enough ETH available from the delegate",
// E6: "Invalid bond ID",
// E7: "Bond has not matured",
// E8: "Fee cannot be more than 100",
// E9: "msg.sender is not the owner",
// E10: "Price is not above the upper peg",
// E11: "Amount greater that max permissible amount",
// E12: "Price is not lower than first lower peg",
// E13: "Amount greater than max permissible amount"
// E14: "Invalid delegate Id"
// E15: "Invalid amount"
// E16: "Funding already paid for the epoch"
// E17: "Zero address"
// E18: "Asset not found"
// E19: "Token not found"

// Code block 3 (unknown):
function approveContractToSpend(
    address _token,
    address _spender,
    uint256 _amount
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    _validate(_token != address(0), 17);
    _validate(_spender != address(0), 17);
-   _validate(_amount > 0, 17);//@audit-info 17 error code wrong.
+   _validate(_amount > 0, 4);
    IERC20WithBurn(_token).approve(_spender, _amount);
  }


wahedtalash77 G

## [G-01] For uint use != 0 instead of > 0
558: `if (_fee > 0) usda.vaultControllerMint(owner(), _fee);`
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L558

660: ` if (_tokenAmount > 0) _tokensToLiquidate = _tokenAmount;`
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L660

206: `if (_totalCrvReward > 0 || _totalCvxReward > 0) {`
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/Vault.sol#L206

```
223: if (_totalCvxReward > 0) CVX.transfer(_msgSender(), _totalCvxReward);
224: if (_totalCrvReward > 0) CRV.transfer(_msgSender(), _totalCrvReward);
```
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/Vault.sol#L223C6-L224C76

## [G-02]  Avoiding Initialization of Loop Index If It Is 0 and Avoid unnecessary read of array length in for loops can save gas
259: `for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {`
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L259

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L382

## [G-03] unchecked { ++i } is more gas efficient than i++ for loops
259: `for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {`
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L259
## [G-04] Sort Solidity operations using short-circuit mode
Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.

```
//f(x) is a low gas cost operation

//f(x) is a low gas cost operation
//g(y) is a high gas cost operation
	//Sort operations with different gas costs as follows
f(x) || g(y)
f(x) && g(y)

// Code block 1 (unknown):
223: if (_totalCvxReward > 0) CVX.transfer(_msgSender(), _totalCvxReward);
224: if (_totalCrvReward > 0) CRV.transfer(_msgSender(), _totalCrvReward);

VaultController.sol#L558, VaultController.sol#L660, Vault.sol#L206, Vault.sol#L223-L6, GovernorCharlie.sol#L259, GovernorCharlie.sol#L382


DadeKuma Q

## Summary

---

### Low Risk Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[L-01]| No limits to max slippage | 1 |
|[L-02]| Missing safe limits in `setMinAmount` and `setMaxAmount` | 1 |
|[L-03]| Owner can renounce ownership | 17 |
|[L-04]| Lack of two-step Ownership | 4 |

Total: 23 instances over 4 issues.

### Non Critical Findings
|Id|Title|Instances|
|:--:|:-------|:--:|
|[NC-01]| Static `keccak256` computed more than once | 1 |
|[NC-02]| Parameter omission in events | 1 |
|[NC-03]| Some functions don't follow the Solidity naming conventions | 4 |
|[NC-04]| Use of floating pragma | 27 |
|[NC-05]| Missing or incomplete NatSpec | 7 |

Total: 40 instances over 5 issues.

## Low Risk Findings

---

### [L-01] No limits to max slippage

A slippage set to 100% could result in massive loss of funds due to frontrunning. Consider setting a max limit to avoid this risk.

*There is 1 instance of this issue.*

```solidity
File: contracts/SafEth/SafEth.sol

202: 		    function setMaxSlippage(
203: 		        uint _derivativeIndex,
204: 		        uint _slippage
205: 		    ) external onlyOwner {
206: 		        derivatives[_derivativeIndex].setMaxSlippage(_slippage);
207: 		        emit SetMaxSlippage(_derivativeIndex, _slippage);
208: 		    }

```

### [L-02] Missing safe limits in `setMinAmount` and `setMaxAmount`

The `minAmount` should never be greater than `maxAmount`, as it would lock the `stake` function. Consider adding some safechecks to those setters.

*There is 1 instance of this issue.*

```solidity
File: contracts/SafEth/SafEth.sol

214: 		    function setMinAmount(uint256 _minAmount) external onlyOwner {
215: 		        minAmount = _minAmount;
216: 		        emit ChangeMinAmount(minAmount);
217: 		    }
223: 		    function setMaxAmount(uint256 _maxAmount) external onlyOwner {
224: 		        maxAmount = _maxAmount;
225: 		        emit ChangeMaxAmount(maxAmount);
226: 		    }

```

### [L-03] Owner can renounce ownership

The contract's owner is the acc

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

202: 		    function setMaxSlippage(
203: 		        uint _derivativeIndex,
204: 		        uint _slippage
205: 		    ) external onlyOwner {
206: 		        derivatives[_derivativeIndex].setMaxSlippage(_slippage);
207: 		        emit SetMaxSlippage(_derivativeIndex, _slippage);
208: 		    }

// Code block 2 (solidity):
File: contracts/SafEth/SafEth.sol

214: 		    function setMinAmount(uint256 _minAmount) external onlyOwner {
215: 		        minAmount = _minAmount;
216: 		        emit ChangeMinAmount(minAmount);
217: 		    }
223: 		    function setMaxAmount(uint256 _maxAmount) external onlyOwner {
224: 		        maxAmount = _maxAmount;
225: 		        emit ChangeMaxAmount(maxAmount);
226: 		    }

File: contracts/SafEth/SafEth.sol

202: 		    function setMaxSlippage(
203: 		        uint _derivativeIndex,
204: 		        uint _slippage
205: 		    ) external onlyOwner {
206: 		        derivatives[_derivativeIndex].setMaxSlippage(_slippage);
207: 		        emit SetMaxSlippage(_derivativeIndex, _slippage);
208: 		    }

File: contracts/SafEth/SafEth.sol

214: 		    function setMinAmount(uint256 _minAmount) external onlyOwner {
215: 		        minAmount = _minAmount;
216: 		        emit ChangeMinAmount(minAmount);
217: 		    }
223: 		    function setMaxAmount(uint256 _maxAmount) external onlyOwner {
224: 		        maxAmount = _maxAmount;
225: 		        emit ChangeMaxAmount(maxAmount);
226: 		    }


catwhiskeys Q

# 1) Function with wrong functionality
The `_mul` function wasn't coded well. It should return multiplication of 2 numbers, but the function might return nothing.

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

PoC
For example, x=1, y=2. In this case, function will not return anything, because it doesn't pass any of the conditions.
y != 0 and y!= x. The `_mul` function provides no value.

Recommended mitigation steps:
According to Company's needs, consider updating this function in this way:
```
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
	if (y != 0) {
	return z = x * y;
}
```

# 2) Unauthorized users can pause oracle.
Even though function `pauseOracle` is included in the admin's functions, it can be paused by any user.
Apparently, Company doesn't need oracle to be paused by unauthorized users, so consider adding access control checks.

Instance:
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L241

Recommended mitigation steps:
Consider making this function similar to `unpauseOracle`, by making it available only for admin.
```
- function pauseOracle() external onlyRole(PAUSER_ROLE) {
+ function pauseOracle() external onlyRole(DEFAULT_ADMIN_ROLE) {
    _pause();
}
```

// Code block 1 (unknown):
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
	if (y != 0) {
	return z = x * y;
}

// Code block 2 (unknown):
- function pauseOracle() external onlyRole(PAUSER_ROLE) {
+ function pauseOracle() external onlyRole(DEFAULT_ADMIN_ROLE) {
    _pause();
}


W0RR1O G

Use ++i instead of i++ to save gas
===============

this gas optimization can be done in the following lines:

`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L74`
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L80`
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L99`
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L112`
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L128`
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L134`

Use delegatecall or callcode instead of call to save gas
===================================
`https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L28`

Use `REQUIRE` Instead of `ASSERT`
======================
The solidity functions `assert()` and `require()` are used for error handling in solidity. Since solidity makes uses of state reverting error handling this means all changes made to the contract or any of its subcalls are undone if an error is occured.

Since both of this functions are quite similar and they do almost the same thing here are the differences between them:

* The `assert()` function when false uses up all the remaining gas and reverts all changes made.

* However, the `require()` function in solidity refunds all the remaining gas that we offered to pay and reverts back all changes made in the contract.

Affected code:
* https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L16

* https://github.com/cod

pragma solidity ^0.8.12;

//Not optimized contract
contract TestA{
    uint private num;
    function testA() public{
        num += 1;
    }
}

//Optimized contract
contract TestB{
    uint private num;
    function testB() public{
        num = num + 1;
    }
}

contract TestA = 50027gas
contract TestB =  50012gas

EntryPoint.sol#L74, EntryPoint.sol#L80, EntryPoint.sol#L99, EntryPoint.sol#L112, EntryPoint.sol#L128, EntryPoint.sol#L134, Executor.sol#L28, Proxy.sol#L16


0xhex G

# Gas-Optimization

## [G-01] Use do while  loops instead of for loops

```solidity
File: src/LRTOracle.sol

66        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L66

```solidity
File: src/LRTDepositPool.sol

82        for (uint256 i; i < ndcsCount;) {

168        for (uint256 i; i < length;) {
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L82

```solidity
File: src/NodeDelegator.sol

109        for (uint256 i = 0; i < strategiesLength;) {
```

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

## [G-02] Using mappings instead of arrays to avoid length checks

```solidity
File: src/LRTDepositPool.sol

22    address[] public nodeDelegatorQueue;
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L22

```solidity
File: src/LRTConfig.sol

19    address[] public supportedAssetList;
```

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

## [G-03] Don't make variables public unless necessary

```solidity
File: src/RSETH.sol

21    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

22    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/RSETH.sol#L21

```solidity
File: src/LRTDepositPool.sol

20    uint256 public maxNodeDelegatorCount;

22    address[] public nodeDelegatorQueue;
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L20

```solidity
File: src/LRTConfig.sol

19    address[] public supportedAssetList;

21    address public rsETH;
```

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

```solidity
File: src/utils/LRTConfigRoleChecker.sol

14    ILRTConfig public lrtConfig;
```

https://github.com/code-423n4/2023-11-kelp/blob/main/src/utils/LRTConfigRoleChecker.sol#L14

```solidity
File: src/utils/LRTConstants.sol

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

66        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {

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

82        for (uint256 i; i < ndcsCount;) {

168        for (uint256 i; i < length;) {

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

109        for (uint256 i = 0; i < strategiesLength;) {

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

22    address[] public nodeDelegatorQueue;

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

19    address[] public supportedAssetList;

// Code block 6 (solidity):
File: src/RSETH.sol

21    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

22    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

// Code block 7 (solidity):
File: src/LRTDepositPool.sol

20    uint256 public maxNodeDelegatorCount;

22    address[] public nodeDelegatorQueue;

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

19    address[] public supportedAssetList;

21    address public rsETH;

// Code block 9 (solidity):
File: src/utils/LRTConfigRoleChecker.sol

14    ILRTConfig public lrtConfig;

File: src/LRTOracle.sol

66        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {

File: src/LRTDepositPool.sol

82        for (uint256 i; i < ndcsCount;) {

168        for (uint256 i; i < length;) {

File: src/NodeDelegator.sol

109        for (uint256 i = 0; i < strategiesLength;) {

File: src/LRTDepositPool.sol

22    address[] public nodeDelegatorQueue;

File: src/LRTConfig.sol

19    address[] public supportedAssetList;

File: src/RSETH.sol

21    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

22    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

File: src/LRTDepositPool.sol

20    uint256 public maxNodeDelegatorCount;

22    address[] public nodeDelegatorQueue;

File: src/LRTConfig.sol

19    address[] public supportedAssetList;

21    address public rsETH;

File: src/utils/LRTConfigRoleChecker.sol

14    ILRTConfig public lrtConfig;

LRTOracle.sol#L66, LRTDepositPool.sol#L82, NodeDelegator.sol#L109, LRTDepositPool.sol#L22, LRTConfig.sol#L19, RSETH.sol#L21, LRTDepositPool.sol#L20, LRTConfigRoleChecker.sol#L14

LRTConfigRoleChecker.sol, LRTOracle.sol, RSETH.sol, NodeDelegator.sol, LRTConfig.sol, LRTDepositPool.sol


spark Q

# 1. Potential Data Inconsistency between `tokenMap` and `isSupportedAsset`

In contract `LRTConfig`, the token info is split into two storage:

- ``tokenMap``
-  `isSupportedAsset`.

And each storage will be updated by the different role:

- ``tokenMap`` -> admin
-  `isSupportedAsset` -> manager

As a result, there could be data inconsistency between the two storages.

# 2. Unused Pause Functionality

The `LRTOracle` declare pause and unpause but no function is restricted with these methods.

# 3. Missing Truncation Protection in `getRsETHAmountToMint`

`getRsETHAmountToMint` will return the value that user can mint, however, there could be truncation when rsETH price is large or amount is too low. As a result user will deposit for nothing:

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

It's better to set a minimum deposit value(amount * asset value).

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


phoenixV110 Q

# [L-01] Modifier `onlySupportedAsset()` is missing in `getAssetBalance()`

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

## Description
The method `getAssetBalance()` doesn't check if the asset is a supportedAsset or not. Which can revert or will return wrong values if **assetStrategy** is present for that asset.

## Recommendations
Add `onlySupportedAsset()` modifier in `getAssetBalance()` method
```
-    function getAssetBalance(address asset) external view override returns (uint256) {
+    function getAssetBalance(address asset) external view override onlySupportedAsset(asset) returns (uint256) {
         address strategy = lrtConfig.assetStrategy(asset);
         return IStrategy(strategy).userUnderlyingView(address(this));
     }
```
# [L-02] While depositing assets in the LRTDepositPool, method getAssetPrice() can revert unexpectedly if price feed for the asset is not present in assetPriceFeed array

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L45-L47

## Summary
The `getAssetPrice()` returns to price of an asset by fetching in from Chainlink oracle. It checks `onlySupportedAsset()` modifier but doesn't check if the price feed for this asset is present or not.

## Vulnerability Details
The `getAssetPrice()` doesn't check if the price feed for the asset is added. It can revert unexpectedly when calculating RSETH or asset prices.

```
function getAssetPrice(address asset) external view onlySupportedAsset(asset) returns (uint256) {
        return AggregatorInterface(assetPriceFeed[asset]).latestAnswer();
    }
```

# [L-03] **rsethAmountToMint > 0** check is missing when executing `_mintRsETH()`

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L151-L157

## Description
The method `_mintRsETH()` mint and sends RSETH to the caller. But the check **rsethAmountToMint != 0** is missing in the function.

## Recommendations
```
if(rsethAmountToMint == 0) {
  revert("RSETH am

// Code block 1 (unknown):
-    function getAssetBalance(address asset) external view override returns (uint256) {
+    function getAssetBalance(address asset) external view override onlySupportedAsset(asset) returns (uint256) {
         address strategy = lrtConfig.assetStrategy(asset);
         return IStrategy(strategy).userUnderlyingView(address(this));
     }

// Code block 2 (unknown):
function getAssetPrice(address asset) external view onlySupportedAsset(asset) returns (uint256) {
        return AggregatorInterface(assetPriceFeed[asset]).latestAnswer();
    }


codeslide Q

### Low Risk Issues List

| Number | Issue                   | Instances |
| :----: | :---------------------- | :-------: |
| [L-01] | Unsafe casting of uints |     7     |

#### [L-01] Unsafe casting of uints

Downcasting from uint256 in Solidity does not revert on overflow. This can result in undesired exploitation or bugs since developers usually assume that overflows raise errors. [OpenZeppelin's SafeCast](https://docs.openzeppelin.com/contracts/3.x/api/utils#SafeCast) restores this intuition by reverting the transaction when such an operation overflows. Using this library instead of the unchecked operations eliminates an entire class of bugs, so it's recommended to always use it.

Although SafeCast is used elsewhere in this code base, the following instances are not using it.

```solidity
File: ajna-core/src/RewardsManager.sol

179:             toBucket.lpsAtStakeTime  = uint128(positionManager.getLP(tokenId_, toIndex));
180:             toBucket.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(toIndex));

222:         stakeInfo.stakingEpoch = uint96(curBurnEpoch);

225:         stakeInfo.lastClaimedEpoch = uint96(curBurnEpoch);

236:             bucketState.lpsAtStakeTime = uint128(positionManager.getLP(
237:                 tokenId_,
238:                 bucketId
239:             ));
240:             // record the bucket exchange rate at the time of staking
241:             bucketState.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(bucketId));

594:         stakeInfo_.lastClaimedEpoch = uint96(epochToClaim_);
```

### Non Critical Issues

| Number  | Issue                               | Instances |
| :-----: | :---------------------------------- | :-------: |
| [NC-01] | Whitespace in Expressions           |    17     |
| [NC-02] | Use the complete name of data types |     6     |
| [NC-03] | Control Structures                  |    13     |
| [NC-04] | Constants in comparisons            |    31     |

#### [NC-01] Whitespace in Ex

// Yes
x = 1;
y = 2;
longVariable = 3;

// No
x            = 1;
y            = 2;
longVariable = 3;

// Code block 1 (solidity):
File: ajna-core/src/RewardsManager.sol

179:             toBucket.lpsAtStakeTime  = uint128(positionManager.getLP(tokenId_, toIndex));
180:             toBucket.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(toIndex));

222:         stakeInfo.stakingEpoch = uint96(curBurnEpoch);

225:         stakeInfo.lastClaimedEpoch = uint96(curBurnEpoch);

236:             bucketState.lpsAtStakeTime = uint128(positionManager.getLP(
237:                 tokenId_,
238:                 bucketId
239:             ));
240:             // record the bucket exchange rate at the time of staking
241:             bucketState.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(bucketId));

594:         stakeInfo_.lastClaimedEpoch = uint96(epochToClaim_);


Aymen0909 Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1 | Potential underflow in `RdpxV2Core.calculateBondCost` will cause DOS of bond operations | Low | 1 |
| 2 | Missing `sync()` after transferring funds to `RdpxV2Core` | Low | 2 |
| 3 | Funds not transferred to correct recipient in `RdpxDecayingBonds.emergencyWithdraw`| Low | 1 |
| 4 | `UniV2LiquidityAMO.approveContractToSpend` can't approve USDT transfer | Low | 1 |
| 5 | `PerpetualAtlanticVaultLP` can never pull funds from `PerpetualAtlanticVault` | Low |  |
| 6 | `MANAGER_ROLE` not used in `PerpetualAtlanticVault` | Low |  |
| 7 | `underlyingSymbol` not set in `PerpetualAtlanticVaultLP` | NC |  |
| 8 | Unused state variable `collateralPrecision` in the `PerpetualAtlanticVault` contract | NC |  |

## Findings

### 1- Potential underflow in `RdpxV2Core.calculateBondCost` will cause DOS of bond operations :

#### Risk : Low

The function `RdpxV2Core.calculateBondCost` is used to calculate the amount of weth and rdpx tokens required to create a bond, in the case where `_rdpxBondId == 0` the function has to calculate the value of `bondDiscount` and it validate that its value is less than `100e8`.

The issue is that in the following lines of code there is this substraction operation :

```
rdpxRequired =
    ((RDPX_RATIO_PERCENTAGE - (bondDiscount / 2)) *
        _amount *
        DEFAULT_PRECISION) /
    (DEFAULT_PRECISION * rdpxPrice * 1e2);
```

where `RDPX_RATIO_PERCENTAGE = 25 * DEFAULT_PRECISION = 25e8`

As you can see if we have `bondDiscount / 2 > RDPX_RATIO_PERCENTAGE` the operation will revert due to an underflow, and because the check present in the function only ensures that `bondDiscount < 100e8`, it means that there are many valid values for `bondDiscount` that will make the function revert. For example if we take `bondDiscount = 60e8` (which is valid as it's less than 100e8) when the subtraction op go

function calculateBondCost(
    uint256 _amount,
    uint256 _rdpxBondId
) public view returns (uint256 rdpxRequired, uint256 wethRequired) {
    uint256 rdpxPrice = getRdpxPrice();
    if (_rdpxBondId == 0) {
        uint256 bondDiscount = (bondDiscountFactor *
            Math.sqrt(IRdpxReserve(addresses.rdpxReserve).rdpxReserve()) *
            1e2) / (Math.sqrt(1e18)); // 1e8 precision

        // @audit allow values of bondDiscount < 100e8
        _validate(bondDiscount < 100e8, 14);

        // @audit can underflow if bondDiscount > 50e8
        rdpxRequired =
            ((RDPX_RATIO_PERCENTAGE - (bondDiscount / 2)) *
                _amount *
                DEFAULT_PRECISION) /
            (DEFAULT_PRECISION * rdpxPrice * 1e2);

        wethRequired =
            ((ETH_RATIO_PERCENTAGE - (bondDiscount / 2)) * _amount) /
            (DEFAULT_PRECISION * 1e2);
    } else {
        ...
}

// Code block 1 (unknown):
rdpxRequired =
    ((RDPX_RATIO_PERCENTAGE - (bondDiscount / 2)) *
        _amount *
        DEFAULT_PRECISION) /
    (DEFAULT_PRECISION * rdpxPrice * 1e2);


rbserver Q

## QA REPORT

| |Issue|
|-|:-|
| [01] | `CHALLENGE_PERIOD_LENGTH`, `DISTRIBUTION_PERIOD_LENGTH`, `FUNDING_PERIOD_LENGTH`, AND `MAX_EFM_PROPOSAL_LENGTH` ARE HARDCODED BASED ON 7200 BLOCKS PER DAY |
| [02] | AMBIGUITY IN `StandardFunding._standardProposalState` FUNCTION |
| [03] | `ExtraordinaryFundingProposal.votesReceived` IN `ExtraordinaryFunding` CONTRACT IS `uint120` INSTEAD OF `uint128` |
| [04] | CALLING `ExtraordinaryFunding.proposeExtraordinary` AND `StandardFunding.proposeStandard` FUNCTIONS CAN REVERT AND WASTE GAS |
| [05] | `ajnaTokenAddress` IS HARDCODED |
| [06] | CODE COMMENT IN `ExtraordinaryFunding._extraordinaryProposalSucceeded` FUNCTION CAN BE INCORRECT |
| [07] | CODE COMMENT FOR `CHALLENGE_PERIOD_LENGTH` CAN BE MORE ACCURATE |
| [08] | MISSING `address(0)` CHECKS FOR CRITICAL CONSTRUCTOR INPUTS |
| [09] | SOLIDITY VERSION `0.8.19` CAN BE USED |
| [10] | SETTING `support` TO `1` WHEN `voteParams_.votesUsed < 0` IS FALSE IN `StandardFunding._fundingVote` FUNCTION IS REDUNDANT |
| [11] | REDUNDANT EXECUTION OF `if (sumOfTheSquareOfVotesCast > type(uint128).max) revert InsufficientVotingPower()` IN `StandardFunding._fundingVote` FUNCTION |
| [12] | `InvalidVote` ERROR CAN BE MORE DESCRIPTIVE |
| [13] | CONSTANTS CAN BE USED INSTEAD OF MAGIC NUMBERS |
| [14] | UNDERSCORES CAN BE ADDED FOR NUMBERS |
| [15] | `uint256` CAN BE USED INSTEAD OF `uint` |
| [16] | SPACES CAN BE ADDED FOR BETTER READABILITY |

## [01] `CHALLENGE_PERIOD_LENGTH`, `DISTRIBUTION_PERIOD_LENGTH`, `FUNDING_PERIOD_LENGTH`, AND `MAX_EFM_PROPOSAL_LENGTH` ARE HARDCODED BASED ON 7200 BLOCKS PER DAY
The following `CHALLENGE_PERIOD_LENGTH`, `DISTRIBUTION_PERIOD_LENGTH`, `FUNDING_PERIOD_LENGTH`, and `MAX_EFM_PROPOSAL_LENGTH` are hardcoded and assume that the number of blocks per day is 7200 and the number of seconds per block is 12. Yet, it is possible that the number of seconds per block is more or less than 12 due to network traffic and future chain upgrades. When the number of seconds p

    /**
     * @notice Length of the challengephase of the distribution period in blocks.
     * @dev    Roughly equivalent to the number of blocks in 7 days.
     * @dev    The period in which funded proposal slates can be checked in updateSlate.
     */
    uint256 internal constant CHALLENGE_PERIOD_LENGTH = 50400;

    /**
     * @notice Length of the distribution period in blocks.
     * @dev    Roughly equivalent to the number of blocks in 90 days.
     */
    uint48 internal constant DISTRIBUTION_PERIOD_LENGTH = 648000;


LethL Q

## Low Issues
|  | Issue | Instances |
|---|---|---|
| Low-1 | Lack of checks-effects-interactions | 1 |
| Low-2 | Loss of precision due to rounding | 1 |
| Low-3 | Outdated Compiler Version | 8 |

### [Low-1] Lack of checks-effects-interactions
External calls should be executed after state changes to avoid reetrancy bugs.
Consider moving the external calls after the state changes in the following instances:

Instances(1):
```
File: Ethos-Core/contracts/LUSDToken.sol

237:        _transfer(sender, recipient, amount);
238:        _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
```
Link to Code: https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L237-L238

### [Low-2] Loss of precision due to rounding
Add scalars so roundings are negligible.

Instances(1):
```
File: Ethos-Vault/contracts/ReaperVaultV2.sol

463:        uint256 performanceFee = (gain * strategies[strategy].feeBPS) / PERCENT_DIVISOR;
```
Link to Code: https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L463

### [Low-3] Outdated Compiler Version
Using an outdated compiler version (0.6.11) can be problematic if there are known bugs and issues with the current compiler version. It is recommended to use a latest stable version of the Solidity compiler (https://blog.soliditylang.org/2020/07/22/Solidity-0612-release-announcement/).

Instances(8):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L3
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L3
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L3
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L3
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L3
https://github.com/code-423n4/2023-02-et

// Code block 1 (unknown):
File: Ethos-Core/contracts/LUSDToken.sol

237:        _transfer(sender, recipient, amount);
238:        _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));

// Code block 2 (unknown):
File: Ethos-Vault/contracts/ReaperVaultV2.sol

463:        uint256 performanceFee = (gain * strategies[strategy].feeBPS) / PERCENT_DIVISOR;

LUSDToken.sol#L237-L238, ReaperVaultV2.sol#L463, CollateralConfig.sol#L3, BorrowerOperations.sol#L3, TroveManager.sol#L3, ActivePool.sol#L3, StabilityPool.sol#L3

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


zigtur Q

# Unused code should be deleted and not commented out

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/MintRateLimiter.sol#L56-L64

MintRateLimiter.sol constructor has some commented out code.

This code should be deleted if not used in production. Here is the fixed constructor:
```solidity
  constructor(uint256 _mintResetDuration, uint256 _instantMintLimit) {
    resetMintDuration = _mintResetDuration; // can be zero for per-block limit
    mintLimit = _instantMintLimit; // can be zero to disable minting

    lastResetMintTime = block.timestamp;
  }
```

# SharesBurnt event is incorrect
In _burnShares, `preRebaseTokenAmount` and `postRebaseTokenAmount` get their value from `getRUSDYByShares(_sharesAmount)` before and after burning shares (`totalShares` and `shares[account]` are updated).

As `getRUSDYByShares` doesn't use any of the updated value and `sharesAmount` is not modified, then `preRebaseTokenAmount` and `postRebaseTokenAmount` will always be equal.

So the event will emit two values that will **always** be the same.

## Proof of Concept
Scope: https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L586-L592, https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L172-L177

Test `test_rUSDY_burn()` in `rUSDY_harness.t.sol` shows an example of it (use `-vvvvv` args with forge, and look at `SharesBurnt` log).

## Recommended Mitigation Steps
Event `SharesBurnt` should emit only one of this variable, as the value doesn't change.

// Code block 1 (solidity):
constructor(uint256 _mintResetDuration, uint256 _instantMintLimit) {
    resetMintDuration = _mintResetDuration; // can be zero for per-block limit
    mintLimit = _instantMintLimit; // can be zero to disable minting

    lastResetMintTime = block.timestamp;
  }


slvDev G

## Gas Findings

|    | Issue | Instances | Total Gas Saved |
|----|-------|:---------:|:---------:|
| [G-01] | Optimize Storage Layout in Structs | 5 | 100000 |
| [G-02] | Optimize Storage with Byte Truncation for Time Related State Variables | 1 | 20000 |
| [G-03] | Contract storage can use fewer slots by changing the order of state variables | 1 | 20000 |
| [G-04] | Optimize Storage Layout in Structs by Truncating Time Related Fields | 8 | 160000 |
| [G-05] | `do`-`while` is cheaper than `for`-loops when the initial check can be skipped | 2 | 510 |
| [G-06] | Use `assembly` for Efficient Event Emission | 49 | 18620 |
| [G-07] | Use custom errors rather than `require()/assert()/revert(with msg)` with string to save gas | 3 | 150 |
| [G-08] | Enable IR-based code generation | 11 | - |
| [G-09] | Avoid Explicit Initialization to Zero for Non-Constant/Non-Immutable State Variables | 13 | 37700 |
| [G-10] | Assembly: Check `msg.sender` using xor and the scratch space | 23 | 483 |
| [G-11] | Assembly: Use scratch space for building calldata | 10 | 5060 |
| [G-12] | Multiple `address`/ID mappings can be combined into a single `mapping` of an `address`/ID to a `struct` | 9 | - |
| [G-13] | Assigning `state` variables directly with struct constructors wastes gas | 2 | 142 |
| [G-14] | Assembly: Use scratch space when building emitted events with two data arguments | 35 | 525 |
| [G-15] | Stack variable is only used once | 41 | 123 |
| [G-16] | `abi.encodePacked `is more gas efficient than `abi.encode` | 4 | 800 |
| [G-17] | Consider Using `selfbalance()` Over `address(this).balance` | 1 | - |
| [G-18] | Use `assembly` to write mutable storage values | 50 | 550 |
| [G-19] | Using `delete` on a `uint/int` variable is cheaper than assigning it to `0` | 5 | 40 |
| [G-20] | Use `revert()` to gain maximum gas savings | 105 | 5250 |
| [G-21] | `x.a = x.a + b` always cheaper than `x.a += b` for Structs | 14 | 70 |
| [G-22] | Using `constants` instead of `enum` can save gas | 2 |

File: src/V3Oracle.sol

/// @audit - Struct used 6 storage slots, but can be packed to 4 storage slots.
//	struct TokenConfig {
//	    Mode mode;
//	    IUniswapV3Pool pool;
//	    AggregatorV3Interface feed;
//	    uint32 twapSeconds;
//	    uint32 maxFeedAge;
//	    uint16 maxDifference;
//	    uint8 tokenDecimals;
//	    uint8 feedDecimals;
//	    bool isToken0;
//	}
43: struct TokenConfig {
        AggregatorV3Interface feed; // chainlink feed
        uint32 maxFeedAge;
        uint8 feedDecimals;
        uint8 tokenDecimals;
        IUniswapV3Pool pool; // reference pool
        bool isToken0;
        uint32 twapSeconds;
        Mode mode;
        uint16 maxDifference; // max price difference x10000
    }

File: src/automators/AutoExit.sol

/// @audit - Struct used 7 storage slots, but can be packed to 6 storage slots.
//	struct ExecuteParams {
//	    uint256 deadline;
//	    uint256 amountRemoveMin1;
//	    uint256 amountRemoveMin0;
//	    bytes swapData;
//	    uint256 tokenId;
//	    uint128 liquidity;
//	    uint64 rewardX64;
//	}
63: struct ExecuteParams {
        uint256 tokenId; // tokenid to process
        bytes swapData; // if its a swap order - must include swap data
        uint128 liquidity; // liquidity the calculations are based on
        uint256 amountRemoveMin0; // min amount to be removed from liquidity
        uint256 amountRemoveMin1; // min amount to be removed from liquidity
        uint256 deadline; // for uniswap operations - operator promises fair value
        uint64 rewardX64; // which reward will be used for protocol, can be max configured amount (considering onlyFees)
    }


DavidGiladi G

# Report: Identified 12 Distinct Gas Optimization Opportunities

 - [Multiplication and Division by 2 Should use in Bit Shifting](#multiplication-and-division-by-2-should-use-in-bit-shifting) (2 results) (Gas Optimization)
 - [Modulus operations that could be unchecked](#modulus-operations-that-could-be-unchecked) (1 results) (Gas Optimization)
 - [State variables that could be declared constant](#state-variables-that-could-be-declared-constant) (2 results) (Gas Optimization)
 - [Inefficient assignments to state variables](#inefficient-assignments-to-state-variables) (1 results) (Gas Optimization)
 - [Inefficient Parameter Storage](#inefficient-parameter-storage) (4 results) (Gas Optimization)
 - [Using Storage Instead of Memory for structs/arrays Saves Gas](#using-storage-instead-of-memory-for-structsarrays-saves-gas) (1 results) (Gas Optimization)
 - [Internal or Private Function that Called Once Should Be Inlined to Save Gas](#internal-or-private-function-that-called-once-should-be-inlined-to-save-gas) (4 results) (Gas Optimization)
 - [Optimal State Variable Order](#optimal-state-variable-order) (1 results) (Gas Optimization)
 - [Use Small Integer For Efficiency](#use-small-integer-for-efficiency) (2 results) (Gas Optimization)
 - [Avoid Unnecessary Computation Inside Loops](#avoid-unnecessary-computation-inside-loops) (1 results) (Gas Optimization)
 - [Redundant Contract Existence Check in Consecutive External Calls](#redundant-contract-existence-check-in-consecutive-external-calls) (1 results) (Gas Optimization)
 - [Usage of Custom Errors for Gas Efficiency](#usage-of-custom-errors-for-gas-efficiency) (4 results) (Gas Optimization)

#
## Optimal State Variable Order
- Severity: Gas Optimization
- Confidence: High

### Description
Detect optimal variable order in contract storage layouts to decrease the number of slots used.

<details>

<summary>
There are 1 instances of this issue:

</summary>

###
- Optimization opportunity in storage variable layout of cont

Line: 17          abstract contract LybraEUSDVaultBase

Line: 159          require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");


Chandr Q

L[1]: The depositEtherToMint function does not follow a standard pattern for total balance calculation

code: https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWstETHVault.sol#L29-L46

```
    function depositEtherToMint(uint256 mintAmount) external payable override {
        require(msg.value >= 1 ether, "DNL");

        uint256 sharesAmount = lido.submit{value: msg.value}(address(configurator));
        require(sharesAmount > 0, "ZERO_DEPOSIT");

        lido.approve(address(collateralAsset), msg.value);

        uint256 wstETHAmount = IWstETH(address(collateralAsset)).wrap(msg.value);

        depositedAsset[msg.sender] += wstETHAmount;

        if (mintAmount > 0) {
            _mintPeUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
        }

        emit DepositEther(msg.sender, address(collateralAsset), msg.value,wstETHAmount, block.timestamp);
    }
```

mitigation:
Modify the depositEtherToMint function to calculate the total balance before and after the deposit

// Code block 1 (unknown):
function depositEtherToMint(uint256 mintAmount) external payable override {
        require(msg.value >= 1 ether, "DNL");

        uint256 sharesAmount = lido.submit{value: msg.value}(address(configurator));
        require(sharesAmount > 0, "ZERO_DEPOSIT");

        lido.approve(address(collateralAsset), msg.value);

        uint256 wstETHAmount = IWstETH(address(collateralAsset)).wrap(msg.value);

        depositedAsset[msg.sender] += wstETHAmount;

        if (mintAmount > 0) {
            _mintPeUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
        }

        emit DepositEther(msg.sender, address(collateralAsset), msg.value,wstETHAmount, block.timestamp);
    }


Strausses Q

L1 - Create an error when the first value is bigger than the second one.

Add this piece of code.
```
_validate(delegate.amount - delegate.activeCollateral, 15);
```
Otherwise tx could just revert without any error message
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L983

L2 - #Missing check for 0 address - missed spots by Bot race

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L120C13-L120C21
Missing check that ```reciever``` is not 0 address

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L1018
Missing check that ```to``` is not 0 address

L3 - Private func, should be named starting from "_"
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L286

L4 - Add custom error message when allowance is already 0/no allowance
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L156

L5 - It would be better to add Sanity Checks

Sanity Check is a call to a contract to make sure that this is the expected/correct input

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV2LiquidityAmo.sol#L74-L102
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L305
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/reLP/ReLPContract.sol#L115

// Code block 1 (unknown):
_validate(delegate.amount - delegate.activeCollateral, 15);

RdpxV2Core.sol#L983, PerpetualAtlanticVaultLP.sol#L120-L13, RdpxV2Core.sol#L1018, PerpetualAtlanticVaultLP.sol#L286, PerpetualAtlanticVaultLP.sol#L156, UniV2LiquidityAmo.sol#L74-L102, RdpxV2Core.sol#L305, ReLPContract.sol#L115

ReLPContract.sol, RdpxV2Core.sol, UniV2LiquidityAmo.sol, PerpetualAtlanticVaultLP.sol


__141345__ G

#### Update value order can be adjusted to simplify the code and save gas

For example, to update the `val` variable with `newVal`, the current way is as following:

```solidity
    uint oldVal = val;
    val = newVal;
    emit update(oldVal, newVal);
```

If the execution order is adjusted, some operations can be saved (memory space allocation, variable assignment), reducing both the deployment and run time gas cost.

```solidity
    emit update(val, newVal);
    val = newVal;
```

The operation saved: 1 sload, 1 memory allocation, 1 mstore.

The demo of the gas comparison can be seen [here](https://github.com/141345/gas_demo/blob/main/oldnew.sol).

There are multiple places can use this trick for optimization, since the updates of parameters are widely and frequently used, the optimization can be beneficial.

Instances number of this issue: 1
```solidity
File: scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol
109:     function setOwner(address _newOwner) external mixedAuth {
110:         require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");
111:         address oldOwner = owner;
112:         owner = _newOwner;
113:         emit EOAChanged(address(this), oldOwner, _newOwner);
114:     }
```

The above can be changed to:
```solidity
     function setOwner(address _newOwner) external mixedAuth {
         require(_newOwner != address(0), "Smart Account:: Signatory address cannot be zero");
         emit EOAChanged(address(this), owner, _newOwner);
         owner = _newOwner;
     }
```

    emit update(val, newVal);
    val = newVal;

// Code block 1 (solidity):
uint oldVal = val;
    val = newVal;
    emit update(oldVal, newVal);

// Code block 2 (solidity):
emit update(val, newVal);
    val = newVal;

// Code block 3 (unknown):
The above can be changed to:

uint oldVal = val;
    val = newVal;
    emit update(oldVal, newVal);

emit update(val, newVal);
    val = newVal;

    emit update(val, newVal);
    val = newVal;


bronze_pickaxe Q

# 1. maxNodeDelegatorCount does not get enforced

### Impact

There are a few problems with the usage of `maxNodeDelegatorCount` in `LRTDepositPool.sol`.

First problem, consider the following:
- `maxNodeDelegatorCount = 10`
- 10 `NodeDelegators` have been added using `LRTDepositPool.addNodeDelegatorContractToQueue`
- `maxNodeDelegatorCount` gets updated to `8`
- However, `LRTDepositPool.getAssetDistributionData` will still use all 10 `NodeDelegators`, despite the max being set to `8`:

[LRTDepositPool.sol#L81-L86](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L81-L86)
```javascript
 uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);
            unchecked {
                ++i;
```

Second problem, consider the following:
First problem, consider the following:
- `maxNodeDelegatorCount = 10`
- 10 `NodeDelegators` have been added using `LRTDepositPool.addNodeDelegatorContractToQueue`
- `maxNodeDelegatorCount` gets updated to `8`
- However, it is still possible to send funds from `LRTDepositPool` to any `NodeDelegator` that has ever been added using `LRTDepositPool.transferAssetToNodeDelegator`

[LRTDepositPool.sol#L193-195](https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L193-195)
```javascript
address nodeDelegator = nodeDelegatorQueue[ndcIndex];
        if (!IERC20(asset).transfer(nodeDelegator, amount)) {
            revert TokenTransferFailed();
```

These two scenarios should not be possible.

### Tools used
Manual Review

### Recommended Mitigation Steps

Either remove `NodeDelegators` from the `nodeDelegatorQueue` when setting a `maxNodeDelegatorCount < currentmaxNodeDelegatorCount`, or, remove the functionality of being able to set a `maxNodeDelegatorCount` that is less than the current

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

// Code block 2 (javascript):
address nodeDelegator = nodeDelegatorQueue[ndcIndex];
        if (!IERC20(asset).transfer(nodeDelegator, amount)) {
            revert TokenTransferFailed();


0xAnah G

# KELP PROTOCOL GAS OPTIMISATIONS

## INTRODUCTION
Some of optimizations underwent benchmarking using the protocol's tests, specifically employing the following configuration: `solc version 0.8.21`, `optimizer enabled,` and `10_000` runs. For optimizations that were not subjected to benchmarking, their rationale is elucidated through EVM gas expenses and opcodes.

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

Please be aware that some code snippets may be shortened to conserve space, and certain code snippets may include @audit tags in comments to facilitate some issue explanations."

## [G-01] Multiple mappings can be replaced with a single struct mapping

### 1 Instance
1. #### The mappings `mapping(address token => bool isSupported) public isSupportedAsset`, `mapping(address token => uint256 amount) public depositLimitByAsset` and `mapping(address token => address strategy) public override assetStrategy` should be converted to a single address to struct mapping
- https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L15-#L17

The mappings `mapping(address token => bool isSupported) public isSupportedAsset`, `mapping(address token => uint256 amount) public depositLimitByAsset` and `mapping(address token => address strategy) public override assetStrategy` should be converted to a single address to struct mapping. Implementing this would reduce the number of storage slots for these mappings (i.e the storage slots used in the keccak to determine the location of the mapping value) from 3 to 1 and the slots required for the values from 3 to 2 (if the newly defined struct is packed) therefore we would save 20k gas in avoiding `SSTORE` in setting each of these storage slots. In functions such as

file: src/LRTConfig.sol

15:    mapping(address token => bool isSupported) public isSupportedAsset;
16:    mapping(address token => uint256 amount) public depositLimitByAsset;
17:    mapping(address token => address strategy) public override assetStrategy;


Timenov G

# AI Arena Gas report by Timenov

## Summary

| |Issue|Instances|
 |-|:-|:-:|
| G-01 | Array's cached length not used optimally. | 8 |
| G-02 | Smaller uint can be used. | 1 |
| G-03 | State variable can be removed. | 1 |
| G-04 | State variable can be updated after loop. | 2 |
| G-05 | Unnecessary validation. | 1 |
| G-06 | Operation can be skipped. | 1 |

### [G-01] Array's cached length not used optimally.
The protocol has done a good job on caching the length of an array before a loop. However there are places that this can be even more gas efficient.

```diff
    function addAttributeDivisor(uint8[] memory attributeDivisors) external {
        require(msg.sender == _ownerAddress);
+       uint256 attributesLength = attributes.length;
-       require(attributeDivisors.length == attributes.length);
+       require(attributeDivisors.length == attributesLength);

-       uint256 attributesLength = attributes.length;
        for (uint8 i = 0; i < attributesLength; i++) {
            attributeToDnaDivisor[attributes[i]] = attributeDivisors[i];
        }
    }
```

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol#L70-L72

```diff
+       uint256 attributesLength = attributes.length;
+       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributesLength);
-       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);

-       uint256 attributesLength = attributes.length;
```

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/AiArenaHelper.sol#L96-L98

```diff
+       uint256 mintPassIdsToBurnLength = mintpassIdsToBurn.length;
+       require(
+           mintPassIdsToBurnLength == mintPassDnas.length &&
+           mintPassIdsToBurnLength == fighterTypes.length &&
+           mintPassIdsToBurnLength == modelHashes.length &&
+           mintPassIdsToBurnLength == modelTypes.length
+       );
+       for (uint16 i = 0; i < mintPassIdsToBurnLength; i++) {

-       require(
-

// Code block 1 (diff):
function addAttributeDivisor(uint8[] memory attributeDivisors) external {
        require(msg.sender == _ownerAddress);
+       uint256 attributesLength = attributes.length;
-       require(attributeDivisors.length == attributes.length);
+       require(attributeDivisors.length == attributesLength);

-       uint256 attributesLength = attributes.length;
        for (uint8 i = 0; i < attributesLength; i++) {
            attributeToDnaDivisor[attributes[i]] = attributeDivisors[i];
        }
    }

// Code block 2 (diff):
+       uint256 attributesLength = attributes.length;
+       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributesLength);
-       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);

-       uint256 attributesLength = attributes.length;

function addAttributeDivisor(uint8[] memory attributeDivisors) external {
        require(msg.sender == _ownerAddress);
+       uint256 attributesLength = attributes.length;
-       require(attributeDivisors.length == attributes.length);
+       require(attributeDivisors.length == attributesLength);

-       uint256 attributesLength = attributes.length;
        for (uint8 i = 0; i < attributesLength; i++) {
            attributeToDnaDivisor[attributes[i]] = attributeDivisors[i];
        }
    }

+       uint256 attributesLength = attributes.length;
+       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributesLength);
-       uint256[] memory finalAttributeProbabilityIndexes = new uint[](attributes.length);

-       uint256 attributesLength = attributes.length;


PNS Q

[L-1] Missing event for critical parameter change
===
```solidity
File: contracts/SafEth/derivatives/Reth.sol
58:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
59:         maxSlippage = _slippage;
60:     }
```
Similarly in `SfrxEth.sol` and `WstEth.sol`
This method can be called directly on a derivative contract or via a `SafETH.setMaxSlippage(uint, uint)` contract (where the event is emitted).
If called directly, there will be no event.
It's better to move the event to the derivative implementation, where we also have access to the changed value.
The event could look like this:
```solidity
emit SetMaxSlippage(oldMaxSlippage, newMaxSlippage);
```
[L-2] Add a timelock to critical functions
===
It is a good practice to give time for users to react and adjust to critical changes. A timelock provides more guarantees and reduces the level of trust required, thus decreasing risk for users.

Consider adding a timelock to:
```solidity
File: contracts/SafEth/SafEth.sol
165:     function adjustWeight(
166:         uint256 _derivativeIndex,
167:         uint256 _weight
168:     ) external onlyOwner {
```
```solidity
File: contracts/SafEth/SafEth.sol
182:     function addDerivative(
183:         address _contractAddress,
184:         uint256 _weight
185:     ) external onlyOwner {
```
```solidity
File: contracts/SafEth/SafEth.sol
202:     function setMaxSlippage(
203:         uint _derivativeIndex,
204:         uint _slippage
205:     ) external onlyOwner {
```

[N-1] Constants in comparisons should appear on the left side
===

[It's a mechanism to avoid mistakes like this](https://stackoverflow.com/a/370373)

```solidity
File: contracts/SafEth/SafEth.sol
79:         if (totalSupply == 0)
```
```solidity
File: contracts/SafEth/SafEth.sol
87:             if (weight == 0) continue;
```
```solidity
File: contracts/SafEth/SafEth.sol
117:             if (derivativeAmount == 0) continue; // if derivative empty ignore
```
```solidity
File: contracts/SafEth/Sa

// Code block 1 (solidity):
File: contracts/SafEth/derivatives/Reth.sol
58:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
59:         maxSlippage = _slippage;
60:     }

// Code block 2 (solidity):
emit SetMaxSlippage(oldMaxSlippage, newMaxSlippage);

// Code block 3 (solidity):
File: contracts/SafEth/SafEth.sol
165:     function adjustWeight(
166:         uint256 _derivativeIndex,
167:         uint256 _weight
168:     ) external onlyOwner {

// Code block 4 (solidity):
File: contracts/SafEth/SafEth.sol
182:     function addDerivative(
183:         address _contractAddress,
184:         uint256 _weight
185:     ) external onlyOwner {

// Code block 5 (solidity):
File: contracts/SafEth/SafEth.sol
202:     function setMaxSlippage(
203:         uint _derivativeIndex,
204:         uint _slippage
205:     ) external onlyOwner {

// Code block 6 (solidity):
File: contracts/SafEth/SafEth.sol
79:         if (totalSupply == 0)

// Code block 7 (solidity):
File: contracts/SafEth/SafEth.sol
87:             if (weight == 0) continue;

// Code block 8 (solidity):
File: contracts/SafEth/SafEth.sol
117:             if (derivativeAmount == 0) continue; // if derivative empty ignore

File: contracts/SafEth/derivatives/Reth.sol
58:     function setMaxSlippage(uint256 _slippage) external onlyOwner {
59:         maxSlippage = _slippage;
60:     }

emit SetMaxSlippage(oldMaxSlippage, newMaxSlippage);

File: contracts/SafEth/SafEth.sol
165:     function adjustWeight(
166:         uint256 _derivativeIndex,
167:         uint256 _weight
168:     ) external onlyOwner {

File: contracts/SafEth/SafEth.sol
182:     function addDerivative(
183:         address _contractAddress,
184:         uint256 _weight
185:     ) external onlyOwner {

File: contracts/SafEth/SafEth.sol
202:     function setMaxSlippage(
203:         uint _derivativeIndex,
204:         uint _slippage
205:     ) external onlyOwner {

File: contracts/SafEth/SafEth.sol
79:         if (totalSupply == 0)

File: contracts/SafEth/SafEth.sol
87:             if (weight == 0) continue;

File: contracts/SafEth/SafEth.sol
117:             if (derivativeAmount == 0) continue; // if derivative empty ignore


QiuhaoLi G

## We can use a put option for all delegate bonds instead of one for every option

In `bondWithDelegate` we can delecate more than once, and for every delegate we buy the option for the bond. Since all the bonds are in the same transaction, the strike price will be the same. To save gas, we can buy a single option outside the for loop in `bondWithDelegate`. This can save gas in two ways:

1. When bonding with a delegate, we can save gas by only calling _purchaseOptions once.
2. When we settle, instead of calling multiple times on different options, we can do that in one call.

This can save considerable gas if there are many delegates when calling `bondWithDelegate`.

## When delegate.amount = 0, we can delete the slot or reuse it

Currently, we don't reuse the delegate ID (mapping) if a delegate is fully withdrawn by the owner, which brings a linear increase in storage used. This can be optimized to reuse slots or delete the mapping index for gas refund.

## When a bond is withdrawn from RdpxV2Core, we can delete bonds[id] for gas refund

Currently, we don't reuse the bonds[id] or delete them if a user redeem the bond, which brings a linear increase in storage used. This can be optimized to resue the slot or delete the slot for gas refund in redeem().

## Only calculate strike and timeToExpiry if putOptionsRequired

Currently, in calculateBondCost(), we always calculate the strike and timeToExpiry even put options is not required. This can be optimized to only calculated them if putOptionsRequired is set.

## owner field of struct Bond in RdpxDecayingBonds is not used

In contracts/decaying-bonds/RdpxDecayingBonds.sol, the struct Bond has an owner field, but this field is never used. For example, in RdpxV2Core.sol:_transfer, we only use the expiry and amount fields. Deleting this field can reduce storage/gas used.

## RdpxDecayingBonds:mint() has redundant hasRole check

In mint(), we have a onlyRole(MINTER_ROLE) modifier, but then we have a `require(hasRole(MINTE

        collateralToken.safeTransfer(
          addresses.perpetualAtlanticVaultLP,
          (currentFundingRate * (nextFundingPaymentTimestamp() - startTime)) / // @audit-issue GAS this can be calculated only once
            1e18
        );

        IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP)
          .addProceeds(
            (currentFundingRate * (nextFundingPaymentTimestamp() - startTime)) /
              1e18
          );

        emit FundingPaid(
          msg.sender,
          ((currentFundingRate * (nextFundingPaymentTimestamp() - startTime)) /
            1e18),
          latestFundingPaymentPointer
        );


a3yip6 Q

## [L-01] Revert may happen if owner invoke `rebalanceToWeights` with too many tokens in derivatives
The function `rebalanceToWeights` is used by the owner to rebalence the weights of derivatives. The function would withdraw and deposit to each derivatives, which will exchange tokens with `maxSlippage`. Thus, the huge amounts of tokens in derivatives would make exchange fail because of exceeding `maxSlippage`, and the owner would fail to reblance the weights.

```
File: /contracts/SafEth.sol

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

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

```
File: /contracts/WstEth.sol

function withdraw(uint256 _amount) external onlyOwner {
    // burn WStETH and withdraw stETH
    IWStETH(WST_ETH).unwrap(_amount);
    uint256 stEthBal = IERC20(STETH_TOKEN).balanceOf(address(this));
    IERC20(STETH_TOKEN).approve(LIDO_CRV_POOL, stEthBal);
    uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;
    // exchange stETH to ETH
    IStEthEthPool(LIDO_CRV_POOL).exchange(1, 0, stEthBal, minOut);
    // solhint-disable-next-line
    (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
        ""
    );
    require(sent, "Failed to send Ether");
}

```

// Code block 1 (unknown):
File: /contracts/SafEth.sol

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

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

// Code block 2 (unknown):
File: /contracts/WstEth.sol

function withdraw(uint256 _amount) external onlyOwner {
    // burn WStETH and withdraw stETH
    IWStETH(WST_ETH).unwrap(_amount);
    uint256 stEthBal = IERC20(STETH_TOKEN).balanceOf(address(this));
    IERC20(STETH_TOKEN).approve(LIDO_CRV_POOL, stEthBal);
    uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;
    // exchange stETH to ETH
    IStEthEthPool(LIDO_CRV_POOL).exchange(1, 0, stEthBal, minOut);
    // solhint-disable-next-line
    (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
        ""
    );
    require(sent, "Failed to send Ether");
}


favelanky Q

## [L-1] Floating pragma

There are 5 instance of this issue:

```solidity
pragma solidity ^0.8.13;
```

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

https://swcregistry.io/docs/SWC-103

Recommendation: using fixed solidity version.

## [L-2] Division before multiplication

The final result may be truncated due to division before multiplication.

There are 1 instance of this issue:

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

https://github.com/code-423n4/2023-03-neotokyo/blob/main/contracts/staking/NeoTokyoStaker.sol#L1155
https://github.com/code-423n4/2023-03-neotokyo/blob/main/contracts/staking/NeoTokyoStaker.sol#L1388

## [L-3] Unexploitable Re-Entrancy

`SafEth.sol` contract has no Re-Entrancy protection in `unstake` and `stake` functions.

There is 2 instance of this issue:

```solidity
File: SafEth.sol

function stake() external payable {

function unstake(uint256 _safEthAmount) external {
```

https://github.com/code-423n4/2023-03-neotokyo/blob/main/contracts/staking/NeoTokyoStaker.sol#L1737

Recommendation: Use Openzeppelin's or Solmate's Re-Entrancy guard.

## [L-4] Usage of `approve`

There a lot of problems with `approve` function. Best pracite is to use Openzepplin's SafeERC20 library when implement any actions with ERC20.

There are 2 instance of this issue:

```solidity
File: Reth.sol

IERC20(_tokenIn).approve(UNISWAP_ROUTER, _amountIn);

File: SfrxEth.sol

IsFrxEth(FRX_ETH_ADDRESS).approve(
	FRX_ETH_CRV_POOL_ADDRESS,
	frxEthBalance
	);

```

https://git

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

// Code block 2 (unknown):
uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) * (10 ** 18 - maxSlippage)) / 10 ** 18;

// Code block 3 (solidity):
File: SafEth.sol

function stake() external payable {

function unstake(uint256 _safEthAmount) external {

// Code block 4 (solidity):
File: Reth.sol

IERC20(_tokenIn).approve(UNISWAP_ROUTER, _amountIn);

File: SfrxEth.sol

IsFrxEth(FRX_ETH_ADDRESS).approve(
	FRX_ETH_CRV_POOL_ADDRESS,
	frxEthBalance
	);

pragma solidity ^0.8.13;

File: SafEth.sol

function stake() external payable {

function unstake(uint256 _safEthAmount) external {

File: Reth.sol

IERC20(_tokenIn).approve(UNISWAP_ROUTER, _amountIn);

File: SfrxEth.sol

IsFrxEth(FRX_ETH_ADDRESS).approve(
	FRX_ETH_CRV_POOL_ADDRESS,
	frxEthBalance
	);

SafEthStorage.sol#L2, SafEth.sol#L2, SfrxEth.sol#L2, WstEth.sol#L2, NeoTokyoStaker.sol#L1155, NeoTokyoStaker.sol#L1388, NeoTokyoStaker.sol#L1737

SfrxEth.sol, SafEthStorage.sol, NeoTokyoStaker.sol, WstEth.sol, SafEth.sol


JCK G

# Gas Optimizations

| Number | Issue | Instances | Total Gas Saved |
|--------|-------|-----------|-----------------|
|[G-01]| State variables which are not modified within functions should be set as constant or immutable for values set at deployment  | 7 | 70000  |
|[G-02]| create variable  immutable and avoid external call to save gas  | 8 |   |
|[G-03]| Avoid emitting storage values  | 4 |   |
|[G-04]| bytes constants are more eficient than string constans  | 13 |   |
|[G-05]| Use assembly to hash values more efficiently  | 9 |   |
|[G-06]| Use assembly to make more efficient back-to-back calls  | 14 |   |
|[G-07]| Use assembly for loops  | 9 |   |
|[G-08]| Use assembly to grab and cast value in byte array  | 1 |   |
|[G-09]| Don’t cache value if it is only used once  | 2 |   |
|[G-10]| Use calldata instead of memory for immutable arguments | 5 | 1500  |
|[G-11]| Use != 0 instead of > 0 for unsigned integer comparison  | 19 | 57  |
|[G-12]| Don’t initialize variables with default value  | 16 | 96  |
|[G-13]| Use assembly to check for address(0)  | 44 | 264  |
|[G-14]| Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead  | 4 | 40  |
|[G-15]| Use hardcode address instead address(this)  | 40 |   |
|[G-16]| require() or revert() statements that check input arguments should be at the top of the function (Also restructured some “if’s”)  | 4 |   |
|[G-17]| Use assembly to validate msg.sender  | 2 |   |
|[G-18]| Cache external calls outside of loop to avoid re-calling function on each iteration  | 3 |   |
|[G-19]| Return values from external calls can be cached to avoid unnecessary call  | 11 | 39281  |
|[G-20]| Move storage pointer to top of function to avoid offset calculation  | 2 | 180  |
|[G-21]| Cache storage values in memory to minimize SLOADs  | 1 | 110  |
|[G-22]| Public Functions not called by they contract should be declear External  | 29 |   |
|[G-23]| Using storage instead of memory for structs/arrays saves gas  | 3 | 12600  |
|[G-24]| Mult

file:  contracts/perp-vault/PerpetualAtlanticVault.sol

51    string public underlyingSymbol;

60    uint256 public collateralPrecision;

file:  contracts/perp-vault/PerpetualAtlanticVaultLP.sol

49     ERC20 public collateral;

52     string public underlyingSymbol;

55     string public collateralSymbol;

67     address public rdpx;

70     address public rdpxRdpxV2Core;


Giorgio Q

## Title
Consider changing variable to Constant

### Details

In the `./PerpetualAtlanticVault.sol` the `roundingPrecision` variable is initialized to 1e6, however this variable could benefit from being set to Constant for clarity since it is never changed.

#### Affected code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L103-L104

## Title
Useless Manager Role

### Details

`./PerpetualAtlanticVault.sol` the MANAGER_ROLE is set to msg.sender but never used for throughout the contract, for clarity I would suggest to remove it.

#### Affected code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L44-L45

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L127-L128

## Title

Redundant Authorization Check in settle and payFunding Functions

### Details

The settle and payFunding functions have a redundant check using _isEligibleSender(), which serves no purpose as the function is already restricted to RDPXV2CORE_ROLE. This could potentially introduce confusion and make the code less maintainable.

#### Affected code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L315-L324

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L372-L380

## Title

Overcomplicated Calculation for timeToExpiry in Funding Logic

### Details

The calculation for timeToExpiry involves redundant and unnecessarily complicated steps.

timeToExpiry=nextFundingPaymentTimestamp()−(genesis+((latestFundingPaymentPointer−1)×fundingDuration))

The function nextFundingPaymentTimestamp returns:

`nextFundingPaymentTimestamp()=genesis+(latestFundingPaymentPointe

timeToExpiry =
genesis + latestFundingPaymentPointer × fundingDuration − genesis − latestFundingPaymentPointer
× fundingDuration + fundingDuration


zabihullahazadzoi Q

## Low Issues

| |Issue|Instances|
|-|:-|:-:|
| [L-1](#L-1) | Code does not follow the best practice of check-effects-interaction | 1 |
| [L-2](#L-2) | Consider implementing two-step procedure for updating protocol addresses | 13 |
| [L-3](#L-3) | Division by zero not prevented | 19 |
| [L-4](#L-4) | Empty `receive()`/`fallback()` function | 1 |
| [L-5](#L-5) | Loss of precision | 33 |
| [L-6](#L-6) | Missing checks for `address(0x0)` in the constructor | 19 |
| [L-7](#L-7) | Missing checks for address(0x0) when updating address state variables | 51 |
| [L-8](#L-8) | Missing contract-existence checks before yul `call()` | 2 |
| [L-9](#L-9) | Numbers downcast to `address`es may result in collisions | 1 |
| [L-10](#L-10) | `receive()`/`payable fallback()` function does not authorize requests | 2 |
| [L-11](#L-11) | `require()` should be used instead of `assert()` | 1 |
| [L-12](#L-12) | `safeApprove()` is deprecated | 2 |
| [L-13](#L-13) | Some tokens may revert when large transfers are made | 6 |
| [L-14](#L-14) | State variables not capped at reasonable values | 11 |
| [L-15](#L-15) | Subtraction may underflow if multiplication is too large | 6 |

 Total: 168 instances over 15 issues.

## Non Critical Issues

| |Issue|Instances|
|-|:-|:-:|
| [NC-1](#NC-1) | Large or complicated code bases should implement invariant tests | 1 |
| [NC-2](#NC-2) | Adding a return statement when the function defines a named return variable, is redundant | 9 |
| [NC-3](#NC-3) | Missing checks for `address(0)` when assigning values to address state variables | 4 |
| [NC-4](#NC-4) | Array indices should be referenced via enums rather than via numeric literals | 2 |
| [NC-5](#NC-5) | Array is `push()ed` but not `pop()ed` | 1 |
| [NC-6](#NC-6) | Assembly blocks should have extensive comments | 4 |
| [NC-7](#NC-7) | Avoid the use of sensitive terms | 7 |
| [NC-8](#NC-8) | Cast to bytes or bytes32 for clearer semantic meaning | 1 |
| [NC-9](#NC-9) | Complex casting | 2 |
| [NC-10](#NC-10) |

File: packages/contracts/contracts/ActivePool.sol

 /// @audit  syncGlobalAccountingAndGracePeriod() prior to this assignment
 398:         feeRecipientAddress = _feeRecipientAddress;

File: packages/contracts/contracts/ActivePool.sol

 390:     function setFeeRecipientAddress(address _feeRecipientAddress) external requiresAuth {


erictee Q

### [L-01] ```require()``` should be used instead of ```assert()```

#### Impact
Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction’s available gas rather than returning it, as ```require()```/```revert()``` do. ```assert()``` should be avoided even past solidity version 0.8.0 as its [documentation](https://docs.soliditylang.org/en/v0.8.14/control-structures.html#panic-via-assert-and-error-via-require) states that “The assert function creates an error of type Panic(uint256). … Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix”.

#### Findings:
```
contracts/cash/factory/CashKYCSenderFactory.sol:L106    assert(cashKYCSenderProxyAdmin.owner() == guardian);

contracts/cash/factory/CashFactory.sol:L97    assert(cashProxyAdmin.owner() == guardian);

contracts/cash/factory/CashKYCSenderReceiverFactory.sol:L106    assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian);

```

### [L-02] ```decimals()``` not part of ERC20 standard.

#### Impact
```decimals()``` is not part of the official ERC20 standard and might fall for tokens that do not implement it. While in practice it is very unlikely, as usually most of the tokens implement it, this should still be considered as a potential issue.

#### Findings:
```
contracts/lending/OndoPriceOracleV2.sol:L262        uint256(IERC20Like(underlying).decimals()) -

contracts/lending/OndoPriceOracleV2.sol:L263        uint256(AggregatorV3Interface(chainlinkOracle).decimals())));

contracts/cash/CashManager.sol:L181        (IERC20Metadata(_cash).decimals() -

contracts/cash/CashManager.sol:L182          IERC20Metadata(_collateral).decimals());

```

### [L-03] Avoid floating pragmas for non-library contracts.

#### Impact
While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk f

// Code block 1 (unknown):
#### Impact
Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction’s available gas rather than returning it, as

// Code block 2 (unknown):
contracts/cash/factory/CashKYCSenderFactory.sol:L106    assert(cashKYCSenderProxyAdmin.owner() == guardian);

contracts/cash/factory/CashFactory.sol:L97    assert(cashProxyAdmin.owner() == guardian);

contracts/cash/factory/CashKYCSenderReceiverFactory.sol:L106    assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian);

// Code block 3 (unknown):
contracts/lending/OndoPriceOracleV2.sol:L262        uint256(IERC20Like(underlying).decimals()) -

contracts/lending/OndoPriceOracleV2.sol:L263        uint256(AggregatorV3Interface(chainlinkOracle).decimals())));

contracts/cash/CashManager.sol:L181        (IERC20Metadata(_cash).decimals() -

contracts/cash/CashManager.sol:L182          IERC20Metadata(_collateral).decimals());


0xWaitress G

### [G-1] paysInterest in USDA access storage repeatedly in a loop

consider retrieve `_vaultControllers.length()` once and use it from memory in the for loop
```solidity
  modifier paysInterest() {
    for (uint256 _i; _i < _vaultControllers.length();) {
      IVaultController(_vaultControllers.at(_i)).calculateInterest();
      unchecked {
        _i++;
      }
    }
    _;
  }
```

### [G-2] _mint/_burn in USDA repeats computation of `_amount * _gonsPerFragment`.

```solidity
    _gonBalances[_target] += _amount * __gonsPerFragment;
    // total supply is in fragments, and so we add amount
    _totalSupply += _amount;
    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
    _totalGons += _amount * __gonsPerFragment;
```

### [G-3] liquidation gas fee increases as enabledToken increases since external read on Vault increases linearly regardless if the liquidatedUser deposited the asset or not.

liquidateVault would call `_getVaultBorrowingPower` at the end of execution to verify that the user is still not over-collateralized. However this function would loop over all the vault's balance on all enabledTokens, each is an external read on Vault, costing linear increasing gas even though the user may have just 1 asset deposited

```solidity
  function _getVaultBorrowingPower(IVault _vault) private returns (uint192 _borrowPower) {
    // loop over each registed token, adding the indivuduals ltv to the total ltv of the vault
    for (uint192 _i; _i < enabledTokens.length; ++_i) {
      CollateralInfo memory _collateral = tokenAddressCollateralInfo[enabledTokens[_i]];
      // if the ltv is 0, continue
      if (_collateral.ltv == 0) continue;
      // get the address of the token through the array of enabled tokens
      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index
      address _tokenAddress = enabledTokens[

// Code block 1 (solidity):
modifier paysInterest() {
    for (uint256 _i; _i < _vaultControllers.length();) {
      IVaultController(_vaultControllers.at(_i)).calculateInterest();
      unchecked {
        _i++;
      }
    }
    _;
  }

// Code block 2 (solidity):
_gonBalances[_target] += _amount * __gonsPerFragment;
    // total supply is in fragments, and so we add amount
    _totalSupply += _amount;
    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
    _totalGons += _amount * __gonsPerFragment;

modifier paysInterest() {
    for (uint256 _i; _i < _vaultControllers.length();) {
      IVaultController(_vaultControllers.at(_i)).calculateInterest();
      unchecked {
        _i++;
      }
    }
    _;
  }

_gonBalances[_target] += _amount * __gonsPerFragment;
    // total supply is in fragments, and so we add amount
    _totalSupply += _amount;
    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
    _totalGons += _amount * __gonsPerFragment;


bin2chen Q

L-1:
CollateralConfig.updateCollateralRatios() will modify the MCR and CCR, these two parameters will affect whether the user is liquidated, related to the user's collateral.
Although the administrator is a multi-signature wallet, but still need to have an effective time, to give the user sufficient time to choose whether to keep trove or repayment

L-2:
increaseLUSDDebt()/decreaseLUSDDebt()
Event missing emit
```solidity
    function increaseLUSDDebt(address _collateral, uint _amount) external override {
        _requireValidCollateralAddress(_collateral);
        _requireCallerIsBOorTroveM();
        LUSDDebt[_collateral] = LUSDDebt[_collateral].add(_amount);
-       ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
+       emit ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
    }
```

L-3:
TroveManager._removeTroveOwner()
Forgot settings `Troves[_borrower][_collateral].arrayIndex = 0` when clearing Trove Owner

    function _removeTroveOwner(address _borrower, address _collateral, uint TroveOwnersArrayLength) internal {
        Status troveStatus = Troves[_borrower][_collateral].status;
        // It’s set in caller function `_closeTrove`
        assert(troveStatus != Status.nonExistent && troveStatus != Status.active);

        uint128 index = Troves[_borrower][_collateral].arrayIndex;
        uint length = TroveOwnersArrayLength;
        uint idxLast = length.sub(1);

        assert(index <= idxLast);

        address addressToMove = TroveOwners[_collateral][idxLast];

        TroveOwners[_collateral][index] = addressToMove;
        Troves[addressToMove][_collateral].arrayIndex = index;
        emit TroveIndexUpdated(addressToMove, _collateral, index);

+       Troves[_borrower][_collateral].arrayIndex = 0;
        TroveOwners[_collateral].pop();
    }

// Code block 1 (solidity):
function increaseLUSDDebt(address _collateral, uint _amount) external override {
        _requireValidCollateralAddress(_collateral);
        _requireCallerIsBOorTroveM();
        LUSDDebt[_collateral] = LUSDDebt[_collateral].add(_amount);
-       ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
+       emit ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);
    }


adriro Q

# Report

## Summary

### Low Issues

Total of **10 issues**:

|ID|Issue|
|:--:|:---|
| [L-1](#l-1-confusing-semantics-for-bridged-messages-approvals) | Confusing semantics for bridged messages approvals |
| [L-2](#l-2-no-default-threshold-configuration) | No default threshold configuration |
| [L-3](#l-3-validate-number-of-approvers-in-setthresholds) | Validate number of approvers in `setThresholds()` |
| [L-4](#l-4-missing-safe-wrapper-for-erc20-transfer-in-rescuetokens) | Missing safe wrapper for ERC20 transfer in `rescueTokens()` |
| [L-5](#l-5-account-may-get-blacklisted-during-the-bridge-process) | Account may get blacklisted during the bridge process |
| [L-6](#l-6-missing-validation-for-index-parameter-in-overriderange) | Missing validation for index parameter in `overrideRange()` |
| [L-7](#l-7-oracle-assumes-asset-has-18-decimals) | Oracle assumes asset has 18 decimals |
| [L-8](#l-8-wrong-argument-in-transfer-event-of-wrap-function) | Wrong argument in `Transfer` event of `wrap()` function |
| [L-9](#l-9-wrong-argument-in-transfershares-event-of-wrap-function) | Wrong argument in `TransferShares` event of `wrap()` function |
| [L-10](#l-10-contracts-can-be-re-deployed-in-rusdy-factory) | Contracts can be re-deployed in rUSDY factory |

### Non Critical Issues

Total of **2 issues**:

|ID|Issue|
|:--:|:---|
| [NC-1](#nc-1-missing-calls-to-base-initializers-in-rusdy) | Missing calls to base initializers in rUSDY |
| [NC-2](#nc-2-missing-event-to-notify-oracle-has-changed-in-rusdy) | Missing event to notify oracle has changed in rUSDY |

## Low Issues

### [L-1] Confusing semantics for bridged messages approvals

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L111

Bridged messages get an "automatic" approval while the message is being executed in the destination bridge contract, which means that all messages effectively get one approval as soon as they are bridged.

This implies that "real" appr

emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount * BPS_DENOMINATOR));

emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR);