lokacho G

# Gas Report

## Summary

### Gas Optimizations

|  | Issue | Instances |
| --- | --- | --- |
| [G‑01] | preRebaseTokenAmount & postRebaseTokenAmount are same | 1 |

Note: The table above as well as its gas numbers are created by considering the **automatic findings** which are not included.

---

## Gas Optimizations

### [G‑01]

summary: The preRebaseTokenAmount & postRebaseTokenAmount are same and its not gas efficient.

*There is 1 instance of this issue:*

```solidity
uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);
```

```solidity
uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);
```

Link(s):
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L586
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L592

Recommended mitigation step:
use only one of them.

// Code block 1 (solidity):
uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

// Code block 2 (solidity):
uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);


8olidity Q

## The return value of AddressSet is not checked

In the EnumerableSet.AddressSet library, both success and failure of adding will have a return value. But all calls do not check the return value, even if the addition fails or the deletion fails. functions can be executed normally

```solidity
function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
}
```
unchecked return value
```solidty
core\solidity\contracts\core\USDA.sol:
  278    function addVaultController(address _vaultController) external onlyOwner {
  279:     _vaultControllers.add(_vaultController);
  280      _grantRole(VAULT_CONTROLLER_ROLE, _vaultController);

core\solidity\contracts\core\USDA.sol:
  287    function removeVaultController(address _vaultController) external onlyOwner {
  288:     _vaultControllers.remove(_vaultController);
  289      _revokeRole(VAULT_CONTROLLER_ROLE, _vaultController);

  297    function removeVaultControllerFromList(address _vaultController) external onlyOwner {
  298:     _vaultControllers.remove(_vaultController);
  299
```

It is recommended to check the return value of the function call

// Code block 1 (solidity):
function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
}

// Code block 2 (solidty):
core\solidity\contracts\core\USDA.sol:
  278    function addVaultController(address _vaultController) external onlyOwner {
  279:     _vaultControllers.add(_vaultController);
  280      _grantRole(VAULT_CONTROLLER_ROLE, _vaultController);

core\solidity\contracts\core\USDA.sol:
  287    function removeVaultController(address _vaultController) external onlyOwner {
  288:     _vaultControllers.remove(_vaultController);
  289      _revokeRole(VAULT_CONTROLLER_ROLE, _vaultController);

  297    function removeVaultControllerFromList(address _vaultController) external onlyOwner {
  298:     _vaultControllers.remove(_vaultController);
  299


W0RR1O Q

## QA Report(low/non-critical)

[L-01] Consider using OpenZeppelin’s SafeCast library to prevent unexpected overflows when casting from uint256
================================================================================================================
In the function `buy()` and `sell()` of the contract `PrivatePool.sol` the function first set the variables `netInputAmount`,`feeAmount`,`protocolFeeAmount` and `weightSum` to be of type `uint256`. However, later on in the function the value of the variables are downcasted to `uint128` and is used to update the virtual reserves.

## Proof Of Concept
The `buy()` function in the contract `PrivatePool.sol`:
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L211-#L231

The `sell()` function in the contract `PrivatePool.sol`:
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L301-#L324

[L-02] Use of `block.timestamp` in the contract `EthRouter.sol`
================================================================
In the contract `EthRouter.sol` there are 4 instances where `block.timestamp` is used to to check whether or not a deadline has been passed. However, `block.timestamp` does not mean current time. Miners can influence the value of `blok.timestamp` to perform a Maximal Extractable Value (MEV) attack. Furthermore, miners can modify the timestamp by up to 900 seconds.

## Proof Of Concept:
There are 4 instances in the contract `EthRouter.sol`:
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L101
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L154
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L228
* https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L256
## Recommended Mitigation
Use `block.number` instead of `block.timestamp` or `now`. Furthermore, it is also recommended to use oracles.

[L-03] Update codes to avoid Compilation Errors
=====================

warning[2072]: Warning: Unused local variable.
   --> test/PrivatePool/Quotes.t.sol:115:37:
    |
115 |         (uint256 returnedFeeAmount, uint256 protocolFeeAmount) = privatePool.changeFeeQuote(inputAmount);
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^

PrivatePool.sol#L211, PrivatePool.sol#L301, EthRouter.sol#L101, EthRouter.sol#L154, EthRouter.sol#L228, EthRouter.sol#L256


j4ld1na G

|      | Issues                                                                                | Instances |
| :--- | :---------------------------------------------------------------------------------------------- | --------: |
| G-01 | Default value initialization.                                                                   |        24 |
| G-02 | Do not calculate constants.                                                                     |         6 |
| G-03 | Using `calldata` instead of `memory` for read-only arguments in `external` functions saves gas. |         6 |
| G-04 | `abi.encode()` is less efficient than `abi.encodePacked()`.                                     |         7 |
| G-05 | Use assembly to check for `address(0)`.                                                         |         1 |
| G-06 | Revert as early as possible.                                                                    |         1 |
| G-07 | When possible, use assembly instead of `unchecked{++i}`.                                        |         1 |
| G-08 | `Assembly` for `If Statements`. Save Gas.                                                       |         1 |

## [01] Default value initialization.

_If a variable is not set/initialized, it is assumed to have the default value (`0`, `false`, `0x0` etc depending on the data type).
Explicitly initializing it with its default value is an anti-pattern and wastes gas._

There are 24 instances:

[ajna-core/src/PositionManager.sol#L181](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L181)

```java
for (uint256 i = 0; i < indexesLength; ) {
```

[ajna-core/src/PositionManager.sol#L364](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L364)

```java
for (uint256 i = 0; i < indexesLength; ) {
```

[ajna-core/src/PositionManager.sol#L474](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L474)

```java

// Code block 1 (java):
for (uint256 i = 0; i < indexesLength; ) {

// Code block 2 (java):
for (uint256 i = 0; i < indexesLength; ) {


yongskiws Q

### [LOW-1] require() should be used instead of assert()
``` solidity
File: cash\factory\CashFactory.sol
97:     assert(cashProxyAdmin.owner() == guardian);

File: cash\factory\CashKYCSenderFactory.sol
106:     assert(cashKYCSenderProxyAdmin.owner() == guardian);

File: cash\factory\CashKYCSenderReceiverFactory.sol
106:     assert(cashKYCSenderReceiverProxyAdmin.owner() == guardian);

File: lending\compound\Comptroller.sol
266:     assert(assetIndex < len);
443:     assert(markets[cToken].accountMembership[borrower]);
```

### [LOW-2] Lack of zero address check
``` solidity
File: cash\CashManager.sol
128:     address _collateral,
129:     address _cash,
132:     address _assetRecipient,
133:     address _assetSender,
134:     address _feeRecipient,
138:     address _kycRegistry,
```

### [LOW-3] function onlyOwner doesn't have strict validation consider use multisig
``` solidity

File: lending\OndoPriceOracle.sol
80:   function setPrice(address fToken, uint256 price) external override onlyOwner {
81:     uint256 oldPrice = fTokenToUnderlyingPrice[fToken];
82:     fTokenToUnderlyingPrice[fToken] = price;
83:     emit UnderlyingPriceSet(fToken, oldPrice, price);
84:   }

File: lending\OndoPriceOracle.sol
92:   function setFTokenToCToken(
93:     address fToken,
94:     address cToken
95:   ) external override onlyOwner {
96:     address oldCToken = fTokenToCToken[fToken];
97:     _setFTokenToCToken(fToken, cToken);
98:     emit FTokenToCTokenSet(fToken, oldCToken, cToken);
99:   }

File: lending\OndoPriceOracle.sol
106:   function setOracle(address newOracle) external override onlyOwner {
107:     address oldOracle = address(cTokenOracle);
108:     cTokenOracle = CTokenOracle(newOracle);
109:     emit CTokenOracleSet(oldOracle, newOracle);
110:   }
111:

File: lending\OndoPriceOracleV2.sol
130:   function setPriceCap(
131:     address fToken,
132:     uint256 value
133:   ) external override onlyOwner {
134:     uint256 oldPriceCap = fTokenToUnderlyingPriceCap[fTok

// Code block 1 (unknown):
### [LOW-2] Lack of zero address check

// Code block 2 (unknown):
### [LOW-3] function onlyOwner doesn't have strict validation consider use multisig


sorrynotsorry Q

# QA (LOW & NON-CRITICAL)

## [L-01] Wrong visibility in SmartAccount contract
SmartAccount contract implements `mixedAuth` modifier to validate the caller is the `owner` OR the contract itself. The functions implementing this modifier have `external` visibility which the contract itself can't call due to visibility. Recommend changing the `external` visibility to `public` visibility.

Modifier;
```solidity
    modifier mixedAuth {
        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
        _;
   }
```
[Link](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L82-L85)

Instances where the modifier is called in external visibility;

[setOwner()](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109)
[UpdateImplementation()](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L120)
[UpdateEntryPoint()](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L127)

## [L-02] SmartAccountFactory's frontrunnable functions
SmartAccountFactory has `deployCounterFactualWallet` and `deployWallet` functions to deploy SCW using create2/create and pointing it to `_defaultImpl` address. However, these functions are prone to be frontrunned by an actor.
### Impact
An actor who monitors the mempool for this func-sig can frontrun it and set the parameters `_entryPointAddress`, `_handler` to arbitrary addresses while the `_owner` address is the frontrunned address.
Since these functions also initialize the proxy and finalizes the SWC setup, it will not be possible to re-create a SWC with the same calling EOA. And if the caller is a dAp

    function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){
        bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));
        bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
        // solhint-disable-next-line no-inline-assembly
        assembly {
            proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
        }
        require(address(proxy) != address(0), "Create2 call failed");
        // EOA + Version tracking
        emit SmartAccountCreated(proxy,_defaultImpl,_owner, VERSION, _index);
        BaseSmartAccount(proxy).init(_owner, _entryPoint, _handler);
        isAccountExist[proxy] = true;
    }

// Code block 1 (solidity):
modifier mixedAuth {
        require(msg.sender == owner || msg.sender == address(this),"Only owner or self");
        _;
   }

SmartAccount.sol#L82-L85, SmartAccount.sol#L109, SmartAccount.sol#L120, SmartAccount.sol#L127


0xhacksmithh Q

### [Low-01] User may lost his ```ETH``` due to precision loss
Inside ```stake()``` User deposited ```ETH``` sent to different derivative contracts according to their weights.
Now the amount of eth will sent to those each derivative contract is decided by following
```solidity
for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            ......
            ......
```
Here calculated ```ethAmount``` may suffers from precision loss and user will get less `safETH` than he actually deserve.
```
File: contracts/SafEth/SafEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84-L88
```

### [Low-02] Old Solidity Version Used
```Instances(4)```
```
File: contracts/SafEth/SafEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L2
```
```
File: contracts/SafEth/derivatives/Reth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L2
```
```
File: contracts/SafEth/derivatives/SfrxEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L2
```
```
File: contracts/SafEth/derivatives/WstEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L2
```

### [Low-03] Single point of failure
The `owner` role has a single point of failure and `onlyOwner` can use critical a few functions.

`owner` role in the project:

Owner is not behind a multisig and changes are not behind a timelock.

Even if protocol admins/developers are not malicious there is still a chance for Owner keys to be stolen. In such a case, the attacker can cause serious damage to the project due to important functions. In such a case, users who have invested in project will suffer high financial

// Code block 1 (solidity):
for (uint i = 0; i < derivativeCount; i++) {
            uint256 weight = weights[i];
            IDerivative derivative = derivatives[i];
            if (weight == 0) continue;
            uint256 ethAmount = (msg.value * weight) / totalWeight;

            ......
            ......

// Code block 2 (unknown):
File: contracts/SafEth/SafEth.sol
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84-L88

SafEth.sol#L84-L88, SafEth.sol#L2, Reth.sol#L2, SfrxEth.sol#L2, WstEth.sol#L2

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


Madalad Q

# Low Summary
| |Issue|Instances|
|:-:|:-|:-:|
|[L-01]|Use two-step ownership transfers|4|
|[L-02]|Sanitise inputs for critical parameter changes|9|
|[L-03]|Use timelock for critical parameter changes|8|

Total Issues: 3

Total instances: 21

# Non-Critical Summary
| |Issue|Instances|
|:-:|:-|:-:|
|[N-01]|Use `indexed` for event parameters|6|
|[N-02]|Use fixed compiler version|4|
|[N-03]|Use appropriate function naming convention|4|
|[N-04]|Update import usages|31|
|[N-05]|Implementing `renounceOwnership` is dangerous|4|
|[N-06]|Remove unused imports|6|
|[N-07]|Missing `address(0)` checks in constructor/initialize|3|
|[N-08]|Use built in constants over magic numbers|3|
|[N-09]|Typo in comments|1|

Total issues: 9

Total instances: 62

# Low Risk Issues
## [L-01] Use two-step ownership transfers

The current ownership transfer process involves the current owner calling `transferOwnership()`. This function checks the new owner is not the zero address and proceeds to write the new owner's address into the owner's state variable.

If the nominated EOA account is not a valid account, it is entirely possible the owner may accidentally transfer ownership to an uncontrolled account, breaking all functions with the onlyOwner() modifier.

Consider implementing a two step process where the owner nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed.

This can be achieved using OpenZeppelin's `Ownable2Step` or `Ownable2StepUpgradeable`.

Instances: 4
```solidity
File: contracts/SafEth/derivatives/Reth.sol

19: contract Reth is IDerivative, Initializable, OwnableUpgradeable {

```
- [contracts/SafEth/derivatives/Reth.sol#L19](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L19)

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

12: contract WstEth is IDerivative, Initializable, OwnableUpgradeable {

```
- [contracts/SafEth/der

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

19: contract Reth is IDerivative, Initializable, OwnableUpgradeable {

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

12: contract WstEth is IDerivative, Initializable, OwnableUpgradeable {

File: contracts/SafEth/derivatives/Reth.sol

19: contract Reth is IDerivative, Initializable, OwnableUpgradeable {

File: contracts/SafEth/derivatives/WstEth.sol

12: contract WstEth is IDerivative, Initializable, OwnableUpgradeable {


gz627 Q

# QA Report

## Non-Critical Issues

C4 checked:
|      | Issue                                                       | Instances |
| ---- |:----------------------------------------------------------- |:---------:|
| NC-1 | Emit event when receiving ETH                               |     1     |
| NC-2 | Missing checks for `address(0)` in low-level call           |     1     |
| NC-3 | Redundant verification                                      |     2     |
| NC-4 | Versioning                                                  |     1     |

### [NC-1] Emit event when receiving ETH

It's a good practice to emit an event whenever a state variable changes especially when receiving ETH. Function `SmartAccount.receive()` can emit an event to indicate receiving ETH, e.g. `emit ReceivedETH(from, to, amount)`. The event can be defined as:

The `receive function can be:`
```solidity
File: https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

    event ReceivedETH(address indexed from, address indexed to, uint256 indexed amount);

    receive() external payable {
        emit ReceivedETH(msg.sender, address(this), msg.value);
    }

```

---
### [NC-2] Missing checks for `address(0)` in low-level call

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

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

477:    function _call(address target, uint256 value, bytes memory data) internal {
478:        (bool success, bytes memory result) = target.call{value : value}(data);
            if (!success) {
                assembly {
                    revert(add(result, 32), mload(result))
                }
            }
        }
```
In the above function, `target` is not verified. So, if there is no code at `target` address, the low-l

// Code block 1 (solidity):
File: https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

    event ReceivedETH(address indexed from, address indexed to, uint256 indexed amount);

    receive() external payable {
        emit ReceivedETH(msg.sender, address(this), msg.value);
    }

// Code block 2 (solidity):
File: https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

477:    function _call(address target, uint256 value, bytes memory data) internal {
478:        (bool success, bytes memory result) = target.call{value : value}(data);
            if (!success) {
                assembly {
                    revert(add(result, 32), mload(result))
                }
            }
        }

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

    event ReceivedETH(address indexed from, address indexed to, uint256 indexed amount);

    receive() external payable {
        emit ReceivedETH(msg.sender, address(this), msg.value);
    }

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

477:    function _call(address target, uint256 value, bytes memory data) internal {
478:        (bool success, bytes memory result) = target.call{value : value}(data);
            if (!success) {
                assembly {
                    revert(add(result, 32), mload(result))
                }
            }
        }


dharma09 G

**Table of contents**

- [The result of a function call should be cached rather than re-calling the function](#The-result-of-a-function-call-should-be-cached-rather-than-re-calling-the-function)
    - [ProtocolRewardsPool.sol.notifyRewardAmount() : Results of `totalStaked()` should be cached](#protocolrewardspool.sol.notifyrewardamount()-:-results-of-`totalstaked()`-should-be-cached)
    - [EUSDMiningIncentives.sol.rewardPerToken() : Result of `totalStaled()` should be cached](#eusdminingincentives.sol.rewardpertoken()`-:-result-of-`totalstaled()`-should-be-cached)
    - [PeUSDMainnetStableVision.sol.convertToPeUSDAndCrossChain() : Results of _msgsender() should be cached](#peusdmainnetstablevision.sol.converttopeusdandcrosschain()-:-results-of-_msgsender()-should-be-cached)
    - [PeUSDMainnetStableVision.sol.convertToPeUSD : Results of `_msgsender()` should be cached](#peusdmainnetstablevision.sol.converttopeusd`-:-results-of-`_msgsender()`-should-be-cached)
- [State variables only set in the constructor should be declared `immutable`](#state-variables-only-set-in-the-constructor-should-be-declared `immutable)
- [Multiple accesses of a mapping/array should use a local variable cache](#multiple-accesses-of-a-mapping/array-should-use-a-local-variable-cache)
    - [LybraGovernance.sol.proposals()` :`proposalData[proposalId] should be cached in local storage](#lybragovernance.sol.proposals()`-:`proposaldata[proposalid]`-should-be-cached-in-local-storage)
    - [LybraGovernance.sol.getReceipt()` :`proposalData[proposalId] should be cached in local storage](#lybragovernance.sol.getreceipt()`-:`proposaldata[proposalid]`-should-be-cached-in-local-storage)
- [Internal or private functions only called once can be inlined to save gas Gas saved: 20 * 20= 400](#internal-or-private-functions-only-called-once-can-be-inlined-to-save-gas gas-saved:-20-*-20=-400)
- [Using storage instead of memory for structs/arrays saves gas](#using-storage-instead-of-memory-for-structs/arrays-sav

File: contracts/lybra/miner/ProtocolRewardsPool.sol
	227: function notifyRewardAmount(uint amount, uint tokenType) external {
	228:        require(msg.sender == address(configurator));
	229:        if (totalStaked() == 0) return; //@audit: Initial call
	230:        require(amount > 0, "amount = 0");
	231:        if(tokenType == 0) {
	232:            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);
	233:            rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked(); //@audit: 2nd call
	234        } else if(tokenType == 1) {
	235:            ERC20 token = ERC20(configurator.stableToken());
	236:            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked(); //@audit  2nd call
	234        } else if(tokenType == 1) {
	237:        } else {
	238:            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked(); //@audit  2nd call
	234        } else if(tokenType == 1) {
	239:        }
	240:    }

# `EUSDMiningIncentives.sol.rewardPerToken()` : Result of `totalStaled()` should be cached

---

- https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L163C4-L169C6


adriro G

# `SafEth` contract:

- `derivatives[i].balance()` is called twice in the `stake` function. Consider caching the first result to avoid an extra call and read from storage.
  https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L73-L74

- `derivatives[i].balance()` is called twice in the `rebalanceToWeights` function. Consider caching the first result to avoid an extra call and read from storage.
  https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L141-L142

- In `adjustWeight`, instead of looping all derivatives to recalculate the `totalWeight`, the function can just subtract the current weight for the derivative being modified and add the new weight.
  https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L171-L173
  ```solidity
  function adjustWeight(
      uint256 _derivativeIndex,
      uint256 _weight
  ) external onlyOwner {
      uint256 currentWeight = weights[_derivativeIndex];
      weights[_derivativeIndex] = _weight;
      totalWeight = totalWeight - currentWeight + _weight;
      emit WeightChange(_derivativeIndex, _weight);
  }
  ```

- In the `addDerivative` function, instead of recalculating the totalWeight by looping all derivatives, the implementations can just add the weight value for the new derivative to the `totalWeight` variable.
  https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L190-L193
  ```solidity
  function addDerivative(
      address _contractAddress,
      uint256 _weight
  ) external onlyOwner {
      derivatives[derivativeCount] = IDerivative(_contractAddress);
      weights[derivativeCount] = _weight;
      derivativeCount++;
      totalWeight += _weight;
      emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
  }
  ```

- `derivativeCount` variable is read from storage 4 times in the `addDerivative` function. Consider caching this value in a local variable.
  ht

// Code block 1 (solidity):
function adjustWeight(
      uint256 _derivativeIndex,
      uint256 _weight
  ) external onlyOwner {
      uint256 currentWeight = weights[_derivativeIndex];
      weights[_derivativeIndex] = _weight;
      totalWeight = totalWeight - currentWeight + _weight;
      emit WeightChange(_derivativeIndex, _weight);
  }

// Code block 2 (solidity):
function addDerivative(
      address _contractAddress,
      uint256 _weight
  ) external onlyOwner {
      derivatives[derivativeCount] = IDerivative(_contractAddress);
      weights[derivativeCount] = _weight;
      derivativeCount++;
      totalWeight += _weight;
      emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
  }

function adjustWeight(
      uint256 _derivativeIndex,
      uint256 _weight
  ) external onlyOwner {
      uint256 currentWeight = weights[_derivativeIndex];
      weights[_derivativeIndex] = _weight;
      totalWeight = totalWeight - currentWeight + _weight;
      emit WeightChange(_derivativeIndex, _weight);
  }

function addDerivative(
      address _contractAddress,
      uint256 _weight
  ) external onlyOwner {
      derivatives[derivativeCount] = IDerivative(_contractAddress);
      weights[derivativeCount] = _weight;
      derivativeCount++;
      totalWeight += _weight;
      emit DerivativeAdded(_contractAddress, _weight, derivativeCount);
  }

SafEth.sol#L73-L74, SafEth.sol#L141-L142, SafEth.sol#L171-L173, SafEth.sol#L190-L193


favelanky G

## [G-1] Move checks to the top

Checks, effects, interactions is a general best practice and can be applicable to more than just reentrancy concerns. When one of the following error scenarios applies, users pay gas for all statements executed up until the revert itself. By performing checks such as these as early as possible, you are saving users gas in failure scenarios without any sacrifice to the happy case costs.

Moving the requirements to the top of the function can also improve readability.

There are 1 instances of this issue:

```solidity
File: /src/PrivatePool.sol

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

```

https://github.com/code-423n4/2023-04-caviar/tree/main/src/PrivatePool.sol#L225

## [G-2] Use function for repetitive blocks of code

There is 1 instances of this issue:

```solidity
File: /src/PrivatePool.sol

277:    if (royaltyFee > 0 && recipient != address(0)) {
			if (baseToken != address(0)) {
				ERC20(baseToken).safeTransfer(recipient, royaltyFee);
			} else {
				recipient.safeTransferETH(royaltyFee);
			}
		}

```

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

## [G-3] Use nested if and, avoid multiple check combinations

Using nested is cheaper than using && multiple check combinations. There are more advantages, such as easier to read code and better coverage reports.

There are 11 instances of this issue:

```solidity
File: /src/Factory.sol

87:  if ((_baseToken == address(0) && msg.value != baseTokenAmount) || (_baseToken != address(0) && msg.value > 0)) {

```

https://github.com/code-423n4/2023-04-caviar/tree/main/src/Factory.sol#L87

```solidity
File: /src/PrivatePool.sol

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

277:  if (royaltyFee > 0 && recipient != address(0)) {

344:  if (royaltyFee > 0 && recipient != address(0)) {

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

489:  if ((ba

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

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

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

277:    if (royaltyFee > 0 && recipient != address(0)) {
			if (baseToken != address(0)) {
				ERC20(baseToken).safeTransfer(recipient, royaltyFee);
			} else {
				recipient.safeTransferETH(royaltyFee);
			}
		}

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

87:  if ((_baseToken == address(0) && msg.value != baseTokenAmount) || (_baseToken != address(0) && msg.value > 0)) {

File: /src/PrivatePool.sol

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

File: /src/PrivatePool.sol

277:    if (royaltyFee > 0 && recipient != address(0)) {
			if (baseToken != address(0)) {
				ERC20(baseToken).safeTransfer(recipient, royaltyFee);
			} else {
				recipient.safeTransferETH(royaltyFee);
			}
		}

File: /src/Factory.sol

87:  if ((_baseToken == address(0) && msg.value != baseTokenAmount) || (_baseToken != address(0) && msg.value > 0)) {


ch0bu Q

## 1. Use a more recent version of Solidity, and avoid floating pragma statements

- Use a solidity version of at least 0.8.4 to get `bytes.concat()` instead of `abi.encodePacked(<bytes>,<bytes>)`
- Use a solidity version of at least 0.8.12 to get `string.concat()` instead of `abi.encodePacked(<str>,<str>)`
- Use a solidity version of at least 0.8.13 to get the ability to use `using for` with a list of free functions

Most contracts in scope use up to date version of 0.8.16, but there are some that use ^0.8.10, 0.6.12, ^0.5.16. These need to be updated, especially the ones that are not in the 0.8.x range.

## 2. File does not contain an SPDX Identifier

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cErc20ModifiedDelegator.sol
https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/JumpRateModelV2.sol

## 3. Function order

Functions should be ordered following [the Solidity conventions](https://docs.soliditylang.org/en/v0.8.17/style-guide.html#order-of-functions):

"Functions should be grouped according to their visibility and ordered:

- constructor
- receive function (if exists)
- fallback function (if exists)
- external
- public
- internal
- private"

In a lot of contracts this best practice has not been followed.

## 4. Use modifiers for better readability and code reuse

To improve readability and code reuse, a modifer function can be defined instead of performing a manual conditional check within multiple affected functions.

```
	require(msg.sender == admin, "Only admin can set KYC registry");
```

## 5. `require()` should be used instead of `assert()`

Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction’s available gas rather than returning it, as `require()`/`revert()` do. `assert()` should be avoided even past solidity version 0.8.0 as its [documentation](https://docs.soliditylang.org/en/v0.8.14/control-structures.html#panic-via-assert-and-error-via-require) states

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

// Code block 1 (unknown):
require(msg.sender == admin, "Only admin can set KYC registry");


0xjuicer G

## Gas - Creating a old variable isn't necessary.

The following pathern can be seen in multiples places in the code.

```solidity
    uint256 oldPriceCap = fTokenToUnderlyingPriceCap[fToken];
    fTokenToUnderlyingPriceCap[fToken] = value;
    emit PriceCapSet(fToken, oldPriceCap, value);
```

It can be replaced by:

```solidity
    emit PriceCapSet(fToken, fTokenToUnderlyingPriceCap[fToken], value);
    fTokenToUnderlyingPriceCap[fToken] = value;
```

The following pathern can be found on the following files:
- OndoPriceOracleV2.sol
- CashManager.sol
- OndoPriceOracle.sol
- KYCRegistryClient.sol
- And some more from compound

## Gas - Use merkle trees instead of array of addresses

On the `completeRedemptions()` function, the input has two arrays, `redeemers` and `refundees`, I suggest to use two merkle tree instead of two lists of addresses in order to save some gas when submiting the `completeRedemptions`.

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/CashManager.sol#L708-L709

// Code block 1 (solidity):
uint256 oldPriceCap = fTokenToUnderlyingPriceCap[fToken];
    fTokenToUnderlyingPriceCap[fToken] = value;
    emit PriceCapSet(fToken, oldPriceCap, value);

// Code block 2 (solidity):
emit PriceCapSet(fToken, fTokenToUnderlyingPriceCap[fToken], value);
    fTokenToUnderlyingPriceCap[fToken] = value;

uint256 oldPriceCap = fTokenToUnderlyingPriceCap[fToken];
    fTokenToUnderlyingPriceCap[fToken] = value;
    emit PriceCapSet(fToken, oldPriceCap, value);

emit PriceCapSet(fToken, fTokenToUnderlyingPriceCap[fToken], value);
    fTokenToUnderlyingPriceCap[fToken] = value;


BRONZEDISC Q

## QA
---

### Layout Order [1]

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

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

```solidity
// place this modifier before the constructor
106:  modifier onlyGov() {
```

---

### Function Visibility [2]

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

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/periphery/oracles/OracleRelay.sol

```solidity
// place this internal function after all the others
19:  function _setUnderlying(address _underlying) internal {
```

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

```solidity
// place this external function right after the constructor
40:  function isStale() external view returns (bool _stale) {
```

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

```solidity
// place this right after the constructor
54:  fallback() external {}
```

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/periphery/oracles/CTokenOracle.sol

```solidity
// place this external function before all the other ones
57:  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {
```

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

```solidity
// place this public function after the external ones.
51:  fun

// Code block 1 (solidity):
// place this modifier before the constructor
106:  modifier onlyGov() {

// Code block 2 (solidity):
// place this internal function after all the others
19:  function _setUnderlying(address _underlying) internal {

// Code block 3 (solidity):
// place this external function right after the constructor
40:  function isStale() external view returns (bool _stale) {

// Code block 4 (solidity):
// place this right after the constructor
54:  fallback() external {}

// Code block 5 (solidity):
// place this external function before all the other ones
57:  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {

// place this modifier before the constructor
106:  modifier onlyGov() {

// place this internal function after all the others
19:  function _setUnderlying(address _underlying) internal {

// place this external function right after the constructor
40:  function isStale() external view returns (bool _stale) {

// place this right after the constructor
54:  fallback() external {}

// place this external function before all the other ones
57:  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {

GovernorCharlie.sol, OracleRelay.sol, ChainlinkTokenOracleRelay.sol, EthSafeStableCurveOracle.sol, CTokenOracle.sol, ChainlinkOracleRelay.sol

EthSafeStableCurveOracle.sol, GovernorCharlie.sol, ChainlinkTokenOracleRelay.sol, OracleRelay.sol, ChainlinkOracleRelay.sol, CTokenOracle.sol


oualidpro G

## Summary

### Gas Optimizations

| |Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
| [[GAS‑1](#gas1-multiple-accesses-of-a-mapping-array-should-use-a-local-variable-cache)] | Multiple accesses of a mapping/array should use a local variable cache | 4 | 168 |
| [[GAS‑2](#gas2-use-assembly-to-calculate-hashes-to-save-gas)] | Use assembly to calculate hashes to save gas | 15 | 1200 |
| [[GAS‑3](#gas3-use-assembly-to-check-for-address-0)] | Use assembly to check for `address(0)` | 29 | 174 |
| [[GAS‑4](#gas4-optimize-address-storage-value-management-with-assembly)] | Optimize Address Storage Value Management with `assembly` | 23 | - |
| [[GAS‑5](#gas5-use-assembly-to-emit-events)] | Use assembly to emit events | 88 | 3344 |
| [[GAS‑6](#gas6-avoid-contract-existence-checks-by-using-low-level-calls)] | Avoid contract existence checks by using low level calls | 10 | 1000 |
| [[GAS‑7](#gas7-using-bools-for-storage-incurs-overhead)] | Using bools for storage incurs overhead | 14 | 1400 |
| [[GAS‑8](#gas8-cache-array-length-outside-of-loop)] | Cache array length outside of loop | 5 | 485 |
| [[GAS‑9](#gas9-state-variables-should-be-cached-in-stack-variables-rather-than-re-reading-them-from-storage)] | State variables should be cached in stack variables rather than re-reading them from storage | 2 | 194 |
| [[GAS‑10](#gas10-use-calldata-instead-of-memory-for-function-arguments-that-do-not-get-mutated)] | Use calldata instead of memory for function arguments that do not get mutated | 4 | - |
| [[GAS‑11](#gas11-add-unchecked-for-subtractions-where-the-operands-cannot-underflow-because-of-a-previous-require-or-if-statement)] | Add `unchecked {}` for subtractions where the operands cannot underflow because of a previous `require()` or `if`-statement | 13 | 1105 |
| [[GAS‑12](#gas12-don-t-compare-boolean-expressions-to-boolean-literals)] | Don't compare boolean expressions to boolean li

File: packages/contracts/contracts/CdpManagerStorage.sol

// @audit Cdps[_cdpId]
272:         Cdps[_cdpId].coll = 0;

File: packages/contracts/contracts/Dependencies/RolesAuthority.sol

// @audit capabilityFlag[target]
98:             capabilityFlag[target][functionSig] = CapabilityFlag.None;

// @audit getRolesWithCapability[target]
113:             getRolesWithCapability[target][functionSig] |= bytes32(1 << role);

// @audit enabledFunctionSigsByTarget[target]
114:             enabledFunctionSigsByTarget[target].add(bytes32(functionSig));


naszam G

# Gas Optimization: consider removing redundant checks in SortedCdps.reInsert()

**Context:** [SortedCdps.sol#L506-L508](https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/SortedCdps.sol#L506-L508)

**Description:**
`reInsert` currently uses a check to ensure `contains(_id)` (the List contains the id) even though it should be already checked in `_remove(_id)` method, same for the other check to ensure `_newNICR > 0` that should be already checked in `_insert(_id, _newNICR, _prevId, _nextId)`.

**Recommendation:**
Consider removing those redundant checks to improve gas efficiency.

```diff
diff --git a/packages/contracts/contracts/SortedCdps.sol b/packages/contracts/contracts/SortedCdps.sol
index 0fd34e7..1e3b413 100644
--- a/packages/contracts/contracts/SortedCdps.sol
+++ b/packages/contracts/contracts/SortedCdps.sol
@@ -502,10 +502,6 @@ contract SortedCdps is ISortedCdps {
         bytes32 _nextId
     ) external override {
         _requireCallerIsBOorCdpM();
-        // List must contain the node
-        require(contains(_id), "SortedCdps: List does not contain the id");
-        // NICR must be non-zero
-        require(_newNICR > 0, "SortedCdps: NICR must be positive");

         // Remove node from the list
         _remove(_id);
```
- Tests PASS
`yarn && forge build && forge test`
`Ran 39 test suites: 229 tests passed, 0 failed, 0 skipped (229 total tests)`

`yarn test`
```
  607 passing (34m)
  40 pending

Done in 2020.92s.
```

- Gas Report:

**Before**
```haskell
| contracts/SortedCdps.sol:SortedCdps contract |                 |        |        |         |         |
|----------------------------------------------|-----------------|--------|--------|---------|---------|
| Deployment Cost                              | Deployment Size |        |        |         |         |
| 1462626                                      | 7742            |        |        |         |         |
| Function Name

// Code block 1 (diff):
diff --git a/packages/contracts/contracts/SortedCdps.sol b/packages/contracts/contracts/SortedCdps.sol
index 0fd34e7..1e3b413 100644
--- a/packages/contracts/contracts/SortedCdps.sol
+++ b/packages/contracts/contracts/SortedCdps.sol
@@ -502,10 +502,6 @@ contract SortedCdps is ISortedCdps {
         bytes32 _nextId
     ) external override {
         _requireCallerIsBOorCdpM();
-        // List must contain the node
-        require(contains(_id), "SortedCdps: List does not contain the id");
-        // NICR must be non-zero
-        require(_newNICR > 0, "SortedCdps: NICR must be positive");

         // Remove node from the list
         _remove(_id);

// Code block 2 (unknown):
607 passing (34m)
  40 pending

Done in 2020.92s.


btk Q

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

| Risk   | Issues Details                                                                          | Number        |
|--------|-----------------------------------------------------------------------------------------|---------------|
| [L-01] | Low level calls with solidity version 0.8.14 and lower can result in optimiser bug      | 1             |
| [L-02] | No Storage Gap for Upgradeable contracts                                                | 2             |
| [L-03] | Loss of precision due to rounding                                                       | 10            |
| [L-04] | ERC4626Cloned 's implmentation is not fully up to EIP-4626's specification              | 1             |
| [L-05] | Multiple Pragma used                                                                    | All Contracts |
| [L-06] | Missing Event for initialize                                                            | 2             |
| [L-07] | Not all tokens support approve-max                                                      | 1             |
| [L-08] | Value is not validated to be different than the existing one	                           | 2             |
| [L-09] | Use require instead of assert                                                           | 20            |
| [L-10] | Misleading comment on `ActivePool.sol`                                                  | 1             |
| [L-11] | Integer overflow by unsafe casting                                                      | 3             |

| Total Non-Critical issues |
|---------------------------|

| Risk    | Issues Details                                                                                | Number        |
|---------|------------------------------------------------------------------------------------------------|---------------|
| [NC-01] | Include return parameters in NatSpec comments                                                  | All Contracts

    function _chainID() private pure returns (uint256 chainID) {
        assembly {
            chainID := chainid()
        }
    }

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


VIELITE Q

Repeated or Identical functions in the `WUSDA.sol` contract

```solidity
function burnAll() external override returns (uint256 _usdaAmount) {
    uint256 _wusdaAmount = balanceOf(_msgSender());
    _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
    _withdraw(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }
  function withdrawAll() external override returns (uint256 _wusdaAmount) {
    _wusdaAmount = balanceOf(_msgSender());
    uint256 _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
    _withdraw(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }
```
`link to code :` core/solidity/contracts/core/WUSDA.sol

// Code block 1 (solidity):
function burnAll() external override returns (uint256 _usdaAmount) {
    uint256 _wusdaAmount = balanceOf(_msgSender());
    _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
    _withdraw(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }
  function withdrawAll() external override returns (uint256 _wusdaAmount) {
    _wusdaAmount = balanceOf(_msgSender());
    uint256 _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
    _withdraw(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }


Baki G

## G-01 Calculate minOut only if minAmount == 0

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

If `minAmount` is > 0 , then we should not calculate `minOut` as it is a gas wastage.

so instead what we can do is
```solidity
    if (minAmount == 0) {
     minAmount = _ethToDpxEth
      ? (((_amount * getDpxEthPrice()) / 1e8) -
        (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16))
      : (((_amount * getEthPrice()) / 1e8) -
        (((_amount * getEthPrice()) * slippageTolerance) / 1e16));
    }

    // Swap the tokens
    amountOut = dpxEthCurvePool.exchange(
      _ethToDpxEth ? int128(int256(a)) : int128(int256(b)),
      _ethToDpxEth ? int128(int256(b)) : int128(int256(a)),
      _amount,
      minAmount
    );
```

## G-02 Only compute strike & timeToExpiry if needed

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

Only compute `strike` & `timeToExpiry` if `putOptionsRequired` == true

so instead what we can do is

```solidity

    if (putOptionsRequired) {
      uint256 timeToExpiry = IPerpetualAtlanticVault(
      addresses.perpetualAtlanticVault
      ).nextFundingPaymentTimestamp() - block.timestamp;

      uint256 strike = IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
      .roundUp(rdpxPrice - (rdpxPrice / 4)); // 25% below the current price

      wethRequired += IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
        .calculatePremium(strike, rdpxRequired, timeToExpiry, 0);
    }
```

## G-03 token balances are tracked separately on storage costing extra gas

there are some instances like https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/amo/UniV2LiquidityAmo.sol#L54 & https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/IRdpxV2Core.sol#L45 where token balances are tracked in a separately in storage rather

// Code block 1 (solidity):
if (minAmount == 0) {
     minAmount = _ethToDpxEth
      ? (((_amount * getDpxEthPrice()) / 1e8) -
        (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16))
      : (((_amount * getEthPrice()) / 1e8) -
        (((_amount * getEthPrice()) * slippageTolerance) / 1e16));
    }

    // Swap the tokens
    amountOut = dpxEthCurvePool.exchange(
      _ethToDpxEth ? int128(int256(a)) : int128(int256(b)),
      _ethToDpxEth ? int128(int256(b)) : int128(int256(a)),
      _amount,
      minAmount
    );

// Code block 2 (solidity):
if (putOptionsRequired) {
      uint256 timeToExpiry = IPerpetualAtlanticVault(
      addresses.perpetualAtlanticVault
      ).nextFundingPaymentTimestamp() - block.timestamp;

      uint256 strike = IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
      .roundUp(rdpxPrice - (rdpxPrice / 4)); // 25% below the current price

      wethRequired += IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
        .calculatePremium(strike, rdpxRequired, timeToExpiry, 0);
    }

if (minAmount == 0) {
     minAmount = _ethToDpxEth
      ? (((_amount * getDpxEthPrice()) / 1e8) -
        (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16))
      : (((_amount * getEthPrice()) / 1e8) -
        (((_amount * getEthPrice()) * slippageTolerance) / 1e16));
    }

    // Swap the tokens
    amountOut = dpxEthCurvePool.exchange(
      _ethToDpxEth ? int128(int256(a)) : int128(int256(b)),
      _ethToDpxEth ? int128(int256(b)) : int128(int256(a)),
      _amount,
      minAmount
    );

if (putOptionsRequired) {
      uint256 timeToExpiry = IPerpetualAtlanticVault(
      addresses.perpetualAtlanticVault
      ).nextFundingPaymentTimestamp() - block.timestamp;

      uint256 strike = IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
      .roundUp(rdpxPrice - (rdpxPrice / 4)); // 25% below the current price

      wethRequired += IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
        .calculatePremium(strike, rdpxRequired, timeToExpiry, 0);
    }

RdpxV2Core.sol#L545, RdpxV2Core.sol#L1189, UniV2LiquidityAmo.sol#L54, IRdpxV2Core.sol#L45


MrPotatoMagic Q

# Quality Assurance

| ID     | Issues                                                                                |
|--------|-------------------------------------------------------------------------------------------------------------------------------------------|
| [L-01] | 0 value function calls can spam off-chain tracking system                                                                                |
| [L-02] | Function spendVoltage() only allows spending 255 voltage maximum                                                                          |
| [L-03] | Avoid hardcoding tokenId 0 for battery game item                                                                                |
| [L-04] | Owner address is not provided admin access on transferOwnership()                                                                         |
| [L-05] | Admin access of previous owner is not revoked when ownership is transferred                                                               |
| [L-06] | Function updateFighterStaking() is not updated if user loses all his stake and round ends                                                 |
| [L-07] | Incorrect use of < instead of <= allows minting maximum of 1 NRN token less than expected total supply of 1 billion                       |
| [L-08] | Consider pausing/access controlling function burn() initially for a guarded launch                                                        |
| [L-09] | Transferring/selling Fighter NFT before winners are picked causes loss of reward NFT to previous owner                                    |
| [L-10] | Function claimRewards() does not check if the custom attribute values for weight and element fall in range [65,95] and [0,2] respectively |
| [L-11] | Nested for loops in function claimRewards() can DOS due to OOG exception                                                                  |
| [L-12] | Attac

File: VoltageManager.sol
109:     function spendVoltage(address spender, uint8 voltageSpent) public {
110:         require(spender == msg.sender || allowedVoltageSpenders[msg.sender]);
111:         if (ownerVoltageReplenishTime[spender] <= block.timestamp) {
112:             _replenishVoltage(spender);
113:         }
114:         ownerVoltage[spender] -= voltageSpent;
115:         emit VoltageRemaining(spender, ownerVoltage[spender]);
116:     }

File: Neuron.sol
147:     function claim(uint256 amount) external {
148:
149:         require(
150:             allowance(treasuryAddress, msg.sender) >= amount,
151:             "ERC20: claim amount exceeds allowance"
152:         );
153:
154:         transferFrom(treasuryAddress, msg.sender, amount);
155:         emit TokensClaimed(msg.sender, amount);
156:     }
157:


darkbluedot Q

Quality Assurance -

Simple Account -

1 - The function call in the modifier is not necessary, we can also use this in the modifier and achieve the same results.  The use of the modifier is suggested as that makes the code clean and readable. Also, it saves some gas on the way as well. So, instead of writing the modifier the way it's written, it can also be written as
```
 modifier onlyOwner(){
        require(msg.sender == address(this) || owner == msg.sender  , "Only Owner");
        _;
    }
```
Also, you can see that here the msg.sender == address(this) is written first, and then the other check is written. This is because the probability that the calling node is not a contract and just an EOA is higher so, the if statement will short-circuit and will not check the other statement. This will in turn save some gas costs in execution.

2 - The function requireFromEntryPointOrOwner is used as a guard check in many functions. It's suggested to use a modifier to add guard checks rather than calling this function everywhere.

// Code block 1 (unknown):
modifier onlyOwner(){
        require(msg.sender == address(this) || owner == msg.sender  , "Only Owner");
        _;
    }


Shubham G

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-01](#GAS-01) | With assembly, `.call (bool sent)` transfer can be done to optimize gas | 5 |
| [GAS-02](#GAS-02) | Setting the constructor to `payable`| 4 |
| [GAS-03](#GAS-03) | Make for loop unchecked | 7 |
| [GAS-04](#GAS-04) | Unnecessary check in `if` condition | 1 |

## [GAS-01]  With assembly, `.call (bool sent)` transfer can be done to optimize gas

"(bool success, bytes memory returnData) = target.call()" automatically copies the return data to memory even if you omit the returnData variable.
If a relayer executes transactions with such calls it can lead to a gas-griefing attack.

In the given contracts, it is advisable that `return` data `(bool sent,)` which by default is stored due to EVM architecture, is executed with the parameters
inOffset, inSize, retOffset, retSize values set to (0,0,0,0). In this way, the storage disappears and gas optimization is provided.

For reference, refer to this thread
https://twitter.com/pashovkrum/status/1607024043718316032?t=xs30iD6ORWtE2bTTYsCFIQ&s=19

|Contract|Method|Before|After|Gas Saved|
|:-|:-:|:-:|:-:|:-:|
| Reth | deposit | 176462 | 176448 | 14 |
| Reth | withdraw | 181057 | 181003 | 54 |
| SafeEth | rebalanceToWeights | 727618 | 727388 | 230|
| SafeEth | stake | 527253 | 527184 | 69 |
| SafeEth | unstake | 516303 | 516075 | 228|
| SafeEthV2Mock | adminWithdrawDerivative | 195722 | 195668 | 54|
| [Total](#Total) | | | | [649](#649) |

There are 5 instances of the topic.
**Before:**

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

60:        (bool sent, ) = address(msg.sender).call{value: address(this).balance}(
            ""
        );
```
```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

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

```

```solidity
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivat

// Code block 1 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

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

// Code block 2 (solidity):
File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

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

File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol

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

File: 2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol

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


dharma09 G

## REVRT LEND GAS OPTIMIZATIONS

## INTRODUCTION

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

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

- [Re-arrange state variable order to save storage slots (Saves ~4000 Gas)](#g-01-re-arrange-state-variable-order-to-save-storage-slots-saves-4000-gas)
- [Reduce the size of struct variables and pack them together to save storage slots(Instances Missed by bot)(Gas Saved ~ 4000 Gas)](#g-02-reduce-the-size-of-struct-variables-and-pack-them-together-to-save-storage-slotsinstances-missed-by-botgas-saved--4000-gas)
- [Reorder accessing state variables in `execute` function to save gas
](#g-03-reorder-accessing-state-variables-in-execute-function-to-save-gas)
- [Cache repeated calculations to avoid recalculating the same values multiple times](#g-04-cache-repeated-calculations-to-avoid-recalculating-the-same-values-multiple-times)
- [Cache calculations in loop to avoid re-calculating on each iteration](#g-05-cache-calculations-in-loop-to-avoid-re-calculating-on-each-iteration)
- [Using YUL's selfbalance is cheaper than address(this).balance](#g-06-using-yuls-selfbalance-is-cheaper-than-addressthisbalance)
- [Reduce Storage Access By not accessing positionBalances[tokenId][token] a second time to subtract amount](#g-07-reduce-storage-access-by-not-accessing-positionbalancestokenidtoken-a-second-time-to-subtract-amount)
- [Utilize local variables instead of repeatedly accessing storage variables](#g-08-utilize-local-variables-instead-of-repeatedly-accessing-storage-variables)

## Gas report

**Note: The issues addressed here were not reported by the bot, f

File: src/V3Oracle.sol
25: uint16 public constant MIN_PRICE_DIFFERENCE = 200; //2% /@audit pack

    uint256 private constant Q96 = 2 ** 96;
    uint256 private constant Q128 = 2 ** 128;

### Instances 2

We can re-arrange the order of V3vault.sol to save 1 storage slot (~2000 Gas) pack emergencyAdmin address with MIN_RESERVE_PROTECTION_FACTOR_X32address
### Proof of Code
- [V3Vault.sol#L167](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L167)


Raihan Q

# [L-01] Reentrancy Vulnerability in DestinationBridge._mintIfThresholdMet Function

The function _mintIfThresholdMet(bytes32) in the DestinationBridge contract is vulnerable to a reentrancy attack. This is due to external calls being made before state variables are updated.

Details
The _mintIfThresholdMet(bytes32) function makes external calls to ALLOWLIST.setAccountStatus(txn.sender,ALLOWLIST.getValidTermIndexes()[0],true) and TOKEN.mint(txn.sender,txn.amount). After these calls, the state variable txnHashToTransaction[txnHash] is deleted. This order of operations opens up the potential for a reentrancy attack.

Code Snippet

```solidity
337: function _mintIfThresholdMet(bytes32 txnHash) internal {
338:     bool thresholdMet = _checkThresholdMet(txnHash);
339:     Transaction memory txn = txnHashToTransaction[txnHash];
340:     if (thresholdMet) {
341:         _checkAndUpdateInstantMintLimit(txn.amount);
342:         if (!ALLOWLIST.isAllowed(txn.sender)) {
343:             ALLOWLIST.setAccountStatus(
344:               txn.sender,
345:               ALLOWLIST.getValidTermIndexes()[0],
346:               true
347:             );
348:         }
349:         TOKEN.mint(txn.sender, txn.amount);
350:         delete txnHashToTransaction[txnHash];
351:         emit BridgeCompleted(txn.sender, txn.amount);
352:     }
353: }
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L337-L353

### Recommendation
To mitigate this issue, the check-effects-interactions pattern should be applied. This means that all the state changes should be made before calling external contracts. This can prevent potential reentrancy attacks. The state variable txnHashToTransaction[txnHash] should be deleted before the external calls to ALLOWLIST.setAccountStatus and TOKEN.mint.

# [L-02] Variable Shadowing in DestinationBridge Constructor
The _owner variable in the DestinationBridge constructor shadows the _owner state variable from the Ownable contrac

60: constructor(
61:     address _token,
62:     address _axelarGateway,
63:     address _allowlist,
64:     address _ondoApprover,
65:     address _owner,
66:     uint256 _mintLimit,
67:     uint256 _mintDuration
68: )

// Code block 1 (solidity):
337: function _mintIfThresholdMet(bytes32 txnHash) internal {
338:     bool thresholdMet = _checkThresholdMet(txnHash);
339:     Transaction memory txn = txnHashToTransaction[txnHash];
340:     if (thresholdMet) {
341:         _checkAndUpdateInstantMintLimit(txn.amount);
342:         if (!ALLOWLIST.isAllowed(txn.sender)) {
343:             ALLOWLIST.setAccountStatus(
344:               txn.sender,
345:               ALLOWLIST.getValidTermIndexes()[0],
346:               true
347:             );
348:         }
349:         TOKEN.mint(txn.sender, txn.amount);
350:         delete txnHashToTransaction[txnHash];
351:         emit BridgeCompleted(txn.sender, txn.amount);
352:     }
353: }


Co0nan Q

1. setPools will reset the pools array if it's called with an empty array.
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L93
```
 function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        //@audit-qa if the function called with 0 array length. it will resetr the pools. Must check it's > 0
        pools = _pools;
    }
```

2. User can front-run the TX which invoke `notifyRewardAmount` and stake large amount to profit from the reward.
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L227
```
function notifyRewardAmount(uint amount, uint tokenType) external { // @audit-qa bot can fornt-run and stake large amount to before ditrbuiteRewards
        require(msg.sender == address(configurator));
        if (totalStaked() == 0) return;
        require(amount > 0, "amount = 0");
        if(tokenType == 0) {
            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);
            rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked();
        } else if(tokenType == 1) {
            ERC20 token = ERC20(configurator.stableToken());
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked();
        } else {
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked();
        }
    }
```

3. `executeFlashloan` shouldn't be marked as payable, any ETH sent by mistake could be lost.
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/PeUSDMainnetStableVision.sol#L129

4. Due to rounding error on `grabEsLBR` function when calculating the amount to be burned, an attacker can send 3 WEI as amount which will result attacker mint 3 WEI without burning any tokens due to the calculati

// Code block 1 (unknown):
function setPools(address[] memory _pools) external onlyOwner {
        for (uint i = 0; i < _pools.length; i++) {
            require(configurator.mintVault(_pools[i]), "NOT_VAULT");
        }
        //@audit-qa if the function called with 0 array length. it will resetr the pools. Must check it's > 0
        pools = _pools;
    }

// Code block 2 (unknown):
function notifyRewardAmount(uint amount, uint tokenType) external { // @audit-qa bot can fornt-run and stake large amount to before ditrbuiteRewards
        require(msg.sender == address(configurator));
        if (totalStaked() == 0) return;
        require(amount > 0, "amount = 0");
        if(tokenType == 0) {
            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);
            rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked();
        } else if(tokenType == 1) {
            ERC20 token = ERC20(configurator.stableToken());
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked();
        } else {
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked();
        }
    }


petrichor G

# GAS OPTIMIZATION

# SUMMARY
|      |  issue  |  instance  |
|------|---------|------------|
|[G‑01]|Optimize External Calls with Assembly for Memory Efficiency|115|
|[G‑02]|Use assembly to validate msg.sender |22|
|[G‑03]|Avoid contract existence checks by using low level calls|19|
|[G‑04]| >= costs less gas than >|14|
|[G‑05]|Amounts should be checked for 0 before calling a transfer|6|
|[G‑06]|Functions guaranteed to revert when called by normal users can be marked payable|11|
|[G‑07]|Structs can be modified to fit in fewer storage slots|17|
|[G‑08]| Use constants instead of type(uintx).max|19|
|[G‑09]|Using storage instead of memory for structs/arrays saves gas|38|
|[G‑10]|Can Make The Variable Outside The Loop To Save Gas |4|
|[G‑11]|Use Modifiers Instead of Functions To Save Gas|9|
|[G‑12]| bytes constants are more eficient than string constans|12|
|[G‑13]|Use assembly to hash instead of solidity|8|
|[G‑14]|internal functions only called once can be inlined to save gas|20|
|[G‑15]|Use nested if statements instead of &&|15|
|[G‑16]|Do-While loops are cheaper than for loops|3|
|[G‑17]|Use hardcode address instead address(this)|17|
|[G‑18]|Not using the named return variables when a function returns, wastes deployment gas|7|
|[G‑19]|Unnecessary computation|3|
|[G‑20]|Pre-increment and pre-decrement are cheaper than +1 ,-1|6|
|[G‑21]|Structs can be packed to use fewer storage slots|3|
|[G‑22]|abi.encode() is less efficient than abi.encodePacked()|2|
|[G‑23]|Use assembly in place of abi.decode to extract calldata values more efficiently|4|
|[G‑24]|Use of += is cheaper for mappings|2|
|[G‑25]|Use assembly to write address storage values|6|
|[G‑26]|Splitting  Require() Statements That Use  && Saves |9|
|[G‑27]|Sort Solidity operations using short-circuit mode|2|
|[G‑28]|Avoid updating storage when the value hasn't changed|12|
|[G‑29]| Multiple Address/id Mappings Can Be Combined Into A Single Mapping Of An Address/id To A Struct, Where Appropriate|2|
|[G‑30]| Use ass

File:  packages/contracts/contracts/HintHelpers.sol
72              vars.currentCdpId = sortedCdps.getPrev(vars.currentCdpId);
                vars.currentCdpUser = sortedCdps.getOwnerAddress(vars.currentCdpId);

90   uint256 currentCdpDebt = cdpManager.getSyncedCdpDebt(vars.currentCdpId);

116             vars.currentCdpId = sortedCdps.getPrev(vars.currentCdpId);
                vars.currentCdpUser = sortedCdps.getOwnerAddress(vars.currentCdpId);

141   uint256 newCollShare = cdpManager.getSyncedCdpCollShares(vars.currentCdpId);

169  uint256 arrayLength = cdpManager.getActiveCdpsCount();

175  hint = sortedCdps.getLast();

185         bytes32 _cId = cdpManager.getIdFromCdpIdsArray(arrayIndex);
            uint256 currentNICR = cdpManager.getSyncedNominalICR(_cId);

File:  packages/contracts/contracts/LeverageMacroBase.sol
143  initialCdpIndex = sortedCdps.cdpCountOf(address(this));

148  IERC3156FlashLender(address(borrowerOperations)).flashLoan(

155  IERC3156FlashLender(address(activePool)).flashLoan(

173  bytes32 cdpId = sortedCdps.cdpOfOwnerByIndex(address(this), initialCdpIndex);

187  ICdpManagerData.Cdp memory cdpInfo = cdpManager.Cdps(checkParams.cdpId);

199  ICdpManagerData.Cdp memory cdpInfo = cdpManager.Cdps(checkParams.cdpId);

220     uint256 ebtcBal = ebtcToken.balanceOf(address(this));
        uint256 collateralBal = stETH.sharesOf(address(this));

224   ebtcToken.transfer(msg.sender, ebtcBal);

228   stETH.transferShares(msg.sender, collateralBal);

237  IERC20(token).safeTransfer(msg.sender, amount);

405  IERC20(swapData.tokenForSwap).safeApprove(

427  IERC20(swapData.tokenForSwap).safeApprove(swapData.addressForApprove, 0);


Sathish9098 Analysis

# Salty.IO Analysis

## Overview
Salty.IO is a Decentralized Exchange (DEX) built on the Ethereum blockchain, featuring an innovative Automatic Atomic Arbitrage (AAA) mechanism. This mechanism seeks to capitalize on market inefficiencies during token swaps, generating profits which are then distributed among liquidity providers and stakers. Salty.IO also introduces USDS, an overcollateralized ERC20 stablecoin, backed by WBTC/WETH LP tokens. The platform is wholly decentralized from inception, with governance, parameters, and contract operations controlled by a Decentralized Autonomous Organization (DAO). Salty.IO emphasizes zero fees on swaps, enhancing its attractiveness to users. It uses a mix of Chainlink, Uniswap v3 TWAP, and its own reserve data to maintain accurate price feeds, ensuring robustness and resilience against market manipulation.

## Systemic risks

### Oracle Reliability and Manipulation

#### Chainlink Oracle Risks (CoreChainlinkFeed Contract):

``Data Source Centralization``: Chainlink, while decentralized, still relies on a set of node operators to provide data. If these nodes are compromised or colluded, it might lead to false data being fed into the system.

 ``Time Lag in Data Updates``: Prices provided by Chainlink are not real-time; they have a certain latency. Rapid market movements might not be reflected immediately in the oracle data, leading to temporal arbitrage opportunities.

  ``Smart Contract Integration Risk``: The integration of Chainlink oracles within the CoreChainlinkFeed contract requires careful handling of data retrieval and error checking. Mismanagement here could lead to incorrect data being accepted as valid.

#### Uniswap v3 TWAP Oracle Risks (CoreUniswapFeed Contract):

``Manipulation via Pool``: TWAP (Time-Weighted Average Price) is derived from a Uniswap v3 pool. If a pool's liquidity is low or if there are large trades, it could temporarily skew the price, affecting the TWAP.

 ``Vulnerability to Flash Loans``

function changeBootstrappingRewards(bool increase) external onlyOwner
function changePercentPolRewardsBurned(bool increase) external onlyOwner
function changeBaseBallotQuorumPercent(bool increase) external onlyOwner
function changeBallotDuration(bool increase) external onlyOwner
function changeRequiredProposalPercentStake(bool increase) external onlyOwner
function changeMaxPendingTokensForWhitelisting(bool increase) external onlyOwner
function changeArbitrageProfitsPercentPOL(bool increase) external onlyOwner
function changeUpkeepRewardPercent(bool increase) external onlyOwner
function whitelistPool( IPools pools, IERC20 tokenA, IERC20 tokenB ) external onlyOwner
function unwhitelistPool( IPools pools, IERC20 tokenA, IERC20 tokenB ) external onlyOwner
function changeMaximumWhitelistedPools(bool increase) external onlyOwner
function changeMaximumInternalSwapPercentTimes1000(bool increase) external onlyOwner
function setContracts( IDAO _dao, ICollateralAndLiquidity _collateralAndLiquidity ) external onlyOwner
function setInitialFeeds( IPriceFeed _priceFeed1, IPriceFeed _priceFeed2, IPriceFeed _priceFeed3 ) public onlyOwner
function setPriceFeed( uint256 priceFeedNum, IPriceFeed newPriceFeed ) public onlyOwner
function changeMaximumPriceFeedPercentDifferenceTimes1000(bool increase) public onlyOwner
function changePriceFeedModificationCooldown(bool increase) public onlyOwner
function changeRewardsEmitterDailyPercent(bool increase) external onlyOwner
function changeEmissionsWeeklyPercent(bool increase) external onlyOwner
function changeStakingRewardsPercent(bool increase) external onlyOwner
function changePercentRewardsSaltUSDS(bool increase) external onlyOwner
function setCollateralAndLiquidity( ICollateralAndLiquidity _collateralAndLiquidity ) external onlyOwner
function changeRewardPercentForCallingLiquidation(bool increase) external onlyOwner
function changeMaxRewardValueForCallingLiquidation(bool increase) external onlyOwner
function changeMinimumCollateralValueForB

function step1() public onlySameContract
function step2(address receiver) public onlySameContract
function step3() public onlySameContract
function step4() public onlySameContract
function step5() public onlySameContract
function step6() public onlySameContract
function step7() public onlySameContract
function step8() public onlySameContract
function step9() public onlySameContract
function step10() public onlySameContract
function step11() public onlySameContract


twcctop Q

https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/VaultController.sol#L561

```solidity

if (_isUSDA) {
      // now send usda to the target, equal to the amount they are owed
      usda.vaultControllerMint(_target, _amount);
    } else {
      // send sUSD to the target from reserve instead of mint
      usda.vaultControllerTransfer(_target, _amount);
    }

// emit the event
    emit BorrowUSDA(_id, address(_vault), _amount, _fee);

```

seem that whether borrow asset is USDA or sUSD,it will emit same message

// Code block 1 (solidity):
if (_isUSDA) {
      // now send usda to the target, equal to the amount they are owed
      usda.vaultControllerMint(_target, _amount);
    } else {
      // send sUSD to the target from reserve instead of mint
      usda.vaultControllerTransfer(_target, _amount);
    }

// emit the event
    emit BorrowUSDA(_id, address(_vault), _amount, _fee);


mrudenko Q

QA report
RWADynamicOracle
1) While the variable names are mostly descriptive, there are places where they could be clearer. For instance, dailyIR could be renamed to dailyInterestRate for clarity.
2) Allow Deletion of Ranges:
```
function deleteRange(uint256 indexToDelete) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(indexToDelete < ranges.length, "Invalid index");
    for (uint256 i = indexToDelete; i < ranges.length - 1; i++) {
        ranges[i] = ranges[i + 1];
    }
    ranges.pop();
}

```

SourceBridge.sol
Add a Function to Remove Supported Chains:
```
function removeDestinationChain(string memory destinationChain) external onlyOwner {
    require(bytes(destChainToContractAddr[destinationChain]).length != 0, "Chain not found");
    delete destChainToContractAddr[destinationChain];
    emit DestinationChainRemoved(destinationChain);
}

```

In the setDestinationChainContractAddress function, consider adding checks to ensure that the provided address is not the zero address.

```
require(contractAddress != address(0), "Invalid address");
```

In the burnAndCallAxelar function, there's a check for msg.value == 0. Consider making the required gas fee a variable or a setting in the contract so that it can be adjusted as gas prices change, rather than hardcoding a check against 0

DestinationBridge.sol
Add a Function to Remove Supported Chains:
```
function removeChainSupport(string memory srcChain) external onlyOwner {
    require(chainToApprovedSender[srcChain] != bytes32(0), "Chain not found");
    delete chainToApprovedSender[srcChain];
    emit ChainSupportRemoved(srcChain);
}

```

In the addChainSupport and setThresholds functions, consider adding checks to ensure that the provided addresses or values are not default values (e.g., address(0) or 0).
```
require(_token != address(0), "Invalid token address");
```

// Code block 1 (unknown):
function deleteRange(uint256 indexToDelete) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(indexToDelete < ranges.length, "Invalid index");
    for (uint256 i = indexToDelete; i < ranges.length - 1; i++) {
        ranges[i] = ranges[i + 1];
    }
    ranges.pop();
}

// Code block 2 (unknown):
function removeDestinationChain(string memory destinationChain) external onlyOwner {
    require(bytes(destChainToContractAddr[destinationChain]).length != 0, "Chain not found");
    delete destChainToContractAddr[destinationChain];
    emit DestinationChainRemoved(destinationChain);
}

// Code block 3 (unknown):
require(contractAddress != address(0), "Invalid address");

// Code block 4 (unknown):
function removeChainSupport(string memory srcChain) external onlyOwner {
    require(chainToApprovedSender[srcChain] != bytes32(0), "Chain not found");
    delete chainToApprovedSender[srcChain];
    emit ChainSupportRemoved(srcChain);
}

// Code block 5 (unknown):
require(_token != address(0), "Invalid token address");


lightoasis Q

## 1. There are no min and max values set for the reserve factor.

The reserve factor is the percentage difference between debt and lend interest. There are currently no max and min values for the reserve factor. This could impact the borrowers and the lenders it is set too high or too low.
[V3Vault.sol#L838](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L838)
```
    function setReserveFactor(uint32 _reserveFactorX32) external onlyOwner {
        reserveFactorX32 = _reserveFactorX32;
        emit SetReserveFactor(_reserveFactorX32);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider implementing max and min values for the reserve factor. These values shouldn't be exceeded when setting the reserve factor.

## 2. Reentrancy issue in V3Vault.create()

### Vulnerability details
### Bug Description

In create, a token transfer is triggered without a reentrancy guard in place which can lead to exploits. The function also doesn't follow CEI (Checks and Effects Interaction pattern) which means to make neccessary checks and update the contract's state before makinng any external calls. The function in it's current implementation is vulnerabile to reentrancy attacks.
[V3Vault.sol#L401C36-L401C52](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L401C36-L401C52)

```
    function create(uint256 tokenId, address recipient) external override {
        nonfungiblePositionManager.safeTransferFrom(msg.sender, address(this), tokenId, abi.encode(recipient));
    }
```

## Impact
Theft of funds through reentrancy exploits. The lack of a reentrancy guard and necessary state updates before the external call leaves the function open to reentrancy attacks which can lead to a theft of funds.

## Recommended Mitigation
Consider Implementing a reentrancy guard on the create function.

// Code block 1 (unknown):
function setReserveFactor(uint32 _reserveFactorX32) external onlyOwner {
        reserveFactorX32 = _reserveFactorX32;
        emit SetReserveFactor(_reserveFactorX32);
    }

// Code block 2 (unknown):
function create(uint256 tokenId, address recipient) external override {
        nonfungiblePositionManager.safeTransferFrom(msg.sender, address(this), tokenId, abi.encode(recipient));
    }


oyc_109 Q

## [L-01] Unspecific Compiler Version Pragma

Avoid floating pragmas for non-library contracts.

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

A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.

It is recommended to pin to a concrete compiler version.

```
2023-02-ethos/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::3 => pragma solidity ^0.8.0;
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultERC4626.sol::3 => pragma solidity ^0.8.0;
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol::3 => pragma solidity ^0.8.0;
```

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

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

```
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::87 => uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::93 => lastIssuanceTimestamp = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::113 => lastDistributionTime = block.timestamp.add(distributionPeriod);
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::114 => lastIssuanceTimestamp = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LUSDToken.sol::128 => deploymentStartTime = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::417 => if (_respons

2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::87 => uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::93 => lastIssuanceTimestamp = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::113 => lastDistributionTime = block.timestamp.add(distributionPeriod);
2023-02-ethos/Ethos-Core/contracts/LQTY/CommunityIssuance.sol::114 => lastIssuanceTimestamp = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/LUSDToken.sol::128 => deploymentStartTime = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::417 => if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::425 => return block.timestamp.sub(_response.timestamp) > TIMEOUT;
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::450 => if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol::458 => return block.timestamp.sub(_tellorResponse.timestamp) > TIMEOUT;
2023-02-ethos/Ethos-Core/contracts/RedemptionHelper.sol::309 => require(block.timestamp >= systemDeploymentTime.add(BOOTSTRAP_PERIOD));
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::1501 => uint timePassed = block.timestamp.sub(lastFeeOperationTime);
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::1504 => lastFeeOperationTime = block.timestamp;
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::1505 => emit LastFeeOpTimeUpdated(block.timestamp);
2023-02-ethos/Ethos-Core/contracts/TroveManager.sol::1517 => return (block.timestamp.sub(lastFeeOperationTime)).div(SECONDS_IN_ONE_MINUTE);
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol::26 => uint256 activation; // Activation block.timestamp
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol::32 => uint256 lastReport; // block.timestamp of the last time a report occured
202

// Code block 1 (unknown):
2023-02-ethos/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::3 => pragma solidity ^0.8.0;
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultERC4626.sol::3 => pragma solidity ^0.8.0;
2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol::3 => pragma solidity ^0.8.0;


T1MOH Q

## 1. Low. `getAssetCurrentLimit()` will revert if admin sets new limit to less than deposited now
### Vulnerability Details
https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTDepositPool.sol#L56-L58

Limit from config can be lower than actually deposited, therefore function will revert instead of returning 0
```solidity
    function getAssetCurrentLimit(address asset) public view override returns (uint256) {
        return lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset);
    }
```

### Recommended mitigation steps
Return 0 instead of revert
```diff
    function getAssetCurrentLimit(address asset) public view override returns (uint256) {
-       return lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset);
+       return lrtConfig.depositLimitByAsset(asset) > getTotalAssetDeposits(asset)
+           ? lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset)
+           : 0;
    }
```

## 2. Low. Protocol assumes that all LSTs have 18 decimals
### Vulnerability Details
https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTOracle.sol#L52-L79

Protocol assumes that all LSTs have 18 decimals, it can be different in the future.
Here function expects that `totalAssetAmt` has 1e18 precission. Otherwise it will over/under-estimate asset:
```solidity
    function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

        if (rsEthSupply == 0) {
            return 1 ether;
        }

        uint256 totalETHInPool;
        address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
        uint256 supportedAssetCount = supportedAssets.length;

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {

// Code block 1 (solidity):
function getAssetCurrentLimit(address asset) public view override returns (uint256) {
        return lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset);
    }

// Code block 2 (diff):
function getAssetCurrentLimit(address asset) public view override returns (uint256) {
-       return lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset);
+       return lrtConfig.depositLimitByAsset(asset) > getTotalAssetDeposits(asset)
+           ? lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset)
+           : 0;
    }


Bough Q

# [L-01] Dependence on predictable environment variable
 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1501
 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1504
 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1505
 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1516

 The block.timestamp environment variable is used to determine a control flow decision. The  the value of variable block.imestamp is predictable and can be manipulated by a malicious miner.

# [N-01] Use a more recent version of Solidity
The use of the more recent version of solidity can help in preventing Integer Arithmetic Bugs (>0.8.0)

# [N-02] Function writing that does not comply with the Solidity Style Guide

Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.

Functions should be grouped according to their visibility and ordered:

-   constructor
-   receive function (if exists)
-   fallback function (if exists)
-   external
-   public
-   internal
-   private
-   within a grouping, place the view and pure functions last

This style guide is proposed by solidity community. It  intended to provide coding conventions for writing Solidity code
https://docs.soliditylang.org/en/v0.8.17/style-guide.html

# [N-03] require()/revert() statements should have descriptive reason strings
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/RedemptionHelper.sol#L179
```require(totals.totalCollateralDrawn > 0);```

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/RedemptionHelper.sol#L304
``` require(_maxFeePercentage >= troveManager.REDEMPTION_FEE_FLOOR() && _maxFeePercentage <= DECIMAL_PRECISION);```

https://github.com/

// Code block 1 (unknown):
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/RedemptionHelper.sol#L304

TroveManager.sol#L1501, TroveManager.sol#L1504, TroveManager.sol#L1505, TroveManager.sol#L1516, RedemptionHelper.sol#L179, RedemptionHelper.sol#L304, RedemptionHelper.sol#L309


minhtrng Q

## [01] missing check for msg.value if baseToken not ETH

`PrivatePool.flashLoan` does not check if ETH has been sent, even though baseToken is not ETH. The functions `buy`, `change`, `deposit` do perform this check:

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

This could lead to users sending ETH to the contract on accident.

Mitigation: perform the check

## [02] underflow for tokens with less than 4 decimals

Function `PrivatePool.changeFeeQuote` will revert if the baseToken has less than 4 decimals, rendering `change` function unusable:

```js
function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) {

uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4;
```

Tokens with less than 4 decimals are too rare for this to likely be an issue, but it could be fixed by adjusting the math (dividing by `10 ** (4-token.decimals)` if `decimals <= 3`).

## [03] owner can do MEV at cost of users

The functions `setFeeRate` and `setVirtualReserves` can be used by the owner to do MEV by frontrunning user transactions and adjusting the values to reach the users max slippage values.

Only viable mitigation would be removing them. But slippage is something that a user has to take into account anyway, so only a minor issue and likely users responsibility to use periphery properly.

## [04] Documentation-implementation-mismatch for deposits and LPs

According to https://docs.caviar.sh/: `"Custom Pools: [...] Like shared pools, liquidity providers earn fees from trades against their pool."`

This is not the case in the current implementation (by design) and users might deposit in the believe of earning yield and being able to redeem, but would lose their deposits instead. Mitigation: update docs

## [05] Function documentation misleading for `EthRouter.buy`

`EthRouter.buy` states:

```js
/// @param payRoyalties Whether to pay royalties or not.
```

It mi

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

// Code block 2 (js):
function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) {

uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4;

// Code block 3 (js):
/// @param payRoyalties Whether to pay royalties or not.


gkrastenov Q

## [NC-01] Unnecessarily checking for non-zero address
The addresses of tokens `stETH`, `rETH` and `cbETH` are unnecessarily checked for a non-zero address in the `initialize` function of the `LRTConfig` contract. This check is also performed in the `_setToken` function.

```solidity
UtilLib.checkNonZeroAddress(stETH);
UtilLib.checkNonZeroAddress(rETH);
UtilLib.checkNonZeroAddress(cbETH);
```

```solidity
 function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);

        .....
 }
```

## [NC-02] Typos in the codebase
1. Typo in the comment above `initialize` function of the `LRTConfig` contract.

```diff
    /// @param cbETH cbETH address -> coinbase staked eth
-   /// @param rsETH_ cbETH address
+   /// @param rsETH_ rsETH_ address
    function initialize(
        address admin,
        address stETH,
```

2. Typo in the naming of the event

```diff
-emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
+emit NodeDelegatorAddedInQueue(nodeDelegatorContracts[i]);
```

## [NC-03] Check return shares for successful deposit
The `depositIntoStrategy` function of the Eigen StrategyManager contract returns shares of a depositor. It can be checked if `shares != 0` for a successful deposit.

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

+uint256 shares = IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

+ if(shares == 0) revert UnsuccessfulDeposit();

```

## [NC-04] Check if new deposit limit is below total asset deposits
The Manager has the right to update the deposit limit for every asset. If the new value for the deposit limit is below the total asset deposits `getTotalAssetDeposits` (of the DepositPool, all NDCs and staked ethers in the EigenLayer protocol) a stack underflow will occur in the calculation of `getAssetCurrentLimit()`.

```solidity
return lrtConfig.depositLimitByA

// Code block 1 (solidity):
UtilLib.checkNonZeroAddress(stETH);
UtilLib.checkNonZeroAddress(rETH);
UtilLib.checkNonZeroAddress(cbETH);

// Code block 2 (solidity):
function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);

        .....
 }

// Code block 3 (diff):
/// @param cbETH cbETH address -> coinbase staked eth
-   /// @param rsETH_ cbETH address
+   /// @param rsETH_ rsETH_ address
    function initialize(
        address admin,
        address stETH,

// Code block 4 (diff):
-emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
+emit NodeDelegatorAddedInQueue(nodeDelegatorContracts[i]);

// Code block 5 (diff):
-IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

+uint256 shares = IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

+ if(shares == 0) revert UnsuccessfulDeposit();

/// @param cbETH cbETH address -> coinbase staked eth
-   /// @param rsETH_ cbETH address
+   /// @param rsETH_ rsETH_ address
    function initialize(
        address admin,
        address stETH,

-emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]);
+emit NodeDelegatorAddedInQueue(nodeDelegatorContracts[i]);

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

+uint256 shares = IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);

+ if(shares == 0) revert UnsuccessfulDeposit();

UtilLib.checkNonZeroAddress(stETH);
UtilLib.checkNonZeroAddress(rETH);
UtilLib.checkNonZeroAddress(cbETH);

function _setContract(bytes32 key, address val) private {
        UtilLib.checkNonZeroAddress(val);

        .....
 }


Aymen0909 G

# Gas Optimizations

## Summary

|       | Issue        | Instances    |
| :---: |:-------------|:------------:|
| 1  | Using `storage` instead of `memory` for struct/array saves gas | 8 |
| 2  | `storage` variable should be cached into `memory` variables instead of re-reading them | 1 |
| 3  | Use `unchecked` blocks to save gas | 3 |
| 4  | Use `calldata` instead of `memory` for function parameters type  | 13 |
| 5  | Multiple address/IDs mappings can be combined into a single mapping of an address/id to a struct | 13 |
| 6  | `x += y/x -= y` costs more gas than `x = x + y/x = x - y` for state variables  | 10 |

## Findings

### 1- Using `storage` instead of `memory` for struct/array saves gas :

When fetching data from a `storage` location, assigning the data to a `memory` variable causes all fields of the struct/array to be read from `storage`, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new `memory` variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the `memory` keyword, declaring the variable with the `storage` keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read.

The only time it makes sense to read the whole struct/array into a `memory` variable, is if the full struct/array is being returned by the function, is being passed to a function that requires `memory`, or if the array/struct is being read from another `memory` array/struct.

There are 8 instances of this issue :

File: RewardsManager.sol [Line 442](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L442)
```
BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];
```

File: StandardFunding.sol [Line 203](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol#L203)
```
uint256[]

uint256[] memory fundingProposalIds = _fundedProposalSlates[fundedSlateHash];

// Code block 1 (unknown):
BucketState memory bucketSnapshot = stakes[tokenId_].snapshot[bucketIndex];