HHK G

### Custom errors instead of requires

```require(pauseStaking == false, "staking is paused");```
```if(pauseStaking == false) revert StakingPaused();```

### In SafETH Stake() can have everything in the same loop.

### Line 88 of SafEth.sol, ```if (weight == 0) continue``` should be one line above

This would save a sload as we wouldn't need to load ```IDerivative derivative = derivatives[i];``` when weight == 0

### All loops can move i++ in unckecked tags

### Using a state variable in *for* condition and reading state variable multiple time should be avoided

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

Declaring a memory d = derivativeCount and then using it in the loop would save gas as each loop will mload instead of sload.
Also because derivativeCount is used 2 times in this function it would save an extra sload to declare d at the beginning.

### rETH ethPerDerivative() does a useless multiplication and division

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

Multiply by 1e18 and then instantly divide by 1e18 is a waste of gas.

### In SafETH addDerivative() ```localTotalWeight``` should be calculated at the beginning

Calculate ```localTotalWeight``` first and then add ```_weight``` to get the new totalWeight, this will save one loop and one sload.

### In SafETH rebalanceToWeights() the check ```ethAmountToRebalance == 0``` is useless

It should be checked before the loop and return or not checked at all since there is low chance this function gets called if there is no eth to rebalance.

// Code block 1 (unknown):
### In SafETH Stake() can have everything in the same loop.

### Line 88 of SafEth.sol,


fs0c Q

## [QA-01] lack of storage `_gap` in upgradable contracts.

For upgradeable contracts, there must be storage gap to “allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments”. Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts.

Refer to the bottom part of this article: [https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable)

### POC

[contracts/cash/token/Cash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/Cash.sol)

[contracts/cash/token/CashKYCSender.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol)

[contracts/cash/token/CashKYCSenderReceiver.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSenderReceiver.sol)

### Recommendation

Add the following to the contracts.

```solidity
uint256[50] private __gap;
```

## [QA-02] `minimumRedeemAmount` can be max without checks

The function `requestRedemption` checks if the value to be redeemed is less than `minimumRedeemAmount` and would revert if it is less than the minimum value. This value can be set using `setRedeemMinimum` without any check for the value.

In case of malicious owner, or the account is compromised, they can set `minimumRedeemAmount` to a very large amount and no user would be able to redeem their collateral.

## [QA-03] pause and unpause roles should be given to the `PAUSER_ADMIN` role.

The pause and unpause functions should be controlled from the `PAUSER_ADMIN` role. In `CashManager` the pause role is correctly controlled by the `PAUSER_ADMIN` but the unpause role is con

function pause() external onlyRole(PAUSER_ADMIN) {
    _pause();
  }

  /**
   * @notice Will unpause minting functionality of this contract
   */
  function unpause() external onlyRole(MANAGER_ADMIN) {
    _unpause();
  }

// Code block 1 (solidity):
uint256[50] private __gap;


ABAIKUNANBAEV G

### Gas Optimizations List

| Number | Optimization Details                                                                                | Instances |
| :----: | :----------------------------------------------------------------------------------------------------------------------- | :-------: |
| [G-01] | Consider not using `nonReentrant` modifier where the possibility of reentrancy is 0.                  |     4    |
| [G-02] | Use hard-coded address instead of `address(this)` to save gas.                  |     6  |
| [G-03] | Consider not using pausability in the contract where `whenNotPaused` modifier is not used.               |  1 |
| [G-04] | Use assembly to implement for-loops.               |  6 |
| [G-05] | Use assembly to implement for address(0).               |  3 |

Total 5 issues.

## [G‑01] Consider not using `nonReentrant` modifier where the possibility of reentrancy is 0.

Although it's considered as the best practice, it's recommended to use it with the functions with callbacks are possible such as ERC1155 or ERC777 functionality, for example:

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L125
```
 function depositAsset(
        address asset,
        uint256 depositAmount
    )
        external
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
    }
```

## [G‑02] Use hard-coded address instead of address(this) to save gas.

In terms of gas, it's better to use pre-calculated contract address instead of `address(this)`:

```
assetLyingInDepositPool = IERC20(asset).balanceOf(address(this));
```

## [G‑03] Consider not using pausability in the contract where `whenNotPaused` modifier is not used.

`LRTOracle.sol` `pause() and `unpause()` functions are initialized but `whenNotPaused` modifier is never used. Therefore, these 2 functions and pausability feature may not be implemented as it just consumes more gas and not actually represent any logic:

https://github.c

// Code block 1 (unknown):
function depositAsset(
        address asset,
        uint256 depositAmount
    )
        external
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
    }

// Code block 2 (unknown):
assetLyingInDepositPool = IERC20(asset).balanceOf(address(this));


Madalad G

# Gas Optimizations Summary
| |Issue|Instances|
|:-:|:-|:-:|
|[G-01]|`abi.encodePacked` is more gas efficient than `abi.encode`|2|
|[G-02]|Functions guaranteed to revert when called by normal users can be marked `payable`|10|
|[G-03]|Use assembly to calculate hashes|2|
|[G-04]|Use `indexed` to save gas|11|
|[G-05]|Refactor modifiers to call a local function|1|
|[G-06]|Use `unchecked` for operations that cannot overflow/underflow|20|
|[G-07]|Change `public` functions to `external`|20|
|[G-08]|`x += y` costs more gas than `x = x + y` for state variables|4|
|[G-09]|Usage of `uint` smaller than 32 bytes (256 bits) incurs overhead|5|
|[G-10]|Use named return values|12|

Total issues: 10

Total instances: 87

# Gas Optimizations
## [G-01] `abi.encodePacked` is more gas efficient than `abi.encode`

`abi.encode` pads all elementary types to 32 bytes, whereas `abi.encodePacked` will only use the minimal required memory to encode the data. See [here](https://docs.soliditylang.org/en/v0.8.11/abi-spec.html?highlight=encodepacked#non-standard-packed-mode) for more info.

Instances: 2
```solidity
File: src/EthRouter.sol

177:                     abi.decode(abi.encode(sells[i].stolenNftProofs), (ReservoirOracle.Message[]))

```
- [src/EthRouter.sol#L177](https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L177)

```solidity
File: src/PrivatePool.sol

675:             leafs[i] = keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i], tokenWeights[i]))));

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

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

If a function modifier such as `onlyOwner` is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.

The extr

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

177:                     abi.decode(abi.encode(sells[i].stolenNftProofs), (ReservoirOracle.Message[]))

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

675:             leafs[i] = keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i], tokenWeights[i]))));

File: src/EthRouter.sol

177:                     abi.decode(abi.encode(sells[i].stolenNftProofs), (ReservoirOracle.Message[]))

File: src/PrivatePool.sol

675:             leafs[i] = keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i], tokenWeights[i]))));


hihen Q

# QA Report

## Summary

### Low Issues

Total **176 instances** over **24 issues**:

|ID|Issue|Instances|
|:--:|:---|:--:|
| [[L‑01]](#l01-variables-shadowing-other-definitions) | Variables shadowing other definitions | 3 |
| [[L‑02]](#l02-missing-zero-address-check-in-constructor) | Missing zero address check in constructor | 50 |
| [[L‑03]](#l03-function-receivepayable-fallback-does-not-authorize-sender) | Function `receive()`/`payable fallback()` does not authorize sender | 2 |
| [[L‑04]](#l04-revert-on-transfer-to-the-zero-address) | Revert on transfer to the zero address | 4 |
| [[L‑05]](#l05-array-is-pushed-but-not-poped) | Array is `push()`ed but not `pop()`ed | 1 |
| [[L‑06]](#l06-state-variables-not-limited-to-reasonable-values) | State variables not limited to reasonable values | 2 |
| [[L‑07]](#l07-tokens-may-be-minted-to-the-zero-address) | Tokens may be minted to the zero address | 1 |
| [[L‑08]](#l08-consider-implementing-two-step-procedure-for-updating-protocol-addresses) | Consider implementing two-step procedure for updating protocol addresses | 1 |
| [[L‑09]](#l09-downcasting-other-types-to-an-address-can-cause-collisions) | Downcasting other types to an address can cause collisions | 1 |
| [[L‑10]](#l10-vulnerable-versions-of-packages-are-being-used) | Vulnerable versions of packages are being used | 4 |
| [[L‑11]](#l11-constructor--initialization-function-lacks-parameter-validation) | Constructor / initialization function lacks parameter validation | 1 |
| [[L‑12]](#l12-contracts-are-vulnerable-to-fee-on-transfer-accounting-related-issues) | Contracts are vulnerable to fee-on-transfer accounting-related issues | 8 |
| [[L‑13]](#l13-functions-calling-contractsaddresses-with-transfer-hooks-should-be-protected-by-reentrancy-guard) | Functions calling contracts/addresses with transfer hooks should be protected by reentrancy guard | 6 |
| [[L‑14]](#l14-c

/// @audit Shadows `function version()`
680:         bytes32 version

/// @audit Shadows `function name()`
237:         bytes32 name,

/// @audit Shadows `function version()`
238:         bytes32 version


SandNallani Q

### [QA-1] Inconsistent use of the `whenNotPaused` modifier in the Oracle and Deposit pool contracts

The Oracle contract inherits and implements functions to pause and unpause. However, the `whenNotPaused` modifier is not applied to any of the functions in this contract.

In the node delegator contract, when paused, cannot transfer assets back to the deposit pool.
However, the deposit pool contract is able to transfer funds to the node delegator when paused.

Consider removing the capability to pause/unpause the Oracle contract as it's not being used in the current version of the contract.
Consider preventing the transfer of assets from the deposit pool to node delegator when paused, for better consistency.

### [QA-2] The node delegator contract doesn’t track the number of shares created by the Eigen stragety.

The node delegator contract's responsible for depositing the user's deposit pool funds into various
Eigenlayer strategies.
Shown below is the function that performs this task.

```
NodeDelegator::depositAssetIntoStrategy()
//NodeDelegator.sol
function depositAssetIntoStrategy(address asset){
...
emit AssetDepositIntoStrategy(asset, strategy, balance);

IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);
...
}
```
However, the number of shares minted by the Eigenlayer strategy aren't tracked or emitted as an argument in the event `AssetDepositIntoStrategy`.
Consider checking the number of shares minted to ensuring that there's no adverse price impact and emitting it in the event to allow for off-chain accounting.

### [QA-3] The deposit pool's `depositAsset()` function could return the number of minted tokens

The main entry point in the Kelp protocol is the `LRTDepositPool::depositAsset()`. Which allows users to deposit approved assets into the pool to receive an amount of `rsETH` tokens.
The minted `rsETH` tokens represent the user's underlying asset contribution of rETH, cbET

    function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
        returns (uint256 rsethAmountToMint) {
        ...
        rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
      }

// Code block 1 (unknown):
NodeDelegator::depositAssetIntoStrategy()
//NodeDelegator.sol
function depositAssetIntoStrategy(address asset){
...
emit AssetDepositIntoStrategy(asset, strategy, balance);

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


CodeFoxInc Q

# Low-risk and Non-critical issues

## ****Codebase Impressions & Summary****

The protocol is built on several other projects and the the enhanced way created by the team to stake Ether is quite interesting.

The protocol’s contracts are well structured and quite simplified and the Natspec documentation is quite well done.  These efforts made by the team made the codebase easier to be understood by the auditors. The overall quality of the documentation is good but an independent documentation rather than only the `README` file is preferred if possible.

However, there are some minor modifications can be done to improve the healthiness of the codebase.

I proposed findings which focused on several issues, e.g. Re-entrancy attack possibility, Upgradeability pattern, solidity version, trusted owner issue, code style and some issues for following the best practice of solidity, etc. If they are to be addressed, the code quality are supposed to be improved.

## L-01 Make sure the `unstake()` function won’t get Re-entrancy attacked

### Vulnerability Details

[https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L108-L129](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L108-L129)

The function `unstake()` send ether to the external accounts. It has a risk of being used as re-entrancy attack. Although the function follows the CEI rule, it is recommended that import the ReentrancyGuard contract and stay safe.

### Impact

The `unstake` function sends Ether to external accounts and has a risk of being Re-entrancy attacked.

### Recommendation

Use OpenZeppelin’s [Re-entrancy modifier library](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/security/ReentrancyGuardUpgradeable.sol) to prevent such attack from happening.

In `SafEth.sol` do this:

```diff
+ import "@openzeppelin/contract

## L-02 The `addDerivative()`, `adjustWeight()` and `rebalanceToWeights()` functions can be used to steal funds

### **Vulnerability details**

There are quite a lot of `onlyOwner` functions and specifically speaking, the `adjustWeight`, `addDerivative` and `rebalanceToWeights` function can be used to drain funds. So there is a centralized risk.

The owner can add a new malicious derivative by calling `addDerivative` and then adjust the weight to e.g. 0/0/0/100. After that if he calls `rebalanceToWeights` function then all the funds can be drained.

[https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L165-L175](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L165-L175)

[https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182-L195](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182-L195)

[https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L138-L155](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L138-L155)

### Impact

So there is a centralized risk. Owner can always drain funds. Owner needs to be trusted.

### Recommendation

Mitigate the risk by at least using a multi-sig wallet for the owner. Plus, the team can create a plan to renounce the ownership in the future if it is possible.

## N-01 Use a struct array structure instead of `uint256 derivativeCount`, mapping `derivatives` and mapping `weights`

[https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEthStorage.sol#L18](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/c

SafEth.sol#L108-L129, ReentrancyGuardUpgradeable.sol, SafEth.sol#L165-L175, SafEth.sol#L182-L195, SafEth.sol#L138-L155, SafEthStorage.sol#L18


Polaris_tow G

##  USING FIXED BYTES IS CHEAPER THAN USING STRING
As a rule of thumb, use bytes for arbitrary-length raw byte data and string for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.
https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/Funding.sol#L61
`        string memory errorMessage = "Governor: call reverted without message";`

## CAN MAKE THE VARIABLE OUTSIDE THE LOOP TO SAVE GAS
Make it outside and only use it inside.
https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/Funding.sol#L112-L141
https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/StandardFunding.sol#L582-L596
```
        for (uint256 i = 0; i < targets_.length;) {

            // check targets and values params are valid
            if (targets_[i] != ajnaTokenAddress || values_[i] != 0) revert InvalidProposal();

            // check calldata function selector is transfer()
            bytes memory selDataWithSig = calldatas_[i];

            bytes4 selector;
            //slither-disable-next-line assembly
            assembly {
                selector := mload(add(selDataWithSig, 0x20))
            }
            if (selector != bytes4(0xa9059cbb)) revert InvalidProposal();

            // https://github.com/ethereum/solidity/issues/9439
            // retrieve tokensRequested from incoming calldata, accounting for selector and recipient address
            uint256 tokensRequested;
            bytes memory tokenDataWithSig = calldatas_[i];
            //slither-disable-next-line assembly
            assembly {
                tokensRequested := mload(add(tokenDataWithSig, 68))
            }

            // update tokens requested for additional calldata
            tokensRequested_ += SafeCast.t

        for (uint256 i = 0; i < targets_.length;) {

            // check targets and values params are valid
            if (targets_[i] != ajnaTokenAddress || values_[i] != 0) revert InvalidProposal();

            // check calldata function selector is transfer()
            bytes memory selDataWithSig = calldatas_[i];

            bytes4 selector;
            //slither-disable-next-line assembly
            assembly {
                selector := mload(add(selDataWithSig, 0x20))
            }
            if (selector != bytes4(0xa9059cbb)) revert InvalidProposal();

            // https://github.com/ethereum/solidity/issues/9439
            // retrieve tokensRequested from incoming calldata, accounting for selector and recipient address
            uint256 tokensRequested;
            bytes memory tokenDataWithSig = calldatas_[i];
            //slither-disable-next-line assembly
            assembly {
                tokensRequested := mload(add(tokenDataWithSig, 68))
            }

            // update tokens requested for additional calldata
            tokensRequested_ += SafeCast.toUint128(tokensRequested);

            unchecked { ++i; }
        }
    }


Buck Q

Ethos-Core
------------
In the AccessControl, there are cases where the comments do not reflect exactly what the code is doing. Not major issues, but not totally accurate.
-------------
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol
contracts/TroveManager.sol
line 1077 - in function applyPendingRewards
the require is less restrictive than a require further down the call stack
```
        _requireCallerIsBorrowerOperationsOrRedemptionHelper();
        // _updateTroveRewardSnapshots calls _requireCallerIsBorrowerOperations() internally, so allowing RedemptionHelper is useless
        // _updateTroveRewardSnapshots is called by _applyPendingRewards(...)
```
so code should read
```
_requireCallerIsBorrowerOperations();
```
-----------------
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol
contracts/ActivePool.sol
line 334 - should include redemptionHelper
```
            "ActivePool: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool nor redemptionHelper");
```

-----------------
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/test/AccessControlTest.js
test/AccessControlTest.js
Access control tests text does not match access controls in solidity code (in some cases)
line 83 - add redemptionHelper
```
it("applyPendingRewards(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {
```
line 107 - add redemptionHelper
```
it("removeStake(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {
```
line 131 - add redemptionHelper
```
it("closeTrove(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {
```
line 278 - add redemptionHelper
```
it("sendCollateral(): reverts when called by an account that is not BO nor TroveM nor SP nor redemptionHelper", async () => {
```
line 285 - add redemptionHelper
```
asse

// Code block 1 (unknown):
_requireCallerIsBorrowerOperationsOrRedemptionHelper();
        // _updateTroveRewardSnapshots calls _requireCallerIsBorrowerOperations() internally, so allowing RedemptionHelper is useless
        // _updateTroveRewardSnapshots is called by _applyPendingRewards(...)

// Code block 2 (unknown):
_requireCallerIsBorrowerOperations();

// Code block 3 (unknown):
"ActivePool: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool nor redemptionHelper");

// Code block 4 (unknown):
it("applyPendingRewards(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {

// Code block 5 (unknown):
it("removeStake(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {

// Code block 6 (unknown):
it("closeTrove(): reverts when called by an account that is not BorrowerOperations nor RedemptionHelper", async () => {

// Code block 7 (unknown):
it("sendCollateral(): reverts when called by an account that is not BO nor TroveM nor SP nor redemptionHelper", async () => {


koxuan G

# Report

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-1](#GAS-1) | Using bools for storage incurs overhead | 10 |
| [GAS-2](#GAS-2) | Cache array length outside of loop | 4 |
| [GAS-3](#GAS-3) | State variables should be cached in stack variables rather than re-reading them from storage | 6 |
| [GAS-4](#GAS-4) | Use calldata instead of memory for function arguments that do not get mutated | 20 |
| [GAS-5](#GAS-5) | For Operations that will not overflow, you could use unchecked | 770 |
| [GAS-6](#GAS-6) | Use Custom Errors | 186 |
| [GAS-7](#GAS-7) | Don't initialize variables with default value | 7 |
| [GAS-8](#GAS-8) | Long revert strings | 54 |
| [GAS-9](#GAS-9) | Functions guaranteed to revert when called by normal users can be marked `payable` | 37 |
| [GAS-10](#GAS-10) | `++i` costs less gas than `i++`, especially when it's used in `for`-loops (`--i`/`i--` too) | 4 |
| [GAS-11](#GAS-11) | Using `private` rather than `public` for constants, saves gas | 11 |
| [GAS-12](#GAS-12) | Splitting require() statements that use && saves gas | 6 |
| [GAS-13](#GAS-13) | Use != 0 instead of > 0 for unsigned integer comparison | 61 |
| [GAS-14](#GAS-14) | `internal` functions not called by the contract should be removed | 27 |
### [GAS-1] Using bools for storage incurs overhead
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past. See [source](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27).

*Instances (10)*:
```solidity
File: OFT/OFTCoreV2.sol

22:     bool public useCustomAdapterParams;

23:     mapping(uint16 => mapping(bytes => mapping(uint64 => bool))) public creditedPackets;

```

```solidity
File: lybra/configuration/LybraConfigurator.sol

38:     mapping(address => bool) public mintVault;

40:     map

File: lybra/configuration/LybraConfigurator.sol

38:     mapping(address => bool) public mintVault;

40:     mapping(address => bool) public vaultMintPaused;

41:     mapping(address => bool) public vaultBurnPaused;

46:     mapping(address => bool) redemptionProvider;

47:     mapping(address => bool) public tokenMiner;

61:     bool public premiumTradingEnabled;

// Code block 1 (solidity):
File: OFT/OFTCoreV2.sol

22:     bool public useCustomAdapterParams;

23:     mapping(uint16 => mapping(bytes => mapping(uint64 => bool))) public creditedPackets;


dontonka G

Some reported issues here are just added as a comment, but most have been applied the suggested corrections already, you can see all the final diffs. Take note that the code compile and all test pass with those too.

```diff
diff --git a/Ethos-Core/contracts/ActivePool.sol b/Ethos-Core/contracts/ActivePool.sol
index 753fcd0..771da52 100644
--- a/Ethos-Core/contracts/ActivePool.sol
+++ b/Ethos-Core/contracts/ActivePool.sol
@@ -176,12 +176,13 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
         emit ActivePoolCollateralBalanceUpdated(_collateral, collAmount[_collateral]);
         emit CollateralSent(_collateral, _account, _amount);

+        //@audit (GAS): save using the parameter _account instead of the state variable defaultPoolAddress and collSurplusPoolAddress
         if (_account == defaultPoolAddress) {
-            IERC20(_collateral).safeIncreaseAllowance(defaultPoolAddress, _amount);
-            IDefaultPool(defaultPoolAddress).pullCollateralFromActivePool(_collateral, _amount);
+            IERC20(_collateral).safeIncreaseAllowance(_account, _amount);
+            IDefaultPool(_account).pullCollateralFromActivePool(_collateral, _amount);
         } else if (_account == collSurplusPoolAddress) {
-            IERC20(_collateral).safeIncreaseAllowance(collSurplusPoolAddress, _amount);
-            ICollSurplusPool(collSurplusPoolAddress).pullCollateralFromActivePool(_collateral, _amount);
+            IERC20(_collateral).safeIncreaseAllowance(_account, _amount);
+            ICollSurplusPool(_account).pullCollateralFromActivePool(_collateral, _amount);
         } else {
             IERC20(_collateral).safeTransfer(_account, _amount);
         }
@@ -274,6 +275,7 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
         }

         // + means deposit, - means withdraw
+        //@audit (GAS) can save some gas here with yieldGenerator[_collateral] being local var
         vars.netAssetMovement = int256(vars.toDeposit) - i

```diff
diff --git a/Ethos-Vault/contracts/ReaperVaultV2.sol b/Ethos-Vault/contracts/ReaperVaultV2.sol
index 2be0b81..a60c162 100644
--- a/Ethos-Vault/contracts/ReaperVaultV2.sol
+++ b/Ethos-Vault/contracts/ReaperVaultV2.sol
@@ -266,7 +266,7 @@ contract ReaperVaultV2 is ReaperAccessControl, ERC20, IERC4626Events, AccessCont
         delete withdrawalQueue;
         for (uint256 i = 0; i < queueLength; i = i.uncheckedInc()) {
             address strategy = _withdrawalQueue[i];
-            StrategyParams storage params = strategies[strategy];
+            StrategyParams storage params = strategies[strategy]; // @audit (GAS) use memory instead of storage, only reading
             require(params.activation != 0, "Invalid strategy address");
             withdrawalQueue.push(strategy);
         }
@@ -610,6 +610,7 @@ contract ReaperVaultV2 is ReaperAccessControl, ERC20, IERC4626Events, AccessCont
      * goes back into Normal Operation.
      */
     function setEmergencyShutdown(bool _active) external {
+        //@audit (GAS) add a require to prevent waste : require(_active != emergencyShutdown, "Already in this state");
         if (_active) {
             _atLeastRole(GUARDIAN);
         } else {

```diff
diff --git a/Ethos-Core/contracts/StabilityPool.sol b/Ethos-Core/contracts/StabilityPool.sol
index a33d8c3..5f8fac6 100644
--- a/Ethos-Core/contracts/StabilityPool.sol
+++ b/Ethos-Core/contracts/StabilityPool.sol
@@ -807,13 +807,14 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool {

     function _updateDepositAndSnapshots(address _depositor, uint _newValue) internal {
         deposits[_depositor].initialValue = _newValue;
+        Snapshots storage depositSnapshotCached = depositSnapshots[_depositor]; //@audit (GAS) added a cache snapshot

         address[] memory collaterals = collateralConfig.getAllowedCollaterals();
         uint[] memory amounts = new uint[](collaterals.length);

         if (_newValue == 0) {
             for (uint i = 0; i < collaterals.length; i++) {
-                delete depositSnapshots[_depositor].S[collaterals[i]];
+                delete depositSnapshotCached.S[collaterals[i]];
             }
             delete depositSnapshots[_depositor];
             emit DepositSnapshotUpdated(_depositor, 0, collaterals, amounts, 0);
@@ -827,16 +828,16 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool {
         uint currentG = epochToScaleToG[currentEpochCached][currentScaleCached];

         // Record new snapshots of the latest running product P, and sum G, for the depositor
-        depositSnapshots[_depositor].P = currentP;
-        depositSnapshots[_depositor].G = currentG;
-        depositSnapshots[_depositor].scale = currentScaleCached;
-        depositSnapshots[_depositor].epoch = currentEpochCached;
+        depositSnapshotCached.P = currentP;
+        depositSnapshotCached.G = currentG;
+        depositSnapshotCached.scale = currentScaleCached;
+        depositSnapshotCached.epoch = currentEpochCached;

         // Record new snapshots of the latest running sum S for all collaterals for the depositor
         for (uint i = 0; i < collaterals.length; i++) {


tsvetanovv G

## [GAS-01] STATE VARIABLES ONLY SET IN THE CONSTRUCTOR SHOULD BE DECLARED IMMUTABLE.
It allows setting contract-level variables at construction time which gets stored in code rather than storage.
```
JumpRateModelV2.sol
24: address public owner;

cErc20ModifiedDelegator.sol
218: ddress payable public admin;
```
***

## [GAS-02] ABI.ENCODE() IS LESS EFFICIENT THAN ABI.ENCODEPACKED()
Use abi.encodePacked() where possible to save gas.

```
KYCRegistry.sol:
93: bytes32 structHash = keccak256(
      abi.encode(_APPROVAL_TYPEHASH, kycRequirementGroup, user, deadline)
```
***

## [GAS-03] Require() or revert() statements that check input arguments should be at the top of the function
```
CErc20.sol:
function sweepToken(EIP20NonStandardInterface token) external override {
    require(
      msg.sender == admin,
      "cErc20::sweepToken: only admin can sweep tokens"
    );
    require(
      address(token) != underlying,
      "cErc20::sweepToken: can not sweep underlying token"
    );
    uint256 balance = token.balanceOf(address(this));
    token.transfer(admin, balance);
  }
```
***

## [GAS-04] USE NAMED RETURNS FOR LOCAL VARIABLES WHERE IT IS POSSIBLE
```
OndoPriceOracleV2.sol:
92: function getUnderlyingPrice(
    address fToken
  ) external view override returns (uint256) {
    uint256 price;

CTokenCash.sol:
771: ) internal returns (uint) {
```
***

## [GAS-05]  UNCHECKING ARITHMETICS OPERATIONS THAT CAN’T UNDERFLOW/OVERFLOW
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block.

```
CashFactory.sol:
127: for (uint256 i = 0; i < exCallData.length; ++i) {

CashKYCSenderFactory.sol:
137: for (uint256 i = 0; i < exCallData.length; ++i) {

CashKYCSenderReceiverFactory.sol:
137: for (uint256 i = 0; i < exCallData.length; ++i) {

KYCRegistry.sol:
163: for (uint

// Code block 1 (unknown):
JumpRateModelV2.sol
24: address public owner;

cErc20ModifiedDelegator.sol
218: ddress payable public admin;

// Code block 2 (unknown):
KYCRegistry.sol:
93: bytes32 structHash = keccak256(
      abi.encode(_APPROVAL_TYPEHASH, kycRequirementGroup, user, deadline)

// Code block 3 (unknown):
CErc20.sol:
function sweepToken(EIP20NonStandardInterface token) external override {
    require(
      msg.sender == admin,
      "cErc20::sweepToken: only admin can sweep tokens"
    );
    require(
      address(token) != underlying,
      "cErc20::sweepToken: can not sweep underlying token"
    );
    uint256 balance = token.balanceOf(address(this));
    token.transfer(admin, balance);
  }

// Code block 4 (unknown):
OndoPriceOracleV2.sol:
92: function getUnderlyingPrice(
    address fToken
  ) external view override returns (uint256) {
    uint256 price;

CTokenCash.sol:
771: ) internal returns (uint) {


Jorgect Q

# LOW REPORT

## [L-01] The slippageTolerance is no used in UniV2LiquidityAmo.sol contract

The slippageTolerance variable is declared in the contract, even have a function to modify it but is never used in the uniswap releted function, the related uniswap functions has his own input value where the admin set the slippage :

```
 uint256 public slippageTolerance = 5e5; // 0.5%

...
file: contracts/amo/UniV2LiquidityAmo.sol
function setSlippageTolerance(
    uint256 _slippageTolerance
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(
      _slippageTolerance > 0,
      "reLPContract: slippage tolerance must be greater than 0"
    );
    slippageTolerance = _slippageTolerance;
  }
```
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV2LiquidityAmo.sol#L109C3-L117C4

### Recomendation

Use the slippageTolerance in case that admin set 0 slippage input in the uniswap releted function.

## [L-02] Set a deadline not too far from the present in addLiquidity function in UniV3LiquidityAmo.sol contract

Check the addLiquidity function:
```
 function addLiquidity(
    AddLiquidityParams memory params
  ) public onlyRole(DEFAULT_ADMIN_ROLE) {
      ...
      INonfungiblePositionManager.MintParams
      memory mintParams = INonfungiblePositionManager.MintParams(
        params._tokenA,
        params._tokenB,
        params._fee,
        params._tickLower,
        params._tickUpper,
        params._amount0Desired,
        params._amount1Desired,
        params._amount0Min,
        params._amount1Min,
        address(this),
        type(uint256).max
      );
...
}
```
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV3LiquidityAmo.sol#L155C1-L211C4

The contract is setting a deadline so far from the future so the transaction can be included in a worst moment

### Recomendation

Use a deadline no too far from the future like the other uniswapv3 releted functi

// Code block 1 (unknown):
uint256 public slippageTolerance = 5e5; // 0.5%

...
file: contracts/amo/UniV2LiquidityAmo.sol
function setSlippageTolerance(
    uint256 _slippageTolerance
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(
      _slippageTolerance > 0,
      "reLPContract: slippage tolerance must be greater than 0"
    );
    slippageTolerance = _slippageTolerance;
  }

// Code block 2 (unknown):
function addLiquidity(
    AddLiquidityParams memory params
  ) public onlyRole(DEFAULT_ADMIN_ROLE) {
      ...
      INonfungiblePositionManager.MintParams
      memory mintParams = INonfungiblePositionManager.MintParams(
        params._tokenA,
        params._tokenB,
        params._fee,
        params._tickLower,
        params._tickUpper,
        params._amount0Desired,
        params._amount1Desired,
        params._amount0Min,
        params._amount1Min,
        address(this),
        type(uint256).max
      );
...
}


codeslide Q

##  Summary

###  Low Risk Issues
There are 80 instances over 14 issues.
|      ID       | Description                                                                             | Count |
| :-----------: | :-------------------------------------------------------------------------------------- | ----: |
| [L-01](#l-01) | Burn functions should be protected with access control                                  |     2 |
| [L-02](#l-02) | Casting of `block.timestamp` can reduce the lifespan of a contract                      |     1 |
| [L-03](#l-03) | Empty `receive()`/`fallback()` function                                                 |     1 |
| [L-04](#l-04) | Execution at deadlines should be allowed                                                |     7 |
| [L-05](#l-05) | Functions calling contracts/addresses with transfer hooks are missing reentrancy guards |     5 |
| [L-06](#l-06) | Loss of precision                                                                       |    19 |
| [L-07](#l-07) | Missing checks for `address(0x0)` when updating `address` state variables               |     6 |
| [L-08](#l-08) | Missing zero address check in constructor/initializer                                   |    17 |
| [L-09](#l-09) | Numbers downcast to `address`es may result in collisions                                |     5 |
| [L-10](#l-10) | `payable` function does not transfer Eth                                                |     3 |
| [L-11](#l-11) | Potential division by zero                                                              |     9 |
| [L-12](#l-12) | `receive()`/`payable fallback()` function does not authorize requests                   |     1 |
| [L-13](#l-13) | Unsafe downcast                                                                         |     3 |
| [L-14](#l-14) | Use of `assert()`                                                                       |     1 |

### <a id="sum

File: packages/contracts/contracts/EBTCToken.sol

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

104:     function burn(uint256 _amount) external {

File: packages/contracts/contracts/CdpManagerStorage.sol

53:             lastGracePeriodStartTimestamp = uint128(block.timestamp);


0xbepresent Q

1 - There is a discrepancy between the documentation and the code in the ```PrivatePool.change()``` function
==

The documentaion about the [change()](https://docs.caviar.sh/technical-reference/custom-pools/smart-contract-api/privatepool#change) function says: *The sum of the caller's NFT weights must be less than or equal to the sum of the output pool NFTs weights.* Additionally in the [next code comment](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L376) it says: *The sum of the caller's NFT weights must be less than or equal to the sum of the output pool NFTs weights.*

The problem is that the code is totally the opposite, the code will revert [if the sum of the caller's NFT weights is less.](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L413)

```solidity
if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight();
```

Fix the discrepancy between the code and the documentation.

2 - The flashLoan fee token should use the ```flashFeeToken()``` to get the fee token
==

The flash loan fee token used in the [next line](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L651):

```solidity
if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);
```

It uses the ```baseToken``` for the fee token but it should use the [flashFeeToken()](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L755) function instead to get the flash fee token.

3 - Add a function that helps to update the ```changeFee```
==

The [changeFee](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L179) is initialized in the beginning and it can not be updated in the future by the private pool owner.

Add a ```updateChangeFee()``` function that help

// Code block 1 (solidity):
if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight();

// Code block 2 (solidity):
if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);

// Code block 3 (unknown):
==

The [changeFee](https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L179) is initialized in the beginning and it can not be updated in the future by the private pool owner.

Add a

if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight();

if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee);

PrivatePool.sol#L376, PrivatePool.sol#L413, PrivatePool.sol#L651, PrivatePool.sol#L755, PrivatePool.sol#L179


Awesome Q

# 1. Improve the Readability by following modularity principles

To make your Solidity code more modern and readable, it is recommended to update your import statements to only include the specific contracts or objects that you need.

This practice, known as modular programming, helps to keep the code cleaner and more organized.

To avoid unnecessarily polluting the source code with unused contracts or objects, it is recommended to use specific imports with curly braces. An example of this syntax is shown below:

```solidity
import {contract1, contract2} from "filename.sol";
```

By using specific imports in this way, you can ensure that your code follows the principles of modularity and is as clear and maintainable as possible.

Affected line of code:

- [CashKYCSender.sol#L18-L19](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol#L18-L19)

# 2. inadequate NatSpec

Contracts can use the Ethereum Natural Language Specification Format (NatSpec) to provide detailed documentation for functions, return variables, and other elements of the contract. This is done using a special type of comment within the contract code.

You can find more information about NatSpec and its usage in Solidity contracts at the following link:
- [NatSpec Format](https://docs.soliditylang.org/en/v0.8.16/natspec-format.html)

This is present in 4 file:
- [Proxy.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/Proxy.sol)
- [Cash.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/Cash.sol)
- [CashKYCSender.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol)
- [CashKYCSenderReceiver.sol](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSenderReceiver.sol)

# 3. Use the `delete` operator to clear variables, rather than assigning a value of `false`.

To clear variables, consider using the `delete` operator rather than assigning

Line 181:    delete kycState[kycRequirementGroup][addresses[i]];

// Code block 1 (solidity):
import {contract1, contract2} from "filename.sol";

CashKYCSender.sol#L18-L19, Proxy.sol, Cash.sol, CashKYCSender.sol, CashKYCSenderReceiver.sol

CashKYCSender.sol, Cash.sol, Proxy.sol, CashKYCSenderReceiver.sol


kinggvd Q

```
function maxApproveToEigenStrategyManager(address asset)
function depositAssetIntoStrategy(address asset)
function transferBackToLRTDepositPool(
      address asset,
      uint256 amount
)
```

Alle the functions above rely on lrtConfig.getContract(CONSTANT) which may return a 0 address if it has not yet been set. This might cause approve/depositIntoStrategy/transfer to this 0 address.

E.g.
```
IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);

IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

if (!IERC20(asset).transfer(lrtDepositPool, amount)) {
     revert TokenTransferFailed();
}
```

Additional, checks and reverting with a CustomError could reduce human-produced errors in this case.

// Code block 1 (unknown):
function maxApproveToEigenStrategyManager(address asset)
function depositAssetIntoStrategy(address asset)
function transferBackToLRTDepositPool(
      address asset,
      uint256 amount
)

// Code block 2 (unknown):
IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);

IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

if (!IERC20(asset).transfer(lrtDepositPool, amount)) {
     revert TokenTransferFailed();
}


shark Q

## 1. `Math.sol` function not being used

In `Math.sol`, the function [`max()`](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L19-L21) is found to be re-implemented in a contract.

The following contract is re-implementing `max()` from `Math.sol` rather than just importing it.

File: `SmartAccount.sol` [Line 181-183](https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L181-L183)

## 2. Use `uint256` instead of `uint`

`uint` is just a shorthand for `uint256`. To be more explicit, consider replacing any instances of `uint` with `uint256` where possible.

- File: `SmartAccount.sol` [Line 449](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L449)
- File: `SmartAccount.sol` [Line 468](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L468)
- File: `SmartAccountFactory.sol` [Line 72](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L72)

## 3. Use delete to clear variables instead of zero assignment

Using the [`delete`](https://docs.soliditylang.org/en/v0.8.17/types.html#delete) keyword more clearly states your intention.

For example:

File: `StakeManager.sol` [Line 86](https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L86)

The instance above can be changed to:

```solidity
        delete info.staked;
```

## 4. Empty block statements

Empty block statements can cause confusion when reading code. It is recommended to write a clear comment for empty block statements or emit an event.

- File: `SmartAccount.sol` [Line 550](https://github.com/code-423n4/2023-01-bicon

449:    function transfer(address payable dest, uint amount) external nonReentrant onlyOwner {
450:        require(dest != address(0), "this action will burn your funds");
451:        (bool success,) = dest.call{value:amount}("");
452:        require(success,"transfer failed");
453:    }

// Code block 1 (solidity):
delete info.staked;

Math.sol#L19-L21, SmartAccount.sol#L181-L183, SmartAccount.sol#L449, SmartAccount.sol#L468, SmartAccountFactory.sol#L72, StakeManager.sol#L86

Math.sol, SmartAccount.sol, StakeManager.sol, SmartAccountFactory.sol


LinKenji Q

GAS 01 Use caching to avoid repetitive storage reads
## Impact
Several contracts like [Pools.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol) and [Proposals.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol) could cache frequently used parameters and addresses in memory rather than reading storage every time. For example, caching the `daoAddress` or _token addresses_.

Each storage read incurs gas costs, so avoiding repetitive reads yields savings that scale with network utilization.
The issue comes up in core contracts [Pools.sol#addLiquidity](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/pools/Pools.sol#L140-L165)

```solidity
function addLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 maxAmountA, uint256 maxAmountB, uint256 minLiquidityReceived, uint256 totalLiquidity ) external nonReentrant returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity)
{
require( msg.sender == address(collateralAndLiquidity), "Pools.addLiquidity is only callable from the CollateralAndLiquidity contract" );
require( exchangeIsLive, "The exchange is not yet live" );
require( address(tokenA) != address(tokenB), "Cannot add liquidity for duplicate tokens" );

require( maxAmountA > PoolUtils.DUST, "The amount of tokenA to add is too small" );
require( maxAmountB > PoolUtils.DUST, "The amount of tokenB to add is too small" );

(bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB);

// Flip the users arguments if they are not in reserve token order with address(tokenA) < address(tokenB)
if ( flipped )
        (addedAmountB, addedAmountA, addedLiquidity) = _addLiquidity( poolID, maxAmountB, maxAmountA, totalLiquidity );
else
        (addedAmountA, addedAmountB, addedLiquidity) = _addLiquidity( poolID, maxAmountA, maxAmountB, totalLiquidity );

// Make sure the minimum liquidity has been added
require( addedLiquidity >= minLiquidityReceiv

function addLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 maxAmountA, uint256 maxAmountB, uint256 minLiquidityReceived, uint256 totalLiquidity ) external nonReentrant returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity)
{
require( msg.sender == address(collateralAndLiquidity), "Pools.addLiquidity is only callable from the CollateralAndLiquidity contract" );
require( exchangeIsLive, "The exchange is not yet live" );
require( address(tokenA) != address(tokenB), "Cannot add liquidity for duplicate tokens" );

require( maxAmountA > PoolUtils.DUST, "The amount of tokenA to add is too small" );
require( maxAmountB > PoolUtils.DUST, "The amount of tokenB to add is too small" );

(bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB);

// Flip the users arguments if they are not in reserve token order with address(tokenA) < address(tokenB)
if ( flipped )
        (addedAmountB, addedAmountA, addedLiquidity) = _addLiquidity( poolID, maxAmountB, maxAmountA, totalLiquidity );
else
        (addedAmountA, addedAmountB, addedLiquidity) = _addLiquidity( poolID, maxAmountA, maxAmountB, totalLiquidity );

// Make sure the minimum liquidity has been added
require( addedLiquidity >= minLiquidityReceived, "Too little liquidity received" );

// Transfer the tokens from the sender - only tokens without fees should be whitelisted on the DEX
tokenA.safeTransferFrom(msg.sender, address(this), addedAmountA );
tokenB.safeTransferFrom(msg.sender, address(this), addedAmountB );

emit LiquidityAdded(tokenA, tokenB, addedAmountA, addedAmountB, addedLiquidity);
}

10 function calls * 200 gas per read = 2,000 gas spent


Awesome G

# 1. Save gas with `payable` functions

Marking functions as `payable` will be cheaper (by ~20 gas) than using non-`payable` functions because the Solidity compiler inserts a check into non-`payable` functions that requires `msg.value` to be zero.

For instance, the code at [SmartAccount.sol#L449](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L449) can be refactored as:

```solidity
Line 449:    function transfer(address payable dest, uint amount) external payable nonReentrant onlyOwner {
```

> **Note**: Although this optimization can save gas, it is important to be aware of the security considerations involving Ether held in contracts that it introduces.
> More information on this topic can be found in the [Solidity Compiler Discussion](https://github.com/ethereum/solidity/issues/12539).

Affected line of code:

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

# 2. Splitting `require()` Statements That Use `&&` Saves Gas - (Saves ~`3` Gas per `&&`)

Instead of using the `&&` operator in a single require statement to check multiple conditions, using various require statements with 1 condition per require statement will save ~3 GAS per `&&`.

Affected line of code:

- [ModuleManager.sol#L34](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L34)
- [ModuleManager.sol#L49](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L49)
- [ModuleManager.sol#L68](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L68)

# 3. An internal function that can reduce to 1 line can

contracts/smart-contract-wallet/SmartAccount.sol
Line 461:    _requireFromEntryPointOrOwner();
Line 466:    _requireFromEntryPointOrOwner();

Line 494:    function _requireFromEntryPointOrOwner() internal view {
Line 495:        require(msg.sender == address(entryPoint()) || msg.sender == owner, "account: not Owner or EntryPoint");
Line 496:    }

// Code block 1 (solidity):
Line 449:    function transfer(address payable dest, uint amount) external payable nonReentrant onlyOwner {

SmartAccount.sol#L449, ModuleManager.sol#L34, ModuleManager.sol#L49, ModuleManager.sol#L68


Infect3d Q

# [L-01] - It is possible to call `borrowUSDS` with a very low amount

## Vulnerability details
Nothing prevents a user to borrow a very low amount of USDS (for example 1 wei)
The issue is that everytime a user borrow some USDS, an element is added to `_walletsWithBorrowedUSDS`

```soldiity
    function borrowUSDS( uint256 amountBorrowed ) external nonReentrant
		{
		require( exchangeConfig.walletHasAccess(msg.sender), "Sender does not have exchange access" );
		require( userShareForPool( msg.sender, collateralPoolID ) > 0, "User does not have any collateral" );
		require( amountBorrowed <= maxBorrowableUSDS(msg.sender), "Excessive amountBorrowed" );

		// Increase the borrowed amount for the user
		usdsBorrowedByUsers[msg.sender] += amountBorrowed;

		// Remember that the user has borrowed USDS (so they can later be checked for sufficient collateralization ratios and liquidated if necessary)
		_walletsWithBorrowedUSDS.add(msg.sender);

		// Mint USDS and send it to the user
		usds.mintTo( msg.sender, amountBorrowed );

		emit BorrowedUSDS(msg.sender, amountBorrowed);
		}
```

This can cause an issue for liquidator who want to find liquidable users, as to high value `numberOfUsersWithBorrowedUSDS` will make the gas cost of the loop to high, possibly causing a revert

```solidity
File: src\stable\CollateralAndLiquidity.sol
345: 	function findLiquidatableUsers() external view returns (address[] memory)
346: 		{
347: 		if ( numberOfUsersWithBorrowedUSDS() == 0 )
348: 			return new address[](0);
349:
350: 		return findLiquidatableUsers( 0, numberOfUsersWithBorrowedUSDS() - 1 );
351: 		}
352: 	}
```

```solidity
File: src\stable\CollateralAndLiquidity.sol311: 	function findLiquidatableUsers( uint256 startIndex, uint256 endIndex ) public view returns (address[] memory) //..?@audit compare with canUserBeLiquidated
312: 		{
313: 		address[] memory liquidatableUsers = new address[](endIndex - startIndex + 1);
314: 		uint256 count = 0;
315:
316: 		// Cache
317: 		uint256 t

```solidity
File: src\stable\CollateralAndLiquidity.sol311: 	function findLiquidatableUsers( uint256 startIndex, uint256 endIndex ) public view returns (address[] memory) //..?@audit compare with canUserBeLiquidated
312: 		{
313: 		address[] memory liquidatableUsers = new address[](endIndex - startIndex + 1);
314: 		uint256 count = 0;
315:
316: 		// Cache
317: 		uint256 totalCollateralShares = totalShares[collateralPoolID];
318: 		uint256 totalCollateralValue = totalCollateralValueInUSD();
319:
320: 		if ( totalCollateralValue != 0 )
321: 			for ( uint256 i = startIndex; i <= endIndex; i++ )

// Code block 1 (soldiity):
function borrowUSDS( uint256 amountBorrowed ) external nonReentrant
		{
		require( exchangeConfig.walletHasAccess(msg.sender), "Sender does not have exchange access" );
		require( userShareForPool( msg.sender, collateralPoolID ) > 0, "User does not have any collateral" );
		require( amountBorrowed <= maxBorrowableUSDS(msg.sender), "Excessive amountBorrowed" );

		// Increase the borrowed amount for the user
		usdsBorrowedByUsers[msg.sender] += amountBorrowed;

		// Remember that the user has borrowed USDS (so they can later be checked for sufficient collateralization ratios and liquidated if necessary)
		_walletsWithBorrowedUSDS.add(msg.sender);

		// Mint USDS and send it to the user
		usds.mintTo( msg.sender, amountBorrowed );

		emit BorrowedUSDS(msg.sender, amountBorrowed);
		}

// Code block 2 (solidity):
File: src\stable\CollateralAndLiquidity.sol
345: 	function findLiquidatableUsers() external view returns (address[] memory)
346: 		{
347: 		if ( numberOfUsersWithBorrowedUSDS() == 0 )
348: 			return new address[](0);
349:
350: 		return findLiquidatableUsers( 0, numberOfUsersWithBorrowedUSDS() - 1 );
351: 		}
352: 	}


kodyvim G

# Gas Optimization and QA report

## Use require to save gas
Replace `assert` with `require` to return unused gas on failure.
instances:
```
Ethos-Core/contracts/BorrowerOperations.sol#L128    assert(MIN_NET_DEBT > 0)
Ethos-Core/contracts/BorrowerOperations.sol#L197     assert(vars.compositeDebt > 0);
Ethos-Core/contracts/BorrowerOperations.sol#L301    assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0))
Ethos-Core/contracts/BorrowerOperations.sol#L331 assert(_collWithdrawal <= vars.coll);
Ethos-Core/contracts/TroveManager.sol#L417 assert(_LUSDInStabPool != 0)
Ethos-Core/contracts/TroveManager.sol#L1224 assert(totalStakesSnapshot[_collateral] > 0);
Ethos-Core/contracts/TroveManager.sol#L1279 assert(closedStatus != Status.nonExistent && closedStatus != Status.active);
Ethos-Core/contracts/TroveManager.sol#L1342  assert(troveStatus != Status.nonExistent && troveStatus != Status.active);
Ethos-Core/contracts/TroveManager.sol#L1348  assert(index <= idxLast);
Ethos-Core/contracts/TroveManager.sol#L1414  assert(newBaseRate > 0);
Ethos-Core/contracts/TroveManager.sol#L1489  assert(decayedBaseRate <= DECIMAL_PRECISION)
Ethos-Core/contracts/StabilityPool.sol#L526 assert(_debtToOffset <= _totalLUSDDeposits);
Ethos-Core/contracts/StabilityPool.sol#L551  assert(_LUSDLossPerUnitStaked <= DECIMAL_PRECISION);
Ethos-Core/contracts/StabilityPool.sol#L591 assert(newP > 0);
Ethos-Core/contracts/LUSDToken.sol#L312 assert(sender != address(0));
Ethos-Core/contracts/LUSDToken.sol#L313 assert(recipient != address(0));
Ethos-Core/contracts/LUSDToken.sol#L321 assert(account != address(0));
Ethos-Core/contracts/LUSDToken.sol#L329 assert(account != address(0))
Ethos-Core/contracts/LUSDToken.sol#L337 assert(owner != address(0))
Ethos-Core/contracts/LUSDToken.sol#L338 assert(spender != address(0));

```

## Place validation of function parameters at the start of the function
This way gas to process the function body would be preserved on

Ethos-Core/contracts/BorrowerOperations.sol#301    assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0))

// Code block 1 (unknown):
Ethos-Core/contracts/BorrowerOperations.sol#L128    assert(MIN_NET_DEBT > 0)
Ethos-Core/contracts/BorrowerOperations.sol#L197     assert(vars.compositeDebt > 0);
Ethos-Core/contracts/BorrowerOperations.sol#L301    assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0))
Ethos-Core/contracts/BorrowerOperations.sol#L331 assert(_collWithdrawal <= vars.coll);
Ethos-Core/contracts/TroveManager.sol#L417 assert(_LUSDInStabPool != 0)
Ethos-Core/contracts/TroveManager.sol#L1224 assert(totalStakesSnapshot[_collateral] > 0);
Ethos-Core/contracts/TroveManager.sol#L1279 assert(closedStatus != Status.nonExistent && closedStatus != Status.active);
Ethos-Core/contracts/TroveManager.sol#L1342  assert(troveStatus != Status.nonExistent && troveStatus != Status.active);
Ethos-Core/contracts/TroveManager.sol#L1348  assert(index <= idxLast);
Ethos-Core/contracts/TroveManager.sol#L1414  assert(newBaseRate > 0);
Ethos-Core/contracts/TroveManager.sol#L1489  assert(decayedBaseRate <= DECIMAL_PRECISION)
Ethos-Core/contracts/StabilityPool.sol#L526 assert(_debtToOffset <= _totalLUSDDeposits);
Ethos-Core/contracts/StabilityPool.sol#L551  assert(_LUSDLossPerUnitStaked <= DECIMAL_PRECISION);
Ethos-Core/contracts/StabilityPool.sol#L591 assert(newP > 0);
Ethos-Core/contracts/LUSDToken.sol#L312 assert(sender != address(0));
Ethos-Core/contracts/LUSDToken.sol#L313 assert(recipient != address(0));
Ethos-Core/contracts/LUSDToken.sol#L321 assert(account != address(0));
Ethos-Core/contracts/LUSDToken.sol#L329 assert(account != address(0))
Ethos-Core/contracts/LUSDToken.sol#L337 assert(owner != address(0))
Ethos-Core/contracts/LUSDToken.sol#L338 assert(spender != address(0));


apostle0x01 G

## [G-01] Splitting require() statements that use && saves gas

### IMPACT

Require statements including conditions with the && operator can be broken down in
multiple require statements to save gas.

### POC

```
contracts/smart-contract-wallet/base/ModuleManager.sol:34:        require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
contracts/smart-contract-wallet/base/ModuleManager.sol:49:        require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
contracts/smart-contract-wallet/base/ModuleManager.sol:68:        require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");
```

### MITIGATION

Breakdown each condition in a separate require
```console
require(module != address(0),"BSA101");
require(module != SENTINEL_MODULES, "BSA101");)
```

// Code block 1 (unknown):
contracts/smart-contract-wallet/base/ModuleManager.sol:34:        require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
contracts/smart-contract-wallet/base/ModuleManager.sol:49:        require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
contracts/smart-contract-wallet/base/ModuleManager.sol:68:        require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");

// Code block 2 (console):
require(module != address(0),"BSA101");
require(module != SENTINEL_MODULES, "BSA101");)


2997ms G

## Gas Optimazition Summary

### Issues

|       | Issue                                    | Contexts |
| ----- | :--------------------------------------- | :------: |
| GAS-1 | Revert directly if the length of returndata is 0 |    3     |
| GAS-2     | <x> += <y> costs more gas than <x> = <x> + <y> for state variables                                         |    2      |
|GAS-3| require()/revert() strings longer than 32 bytes cost extra gas | 3 |

### [GAS-01]Revert directly if the length of returndata is 0

There is no need to do the calculation of ```add``` and ```mload``` if the length of returndata is 0.

**Proof Of Concept**

```
        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
```

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

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

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/samples/SimpleAccount.sol#L119

**Recommended Mitigation Steps**

Add in this way.

```
if (returndata.length == 0) {
	revert();
}
assembly {
	revert(add(32, returndata), mload(returndata))
}
```

### [GAS-02]<x> += <y> costs more gas than <x> = <x> + <y> for state variables
Using the addition operator instead of plus-equals saves gas
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L148
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L261-L286

### [G-03] REQUIRE()/REVERT() STRINGS LONGER THAN 32 BYTES COST EXTRA GAS
Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas.
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-c

// Code block 1 (unknown):
if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }

// Code block 2 (unknown):
if (returndata.length == 0) {
	revert();
}
assembly {
	revert(add(32, returndata), mload(returndata))
}

SmartAccountNoAuth.sol#L469, SmartAccount.sol#L479, SimpleAccount.sol#L119, Math.sol#L148, Math.sol#L261-L286

Math.sol, SmartAccount.sol, SimpleAccount.sol, SmartAccountNoAuth.sol


Kose Q

# QA Report

## Summary

|               | Issue         | Risk     | Instances     |
| :-------------: |:-------------|:-------------:|:-------------:|
| 1 | If/validation statements should be on the top of the function | NC | 1 |
| 2 | Immutables should be uppercase to distinguish from regular variables | NC | 3 |

## Findings

### 1- If/validation statements should be on the top of the function :

#### Risk : Non-critical

#### Proof of Concept

Issue occurs in the code below :

File: PositionManager.sol [Line 227-233] (https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#LL227-L233)
```
function mint(
        MintParams calldata params_
    ) external override nonReentrant returns (uint256 tokenId_) {
        tokenId_ = _nextId++;

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

### 2- Immutables should be uppercase to distinguish from regular variables :

#### Risk : Non-critical

#### Proof of Concept

Issue occurs in these code below :

File: PositionManager.sol [Line 68-71] (https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L68-L71)
```
    /// @dev The `ERC20` pools factory contract, used to check if address is an `Ajna` pool.
    ERC20PoolFactory  private immutable erc20PoolFactory;
    /// @dev The `ERC721` pools factory contract, used to check if address is an `Ajna` pool.
    ERC721PoolFactory private immutable erc721PoolFactory;
```

File : RewardsManager.sol [Line 86-89] (https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L86-L89)
```
    /// @dev Address of the `Ajna` token.
    address public immutable ajnaToken;
    /// @dev The `PositionManager` contract
    IPositionManager public immutable positionManager;
```

File : Funding.sol [Line 20-21] (https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/Funding.sol#L20-L21

// Code block 1 (unknown):
function mint(
        MintParams calldata params_
    ) external override nonReentrant returns (uint256 tokenId_) {
        tokenId_ = _nextId++;

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

// Code block 2 (unknown):
/// @dev The `ERC20` pools factory contract, used to check if address is an `Ajna` pool.
    ERC20PoolFactory  private immutable erc20PoolFactory;
    /// @dev The `ERC721` pools factory contract, used to check if address is an `Ajna` pool.
    ERC721PoolFactory private immutable erc721PoolFactory;

// Code block 3 (unknown):
/// @dev Address of the `Ajna` token.
    address public immutable ajnaToken;
    /// @dev The `PositionManager` contract
    IPositionManager public immutable positionManager;

PositionManager.sol, PositionManager.sol#L68-L71, RewardsManager.sol#L86-L89, Funding.sol#L20-L21


j4ld1na G

|      | Issues                                  | Instances |
| :--- | :-------------------------------------- | --------: |
| G-01 | Remove `SafeMath` library.              |         1 |
| G-02 | Use assembly to check for `address(0)`. |         6 |

## G-01 - Remove `SafeMath` library.

_Gas saved: 2.1k for each tx that uses `SafeMath` + 100 units per call._

_It seems like safe math doesn’t serve any purpose here and it can simply be replaced with normal math operands. The usage of library is pretty expensive since it makes a delegate call to an external address._

There an instance:

[contracts/lybra/token/EUSD.sol#5](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/EUSD.sol#L5)

Recommended Mitigation Steps:

-   Remove `SafeMath` library.

## G-02 - Use assembly to check for `address(0)`.

_Saves 6 gas per instance._

Example:

```ts
function ownerNotZero(address _addr) public pure {
    require(_addr != address(0), "zero address)");
}

// Gas: 258
```

```ts
function assemblyOwnerNotZero(address _addr) public pure {
    assembly {
        if iszero(_addr) {
            mstore(0x00, "zero address")
            revert(0x00, 0x20)
        }
    }
}

// Gas: 252
```

There are 14 instances and [34 future instances](https://gist.github.com/liveactionllama/27513952718ec3cbcf9de0fda7fef49c#l01-missing-checks-for-address0x0-when-assigning-values-to-address-state-variables):

-   [contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L83](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L83)

```ts
require(onBehalfOf != address(0), 'TZA');
```

-   [contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L97](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L97)

```ts
require(onBehalfOf != address(0), 'TZA');
```

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

// Code block 1 (ts):
function ownerNotZero(address _addr) public pure {
    require(_addr != address(0), "zero address)");
}

// Gas: 258

// Code block 2 (ts):
function assemblyOwnerNotZero(address _addr) public pure {
    assembly {
        if iszero(_addr) {
            mstore(0x00, "zero address")
            revert(0x00, 0x20)
        }
    }
}

// Gas: 252

// Code block 3 (ts):
require(onBehalfOf != address(0), 'TZA');

// Code block 4 (ts):
require(onBehalfOf != address(0), 'TZA');


MohammedRizwan Q

## Summary

### Low Risk Issues
|Number|Issue|Instances| |
|-|:-|:-:|:-:|
| [L‑01] | openzeppelin's `setupRole()` is deprecated | 7 |
| [L‑02] | set max limit of `slippageTolerance` in `setSlippageTolerance()` | 2 |
| [L‑03] | Calls inside loops that may address DoS | 1 |
| [L‑04] | Admin may not receive usdc/usdt if blacklisted in case of emergency | 1 |
| [L‑05] | Insufficient validations in `setAddresses()` can cause both `tokenA` and `tokenB` same | 2 |
| [L‑06] | `emergencyWithdraw()` functionality should be added in `UniV3LiquidityAmo.sol` like other contracts | 1 |
| [L‑07] | `TickMath.sol` might revert in solidity version 0.8.19 as used in contracts | 1 |
| [L‑08] | Update solc version and use unchecked in Uniswap related libraries like `LiquidityAmounts.sol` | 1 |
| [L‑09] | Solmate safeTransferLib.sol functions does not check the codesize of the token address, which may lead to fund loss | 1 |
| [L‑10] | An issue with `TransferHelper.sol` which is extensively used in `UniV3LiquidityAmo.sol` | 1 |
| [L‑11] | Do not hard code the contract address, pass as constructor argument in `UniV3LiquidityAmo.sol` | 1 |
| [L‑12] | In `RdpxDecayingBonds.sol`, `mint()` does not verify that the bond `expiry` has passed/expired | 1 |
| [L‑13] | In `DpxEthToken.sol`, `mint()`, `burn()` and `burnFrom()` should only be called when the contract is not paused | 3 |
| [L‑14] | `addToContractWhitelist()` does not check the contract code existence before whitelisting | 1 |
| [L‑15] | In `RdpxV2Bond.sol`, `mint()` should be called when the contract is not paused | 1 |
| [L‑16] | Misleading Natspec on `getLpPrice()` in `UniV2LiquidityAmo.sol` | 1 |
| [L‑17] | `deadline` should be passed as an argument instead of hardcoding it | contracts |

### [L‑01]  openzeppelin's `setupRole()` is deprecated
Contracts has used `_setupRole()` in functions for setting the rol

File: contracts/amo/UniV2LiquidityAmo.sol

  function emergencyWithdraw(
    address[] calldata tokens
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    IERC20WithBurn token;

    for (uint256 i = 0; i < tokens.length; i++) {
      token = IERC20WithBurn(tokens[i]);
>>    token.safeTransfer(msg.sender, token.balanceOf(address(this)));
    }

    emit LogEmergencyWithdraw(msg.sender, tokens);

File: contracts/amo/UniV2LiquidityAmo.sol

  function emergencyWithdraw(
    address[] calldata tokens
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    IERC20WithBurn token;

    for (uint256 i = 0; i < tokens.length; i++) {
      token = IERC20WithBurn(tokens[i]);
      token.safeTransfer(msg.sender, token.balanceOf(address(this)));
    }

    emit LogEmergencyWithdraw(msg.sender, tokens);
  }


descharre G

## Summary
|ID     | Finding|  Gas saved| Instances |
|:----: | :---           |              :----:    |  :----:         |
|1       |Make for loops unchecked| 414 | 1 |
| 2      |Save storage variable to memory when it's used more than once in a function | 296| 3 |
| 3      |Don't write old value to memory | 500| 25 |
| 4      |Function getBlockNumber is useless | 30| 2 |
| 5      |Don't emit 2 similar events in 1 function | 1289| 2 |
| 7      |Miscellaneous| 4000| 2 |

## Details
## 1 Make for loops unchecked
The risk of for loops getting overflowed is extremely low. Because it always increments by 1. Even if the arrays are extremely long, it will take a massive amount of time and gas to let the for loop overflow.

[KYCRegistry.sol#L163-L165](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/kyc/KYCRegistry.sol#L163-L165)

addKycAdresses function avg gas saved: 414

The more addresses you want to add, the more gas you will save with this gas optimization
```diff
L163
+   uint256 i;
-	for (uint256 i = 0; i < length; i++) {
+   for (; i < lenght; ){
		kycState[kycRequirementGroup][addresses[i]] = true;
+	unchecked {
+		i++;
+		}
	}
```
It can also be done on every other for loop in the project. The larger the array the more gas you will save.

## 2 Save storage variable to memory when it's used more than once in a function
When a storage variable is read for the second time from storage it costs 100 gas. When it's read from memory it only costs 3 gas.
[CashManager.sol#L241-L269](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L241-L269)

claimMint function avg gas saved: 130
```diff
  function claimMint(
    address user,
    uint256 epochToClaim
  ) external override updateEpoch nonReentrant whenNotPaused checkKYC(user) {
+   uint256 epochToExchangeRateMemory = epochToExchangeRate[epochToClaim];
    uint256 collateralDeposited = mintRequestsPerEpoch[epochToClaim][user];
    if (collateralDeposited == 0) {

[CashManager.sol#L576-L587](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L576-L587)

transitionEpoch function avg gas saved: 89

// Code block 1 (diff):
L163
+   uint256 i;
-	for (uint256 i = 0; i < length; i++) {
+   for (; i < lenght; ){
		kycState[kycRequirementGroup][addresses[i]] = true;
+	unchecked {
+		i++;
+		}
	}


eeshenggoh Q

## Summary
 Unsafe ERC20 Operation(s)

## Vulnerability Details

ERC20 operations can be unsafe due to different implementations and vulnerabilities in the standard.

It is therefore recommended to always either use OpenZeppelin's SafeERC20 library or at least to wrap each operation in a require statement.

To circumvent ERC20's approve functions race-condition vulnerability use OpenZeppelin's SafeERC20 library's safe{Increase|Decrease}Allowance functions.

```
File: core\solidity\contracts\core\AMPHClaimer.sol
128:   function recoverDust(address _token, uint256 _amount) external override onlyOwner {
129:     IERC20(_token).transfer(owner(), _amount);
130:
131:     emit RecoveredDust(_token, owner(), _amount);
132:   }
```
```
File: core\solidity\contracts\core\USDA.sol
101:   function _deposit(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
102:     if (_susdAmount == 0) revert USDA_ZeroAmount();
103:     sUSD.transferFrom(_msgSender(), address(this), _susdAmount);
104:     _mint(_target, _susdAmount);
105:     // Account for the susd received
106:     reserveAmount += _susdAmount;
107:
108:     emit Deposit(_target, _susdAmount);
109:   }
```
```
File: core\solidity\contracts\core\USDA.sol
147:   function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
148:     if (reserveAmount == 0) revert USDA_EmptyReserve();
149:     if (_susdAmount == 0) revert USDA_ZeroAmount();
150:     if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
151:     // Account for the susd withdrawn
152:     reserveAmount -= _susdAmount;
153:     sUSD.transfer(_target, _susdAmount);
154:     _burn(_msgSender(), _susdAmount);
155:
156:     emit Withdraw(_target, _susdAmount);
157:   }
```
```
File: core\solidity\contracts\core\USDA.sol
202:   function donate(uint256 _susdAmount) external override paysInterest whenNotPaused {
203:     if (_susdAmount == 0) revert USDA_ZeroAmount();
204:     // Acco

// Code block 1 (unknown):
File: core\solidity\contracts\core\AMPHClaimer.sol
128:   function recoverDust(address _token, uint256 _amount) external override onlyOwner {
129:     IERC20(_token).transfer(owner(), _amount);
130:
131:     emit RecoveredDust(_token, owner(), _amount);
132:   }

// Code block 2 (unknown):
File: core\solidity\contracts\core\USDA.sol
101:   function _deposit(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
102:     if (_susdAmount == 0) revert USDA_ZeroAmount();
103:     sUSD.transferFrom(_msgSender(), address(this), _susdAmount);
104:     _mint(_target, _susdAmount);
105:     // Account for the susd received
106:     reserveAmount += _susdAmount;
107:
108:     emit Deposit(_target, _susdAmount);
109:   }

// Code block 3 (unknown):
File: core\solidity\contracts\core\USDA.sol
147:   function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
148:     if (reserveAmount == 0) revert USDA_EmptyReserve();
149:     if (_susdAmount == 0) revert USDA_ZeroAmount();
150:     if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
151:     // Account for the susd withdrawn
152:     reserveAmount -= _susdAmount;
153:     sUSD.transfer(_target, _susdAmount);
154:     _burn(_msgSender(), _susdAmount);
155:
156:     emit Withdraw(_target, _susdAmount);
157:   }


lukris02 Q

# QA Report for Ethos Reserve contest
## Overview
During the audit, 2 low and 12 non-critical issues were found.

№ | Title | Risk Rating  | Instance Count
--- | --- | --- | ---
L-1 | Check multisigRoles.length in ReaperVaultV2 | Low | 1
L-2 | Some events could be more informative | Low | 4
NC-1 | Order of Functions | Non-Critical | 10
NC-2 | Order of Layout | Non-Critical | 1
NC-3 | Missing leading underscores  | Non-Critical | 12
NC-4 | Inconsistency when using uint and uint256 | Non-Critical | 5
NC-5 | Unused named return variables | Non-Critical | 13
NC-6 | Inconsistency when using the number 10000 | Non-Critical | 1
NC-7 | Visibility is not set  | Non-Critical | 5
NC-8 | Wrong modifier order in  function declaration | Non-Critical | 2
NC-9 | Missing NatSpec | Non-Critical | 4
NC-10 | Incomplete NatSpec | Non-Critical | 1
NC-11 | No space between the control structures | Non-Critical | 3
NC-12 | Maximum line length exceeded | Non-Critical | 42

## Low Risk Findings(2)
### L-1. Check multisigRoles.length in ReaperVaultV2
##### Description
Function [__ReaperBaseStrategy_init](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L63-L88) checks that ```multisigRoles.length == 3``` before granting roles:
```
        require(_multisigRoles.length == 3, "Invalid number of multisig roles");
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(DEFAULT_ADMIN_ROLE, _multisigRoles[0]);
        _grantRole(ADMIN, _multisigRoles[1]);
        _grantRole(GUARDIAN, _multisigRoles[2]);
```
But in [ReaperVaultV2 constructor](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L111-L136), there is no such check.
##### Instances
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L132-L135
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L81-L85

##### Recommenda

// Code block 1 (unknown):
require(_multisigRoles.length == 3, "Invalid number of multisig roles");
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(DEFAULT_ADMIN_ROLE, _multisigRoles[0]);
        _grantRole(ADMIN, _multisigRoles[1]);
        _grantRole(GUARDIAN, _multisigRoles[2]);


apostle0x01 Q

## \[L-01\] NO TRANSFER OWNERSHIP PATTERN

Recommend considering implementing a two step process where the owner or admin nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.

### ./contracts/smart-contract-wallet/SmartAccount.sol :
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109-L114
```solidity
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:    }

```

// Code block 1 (solidity):
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:    }


Diana Q

## 01 Require() should be used instead of assert()

Assert should not be used except for tests, `require` should be used

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

Assertion() should be avoided even after solidity version 0.8.0, because its documentation states “The Assert function generates an error of type Panic(uint256). Code that works properly should never Panic, even on invalid external input. If this happens, you need to fix it in your contract. there’s a mistake”.

_There are 3 instances of this issue_

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashFactory.sol

```
File: contracts/cash/factory/CashFactory.sol

97: assert(cashProxyAdmin.owner() == guardian);
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderFactory.sol

```
File: contracts/cash/factory/CashKYCSenderFactory.sol

106: assert(cashKYCSenderProxyAdmin.owner() == guardian);
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderReceiverFactory.sol

```
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

106: assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian)
```

-----

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

See [this](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps) link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

_There are 5 instances of this issue_

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/Proxy.sol

```
File: contracts/cash/Proxy.sol

20: contract TokenProxy is TransparentUpgradeableProxy {
25: ) TransparentUpgradeableProxy(_logic, _admin, _data) {}
```

https://github.com/code-423n4/2023-01-o

// Code block 1 (unknown):
File: contracts/cash/factory/CashFactory.sol

97: assert(cashProxyAdmin.owner() == guardian);

// Code block 2 (unknown):
File: contracts/cash/factory/CashKYCSenderFactory.sol

106: assert(cashKYCSenderProxyAdmin.owner() == guardian);

// Code block 3 (unknown):
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

106: assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian)

// Code block 4 (unknown):
File: contracts/cash/Proxy.sol

20: contract TokenProxy is TransparentUpgradeableProxy {
25: ) TransparentUpgradeableProxy(_logic, _admin, _data) {}

CashFactory.sol, CashKYCSenderFactory.sol, CashKYCSenderReceiverFactory.sol, Proxy.sol

CashFactory.sol, CashKYCSenderFactory.sol, Proxy.sol, CashKYCSenderReceiverFactory.sol


Tigerfrake G

# [G-01] Switching between 1 and 2 instead of 0 and 1 (or false and true) is more gas efficient.

### Description:
`SSTORE` from 0 to 1 (or any non-zero value) costs 20000 gas. `SSTORE` from 1 to 2 (or any other non-zero value) costs 5000 gas.
By storing the original value once again, a refund is triggered ( https://eips.ethereum.org/EIPS/eip-2200).
Since refunds are capped to a percentage of the total transaction’s gas, it is best to keep them low, to increase the likelihood of the full refund coming into effect.
Therefore, switching between 1, 2 instead of 0, 1 will be more gas efficient.
See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/86bd4d73896afcb35a205456e361436701823c7a/contracts/security/ReentrancyGuard.sol#L29-L33

### Instances:
- https://github.com/code-423n4/2024-01-salty/blob/main/src%2Fdao%2FDAO.sol#L187
- https://github.com/code-423n4/2024-01-salty/blob/main/src%2Fdao%2FDAO.sol#L194

# [G-02] Emit local variables instead of state variable.
(Save ~100 Gas)

### Instance:
- https://github.com/code-423n4/2024-01-salty/blob/main/src%2FManagedWallet.sol#L42-L55

```Solidity
        function proposeWallets( address _proposedMainWallet, address _proposedConfirmationWallet ) external

    //... require statements
              proposedMainWallet = _proposedMainWallet;
                proposedConfirmationWallet = _proposedConfirmationWallet;

                emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);
                }
```

### Description:
Since we are setting our `state variables` `proposedMainWallet` and `proposedConfirmationWallet` to the function parameter `_proposedMainWallet` and `_proposedConfirmationWallet` respectively, we should `emit the function parameter` as it is cheaper to read compared to the `state variable`:

```Solidity
                emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);
```

// Code block 1 (Solidity):
function proposeWallets( address _proposedMainWallet, address _proposedConfirmationWallet ) external

    //... require statements
              proposedMainWallet = _proposedMainWallet;
                proposedConfirmationWallet = _proposedConfirmationWallet;

                emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);
                }

// Code block 2 (Solidity):
emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);

function proposeWallets( address _proposedMainWallet, address _proposedConfirmationWallet ) external

    //... require statements
              proposedMainWallet = _proposedMainWallet;
                proposedConfirmationWallet = _proposedConfirmationWallet;

                emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);
                }

emit WalletProposal(proposedMainWallet, proposedConfirmationWallet);

ReentrancyGuard.sol#L29-L33, src%2Fdao%2FDAO.sol#L187, src%2Fdao%2FDAO.sol#L194, src%2FManagedWallet.sol#L42-L55


VIELITE Analysis

There are no checks for address(0) in the `_burn()` function in the USDA.sol contract which can lead to unnecessary reverts

```solidity
function _burn(address _target, uint256 _amount) internal {
    uint256 __gonsPerFragment = _gonsPerFragment;
    // modify the gonbalances of the sender, subtracting the amount of gons, therefore amount * gonsperfragment
    _gonBalances[_target] -= (_amount * __gonsPerFragment);
    // modify totalSupply and totalGons
    _totalSupply -= _amount;
    _totalGons -= (_amount * __gonsPerFragment);
    // emit both a burn and transfer event
    emit Transfer(_target, address(0), _amount);
    emit Burn(_target, _amount);
  }
```

could be found at `core/solidity/contracts/core/USDA.sol`

### Time spent:
26 hours

// Code block 1 (solidity):
function _burn(address _target, uint256 _amount) internal {
    uint256 __gonsPerFragment = _gonsPerFragment;
    // modify the gonbalances of the sender, subtracting the amount of gons, therefore amount * gonsperfragment
    _gonBalances[_target] -= (_amount * __gonsPerFragment);
    // modify totalSupply and totalGons
    _totalSupply -= _amount;
    _totalGons -= (_amount * __gonsPerFragment);
    // emit both a burn and transfer event
    emit Transfer(_target, address(0), _amount);
    emit Burn(_target, _amount);
  }


0x_Scar Q

The `NodeDelegator` contract and the `RSETH` contract allows for pausing and unpausing via the `pause ()` and `unpause` function. The contract code on the `pause ()` function reads as `Only callable by LRT config manager. Contract must NOT be paused.` and `unpause ()` reads as `Only callable by the rsETH admin. Contract must be paused`

Clearly, the intent of the project is to ensure the `pause ()` function is called only when the contract is NOT paused and vice versa for `unpause ()`.

To ensure this is the case, the project should consider adding the `whenPaused` and `whenNotPaused` modifier on the two functions as follows:

```
 function pause() external onlyLRTManager whenNotPaused {
        _pause();
    }

    /// @notice Returns to normal state.
    /// @dev Only callable by the rsETH admin. Contract must be paused
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) whenPaused {
        _unpause();
    }
```

// Code block 1 (unknown):
function pause() external onlyLRTManager whenNotPaused {
        _pause();
    }

    /// @notice Returns to normal state.
    /// @dev Only callable by the rsETH admin. Contract must be paused
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) whenPaused {
        _unpause();
    }


chaduke Q

QA1.  The ``updateDistributionPeriod()`` function has no range check for the input ``_newDistributionPeriod``. As a result, it might cause an overflow for L112 of function ``fund()``:

[https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L101-L117](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L101-L117)

In addition, as ``_newDistributionPeriod`` is an important configuration parameter, a timelock should be introduced  help a smooth transition.

Mitigation: 1) introduce a range check for ``_newDistributionPeriod``; 2) introduce a timelock delay;

QA2. ``issueOath()`` and ``fund()`` have a division-followed-by-multiplication rounding error issue, because the ``fund()`` performs a division first to calculate ``rewardPerSecond`` and then   ``issueOath()`` uses a multiplication to calculate the amount ``issuance`` to issue. For example, there will be around $871 lost for distributing 1M over the given 14 days period using this division-followed-by-multiplication approach.

[https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L84-L110](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L84-L110)

Mitigation: we multiply ``rewardPerSecond`` by WAD and then divide ``issuance`` by WAD afterwards, where WAD = 1e18:

```diff
 function issueOath() external override returns (uint issuance) {
        _requireCallerIsStabilityPool();
        if (lastIssuanceTimestamp < lastDistributionTime) {
            uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;
            uint256 timePassed = endTimestamp.sub(lastIssuanceTimestamp);
-            issuance = timePassed.mul(rewardPerSec

QA3. The ``permit()`` function fails to check v ∈ {27, 28}, so it might be subject to signature malleability.

https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L262-L290

Mitigation:
Use Zeppelin's ECDSA: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol

QA4. The ``BorrowerOperations`` contract might not work for fees-on-transfer collateral tokens.

For example, the function ``_moveTokensAndCollateralFromAdjust calls ``safeTransferFrom()`` at L 497 to try to move ``_collchange`` amount of collateral tokens to the ``BorrowerOperations`` contract, however, the contract might receive less amount due to fees-on-transfer, as a result, the next statement that pull ``_collChange``, the same amount, to the ``_activePool`` will fail due to insufficient balance of collateral tokens.

[https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/BorrowerOperations.sol#L476-L502](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/BorrowerOperations.sol#L476-L502)

Mitigation: always measure the  balance before and after transfer to decide the amount that has been transferred for fees-on-transfer tokens.

QA5: ``_computeNominalCR()`` assumes the debt token has a 18 decimals. Need to make this explicit or adjust the debit tokens to 18 decimals as well.

https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/Dependencies/LiquityMath.sol#L97-L106

QA6. Nothing concrete regarding ``lqtystaking`` is implemented now in ``TroveManager.sol``, so state variable or event can be deleted regarding ``lqtystaking`` or complete the implementation if necessary.

QA7. ``updateGovernance()`` and  ``updateGuardian()`` use only one step to change address.

[https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L146-L158](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L146-L158)

Changing adminS in one step is risky: if the new address is a wrong input by mistake, we will lose all the privileges of the owner.

Recommendation:  Use OpenZeppelin's Ownable2Step. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol

QA8. There is no way to revoke a permit of approval. It the approval permit was given to the wrong person or with the wrong amount, a revocation is necessary.

https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L262-L290

Mitigation: One can revoke a permit by increasing the nonce number.

QA9. ``permit()`` will bypass signature check when ``owner = 0x0``, allowing allowance approval from the zero address and possible other future exploits (such as transfer LUSD from zero address to the attacker's address)

For example, Bob can call ``permit(address(0), Bob, balanceOf(adress(0)), deadline, v, r, s)`` to get an allowance from zero address with the amount of the LUSD balance of the zero address.

[https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L262-L294](https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/LUSDToken.sol#L262-L294)

He will pick a value ``s`` such that it will pass the check

CommunityIssuance.sol#L101-L117, CommunityIssuance.sol#L84-L110, LUSDToken.sol#L262-L290, ECDSA.sol, BorrowerOperations.sol#L476-L502, LiquityMath.sol#L97-L106, LUSDToken.sol#L146-L158, Ownable2Step.sol

BorrowerOperations.sol, LUSDToken.sol, Ownable2Step.sol, CommunityIssuance.sol, LiquityMath.sol, ECDSA.sol