Brenzee Q

# QA Report

## [L-01] `receive()` function allows anyone to send ETH
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L246
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L126
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L97
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L244

Intentional use of `receive()` in `SafEth.sol` is to receive ETH when `derivative.withdraw` is called. Since there are no checks on `msg.sender`, anyone can send ETH and it will result in a loss of funds. Suggest checking if the `msg.sender` is one of the derivative contracts.

The same goes for derivative contracts - ETH is only expected to be received from known protocol contracts. And because all of the derivative contracts transfer ETH via `withdraw` function where the whole ETH balance is sent, these funds can be stolen.

```solidity
 receive() external payable {}
```

### Example
Alice accidentally sends ETH straight to `WstEthStaking` contract instead of calling `safEthProxy.stake` .

Bob sees that Alice has sent ETH to `WstEthStaking` contract. He stakes and unstakes ETH and receives that 1 ETH that has been sent by Alice.

```typescript
it("Bob should get Alice's sent ETH ", async () => {
      const [_, alice, bob] = await ethers.getSigners();

      // derivatives addresses
      const WstEthStakingAddress = await safEthProxy.derivatives(2);

      const bobBalanceBefore = await ethers.provider.getBalance(bob.address);

      // Transfer ETH to WstEthStakingAddress
      await alice.sendTransaction({
        to: WstEthStakingAddress,
        value: ethers.utils.parseEther("1"),
      });

      // Bob stakes and unstakes 1 ETH
      await safEthProxy.connect(bob).stake({
        value: ethers.utils.parseEther("1"),
      });
      await safEthProxy
        .connect(bob)
        .unstake(await

## [L-02] No need to check balance in `deposit()` and `withdraw()` functions in `SfrxEth.sol`

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L98-L104
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L61-L68
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L57-L58

`SfrxEth.sol` - `frxETHMinterContract.submitAndDeposit` and `IsFrxEth(SFRX_ETH_ADDRESS).redeem` returns how many tokens have been received, there is no need to call `balanceOf`. After changing the code to the example below, all tests still succeed.

// Code block 1 (solidity):
receive() external payable {}

SafEth.sol#L246, SfrxEth.sol#L126, WstEth.sol#L97, Reth.sol#L244, SfrxEth.sol#L98-L104, SfrxEth.sol#L61-L68, WstEth.sol#L57-L58

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


pontifex Q

### Wrong amounts at the events
The events at the `rUSDY.wrap` function emit wrong amounts due to receiving `_USDYAmount` tokens amount instead of `_USDYAmount * BPS_DENOMINATOR`. This would affect applications utilizing event logs like subgraphs.
```solidity
438    emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
439    emit TransferShares(address(0), msg.sender, _USDYAmount);
```
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L438-L439

// Code block 1 (solidity):
438    emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
439    emit TransferShares(address(0), msg.sender, _USDYAmount);


QiuhaoLi Q

## [low] RdpxV2Core.sol _transfer should update reserveAsset[reservesIndex["RDPX"]] if (rdpxBurnPercentage + rdpxFeePercentage) < 1e10, or add constraints in setRdpxBurnPercentage/setRdpxFeePercentage

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L657
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L662
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L180
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L193

In the setters of rdpxBurnPercentage and rdpxFeePercentage, we only check if the value is bigger than zero, not check if they sum up to 1e10.And in _transfer(), we didn't check if rdpxBurnPercentage + rdpxFeePercentage) == 1e10 and didn't update reserveAsset[reservesIndex["RDPX"]].tokenBalance relatively. This can lead to two results if the admin changes rdpxBurnPercentage/rdpxFeePercentage wrongly:

1. When (rdpxBurnPercentage + rdpxFeePercentage) < 1e10, reserveAsset[reservesIndex["RDPX"]].tokenBalance will be smaller than the actual value.
2. When (rdpxBurnPercentage + rdpxFeePercentage) > 1e10, more rdpx is spent than designed. Eventually, the function of the core can be stopped since no more rdpx is usable.

To solve this, we need to make sure (rdpxBurnPercentage + rdpxFeePercentage) == 1e10 in setters or _transfer, and update the reserveAsset[reservesIndex["RDPX"]].tokenBalance if so.

## [low] PerpetualAtlanticVault should round up when calculating requiredCollateral

In purchase(), we round down the requiredCollateral. This can lead to insufficient collateral, even zero collateral when (amount * strike) < 1e8. Rounding up is safer.

## [low] UniV3LiquidityAmo should add setter for univ3_factory, univ3_positions, and univ3_router

In UniV3LiquidityAmo's constructor, we hardcoded the uniswap contract's address. However, these addresses are different on other blockchains like celo: https://docs.uniswap.org/cont

    ISwapRouter.ExactInputSingleParams memory swap_params = ISwapRouter
      .ExactInputSingleParams(
        _tokenA,
        _tokenB,
        _fee_tier,
        address(this),
        2105300114, // Expiration: a long time from now @audit-issue deadline vulnerable to MEV
        _amountAtoB,
        _amountOutMinimum,
        _sqrtPriceLimitX96
      );

RdpxV2Core.sol#L657, RdpxV2Core.sol#L662, RdpxV2Core.sol#L180, RdpxV2Core.sol#L193


BlueAlder G

## [G-01] Unnecessary recalculation of total weights in `addDerivative`

In the `addDerivative` function, the total weight of all the
derivatives is recalculated by iterating through the `weights` storage variable
and individually adding these all up in a for loop.

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

This is not required since `addDerivative` is only adding 1 weight, and not
adjusting the existing ones. So you can simply add the new weight to the
existing totalWeight with:

```sol
totalWeight = totalWeight + _weight;
```

Gas saving is 522 gas and increases with the number of derivatives.

## [G-02] Unnecessary recalculation of total weights in `adjustWeight`

Similar to above, the `adjustWeight` function is only changing 1 weight and so
we can alter the `totalWeight` variable directly before altering the `weights`
mapping to get the calculation.

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

```sol
totalWeight = totalWeight - weights[_derivativeIndex] + _weight;
weights[_derivativeIndex] = _weight;
```

Gas saving is 154 and increases with the number of derivatives.

# [G-03] **Loop variable in for loop can be unchecked**

```markdown
## [G-03] Loop variable in for loop can be unchecked

Overflowing the loop variable in may for loops is very unlikely when iterating
derivatives so we can remove the overflow check to save some gas. This is
because most loops start at 0 and increment by 1 and will never reach the max
uint256 or will run out of gas by then.

This will save about 50 gas per iteration.

For example in the `unstake()` function:

```sol
for (uint256 i = 0; i < derivativeCount; i++) {
    // withdraw a percentage of each asset based on the amount of safETH
    uint256 derivativeAmount = (derivatives[i].balance() * _safEthAmount) / safEthTotalSupply;
    if (derivativeAmount == 0) con

// Code block 1 (sol):
totalWeight = totalWeight + _weight;

// Code block 2 (sol):
totalWeight = totalWeight - weights[_derivativeIndex] + _weight;
weights[_derivativeIndex] = _weight;

// Code block 3 (markdown):
## [G-03] Loop variable in for loop can be unchecked

Overflowing the loop variable in may for loops is very unlikely when iterating
derivatives so we can remove the overflow check to save some gas. This is
because most loops start at 0 and increment by 1 and will never reach the max
uint256 or will run out of gas by then.

This will save about 50 gas per iteration.

For example in the `unstake()` function:

totalWeight = totalWeight + _weight;

totalWeight = totalWeight - weights[_derivativeIndex] + _weight;
weights[_derivativeIndex] = _weight;


chrisdior4 Q

# QA report

## Low Risk
| L-N    |Issue|Instances|
|:------:|:----|:-------:|
| [L‑01] | Functions that send ether with a low level `call` are missing `nonReentrant` modifier | 1 |
| [L‑02] | Ownable uses single-step ownership transfer | 1 |
| [L‑03] | Missing zero address check in constructor  | 2 |
| [L‑04] | Consider adding initializer modifier to _initialize** functions  | 2 |

Total: 6 instances over 4 issues

## Non-critical

| N-C    |Issue|Instances|
|:------:|:----|:-------:|
| [N‑01] | Missing params in NatSpec| 2 |
| [N‑02] | Use a safe pragma statement | 1 |
| [N‑03] | Typos in comments | 3 |
| [N‑04] | Use solidity's native units for dealing with time | 1 |
| [N‑05] | Constants should be defined rather than using magic numbers  | 1 |
| [N‑06] | Events are missing indexed fields | 3 |
| [N‑07] | Constants should be capitalized | 4 |

Total: 15 instances over 7 issues

## Low Risk

### \[L-01\] Function that sends ether with a low level `call` is missing `nonReentrant` modifier

File: `CashFactory.sol`

Add a nonReentrant modifier to this external function that is making a low level call in order to be sure 100% that reentrancy can not occur:

```solidity
(bool success, bytes memory ret) = address(exCallData[i].target).call{
 value: exCallData[i].value
}(exCallData[i].data);
```

Lines of code:

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

### \[L-02\] Ownable uses single-step ownership transfer

File: `OndoPriceOracleV2.sol`

```solidity
import "contracts/cash/external/openzeppelin/contracts/access/Ownable.sol";
```

The OndoPriceOracleV2 contract inherits from Ownable and the owner is the one who can set couple of important features such as Oracle, Price of an fToken contract's underlying asset, etc.. The Ownable contract implements single-step ownership transfer, which means that if a

// Code block 1 (solidity):
(bool success, bytes memory ret) = address(exCallData[i].target).call{
 value: exCallData[i].value
}(exCallData[i].data);

// Code block 2 (solidity):
import "contracts/cash/external/openzeppelin/contracts/access/Ownable.sol";

(bool success, bytes memory ret) = address(exCallData[i].target).call{
 value: exCallData[i].value
}(exCallData[i].data);

import "contracts/cash/external/openzeppelin/contracts/access/Ownable.sol";


fouzantanveer Analysis

## Conceptual Overview
As an auditor of the Salty.IO project, I present a conceptual narrative overview, highlighting the user journey, interaction points, and the features of the platform.

At the heart of the Salty.IO ecosystem are two primary tokens: `Salt` and `USDS`. A user's journey often begins with acquiring these tokens. `Salt` serves as the backbone of the platform, facilitating not only staking and governance participation but also acting as a key to earning rewards. On the other hand, `USDS` offers a stablecoin option, pegging its value to a stable asset like the USD, thus providing a haven from the volatility typically associated with cryptocurrencies.

Once users have these tokens, they can engage in liquidity provision. By contributing to liquidity pools, they receive liquidity tokens in return. This is not just a passive investment; it's a crucial role in maintaining the ecosystem's health. In return, users are rewarded with a share of transaction fees, fostering a symbiotic relationship between the platform and its participants.

Staking is another pivotal aspect of the user journey on Salty.IO. Users stake their `Salt` tokens to earn rewards and gain governance rights, influencing the platform's direction. The staking mechanism is designed with flexibility in mind, offering options like unstaking with a cooldown period or early unstaking at a reduced reward, accommodating various user strategies and needs.

The platform's dynamic reward structure, governed by contracts like `RewardsConfig.sol` and `Emissions.sol`, ensures that users are incentivized for their participation. Whether through staking or liquidity provision, the rewards system adapts to market conditions and participation levels, making it a responsive and attractive feature for users.

A notable feature of Salty.IO is its integration of price feeds and oracles, as seen in contracts like `CoreUniswapFeed.sol` and `PriceAggregator.sol`. This integration ensures that the asset valuations

contract Upkeep {
  // ...
  IPriceAggregator immutable public priceAggregator;
  // ...
  function performUpkeep() public {
    // ...
    uint256 btcPrice = priceAggregator.getPriceBTC();
    uint256 ethPrice = priceAggregator.getPriceETH();
    // ...
  }
  // ...
}


0xSmartContract G

### Gas Optimizations List
| Number | Optimization Details | Context |
|:--:|:-------| :-----:|
| [G-01] | Remove the `initializer` modifier | 2 |
| [G-02] |Use hardcode address instead ``address(this)``| 36 |
| [G-03] |Structs can be packed into fewer storage slots |2 |
| [G-04] |Pack state variables | 4 |
| [G-05] | Remove or replace unused state variables |1|
| [G-06] | ``Deposit`` struct can be removed | 1 |
| [G-07] |Using ``delete``  instead of setting mapping/state variable ``0`` saves gas | 9 |
| [G-08] | Using ``delete`` instead of setting ``address(0)`` saves gas | 1 |
| [G-09] | Using ``delete`` to set ``bool`` values to their default values saves gas  | 3 |
| [G-10] |Using `storage` instead of `memory` for `structs/arrays` saves gas | 7 |
| [G-11] | Multiple ``address``/ID mappings can be combined into a single ``mapping`` of an ``address``/ID to a ``struct``, where appropriate |7 |
| [G-12] |Multiple accesses of a mapping/array should use a local variable cache | 30 |
| [G-13] |The result of function calls should be cached rather than re-calling the function  | 4 |
| [G-14] |Avoid using ``state variable`` in emit |1 |
| [G-15] |Superfluous event fields | 1 |
| [G-16] |Avoid contract existence checks by using low level calls |97 |
| [G-17] |``bytes`` constants are more eficient than ``string`` constans | 8 |
| [G-18] |Change ``public`` function visibility to ``external`` |3 |
| [G-19] |Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead |6|
| [G-20] |``internal`` functions only called once can be inlined to save gas | 45 |
| [G-21] |Do not calculate constants variables | 11 |
| [G-22] |Use ``assembly`` to write _address storage values_  | 18 |
| [G-23] |Setting the _constructor_ to `payable` | 6 |
| [G-24] |Add ``unchecked {}`` for subtractions where the operands cannot underflow because of a previous ``require`` or ``if`` statement | 10 |
| [G-25] |++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overf

Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol:

  67:     ) public initializer {

Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol:

  61:     constructor() initializer {}


SY_S G

## Summary

### Gas Optimization

no |Issue |Instances||
|-|:-|:-:|:-:|
| [G-01] | Pre-increments and pre-decrements are cheaper than post-increments and post-decrements |  |3|--|
| [G-02] | Can make the variable outside the loop to save gas |  |2|--|
| [G-03] |Before transfer of  some functions, we should check some variables for possible gas save |  |13|--|
| [G-04] |Do not calculate constants |  |17|--|
| [G-05] |TERNARY UNNECESSARY |  |7|--|
| [G-06] |Empty blocks should be removed or emit something |  |3|--|
| [G-07] | Using storage instead of memory for structs/arrays saves gas |  |14|--|
| [G-08] |ith assembly, .call (bool success)  transfer can be done gas-optimized |  |2|--|
| [G-09] |Use constants instead of type(uintx).max |  |7|--|
| [G-10] |Use hardcode address instead address(this) |  |9|--|

## Gas Optimizations

## [G-1] Pre-increments and pre-decrements are cheaper than post-increments and post-decrements

```solidity
file:/src/transformers/V3Utils.sol
 619           transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed0);
 623            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed1);
 627            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.neededOther);

```
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/transformers/V3Utils.sol#L619

## [G-2] Can make the variable outside the loop to save gas

Consider making the stack variables before the loop which gonna save gas

2 instances here collection and collectionScore

```solidity
File:/src/automators/Automator.sol
112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));

```
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/automators/Automator.sol#L112

```solidity
file:/src/transformers/AutoCompound.

// Code block 1 (solidity):
file:/src/transformers/V3Utils.sol
 619           transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed0);
 623            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed1);
 627            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.neededOther);

// Code block 2 (solidity):
File:/src/automators/Automator.sol
112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));

file:/src/transformers/V3Utils.sol
 619           transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed0);
 623            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.needed1);
 627            transferDetails[state.i++] = ISignatureTransfer.SignatureTransferDetails(address(this), state.neededOther);

File:/src/automators/Automator.sol
112            uint256 balance = IERC20(tokens[i]).balanceOf(address(this));


hunter_w3b Q

## [L-01] Calls to `VaultController::updateRegisteredErc20()` could render vaults instantly insolvent

updateRegisteredErc20() has the ability to modify the LTV ratio of a token across the protocol to any arbitrary, potentially dangerously low value.
If this value is lowered so that the allowed loan amount for a given deposit value drops, then, in a single transaction, any vault that has borrowed an amount that is within the old LTV but above the new LTV would become instantly insolvent.
This would then cause them to be liquidated, damaging the protocol.
This is mitigated by the presumed procedure of such a change, which would work through governance and so there would be time for vault owners to react during the proposal and the timelock implementation period.

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L383-L412

```solidity
File: contracts/core/VaultController.sol

394    if (_ltv >= (EXP_SCALE - _liquidationIncentive)) revert VaultController_LTVIncompatible();
395    if (_poolId != 0) {
396      (address _lpToken,,, address _crvRewards,,) = BOOSTER.poolInfo(_poolId);
397     if (_lpToken != _tokenAddress) revert VaultController_TokenAddressDoesNotMatchLpAddress();
398      _collateral.collateralType = CollateralType.CurveLPStakedOnConvex;
399      _collateral.crvRewardsContract = IBaseRewardPool(_crvRewards);
400      _collateral.poolId = _poolId;
401    }
402    // set the oracle of the token
403    _collateral.oracle = IOracleRelay(_oracleAddress);
404    // set the ltv of the token
405    _collateral.ltv = _ltv;
```

## [L-02] Whitelisting can expire mid way through a proposal

Whitelisted proposals can expire during the assessment period, making a user, and a proposal, lose its status and beneﬁts.

Diﬀerent procedures and privileges are enacted for proposers based on whether they are whitelisted or not.

The assessment of whether an account is whitelisted is based on the test performed in `isWhite

File:  contracts/governance/GovernorCharlie.sol

559  function isWhitelisted(address _account) public view override returns (bool _isWhitelisted) {
560    return (whitelistAccountExpirations[_account] > block.timestamp);
561  }

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

394    if (_ltv >= (EXP_SCALE - _liquidationIncentive)) revert VaultController_LTVIncompatible();
395    if (_poolId != 0) {
396      (address _lpToken,,, address _crvRewards,,) = BOOSTER.poolInfo(_poolId);
397     if (_lpToken != _tokenAddress) revert VaultController_TokenAddressDoesNotMatchLpAddress();
398      _collateral.collateralType = CollateralType.CurveLPStakedOnConvex;
399      _collateral.crvRewardsContract = IBaseRewardPool(_crvRewards);
400      _collateral.poolId = _poolId;
401    }
402    // set the oracle of the token
403    _collateral.oracle = IOracleRelay(_oracleAddress);
404    // set the ltv of the token
405    _collateral.ltv = _ltv;


Bnke0x0 Q

### [L01] require() should be used instead of assert()

#### Findings:
```
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::128 => assert(MIN_NET_DEBT > 0);
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::197 => assert(vars.compositeDebt > 0);
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::301 => assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0));
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::331 => assert(_collWithdrawal <= vars.coll);
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::312 => assert(sender != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::313 => assert(recipient != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::321 => assert(account != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::329 => assert(account != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::337 => assert(owner != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::338 => assert(spender != address(0));
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::526 => assert(_debtToOffset <= _totalLUSDDeposits);
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::551 => assert(_LUSDLossPerUnitStaked <= DECIMAL_PRECISION);
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::591 => assert(newP > 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::417 => assert(_LUSDInStabPool != 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1224 => assert(totalStakesSnapshot[_collateral] > 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1279 => assert(closedStatus != Status.nonExistent && closedStatus != Status.active);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1342 => assert(troveStatus != Status.nonExistent && troveStatus != Status.active);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1348 => asse

2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::128 => assert(MIN_NET_DEBT > 0);
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::197 => assert(vars.compositeDebt > 0);
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::301 => assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0));
2023-02-ethos-main/Ethos-Core/contracts/BorrowerOperations.sol::331 => assert(_collWithdrawal <= vars.coll);
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::312 => assert(sender != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::313 => assert(recipient != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::321 => assert(account != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::329 => assert(account != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::337 => assert(owner != address(0));
2023-02-ethos-main/Ethos-Core/contracts/LUSDToken.sol::338 => assert(spender != address(0));
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::526 => assert(_debtToOffset <= _totalLUSDDeposits);
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::551 => assert(_LUSDLossPerUnitStaked <= DECIMAL_PRECISION);
2023-02-ethos-main/Ethos-Core/contracts/StabilityPool.sol::591 => assert(newP > 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::417 => assert(_LUSDInStabPool != 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1224 => assert(totalStakesSnapshot[_collateral] > 0);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1279 => assert(closedStatus != Status.nonExistent && closedStatus != Status.active);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1342 => assert(troveStatus != Status.nonExistent && troveStatus != Status.active);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.sol::1348 => assert(index <= idxLast);
2023-02-ethos-main/Ethos-Core/contracts/TroveManager.so

2023-02-ethos-main/Ethos-Core/contracts/CollateralConfig.sol::46 => function initialize(
2023-02-ethos-main/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::62 => function initialize(
2023-02-ethos-main/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol::67 => ) public initializer {


MrPotatoMagic G

## [G-01] Function can be made external instead of public
There are 2 instances of this:

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/governance/GovernanceTimelock.sol#L25

```solidity
File: contracts/lybra/governance/GovernanceTimelock.sol
25: function checkRole(bytes32 role, address _sender) public view  returns(bool){
26:         return hasRole(role, _sender) || hasRole(DAO, _sender);
27:     }
```

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L257

```solidity
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
257: function getPoolTotalPeUSDCirculation() public view returns (uint256) {
```

## [G-02] Struct member can be modified to save slot
There are 3 instances of this:

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/governance/LybraGovernance.sol#L23

uint256 votes can be made uint128 votes. This is significantly larger than the proposal threshold (1e23) as well.
```solidity
File: contracts/lybra/governance/LybraGovernance.sol
15: struct Receipt {
16:        /// @notice Whether or not a vote has been cast
17:        bool hasVoted;
18:
19:        /// @notice Whether or not the voter supports the proposal or abstains
20:        uint8 support;
21:
22:        /// @notice The number of votes the voter had, which were cast
23:        uint256 votes; //@audit can be made uint128 to fit in one slot instead of two
24:    }
```

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/esLBRBoost.sol#L12

uint256 duration can be made uint16, which can be good enough for 180 years and miningBoost could be made uint128. This could fit in one slot instead of two.
```solidity
File: contracts/lybra/miner/esLBRBoost.sol
12: struct esLBRLockSetting {
13:        uint256 duration; //@audit use uint16
14:

// Code block 1 (solidity):
File: contracts/lybra/governance/GovernanceTimelock.sol
25: function checkRole(bytes32 role, address _sender) public view  returns(bool){
26:         return hasRole(role, _sender) || hasRole(DAO, _sender);
27:     }

// Code block 2 (solidity):
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
257: function getPoolTotalPeUSDCirculation() public view returns (uint256) {

// Code block 3 (solidity):
File: contracts/lybra/governance/LybraGovernance.sol
15: struct Receipt {
16:        /// @notice Whether or not a vote has been cast
17:        bool hasVoted;
18:
19:        /// @notice Whether or not the voter supports the proposal or abstains
20:        uint8 support;
21:
22:        /// @notice The number of votes the voter had, which were cast
23:        uint256 votes; //@audit can be made uint128 to fit in one slot instead of two
24:    }

File: contracts/lybra/governance/GovernanceTimelock.sol
25: function checkRole(bytes32 role, address _sender) public view  returns(bool){
26:         return hasRole(role, _sender) || hasRole(DAO, _sender);
27:     }

File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
257: function getPoolTotalPeUSDCirculation() public view returns (uint256) {

File: contracts/lybra/governance/LybraGovernance.sol
15: struct Receipt {
16:        /// @notice Whether or not a vote has been cast
17:        bool hasVoted;
18:
19:        /// @notice Whether or not the voter supports the proposal or abstains
20:        uint8 support;
21:
22:        /// @notice The number of votes the voter had, which were cast
23:        uint256 votes; //@audit can be made uint128 to fit in one slot instead of two
24:    }

GovernanceTimelock.sol#L25, LybraPeUSDVaultBase.sol#L257, LybraGovernance.sol#L23, esLBRBoost.sol#L12

GovernanceTimelock.sol, esLBRBoost.sol, LybraPeUSDVaultBase.sol, LybraGovernance.sol


IllIllI G

## Summary

### Gas Optimizations
| |Issue|Instances|Total Gas Saved|
|-|:-|:-:|:-:|
| [G‑01] | Multiple `address`/ID mappings can be combined into a single `mapping` of an `address`/ID to a `struct`, where appropriate | 4 |  - |
| [G‑02] | Avoid contract existence checks by using low level calls | 2 |  200 |
| [G‑03] | State variables should be cached in stack variables rather than re-reading them from storage | 15 |  1455 |
| [G‑04] | Multiple accesses of a mapping/array should use a local variable cache | 8 |  336 |
| [G‑05] | `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables | 3 |  339 |
| [G‑06] | `internal` functions only called once can be inlined to save gas | 4 |  80 |
| [G‑07] | Add `unchecked {}` for subtractions where the operands cannot underflow because of a previous `require()` or `if`-statement | 4 |  340 |
| [G‑08] | `++i`/`i++` should be `unchecked{++i}`/`unchecked{i++}` when it is not possible for them to overflow, as is the case when used in `for`- and `while`-loops | 9 |  540 |
| [G‑09] | `require()`/`revert()` strings longer than 32 bytes cost extra gas | 12 |  - |
| [G‑10] | Optimize names to save gas | 8 |  176 |
| [G‑11] | Use a more recent version of solidity | 2 |  - |
| [G‑12] | Splitting `require()` statements that use `&&` saves gas | 1 |  3 |
| [G‑13] | Using `private` rather than `public` for constants, saves gas | 1 |  - |
| [G‑14] | Use custom errors rather than `revert()`/`require()` strings to save gas | 16 |  - |
| [G‑15] | Functions guaranteed to revert when called by normal users can be marked `payable` | 34 |  714 |
| [G‑16] | Don't use `_msgSender()` if not supporting EIP-2771 | 3 |  48 |

Total: 126 instances over 16 issues with **4231 gas** saved

Gas totals use lower bounds of ranges and count two iterations of each `for`-loop. All values above are runtime, not deployment, values; deployment

File: contracts/cash/CashManager.sol

79      mapping(uint256 => RedemptionRequests) public redemptionInfoPerEpoch;
80
81      // Mapping used for getting the exchange rate during a given epoch
82      mapping(uint256 => uint256) public epochToExchangeRate;
83
84      // Nested mapping containing mint requests for an epoch
85      // { <epoch> : {<user> : <collateralAmount> }
86:     mapping(uint256 => mapping(address => uint256)) public mintRequestsPerEpoch;

File: contracts/lending/OndoPriceOracle.sol

45      mapping(address => uint256) public fTokenToUnderlyingPrice;
46
47      /// @notice fToken to cToken associations for piggy backing off
48      ///         of cToken oracles
49:     mapping(address => address) public fTokenToCToken;


dicethedev G

a. Use the `abi.encodeWithSelector` function instead of `abi.encode` to encode the function selector and arguments. This can reduce the gas cost of encoding the call data by up to 10%. For example, you can replace `_hashProposal(targets_, values_, calldatas_, keccak256(abi.encode(DESCRIPTION_PREFIX_HASH_EXTRAORDINARY, keccak256(bytes(description_)))))` with `abi.encodeWithSelector(this._hashProposal.selector, targets_, values_, calldatas_, keccak256(abi.encode(DESCRIPTION_PREFIX_HASH_EXTRAORDINARY, keccak256(bytes(description_)))))`.

https://github.com/code-423n4/2023-05-ajna/blob/a51de1f0119a8175a5656a2ff9d48bbbcb4436e7/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L92

b. Instead of using `if (address(wrappedToken) != AJNA_TOKEN_ADDRESS)`, you can use `require(wrappedToken == IERC20(AJNA_TOKEN_ADDRESS), "InvalidWrappedToken")`. This will result in less bytecode and lower gas costs.

you can modify the code with this example -

```
constructor(IERC20 wrappedToken)
        ERC20("Burn Wrapped AJNA", "bwAJNA")
        ERC20Permit("Burn Wrapped AJNA") // enables wrapped token to also use permit functionality
        ERC20Wrapper(wrappedToken)
    {
        require(wrappedToken == IERC20(AJNA_TOKEN_ADDRESS), "InvalidWrappedToken");
    }
```
This way, the contract will revert with an error message if the wrappedToken address is not equal to the `AJNA_TOKEN_ADDRESS`, rather than using an if statement that checks the condition and then reverts with an error message. This results in less bytecode and lower gas costs because the `require` statement combines both the check and revert into one step, whereas the if statement requires an additional step to check the condition before reverting.

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/token/BurnWrapper.sol#L36-L38

c. Instead of checking for the `wrappedToken` address during deployment, you can use the `require` statement in the wrap function. This would

// SPDX-License-Identifier: MIT

//slither-disable-next-line solc-version
pragma solidity 0.8.7;

import { ERC20 }          from "@oz/token/ERC20/ERC20.sol";
import { IERC20 }         from "@oz/token/ERC20/IERC20.sol";
import { ERC20Burnable }  from "@oz/token/ERC20/extensions/ERC20Burnable.sol";
import { ERC20Permit }    from "@oz/token/ERC20/extensions/draft-ERC20Permit.sol";
import { ERC20Wrapper }   from "@oz/token/ERC20/extensions/ERC20Wrapper.sol";
import { IERC20Metadata } from "@oz/token/ERC20/extensions/IERC20Metadata.sol";

contract BurnWrappedAjna is ERC20, ERC20Burnable, ERC20Permit, ERC20Wrapper {

    /**
     * @notice Tokens that have been wrapped cannot be unwrapped.
     */
    error UnwrapNotAllowed();

    modifier onlyValidWrappedToken(IERC20 wrappedToken) {
        require(address(wrappedToken) == 0x9a96ec9B57Fb64FbC60B423d1f4da7691Bd35079, "InvalidWrappedToken");
        _;
    }

    constructor()
        ERC20("Burn Wrapped AJNA", "bwAJNA")
        ERC20Permit("Burn Wrapped AJNA") // enables wrapped token to also use permit functionality
    {
        // No need to check the wrappedToken address during deployment
    }

    /**
     * @notice Wrap the mainnet Ajna token into a wrapped token for use in other networks.
     * @param amount The amount of Ajna tokens to wrap.
     */
    function wrap(uint256 amount) public onlyValidWrappedToken(IERC20(_wrappedToken)) {
        ERC20Wrapper.wrap(amount);
    }

    /*****************/
    /*** OVERRIDES ***/
    /*****************/

    /**
     * @dev See {ERC20-decimals} and {ERC20Wrapper-decimals}.
     */
    function decimals() public pure override(ERC20, ERC20Wrapper) returns (uint8) {
        // since the Ajna Token has 18 decimals, we can just return 18 here.
        return 18;
    }

    /**
     * @notice Override unwrap method to ensure burn wrapped tokens can't be unwrapped.
     */
    function withdrawTo(address, uint256) public pure override returns (bool) {
        revert Unwrap

// Code block 1 (unknown):
constructor(IERC20 wrappedToken)
        ERC20("Burn Wrapped AJNA", "bwAJNA")
        ERC20Permit("Burn Wrapped AJNA") // enables wrapped token to also use permit functionality
        ERC20Wrapper(wrappedToken)
    {
        require(wrappedToken == IERC20(AJNA_TOKEN_ADDRESS), "InvalidWrappedToken");
    }


dev0cloo Q

# QA Report

## Low Severity Findings

### Range Start and End times must be in Unix format to prevent errors in `RWADynamicOracle`
- The ranges set in the DynamicOracle contract must follow the Unix format since the calculations they are involved in use block.timestamp, which is also in Unix format, to prevent unexpected results. This is especially so when no visible checks exist for these ranges to be in the required format. Here are some instances:
```
function getPrice() public view whenNotPaused returns (uint256 price) {
        uint256 length = ranges.length;
        for (uint256 i = 0; i < length; ++i) {
            Range storage range = ranges[(length - 1) - i];
            if (range.start <= block.timestamp) {
                if (range.end <= block.timestamp) {
                    return derivePrice(range, range.end - 1);
                } else {
                    return derivePrice(range, block.timestamp); //@audit - block.timestamp returns unix values
                }
            }
        }
    }

    function derivePrice(
        Range memory currentRange,
        uint256 currentTime
    ) internal pure returns (uint256 price) {
        uint256 elapsedDays = (currentTime - currentRange.start) / DAY; //@audit - if range.start is not in unix format, this will lead to serious errors
        return
            roundUpTo8(
                _rmul(
                    _rpow(currentRange.dailyInterestRate, elapsedDays + 1, ONE),
                    currentRange.prevRangeClosePrice
                )
            );
    }
```
This is marked as low severity because the `overrideRange` function easily allows a fix for this if it occurs.

// Code block 1 (unknown):
function getPrice() public view whenNotPaused returns (uint256 price) {
        uint256 length = ranges.length;
        for (uint256 i = 0; i < length; ++i) {
            Range storage range = ranges[(length - 1) - i];
            if (range.start <= block.timestamp) {
                if (range.end <= block.timestamp) {
                    return derivePrice(range, range.end - 1);
                } else {
                    return derivePrice(range, block.timestamp); //@audit - block.timestamp returns unix values
                }
            }
        }
    }

    function derivePrice(
        Range memory currentRange,
        uint256 currentTime
    ) internal pure returns (uint256 price) {
        uint256 elapsedDays = (currentTime - currentRange.start) / DAY; //@audit - if range.start is not in unix format, this will lead to serious errors
        return
            roundUpTo8(
                _rmul(
                    _rpow(currentRange.dailyInterestRate, elapsedDays + 1, ONE),
                    currentRange.prevRangeClosePrice
                )
            );
    }


Secureverse Q

### [Low-01] Multiple Solidity version used i.e some using ```^0.8.12``` and some using ```0.8.12```

Try to use Updated, stable, and most recent Solidity version to remain bug free

Below  Solidity ```^0.8.12```
```solidity
File: aa-4337/interfaces/IAccount.sol
File: aa-4337/core/EntryPoint.sol
File: aa-4337/core/SenderCreator.sol
File: aa-4337/core/StakeManager.sol
File: aa-4337/utils/Exec.sol
File: aa-4337/interfaces/IAggregator.sol
File: aa-4337/interfaces/UserOperation.sol
File: aa-4337/interfaces/IStakeManager.sol
File: aa-4337/interfaces/IEntryPoint.sol
File: aa-4337/interfaces/IAggregatedAccount.sol
File: aa-4337/interfaces/IPaymaster.sol
File: paymasters/BasePaymaster.sol
```

Below Solidity ```0.8.12```
```solidity
File: BaseSmartAccount.sol
File: Proxy.sol
File: SmartAccount.sol
File: SmartAccountFactory.sol
File: interfaces/ISignatureValidator.sol
File: interfaces/ERC1155TokenReceiver.sol
File: interfaces/ERC721TokenReceiver.sol
File: interfaces/ERC777TokensRecipient.sol
File: interfaces/IERC1271Wallet.sol
File: common/Singleton.sol
File: common/SignatureDecoder.sol
File: base/Executor.sol
File: common/SecuredTokenTransfer.sol
File: base/ModuleManager.sol
File: base/FallbackManager.sol
File: interfaces/IERC165.sol
File: libs/Math.sol
File: libs/LibAddress.sol
File: paymasters/PaymasterHelpers.sol
File: paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol
File: common/Enum.sol
File: libs/MultiSend.sol
File: MultiSendCallOnly.sol
```

### [Low-2] Changing Owner should be a 2 Step-process
*Instances(1)*
```solidity
File: SmartAccount.sol
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109-L114
```

### [Low-03] ETH could remain stuck in below contracts, as there are no functionality to resuce sent ETH to those contract

*Instances(4)*
```solidity
File: BaseSmartAccount.sol

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

// Code block 1 (unknown):
Try to use Updated, stable, and most recent Solidity version to remain bug free

Below  Solidity

// Code block 2 (unknown):
Below Solidity

// Code block 3 (unknown):
### [Low-2] Changing Owner should be a 2 Step-process
*Instances(1)*

// Code block 4 (unknown):
### [Low-03] ETH could remain stuck in below contracts, as there are no functionality to resuce sent ETH to those contract

*Instances(4)*


hunter_w3b G

# Gas Optimization

# Summary

| Number | Issue                                                                                | Instances |
| :----: | :------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: |
| [G-01] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant                                           |     5     |
| [G-02] | ++i COSTS LESS GAS THAN i++                                                                                |     1     |
| [G-03] | Stack variable used as a cheaper cache for a state variable is only used once                                                                      |     2     |
| [G-04] | Can make the variable outside the loop to save Gas                                                                                |     2     |
| [G-05] | Require() statements should be used sorted from cheapest to most expensive                                                                         |    11     |
| [G-06] | Should use arguments instead of state variable                                                                                |     5     |
| [G-07] | Instead of calculating a STATEVAR with keccak256() every time the contract is made pre calclate them before and only give the result to a constant |     6     |
| [G-08] | Structs can be packed into fewer storage slots                                                                                |     1     |
| [G-09] | abi.encode() is less efficient than abi.encodePacked()                                                                                |     3     |
| [G-10] | Do not calculate constants

File: contracts/usdy/rUSDY.sol

  bytes32 public constant USDY_MANAGER_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
  bytes32 public constant LIST_CONFIGURER_ROLE =
    keccak256("LIST_CONFIGURER_ROLE");

File: rwaOracles/RWADynamicOracle.sol

27  bytes32 public constant SETTER_ROLE = keccak256("SETTER_ROLE");
28  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");


Kenshin G

# Gas Optimization Report
## Summary
The benchmark used [Foundry's default optimizer setting](https://book.getfoundry.sh/reference/config/solidity-compiler#optimizer). The [`ExtraordinaryFunding.sol`](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol) contract was tested using all functions in the [`ExtraordinaryFunding.t.sol`](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/test/unit/ExtraordinaryFunding.t.sol) contract, excluding the `testFuzzExtraordinaryFunding()` function. The [`StandardFunding.sol`](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/StandardFunding.sol) contract was tested using only the `testDistributionPeriodEndToEnd()` function.

Running command:
-   `forge test --gas-report --match-contract ExtraordinaryFundingGrantFundTest --no-match-test testFuzzExtraordinaryFunding`
-   `forge test --gas-report --match-test testDistributionPeriodEndToEnd`

The overall **average** gas savings are summarized in the table below.
|   **Contract**    |   **Function Name**    |   **Before**    |   **After**   |   **Gas Savings**  |
|   :---   |   :---   |   :---:   |   :---:   |   :---:   |
|   ExtraordinaryFunding    |   executeExtraordinary    |   31595   |   29790   |	1805    |
|   ExtraordinaryFunding    |   hashProposal    |   3985    |   2256    |   1729    |
|   ExtraordinaryFunding    |   proposeExtraordinary    |   55381   |	51113   |	4268    |
|   ExtraordinaryFunding    |   voteExtraordinary   |   26705   |   26245   |	460 |
||||    *Subtotal:*   |   *8262*    |
|   StandardFunding |   claimDelegateReward    |   33591    |   33741   |   -150    |
|   StandardFunding |   executeStandard |   3561    |   21737   |   1824    |
|   StandardFunding |   fundingVote |   121127  |   120444  |   683 |
|   StandardFunding |   proposeStandard |   83311   |   77480   |   5831    |
|   StandardFunding |   screeningVote   |   47128   |   46065   |   1063    |
|

File: ajna-grants/src/grants/base/Funding.sol

115:            if (targets_[i] != ajnaTokenAddress || values_[i] != 0) revert InvalidProposal();

```diff
run: diff -u before/2023-05-ajna/ajna-grants/src/grants/interfaces/IExtraordinaryFunding.sol after/2023-05-ajna/ajna-grants/src/grants/interfaces/IExtraordinaryFunding.sol
--- before/2023-05-ajna/ajna-grants/src/grants/interfaces/IExtraordinaryFunding.sol
+++ after/2023-05-ajna/ajna-grants/src/grants/interfaces/IExtraordinaryFunding.sol
@@ -44,15 +44,11 @@

     /**
      * @notice Execute an extraordinary funding proposal if it has passed its' requisite vote threshold.
-     * @param targets_         The addresses of the contracts to call.
-     * @param values_          The amounts of ETH to send to each target.
      * @param calldatas_       The calldata to send to each target.
      * @param descriptionHash_ The hash of the proposal's description string.
      * @return proposalId_     The ID of the executed proposal.
      */
     function executeExtraordinary(
-        address[] memory targets_,
-        uint256[] memory values_,
         bytes[] memory calldatas_,
         bytes32 descriptionHash_
     ) external returns (uint256 proposalId_);


RaymondFam Q

## Missing minter role granting
In `deployCash()` of CashFactory.sol, `cashProxied.grantRole()` did not grant `MINTER_ROLE` to `guardian` address. Similarly, in `deployCashKYCSender()` of CashKYCSenderFactory.sol, `cashKYCSenderProxied.` did not grant `MINTER_ROLE` to `guardian` address either. (Note: The same instance also goes with `deployCashKYCSenderReceiver()` of CashKYCSenderReceiverFactory.sol). Although this can later be separately executed by the guardian who is now the default admin, consider having this specific role granting included in the atomic function call or having it commented in the NatSpec the reason for this omission.

[File: CashFactory.sol#L75-L110](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashFactory.sol#L75-L110)

```diff
    cashProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashProxied.grantRole(PAUSER_ROLE, guardian);
+    cashProxied.grantRole(MINTER_ROLE, guardian);
```

[File: CashKYCSenderFactory.sol#L98-L99](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderFactory.sol#L98-L99)

```diff
    cashKYCSenderProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderProxied.grantRole(MINTER_ROLE, guardian);
```
[File: CashKYCSenderReceiverFactory.sol#L98-L99](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/factory/CashKYCSenderReceiverFactory.sol#L98-L99)

```diff
    cashKYCSenderReceiverProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderReceiverProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderReceiverProxied.grantRole(MINTER_ROLE, guardian);
```
## Use of deprecated file
As denoted in [OpenZeppelin's ERC20PresetMinterPauser.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol#L26) as well as [Ondo Finance's ERC20PresetMinterPauserUpgradeable.sol](https://github.com/code-

// Code block 1 (diff):
cashProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashProxied.grantRole(PAUSER_ROLE, guardian);
+    cashProxied.grantRole(MINTER_ROLE, guardian);

// Code block 2 (diff):
cashKYCSenderProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderProxied.grantRole(MINTER_ROLE, guardian);

// Code block 3 (diff):
cashKYCSenderReceiverProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderReceiverProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderReceiverProxied.grantRole(MINTER_ROLE, guardian);

cashProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashProxied.grantRole(PAUSER_ROLE, guardian);
+    cashProxied.grantRole(MINTER_ROLE, guardian);

cashKYCSenderProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderProxied.grantRole(MINTER_ROLE, guardian);

cashKYCSenderReceiverProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    cashKYCSenderReceiverProxied.grantRole(PAUSER_ROLE, guardian);
+    cashKYCSenderReceiverProxied.grantRole(MINTER_ROLE, guardian);

CashFactory.sol, ERC20PresetMinterPauser.sol, CashKYCSenderFactory.sol, CashKYCSenderReceiverFactory.sol


gz627 G

## Gas Optimizations

|       | Issue                                                                | Instances |
| ----- |:-------------------------------------------------------------------- |:---------:|
| GAS-1 | Applying `unchecked` operations where no overflow/underflow possible |    13     |
| GAS-2 | Gas optimizatin for `SmartAccount.execTransaction`                   |     1     |
| GAS-3 | Gas optimizatin for `SmartAccount.requiredTxGas`                     |     2     |
| GAS-4 | Gas optimizatin for `SmartAccount._validateSignature`                |     1     |
| GAS-5 | Using named variable returns saves gas                             |     2     |

### [GAS-1] Applying `unchecked` operations where no overflow/underflow possible

*Instances (13)*

#### Applying `unchecked` operations on the index increment in for-loops: *Instances (7)*
In addtion to the for-loop gas optimization in the C4audit output, a further gas optimization can be achieved to save another 63/120 gas per loop.
For stack variables like `uint256 index`, each `unchecked{++index;}` saves some gas compared with `++index;`. The saved gas amount varies under different situations - 120  without compiler optimization, and 63 with compiler optimization (200 runs). The `index` of a for-loop won't overflow so it is safe to apply unchecked increment. A typical optimized for-loop looks like:
```
...
uint256 length = someArray.length;
for (uint256 index; index<length;){
  ...do sth with someArray[index]...
  unchecked { ++index; }
}
```

[Link to code](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol)
```solidity
File: https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

74:         for (uint256 i = 0; i < opslen; i++) {

80:         for (uint256 i = 0; i < opslen; i++) {

100:         for (uint256 i = 0; i < opasLen; i++) {

107:

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

74:         for (uint256 i = 0; i < opslen; i++) {

80:         for (uint256 i = 0; i < opslen; i++) {

100:         for (uint256 i = 0; i < opasLen; i++) {

107:         for (uint256 a = 0; a < opasLen; a++) {

112:             for (uint256 i = 0; i < opslen; i++) {

128:         for (uint256 a = 0; a < opasLen; a++) {

134:             for (uint256 i = 0; i < opslen; i++) {

// Code block 1 (unknown):
...
uint256 length = someArray.length;
for (uint256 index; index<length;){
  ...do sth with someArray[index]...
  unchecked { ++index; }
}


P7N8ZK G

# [G-01] State variable `derivativeCount` should not be looked up in every loop of a for-loop

There are 7 instances of this issue:

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

 71:       for (uint i = 0; i < derivativeCount; i++)
 84:       for (uint i = 0; i < derivativeCount; i++) {
113:       for (uint i = 0; i < derivativeCount; i++) {
140:       for (uint i = 0; i < derivativeCount; i++) {
147:       for (uint i = 0; i < derivativeCount; i++) {
171:       for (uint i = 0; i < derivativeCount; i++)
191:       for (uint i = 0; i < derivativeCount; i++)
```

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

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

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

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

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

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

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

```
diff --git forkSrcPrefix/contracts/SafEth/SafEth.sol forkDstPrefix/contracts/SafEth/SafEth.sol
index ebadb4bbf2b55e7a28b0334c5dd3ea40aa163456..85b27ebb620f7ad1566145677d3b17ad4a2c8517 100644
--- forkSrcPrefix/contracts/SafEth/SafEth.sol
+++ forkDstPrefix/contracts/SafEth/SafEth.sol
@@ -68,7 +68,8 @@ contract SafEth is
         uint256 underlyingValue = 0;

         // Getting underlying value in terms of ETH for each derivative
-        for (uint i = 0; i < derivativeCount; i++)
+        uint _derivativeCount = derivativeCount;
+        for (uint i = 0; i < _derivativeCount; i++)
             underlyingValue +=
                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                     derivatives[i].balance()) /
@@ -81,7 +82,8 @@ contract SafEth is

diff --git forkSrcPrefix/contracts/SafEth/SafEth.sol forkDstPrefix/contracts/SafEth/SafEth.sol
index ebadb4bbf2b55e7a28b0334c5dd3ea40aa163456..85b27ebb620f7ad1566145677d3b17ad4a2c8517 100644
--- forkSrcPrefix/contracts/SafEth/SafEth.sol
+++ forkDstPrefix/contracts/SafEth/SafEth.sol
@@ -68,7 +68,8 @@ contract SafEth is
         uint256 underlyingValue = 0;

         // Getting underlying value in terms of ETH for each derivative
-        for (uint i = 0; i < derivativeCount; i++)
+        uint _derivativeCount = derivativeCount;
+        for (uint i = 0; i < _derivativeCount; i++)
             underlyingValue +=
                 (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                     derivatives[i].balance()) /
@@ -81,7 +82,8 @@ contract SafEth is
         else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;

         uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
-        for (uint i = 0; i < derivativeCount; i++) {
+        uint _derivativeCount = derivativeCount;
+        for (uint i = 0; i < _derivativeCount; i++) {
             uint256 weight = weights[i];
             IDerivative derivative = derivatives[i];
             if (weight == 0) continue;
@@ -110,7 +112,8 @@ contract SafEth is
         uint256 safEthTotalSupply = totalSupply();
         uint256 ethAmountBefore = address(this).balance;

-        for (uint256 i = 0; i < derivativeCount; i++) {
+        uint _derivativeCount = derivativeCount;
+        for (uint256 i = 0; i < _derivativeCount; i++) {
             // withdraw a percentage of each asset based on the amount of safETH
             uint256 derivativeAmount = (derivatives[i].balance() *
                 _safEthAmount) / safEthTotalSupply;
@@ -137,14 +140,16 @@ contract SafEth is
     */
     function rebalanceToWeights() external onlyOwner {
         uint256 ethAmountBefore = address(this).balance;
-        for (uint i = 0; i < derivativeCount; i++) {
+        uin

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

 71:       for (uint i = 0; i < derivativeCount; i++)
 84:       for (uint i = 0; i < derivativeCount; i++) {
113:       for (uint i = 0; i < derivativeCount; i++) {
140:       for (uint i = 0; i < derivativeCount; i++) {
147:       for (uint i = 0; i < derivativeCount; i++) {
171:       for (uint i = 0; i < derivativeCount; i++)
191:       for (uint i = 0; i < derivativeCount; i++)

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


RaymondFam Q

## Modularity on import usages
For cleaner Solidity code in conjunction with the rule of modularity and modular programming, use named imports with curly braces instead of adopting the global import approach.

For instance, the import instances below could be refactored as follows:

[File: ModuleManager.sol#L4-L6](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L4-L6)

```diff
- import "../common/Enum.sol";
- import "../common/SelfAuthorized.sol";
- import "./Executor.sol";
+ import {Enum} from "../common/Enum.sol";
+ import {SelfAuthorized} from "../common/SelfAuthorized.sol";
+ import {Executor} from "./Executor.sol";
```
## Expression for Hashed Values
Long bytes of literal values assigned to constants may incur typo/human error. In fact, it was validated in the constructor of Proxy.sol and Singleton.sol using `assert()` to compare the hashed constant with its `bytes32()` result to avoid this error:

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

```solidity
         assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));
```
As such, consider at least assigning these constants with their corresponding `bytes32()` expression where possible. For instance, the instance below could have its code line refactored as follows:

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

```diff
-    bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
+    bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = keccak256("EIP712Domain

// Code block 1 (diff):
- import "../common/Enum.sol";
- import "../common/SelfAuthorized.sol";
- import "./Executor.sol";
+ import {Enum} from "../common/Enum.sol";
+ import {SelfAuthorized} from "../common/SelfAuthorized.sol";
+ import {Executor} from "./Executor.sol";

// Code block 2 (solidity):
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

ModuleManager.sol#L4-L6, Singleton.sol#L13, Proxy.sol#L16, SmartAccount.sol#L42-L48

ModuleManager.sol, SmartAccount.sol, Proxy.sol, Singleton.sol


COSMIC BEE REACH Q

## Summary
### [LOW]
1. Possible unhandled underflow in PerpetualAtlanticVaultLP::redeem()
2. No validations to verify that `rdpxAmount` is not 0
3. UniV2LiquidityAmo::liquidityInPool() needs validation to check for pool existence
4. The contract `RdpxV2Core` needs to add validations before using reserve assets
5. No validations to verify that expiry is at least `block.timestamp`
6. Adding of assets to the token reserve may DoS due to block gas limits
7. `UniV2LiquidityAmo::addLiquidity()` needs validation for token X amount min <= token X amount
8. Missing validation `UniV3LiquidityAmo::addLiquidity()` to verify that min amount is less than the desired

### [QA]
###### 1. Documentation is just placeholder
The documentation template was not updated to reflect the actual docs
```
  /// @dev the pointer to the lattest funding payment timestamp
  /// @notice Explain to an end user what this does
  /// @dev Explain to a developer any extra details
  /// @return Documents the return variables of a contract’s function state variable
  /// @inheritdoc IPerpetualAtlanticVault
```
Code: [link](https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L84-L88)

###### 2. The 75% calculation can be made simplified
The code below calculates `// 25% below the current price` by subtracting 25% of value from the value. This can rather be made simpler by just doing (3*X)/4

##### Recommendation
`uint256 strike = roundUp(currentPrice - (currentPrice / 4)); // 25% below the current price`

Code: [link](https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L270)

###### 3. `PerpetualAtlanticVaultLP::convertToShares()` can return assets directly if supply is 0, skipping all the extra calculations

In the final return statement of the function` PerpetualAtlanticVaultLP::convertToShares()`, it checks if the supply is 0, and just returns the assets. The code can be refactored to do this check early

return
  supply == 0 ? assets : assets.mulDivDown(supply, totalVaultCollateral);

// Code block 1 (unknown):
/// @dev the pointer to the lattest funding payment timestamp
  /// @notice Explain to an end user what this does
  /// @dev Explain to a developer any extra details
  /// @return Documents the return variables of a contract’s function state variable
  /// @inheritdoc IPerpetualAtlanticVault


Bauchibred Q

# DopeX QA Report

|       | Issue                                                                                |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------- |
| QA-01 | RdpxV2Core's `addToDelegate()` should be refactored                                                                                |
| QA-02 | Swaps are made on curve without a deadline                                                                                |
| QA-03 | Users are presently not allowed to withdraw DSC when a bond just matured                                                               |
| QA-04 | `RdpxDecayingBonds.decreaseAmount()` needs to be refactored                                                                             |
| QA-05 | `calculatePremium()` wouldn't work for call option types since `isPut` bool is hardcoded to true value breaks multiple functionalities |
| QA-06 | Getting prices would always return stale price                                                                                |
| QA-07 | Unfulfilled heartbeat prices could be used                                                                                |

## QA 01 RdpxV2Core's `addToDelegate()` should be refactored

### Impact

Medium to low, since this easily curbs user's ability to properly delegate their WETH, this is cause if user follows the recommendation of the function the problem never gets fixed and just leads to more failed attempts

### Proof of Concept

Take a look at [addToDelegate()](https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L941-L969)

```solidity

  function addToDelegate(
    uint256 _amount,
    uint256 _fee
  ) external returns (uint256) {
    _whenNotPaused();
    // fee less than 100%
    _validate(_fee <

  function addToDelegate(
    uint256 _amount,
    uint256 _fee
  ) external returns (uint256) {
    _whenNotPaused();
    // fee less than 100%
    _validate(_fee < 100e8, 8);
    // amount greater than 0.01 WETH
    _validate(_amount > 1e16, 4);
    // fee greater than 1%
    //@audit
    _validate(_fee >= 1e8, 8);

    IERC20WithBurn(weth).safeTransferFrom(msg.sender, address(this), _amount);

    Delegate memory delegatePosition = Delegate({
      amount: _amount,
      fee: _fee,
      owner: msg.sender,
      activeCollateral: 0
    });
    delegates.push(delegatePosition);

    // add amount to total weth delegated
    totalWethDelegated += _amount;

    emit LogAddToDelegate(_amount, _fee, delegates.length - 1);
    return (delegates.length - 1);
  }


RED LOTUS REACH Q

# Low Risk and Non-Critical Issues
# Some values cannot be amended without deploying the contract
Critical values may be set to nonsense values during deployment.

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L67
```
address public weth;
```

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L124-L126
```
constructor(address _weth) {
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    weth = _weth;
```
## Risk
Because the audit does not include the actual final deploy scripts, there is a greater than 0 chance that there are errors there.  For values that are deployed incorrectly, that would cause a need to redeploy those contracts, possibly incurring a not-insignificant financial cost.

## Mitigation
Add a permissioned setter for critical (yet underlooked) values

# Old version of OpenZeppelin Contracts used
Using old versions of 3rd-party libraries in the build process can introduce vulnerabilities to the protocol code.

- Latest is 4.9.3 (as of August 28, 2023), version used in project is 4.9.0
### Risks

1. Older versions of OpenZeppelin contracts might not have fixes for known security vulnerabilities.
2. Older versions might contain features or functionality that have been deprecated in recent versions.
3. Newer versions often come with new features, optimizations, and improvements that are not available in older versions.

## Recommendation

Review the latest version of the OpenZeppelin contracts and upgrade.

# Redundant checks  for roles and whitelist
Calls to `mint()` are already guarded against addresses lacking the `MINTER_ROLE`, but there is a redundant `require` for the same condition that can be removed

https://github.com/code-423n4/2023-08-dopex/blob/0ea4387a4851cd6c8811dfb61da95a677f3f63ae/contracts/decaying-bonds/RdpxDecayingBonds.sol#L120
```
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a

// Code block 1 (unknown):
address public weth;

// Code block 2 (unknown):
constructor(address _weth) {
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    weth = _weth;


halden Q

# Low/NC issues

## [L-01] quorumVotes can be setted to 0
### Impact
It is possible for `quorumVotes` to be set to zero by the government.

```solidity
function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    //@audit _newQuorumVotes != 0
    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }
```

If this happens, it will block the creation of new proposals.

```solidity
    // Reject proposals before initiating as Governor
    if (quorumVotes == 0) revert GovernorCharlie_NotActive();
```

### Recommended Mitigation Steps
Add additional check for that:

```solidity
function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    if(_newQuorumVotes == 0) revert ZeroQuorumVotes();

    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }
```

## [L-02] Check if msg.sender is equal to signatory
### Impact
From the provided signature by `msg.sender`, the address of the `signatory` (the user who signed this signature) is obtained. The address of the `signatory` is checked to confirm if it is equal to `address(0)`, but it is never checked whether `msg.sender == signatory`.

```solidity
address _signatory = ecrecover(_digest, _v, _r, _s);
if (_signatory == address(0)) revert GovernorCharlie_InvalidSignature();
//@note check if msg.sender == signatory
```

msg.sender can use someone else's signature to vote on a given proposal.

### Recommended Mitigation Steps
Add additional check for that

## [L-03] Missed access modifier for deployVault function
### Impact
The modifier for vault controller access for the `deployVault` function is missing, and if a user calls this function, they will create a vault but will not be registered in the system (not included in VaultController). Users can create such malfunctioning vaults and get confused.

### Recommended Mitigation Steps
Add addi

// Code block 1 (solidity):
function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    //@audit _newQuorumVotes != 0
    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }

// Code block 2 (solidity):
// Reject proposals before initiating as Governor
    if (quorumVotes == 0) revert GovernorCharlie_NotActive();

// Code block 3 (solidity):
function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    if(_newQuorumVotes == 0) revert ZeroQuorumVotes();

    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }

// Code block 4 (solidity):
address _signatory = ecrecover(_digest, _v, _r, _s);
if (_signatory == address(0)) revert GovernorCharlie_InvalidSignature();
//@note check if msg.sender == signatory

function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    //@audit _newQuorumVotes != 0
    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }

// Reject proposals before initiating as Governor
    if (quorumVotes == 0) revert GovernorCharlie_NotActive();

function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {
    if(_newQuorumVotes == 0) revert ZeroQuorumVotes();

    uint256 _oldQuorumVotes = quorumVotes;
    quorumVotes = _newQuorumVotes;

    emit NewQuorum(_oldQuorumVotes, quorumVotes);
  }

address _signatory = ecrecover(_digest, _v, _r, _s);
if (_signatory == address(0)) revert GovernorCharlie_InvalidSignature();
//@note check if msg.sender == signatory


spark Q

In the PositionManager.sol, the function `memorializePositions` is trying to update `positions` inside the for loop based on the length of `params_.indexes`.

```solidity
        uint256 indexesLength = params_.indexes.length;
        uint256 index;

        for (uint256 i = 0; i < indexesLength; ) {...}

```

However, the `params_` is the input value for the function, when this value is too large, the transaction may fail due to the gas outage.

// Code block 1 (solidity):
uint256 indexesLength = params_.indexes.length;
        uint256 index;

        for (uint256 i = 0; i < indexesLength; ) {...}


IceBear Q

## Non Critical Issues

### [N-1] 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.
#### Recommended Mitigation Steps
Block timestamps should not be used for entropy or generating random numbers — i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.

Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.

Instances where block.timestamp is used:

*Find (24) instance(s) in contracts*:
```solidity
File: Ethos-Core/contracts/LQTY/CommunityIssuance.sol

87:             uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;

87:             uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;

93:         lastIssuanceTimestamp = block.timestamp;

113:         lastDistributionTime = block.timestamp.add(distributionPeriod);

114:         lastIssuanceTimestamp = block.timestamp;

```
[Ethos-Core/contracts/LQTY/CommunityIssuance.sol](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol)

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

128:

File: Ethos-Core/contracts/LUSDToken.sol

128:         deploymentStartTime = block.timestamp;

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

87:             uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;

87:             uint256 endTimestamp = block.timestamp > lastDistributionTime ? lastDistributionTime : block.timestamp;

93:         lastIssuanceTimestamp = block.timestamp;

113:         lastDistributionTime = block.timestamp.add(distributionPeriod);

114:         lastIssuanceTimestamp = block.timestamp;


hl_ Q

# Table of contents

- [L-01] Unable to remove derivative
- [L-02] Unstaking should not be paused
- [L-03] Function `RebalanceToWeight` may revert
- [L-04] Functions `stake` and `unstake` may revert
- [L-05] Include check to ensure sufficient funds for unstaking
- [N-01] Insufficient test coverage
- [N-02] Import declarations should import specific identifiers, rather than the whole file
- [N-03] Use a more recent version of Solidity
- [N-04] Non-library/interface files should use fixed compiler versions, not floating ones
- [N-05] NatSpec comments should be increased in contracts

## [L-01] Unable to remove derivative

In  `SafEth.sol`, there is no function available to remove a derivative. There should be such a function available in case a derivative should be removed.

## [L-02] Unstaking should not be paused

In general, function `unstake` should not have a pause modifier. Users should always be able to withdraw their funds.

```
File:  2023-03-asymmetry/contracts/SafEth/SafEth.sol

108:    function unstake(uint256 _safEthAmount) external {
109:    require(pauseUnstaking == false, "unstaking is paused");
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L109

##  [L-03] Function `RebalanceToWeight` may revert

Function `RebalanceToWeight` in `SafEth.sol` withdraws all derivatives and re-deposits them to have the correct weights. When it comes to a point where there are many derivatives added and a huge number of users, the function may revert due to reaching maximum gas limit. Consider breaking up the loop into smaller loops so that each execution can be successful.

```
File:  2023-03-asymmetry/contracts/SafEth/SafEth.sol (L138-155)

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

File:  2023-03-asymmetry/contracts/SafEth/SafEth.sol (L138-155)

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

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

// Code block 1 (unknown):
File:  2023-03-asymmetry/contracts/SafEth/SafEth.sol

108:    function unstake(uint256 _safEthAmount) external {
109:    require(pauseUnstaking == false, "unstaking is paused");


Bason G

## Gas Optimization Findings
| Number |Issues Details |
|:--:|:-------|
|[GAS-01]| Use if statements instead of require and revert with custom errors instead of using strings
|[GAS-02]| Make some constants private
|[GAS-03]| Use calldata type instead of memory
|[GAS-04]| Compute off-chain and use directly the hashes of contract addresses if possible
|[GAS-05]| Streamline if/else statements to save gas
|[GAS-06]| Assign a local variable for for loops when comparing to state variables to save from SLOAD operations
***

## [GAS-01] Use if statements instead of require and revert with custom errors instead of using strings

Even thought it is a public finding I would strongly advise to replace all require statements if statements and use custom errors.
There are many places in the contract where require is being used and that can save quite a bit of gas.

SafEth.sol

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

require(pauseUnstaking == false, "unstaking is paused");

```
Reth.sol
require(sent, "Failed to send Ether");
require(rethBalance2 > rethBalance1, "No rETH was minted");

```

SfrxEth.sol
require(sent, "Failed to send Ether");

```

WstEth.sol
require(sent, "Failed to send Ether");

```
## |[GAS-02]| Make some constants private

Reth.sol
All of these can be made private to save gas

address public constant ROCKET_STORAGE_ADDRESS =
    0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46;
address public constant W_ETH_ADDRESS =
    0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant UNISWAP_ROUTER =
    0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
address public constant UNI_V3_FACTORY =
    0x1F98431c8aD98523631AE4a59f267346ea31F984;

```
SfrxEth.sol

address public constant SFRX_ETH_ADDRESS =
    0xac3E018457B222d93114458476f3E3416Abbe38F;
address public constant FRX_ETH_ADDRESS =
    0x5E8422345238F34275888049021821E8E08CAa1f;
address public cons

// Code block 1 (unknown):
Reth.sol
require(sent, "Failed to send Ether");
require(rethBalance2 > rethBalance1, "No rETH was minted");

// Code block 2 (unknown):
WstEth.sol
require(sent, "Failed to send Ether");


0xSmartContractSamurai Q

0. `Proposals::proposeSendSALT()` - L202: Whenever salt balance of DAO.sol is `0 < DAO salt balance < 20`, `maxSendable` will round down to zero, therefore 0 salt will be sent even when salt balance is `19.99`.

https://github.com/code-423n4/2024-01-salty/blob/ce719503b43ebf086f5147c0f6efc3c0890bf0f9/src/dao/Proposals.sol#L202

Whenever `exchangeConfig.salt().balanceOf( address(exchangeConfig.dao()) )` is `> 0 but < 20`, `maxSendable` will round down to zero, and therefore user passed parameter `amount` (which should be > 0) will trigger a revert in `require( amount <= maxSendable, "Cannot send more than 5% of the DAO SALT balance" );`, therefore wont be possible to send salt to anyone whenever DAO has `salt tokens > 0 but < 20`.

Impact:

- should be minimal on average
- approved proposal for sending salt reverting
- approved multi-proposal reverting due to salt balance less than 20, this could be more serious

PoC:

L202:
`maxSendable = balance * 5 / 100`
Will round down to zero for `balance * 5 / 100 < 1`:
`balance * 5 / 100 < 1`
`balance < 100/5`
`balance < 20`

So for DAO salt `balance < 20`, e.g. 19.99, `maxSendable` will round down to zero due to how solidity/evm treats non-integer numbers, especially anything less than 1.

Recommendation:

Might be OK if DAO salt balance will never be less than 20, otherwise:
- maybe leverage Fixed-Point Arithmetic library

=======
=======

1. `DAO::_executeApproval()` - L170: `( exchangeConfig.salt().balanceOf(address(this)) >= ballot.number1 )` should use `>` instead of `>=` because of the related check in `Proposals::proposeSendSALT()` at L203: `require( amount <= maxSendable`.

https://github.com/code-423n4/2024-01-salty/blob/ce719503b43ebf086f5147c0f6efc3c0890bf0f9/src/dao/Proposals.sol#L203
https://github.com/code-423n4/2024-01-salty/blob/ce719503b43ebf086f5147c0f6efc3c0890bf0f9/src/dao/DAO.sol#L170

`_executeApproval()` L170:
```solidity
if ( exchangeConfig.salt().balanceOf(address(this)) >= ballot.number1 )
```

Shou

if ( exchangeConfig.salt().balanceOf(address(this)) * 5 / 100 >= ballot.number1 )

// Code block 1 (solidity):
if ( exchangeConfig.salt().balanceOf(address(this)) >= ballot.number1 )


sandy Q

# Summary

## Low Risk Issues
|Id|Title|
|:--:|:-------|
|L-01| Manually approve allowance to 0 to reduce chance of approval front-running |

## [L-01] Manually approve allowance to 0 to reduce chance of approval front-running.
### Instance:
1. https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L485-L495

### Description
The EIP-20 token's approve() function creates the potential for an approved spender to spend more than the intended amount. A front running attack can be used, enabling an approved spender to call transferFrom() both before and after the call to approve() is processed.

###  Recommendations
The ``_approve()`` of ``rUSDY.sol`` should manually approve allowances of spender from owner to 0 to before approving it to any other value. This will prevent approval front-running attacks.

Change From:
```
  function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }
```
To:
```diff
  function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

+   allowances[_owner][_spender] = 0; //manually approve to 0
    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }
```

## Non-Critical Issues
|Id|Title
|:--:|:-------|
|NC-01| Use named imports
|NC-02| Add more indexed parameters in events
|NC-03| Struct and errors can use more inline documentation
|NC-04| ``_rpow()`` lacks proper functional documentation

## [NC-01] Use named imports
There are no named imports used in any of the contracts in scope. Named imports sh

// Code block 1 (unknown):
function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }

// Code block 2 (diff):
function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

+   allowances[_owner][_spender] = 0; //manually approve to 0
    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }


0xhex G

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

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

734       require(
735            recoveredAddress != address(0) && recoveredAddress == _borrower,
736            "BorrowerOperations: Invalid signature"
737        );
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L734-L737

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

762        require(
763            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION,
764            "Max fee percentage must be between redemption fee floor and 100%"
765        );
```

https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManager.sol#L762

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

261        require(
262            closedStatus != Status.nonExistent && closedStatus != Status.active,
263            "CdpManagerStorage: close non-exist or non-active CDP!"
264        );

466        require(
467            cdpStatus != Status.nonExistent && cdpStatus != Status.active,
468            "CdpManagerStorage: remove non-exist or non-active CDP!"
469        );

653     require(
654            CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,
655            "CdpManager: Only one cdp in the system"
656        );
```

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

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

296        require(
297            _recipient != address(0) && _recipient != address(this),
298            "EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
299        );

300        require(
301            _recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
302            "EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
303        );
```

https://github

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

734       require(
735            recoveredAddress != address(0) && recoveredAddress == _borrower,
736            "BorrowerOperations: Invalid signature"
737        );

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

762        require(
763            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION,
764            "Max fee percentage must be between redemption fee floor and 100%"
765        );

// Code block 3 (solidity):
File: contracts/contracts/CdpManagerStorage.sol

261        require(
262            closedStatus != Status.nonExistent && closedStatus != Status.active,
263            "CdpManagerStorage: close non-exist or non-active CDP!"
264        );

466        require(
467            cdpStatus != Status.nonExistent && cdpStatus != Status.active,
468            "CdpManagerStorage: remove non-exist or non-active CDP!"
469        );

653     require(
654            CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,
655            "CdpManager: Only one cdp in the system"
656        );

// Code block 4 (solidity):
File: contracts/contracts/EBTCToken.sol

296        require(
297            _recipient != address(0) && _recipient != address(this),
298            "EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
299        );

300        require(
301            _recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
302            "EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
303        );

File: contracts/contracts/BorrowerOperations.sol

734       require(
735            recoveredAddress != address(0) && recoveredAddress == _borrower,
736            "BorrowerOperations: Invalid signature"
737        );

File: contracts/contracts/CdpManager.sol

762        require(
763            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION,
764            "Max fee percentage must be between redemption fee floor and 100%"
765        );

File: contracts/contracts/CdpManagerStorage.sol

261        require(
262            closedStatus != Status.nonExistent && closedStatus != Status.active,
263            "CdpManagerStorage: close non-exist or non-active CDP!"
264        );

466        require(
467            cdpStatus != Status.nonExistent && cdpStatus != Status.active,
468            "CdpManagerStorage: remove non-exist or non-active CDP!"
469        );

653     require(
654            CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1,
655            "CdpManager: Only one cdp in the system"
656        );

File: contracts/contracts/EBTCToken.sol

296        require(
297            _recipient != address(0) && _recipient != address(this),
298            "EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
299        );

300        require(
301            _recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
302            "EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
303        );


0x1f8b Q

- [Low](#low)
    - [**1. Integer overflow by unsafe casting**](#1-integer-overflow-by-unsafe-casting)
    - [**2. Mixing and Outdated compiler**](#2-mixing-and-outdated-compiler)
- [Non critical](#non-critical)
    - [**3. Wong naming**](#3-wong-naming)
    - [**4. Lack of checks supportsInterface**](#4-lack-of-checks-supportsinterface)
    - [**5. Avoid hardcoded values**](#5-avoid-hardcoded-values)
    - [**6. Use abstract for base contracts**](#6-use-abstract-for-base-contracts)

# Low

## **1. Integer overflow by unsafe casting**

Keep in mind that the version of solidity used, despite being greater than `0.8`, does not prevent integer overflows during casting, it only does so in mathematical operations.

It is necessary to safely convert between the different numeric types.

**Recommendation:**

Use a [safeCast](https://docs.openzeppelin.com/contracts/3.x/api/utils#SafeCast) from Open Zeppelin.

```javascript
return uint256(answer) * chainlinkInfo.scaleFactor;
```

**Affected source code:**

- [OndoPriceOracleV2.sol:300](https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/OndoPriceOracleV2.sol#L300)

## **2. Mixing and Outdated compiler**

The pragma version used are:

```
pragma solidity ^0.5.12;
pragma solidity ^0.5.16;
pragma solidity 0.8.16;
```

The minimum required version must be [0.8.17](https://github.com/ethereum/solidity/releases/tag/v0.8.17); otherwise, contracts will be affected by the following **important bug fixes**:

[0.8.17](https://blog.soliditylang.org/2022/09/08/solidity-0.8.17-release-announcement/)

- Yul Optimizer: Prevent the incorrect removal of storage writes before calls to Yul functions that conditionally terminate the external EVM call.

Apart from these, there are several minor bug fixes and improvements.

---

# Non critical

## **3. Wong naming**

Public methods should not use '`_`' before the name. We have an example in [CTokenModified.sol:1063](https://github.com/code-42

// Code block 1 (javascript):
return uint256(answer) * chainlinkInfo.scaleFactor;

// Code block 2 (unknown):
pragma solidity ^0.5.12;
pragma solidity ^0.5.16;
pragma solidity 0.8.16;


c3phas G

### Table of contents
- [FINDINGS](#findings)
- [Using immutable on variables that are only set in the constructor and never after  (2.1k gas per var)](#using-immutable-on-variables-that-are-only-set-in-the-constructor-and-never-after--21k-gas-per-var)
- [Tighly pack storage variables/optimize the order of variable declaration(Gas Savings: 6k in total)](#tighly-pack-storage-variablesoptimize-the-order-of-variable-declarationgas-savings-6k-in-total)
  - [\_notEnteredand  admin can be packed together (Saves 1 SLOT) `Gas savings: 1 * 2k = 2k`](#_notenteredand--admin-can-be-packed-together-saves-1-slot-gas-savings-1--2k--2k)
  - [\_notEntered and admin can be packed together (Saves 1 SLOT) `Gas savings: 1 * 2k = 2k`](#_notentered-and-admin-can-be-packed-together-saves-1-slot-gas-savings-1--2k--2k)
  - [\_notEntered can be packed with an address variable(saves 1 SLOT) `Gas savings: 1 * 2k = 2k`](#_notentered-can-be-packed-with-an-address-variablesaves-1-slot-gas-savings-1--2k--2k)
- [Massive 15k per tx gas savings - use 1 and 2 for Reentrancy guard](#massive-15k-per-tx-gas-savings---use-1-and-2-for-reentrancy-guard)
- [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)
  - [CTokenCash.sol.liquidateBorrowFresh(): getBlockNumber() should be cached](#ctokencashsolliquidateborrowfresh-getblocknumber-should-be-cached)
  - [CTokenModified.sol.liquidateBorrowFresh(): getBlockNumber() should be cached](#ctokenmodifiedsolliquidateborrowfresh-getblocknumber-should-be-cached)
- [Cache the mapping values rather than fetch it every time](#cache-storage-values-in-memory-to-minimize-sloads)
  - [OndoPriceOracle.sol.getUnderlyingPrice(): fTokenToUnderlyingPrice\[fToken\] should be cached](#ondopriceoraclesolgetunderlyingprice-ftokentounderlyingpriceftoken-should-be-cached)
  - [OndoPriceOracleV2.sol.getUnderlyingPrice(): fTokenToUnderlyingPriceCap\[fToken\] should be cac

File: /contracts/lending/JumpRateModelV2.sol
24:  address public owner;

## Tighly pack storage variables/optimize the order of variable declaration(Gas Savings: 6k in total)

Here, the storage variables can be tightly packed to save some slots
https://github.com/code-423n4/2023-01-ondo/blob/f3426e5b6b4561e09460b2e6471eb694efdd6c70/contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol#L13-L48
### \_notEnteredand  admin can be packed together (Saves 1 SLOT) `Gas savings: 1 * 2k = 2k`