Morraez G

# Gas Optimizations - (Total Optimizations 326)

## Mark storage variables as `immutable` if they never change after contract initialization.

State variables can be declared as constant or immutable. In both cases, the variables cannot be modified after the contract has been constructed. For constant variables, the value has to be fixed at compile-time, while for immutable, it can still be assigned at construction time.

The compiler does not reserve a storage slot for these variables, and every occurrence is inlined by the respective value.

Compared to regular state variables, the gas costs of constant and immutable variables are much lower. For a constant variable, the expression assigned to it is copied to all the places where it is accessed and also re-evaluated each time. This allows for local optimizations. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed. For these values, 32 bytes are reserved, even if they would fit in fewer bytes. Due to this, constant values can sometimes be cheaper than immutable values.

```js

contract GasTest is DSTest {
Contract0 c0;
Contract1 c1;
Contract2  c2;

function setUp() public {
   c0 = new Contract0();
   c1 = new Contract1();
   c2 = new Contract2();

}

function testGas() public view {
   c0.addValue();
   c1.addImmutableValue();
   c2.addConstantValue();
}
}

contract Contract0 {
uint256 val;

constructor() {
   val = 10000;
}

function addValue() public view {
   uint256 newVal = val + 1000;
}
}

contract Contract1 {
uint256 immutable val;

constructor() {
   val = 10000;
}

function addImmutableValue() public view {
   uint256 newVal = val + 1000;
}
}

contract Contract2 {
uint256 constant val = 10;

function addConstantValue() public view {
   uint256 newVal = val + 1000;
}
}

```

### Gas Report
```js
╭────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮
│ Contract0 contract ┆                 ┆      ┆

╭────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮
│ Contract0 contract ┆                 ┆      ┆        ┆      ┆         │
╞════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡
│ Deployment Cost    ┆ Deployment Size ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 54593              ┆ 198             ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name      ┆ min             ┆ avg  ┆ median ┆ max  ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ addValue           ┆ 2302            ┆ 2302 ┆ 2302   ┆ 2302 ┆ 1       │
╰────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮
│ Contract1 contract ┆                 ┆     ┆        ┆     ┆         │
╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡
│ Deployment Cost    ┆ Deployment Size ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 38514              ┆ 239             ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name      ┆ min             ┆ avg ┆ median ┆ max ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ addImmutableValue  ┆ 199             ┆ 199 ┆ 199    ┆ 199 ┆ 1       │
╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮
│ Contract2 contract ┆                 ┆     ┆        ┆     ┆         │
╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡
│ Deployment Cost    ┆ Deployment Size ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 32287              ┆ 191

// Code block 1 (js):
contract GasTest is DSTest {
Contract0 c0;
Contract1 c1;
Contract2  c2;

function setUp() public {
   c0 = new Contract0();
   c1 = new Contract1();
   c2 = new Contract2();

}

function testGas() public view {
   c0.addValue();
   c1.addImmutableValue();
   c2.addConstantValue();
}
}

contract Contract0 {
uint256 val;

constructor() {
   val = 10000;
}

function addValue() public view {
   uint256 newVal = val + 1000;
}
}

contract Contract1 {
uint256 immutable val;

constructor() {
   val = 10000;
}

function addImmutableValue() public view {
   uint256 newVal = val + 1000;
}
}

contract Contract2 {
uint256 constant val = 10;

function addConstantValue() public view {
   uint256 newVal = val + 1000;
}
}


j4ld1na Q

|     | Issues                                                                                | Instances |
| :-- | :--------------------------------------------------------------------------------------------------- | --------: |
| 01  | Constants in comparisons should appear on the left side.                                             |        28 |
| 02  | Don't use of `Block.Timestamp` can be manipulat                                                      |         1 |
| 03  | According to the syntax rules, use `=> mapping ( ` instead of `=> mapping(` using spaces as keyword. |        19 |

## [01] Constants in comparisons should appear on the left side.

_Constants on the left are better, but this is often trumped by a preference for English word order_

_Doing so will prevent [typo bugs](https://www.moserware.com/2008/01/constants-on-left-are-better-but-this.html)._

Typically we all write comparison statements like this:

```java
if (currentValue == 5)
{
    // do work
}
```

But, the following is just as valid:

```java
if (5 == currentValue)
{
    // do work
}
```

There are 28 instances:

[ajna-grants/src/grants/GrantFund.sol#L39-L40](https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/GrantFund.sol#L39-L40)

```java
if (_standardFundingProposals[proposalId_].proposalId != 0) return FundingMechanism.Standard;
        else if (_extraordinaryFundingProposals[proposalId_].proposalId != 0) return FundingMechanism.Extraordinary;
```

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

```java
if (positionIndexes[params_.tokenId].length() != 0) revert LiquidityNotRemoved();
```

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

```java
if (position.depositTime != 0) {
```

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

if (currentValue == 5)
{
    // do work
}

// Code block 1 (java):
if (currentValue == 5)
{
    // do work
}

// Code block 2 (java):
if (5 == currentValue)
{
    // do work
}

// Code block 3 (java):
if (_standardFundingProposals[proposalId_].proposalId != 0) return FundingMechanism.Standard;
        else if (_extraordinaryFundingProposals[proposalId_].proposalId != 0) return FundingMechanism.Extraordinary;

// Code block 4 (java):
if (positionIndexes[params_.tokenId].length() != 0) revert LiquidityNotRemoved();

// Code block 5 (java):
if (position.depositTime != 0) {


ayden Q

1.The result of function getPreUnlockableAmount should be cached into local variable instead of double calculate.
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#128#L144

```solidity
    function unlockPrematurely() external {
        require(
            block.timestamp + exitCycle - 3 days >
                time2fullRedemption[msg.sender],
            "ENW"
        );
+       uint256 preUnlockableAmount = getPreUnlockableAmount(msg.sender);
-       uint256 burnAmount = getReservedLBRForVesting(msg.sender) -
-            getPreUnlockableAmount(msg.sender);
-       uint256 amount = getPreUnlockableAmount(msg.sender) +
            getClaimAbleLBR(msg.sender);
+       uint256 burnAmount = getReservedLBRForVesting(msg.sender) + preUnlockableAmount;
+       uint256 amount = preUnlockableAmount - getClaimAbleLBR(msg.sender);
        if (amount > 0) {
            LBR.mint(msg.sender, amount);
        }
        unstakeRatio[msg.sender] = 0;
        time2fullRedemption[msg.sender] = 0;
        grabableAmount += burnAmount;
    }
```

2.LBR.burn will lost precision when amount is small

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L156

```solidity
    function testGrabEsLBR() public {
        vm.startPrank(Alice);
        assertEq(lbr.balanceOf(Alice), 1e18);

        for (uint256 i = 0; i < 10_000; i++) {
            pool.grabEsLBR(3);
        }

        assertEq(lbr.balanceOf(Alice), 1e18);
        assertEq(eslbr.balanceOf(Alice), 30_000);
    }
```

3.Change `x <= y ? x : y` to `x < y ? x : y` save 3 gas.

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/stakerewardV2pool.sol#L147#L150

4.`unlockPrematurely` should ensure user `time2fullRedemption` > 0.

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L129#L145

```solidity
    function unlockPrematurely() external {
+       require(tim

// Code block 1 (solidity):
function unlockPrematurely() external {
        require(
            block.timestamp + exitCycle - 3 days >
                time2fullRedemption[msg.sender],
            "ENW"
        );
+       uint256 preUnlockableAmount = getPreUnlockableAmount(msg.sender);
-       uint256 burnAmount = getReservedLBRForVesting(msg.sender) -
-            getPreUnlockableAmount(msg.sender);
-       uint256 amount = getPreUnlockableAmount(msg.sender) +
            getClaimAbleLBR(msg.sender);
+       uint256 burnAmount = getReservedLBRForVesting(msg.sender) + preUnlockableAmount;
+       uint256 amount = preUnlockableAmount - getClaimAbleLBR(msg.sender);
        if (amount > 0) {
            LBR.mint(msg.sender, amount);
        }
        unstakeRatio[msg.sender] = 0;
        time2fullRedemption[msg.sender] = 0;
        grabableAmount += burnAmount;
    }

// Code block 2 (solidity):
function testGrabEsLBR() public {
        vm.startPrank(Alice);
        assertEq(lbr.balanceOf(Alice), 1e18);

        for (uint256 i = 0; i < 10_000; i++) {
            pool.grabEsLBR(3);
        }

        assertEq(lbr.balanceOf(Alice), 1e18);
        assertEq(eslbr.balanceOf(Alice), 30_000);
    }

function unlockPrematurely() external {
        require(
            block.timestamp + exitCycle - 3 days >
                time2fullRedemption[msg.sender],
            "ENW"
        );
+       uint256 preUnlockableAmount = getPreUnlockableAmount(msg.sender);
-       uint256 burnAmount = getReservedLBRForVesting(msg.sender) -
-            getPreUnlockableAmount(msg.sender);
-       uint256 amount = getPreUnlockableAmount(msg.sender) +
            getClaimAbleLBR(msg.sender);
+       uint256 burnAmount = getReservedLBRForVesting(msg.sender) + preUnlockableAmount;
+       uint256 amount = preUnlockableAmount - getClaimAbleLBR(msg.sender);
        if (amount > 0) {
            LBR.mint(msg.sender, amount);
        }
        unstakeRatio[msg.sender] = 0;
        time2fullRedemption[msg.sender] = 0;
        grabableAmount += burnAmount;
    }

function testGrabEsLBR() public {
        vm.startPrank(Alice);
        assertEq(lbr.balanceOf(Alice), 1e18);

        for (uint256 i = 0; i < 10_000; i++) {
            pool.grabEsLBR(3);
        }

        assertEq(lbr.balanceOf(Alice), 1e18);
        assertEq(eslbr.balanceOf(Alice), 30_000);
    }

ProtocolRewardsPool.sol, ProtocolRewardsPool.sol#L156, stakerewardV2pool.sol#L147, ProtocolRewardsPool.sol#L129


0xnacho Q

# [L-01]: LybraWBETHVault: Misleading WBETH address specified in the comments

The WBETH address specified in the [LybraWBETHVault comments](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWbETHVault.sol#L16-L17) is misleading. The displayed address is actually the `rETH` mainnet address (https://etherscan.io/token/0xae78736Cd615f374D3085123A210448E74Fc6393).

# [L-02]: LybraRETHVault: Initial value of `rkPool` variable is not valid on all the chains

The address which is used to initialize the `rkPool` variable is only valid on `mainnet`, but it doesn't correspond to a RocketPool Deposit pool on `Arbitrum` or other L2s.

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraRETHVault.sol#L18

```
IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);
```

Therefore, removing the `rkPool` initialization is highly recommended.

# [L-03]: LybraPeUSDVaultBase#liquidation: redundant allowance check

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

```
require(PeUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
```

1. This is checked for ulterior `PeUSD.transferFrom` (https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L197-L204) in the `_repay` function.
2. The `transferFrom` method doesn't check for allowance if the vault is already a mint vault.
3. `LybraPeUSDVaultBase` is a mint vault, so the allowance is not checked

Given these, the allowance check is redundant.

# [L-04]: LybraPeUSDVaultBase: emitted `WithdrawAsset` - wrong params order

Declaration:

```
event WithdrawAsset(address sponsor, address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
```

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

```
  emit WithdrawAsset(
    _provider,

// Code block 1 (unknown):
IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);

// Code block 2 (unknown):
require(PeUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");

// Code block 3 (unknown):
event WithdrawAsset(address sponsor, address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);

LybraWbETHVault.sol#L16-L17, LybraRETHVault.sol#L18, LybraPeUSDVaultBase.sol#L131, LybraPeUSDVaultBase.sol#L197-L204, LybraPeUSDVaultBase.sol#L219


Matin Q

## Summary

### Low-Risk Issues

|  | Issue | Instances |
| --- | --- | --- |
| [L‑01] | Early users can modify the underlying assets' unit share price | 1 |
| [L‑02] | Multiple roles needed to invoke the function settle() | 1 |

Total: 2 instances over 2 issues

### Non-critical Issues

|  | Issue | Instances |
| --- | --- | --- |
| [N‑01] | Wrong natspec descriptions | 1 |

Total: 8 instances over 3 issues

Note: The above tables were created, considering the automatic findings and thus, those are not included.

---

## Low-Risk Issues

### [L‑01] **Early users can modify the underlying assets' unit share price**

Summary:

ERC4626, an extension of ERC20, is a standard that is mostly used in yield-bearing tokens. The contract of an ERC4626 token itself is an ERC20 which serves as a "shares" token and has an underlying asset which is another ERC20. The idea behind this is in such a way that the users deposit their assets and get corresponding shares. The function `convertToShares()` is responsible for returning the corresponding share of a user with respect to his deposited assets.

```Solidity
    function convertToShares(
    uint256 assets
  ) public view returns (uint256 shares) {
    uint256 supply = totalSupply;
    uint256 rdpxPriceInAlphaToken = perpetualAtlanticVault.getUnderlyingPrice();

    uint256 totalVaultCollateral = totalCollateral() +
      ((_rdpxCollateral * rdpxPriceInAlphaToken) / 1e8);
    return
      supply == 0 ? assets : assets.mulDivDown(supply, totalVaultCollateral);
  }
```

One of the main problems with these tokens lies in the fact that the first depositor can manipulate the unit share price in a bad way. The ratio of $\large\dfrac{totalSupply}{totalAssets}$ determines the corresponding share of a deposited asset. As it is clear from the ratio if the first user deposits 1 Wei, will get 1 Wei worth of share. Now consider a scenario with the preceding steps:

1 - The same bad guy deposits $\large 10000 \times 10^{18} - 1$ amount of un

// Code block 1 (Solidity):
function convertToShares(
    uint256 assets
  ) public view returns (uint256 shares) {
    uint256 supply = totalSupply;
    uint256 rdpxPriceInAlphaToken = perpetualAtlanticVault.getUnderlyingPrice();

    uint256 totalVaultCollateral = totalCollateral() +
      ((_rdpxCollateral * rdpxPriceInAlphaToken) / 1e8);
    return
      supply == 0 ? assets : assets.mulDivDown(supply, totalVaultCollateral);
  }


turvy_fuzz G

## the costly _attachThreshold() function can be adjusted to save gas
this:
```solidity
function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnToThresholdSet[txnHash] = TxnThreshold(
          t.numberOfApprovalsNeeded,
          new address[](0)
        );
        break;
      }
    }
    if (txnToThresholdSet[txnHash].numberOfApprovalsNeeded == 0) {
      revert NoThresholdMatch();
    }
  }
```
can be adjusted to this:
```solidity
function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    TxnThreshold memory txnThreshold;
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnThreshold = TxnThreshold(
        t.numberOfApprovalsNeeded,
        new address[](0)
        );
        break;
      }
    }
    if(txnThreshold.numberOfApprovalsNeeded == 0) {
    revert NoThresholdMatch();
    }
    txnToThresholdSet[txnHash] = txnThreshold;
}
```

// Code block 1 (solidity):
function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnToThresholdSet[txnHash] = TxnThreshold(
          t.numberOfApprovalsNeeded,
          new address[](0)
        );
        break;
      }
    }
    if (txnToThresholdSet[txnHash].numberOfApprovalsNeeded == 0) {
      revert NoThresholdMatch();
    }
  }

// Code block 2 (solidity):
function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    TxnThreshold memory txnThreshold;
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnThreshold = TxnThreshold(
        t.numberOfApprovalsNeeded,
        new address[](0)
        );
        break;
      }
    }
    if(txnThreshold.numberOfApprovalsNeeded == 0) {
    revert NoThresholdMatch();
    }
    txnToThresholdSet[txnHash] = txnThreshold;
}

function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnToThresholdSet[txnHash] = TxnThreshold(
          t.numberOfApprovalsNeeded,
          new address[](0)
        );
        break;
      }
    }
    if (txnToThresholdSet[txnHash].numberOfApprovalsNeeded == 0) {
      revert NoThresholdMatch();
    }
  }

function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    TxnThreshold memory txnThreshold;
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnThreshold = TxnThreshold(
        t.numberOfApprovalsNeeded,
        new address[](0)
        );
        break;
      }
    }
    if(txnThreshold.numberOfApprovalsNeeded == 0) {
    revert NoThresholdMatch();
    }
    txnToThresholdSet[txnHash] = txnThreshold;
}


Tigerfrake Q

# [01] Improper Indentation:

### Description:
The indentation in the following instances is inconsistent, making it harder to read and understand the code.
Example:

```Solidity
                if ( address(swapTokenIn) == address(wbtc))
                if ( address(swapTokenOut) == address(weth))
                        return (wbtc, salt);
```
Proper indentation improves code readability.

### Instances:

- https://github.com/code-423n4/2024-01-salty/blob/main/src%2Farbitrage%2FArbitrageSearch.sol#L35-L37
- https://github.com/code-423n4/2024-01-salty/blob/main/src%2Farbitrage%2FArbitrageSearch.sol#L41-L43

### Recommendation:
> Nest the two if checks with proper indentation or use the && operand in one if-check if both checks are required to be valid for these operations

# [02] Avoid Block `Timestamp` Manipulation

### Description:
`Block timestamps` have been used historically for a number of purposes, including `entropy for random numbers locking funds for a set amount of time, and different state-changing, time-dependent conditional statements`.
Because validators have the capacity to slightly alter `timestamps`, using `block timestamps` wrongly in smart contracts can be quite risky.
The time difference between events can be estimated using `block.number` and the average `block time`. However, because `block times` can change and break functionality, it's best to avoid its use.

### Instances:
https://github.com/code-423n4/2024-01-salty/blob/main/src%2Fdao%2FProposals.sol#L392
```Solidity
        if (block.timestamp < ballot.ballotMinimumEndTime )
```

# [03] Missing or Incomplete NatSpec

### Description
Some functions are missing `@notice/@dev` NatSpec comments for the function, `@param` for all/some of their parameters and `@return` for return values. Given that NatSpec is an important part of code documentation, this affects code comprehension, auditability and usability.

### Instances:
All contracts

### Recommendation
> Consider adding in full NatSpec

// Code block 1 (Solidity):
if ( address(swapTokenIn) == address(wbtc))
                if ( address(swapTokenOut) == address(weth))
                        return (wbtc, salt);

// Code block 2 (Solidity):
if (block.timestamp < ballot.ballotMinimumEndTime )

if ( address(swapTokenIn) == address(wbtc))
                if ( address(swapTokenOut) == address(weth))
                        return (wbtc, salt);

if (block.timestamp < ballot.ballotMinimumEndTime )


SleepingBugs Q

## Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions
### Impact
For upgradeable contracts, there must be storage gap to "allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments" (quote OpenZeppelin). Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts, potentially causing loss of user fund or cause the contract to malfunction completely.

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

### Vulnerability Details
In the following context of the upgradeable contracts they are expected to use gaps for avoiding collision:
- `ERC20PresetMinterPauserUpgradeable`
- `Initializable`

However, none of these contracts contain storage gap. The storage gap is essential for upgradeable contract because "It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments". Refer to the bottom part of this [article](https://docs.openzeppelin.com/contracts/4.x/upgradeable).

If a contract inheriting from a base contract contains additional variable, then the base contract cannot be upgraded to include any additional variable, because it would overwrite the variable declared in its child contract. This greatly limits contract upgradeability.

### Github Permalinks
https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/kyc/KYCRegistryClientInitializab

## Missing checks for address(0x0) when assigning values to `address` state or `immutable` variables
`NOTE`: None of these findings where found by [Picodes. NC-1](https://gist.github.com/iFrostizz/bbbadb3d93229f60c94f01b6626c056d)

### Summary
Zero address should be checked for state variables, immutable variables.

### Impact
Unexpected behaviors in edge cases

### Github Permalinks
These 2 are not found by the automatic tool

https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/kyc/KYCRegistry.sol#L57

https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/lending/JumpRateModelV2.sol#L66

`NOTE`: These are already "found" but not at the correct line, so better don't count them, I add them to improve the review. All lines in the later 3 links in the automatic findings are L70, while real line is L54 or L57

https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/factory/CashKYCSenderFactory.sol#L54

https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/factory/CashKYCSenderReceiverFactory.sol#L54

https://github.com/code-423n4/2023-01-ondo/blob/12537cc94f4b0e9b213d53906aa2f95e425dd899/contracts/cash/kyc/KYCRegistry.sol#L57

### Mitigation
Check zero address before assigning or using it

KYCRegistry.sol#L57, JumpRateModelV2.sol#L66, CashKYCSenderFactory.sol#L54, CashKYCSenderReceiverFactory.sol#L54

JumpRateModelV2.sol, KYCRegistry.sol, CashKYCSenderReceiverFactory.sol, CashKYCSenderFactory.sol


Rickard Q

# [N-01] Move IF/validation statements to the top of the function when validating input parameters
````solidity
File: contracts/lybra/pools/LybraRETHVault.sol

27    function depositEtherToMint(uint256 mintAmount) external payable override {
28            require(msg.value >= 1 ether, "DNL");
29            uint256 preBalance = collateralAsset.balanceOf(address(this));
30            rkPool.deposit{value: msg.value}();
31            uint256 balance = collateralAsset.balanceOf(address(this));
32            depositedAsset[msg.sender] += balance - preBalance;
33
34:           if (mintAmount > 0) {
35:               _mintPeUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
36:           }
````
[LybraRETHVault.sol#L27-L39](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraRETHVault.sol#L27-L39)

Same goes for:
[LybraStETHVault.sol#L47-L49](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L47-L49), [LybraWbETHVault.sol#L27-L29](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWbETHVault.sol#L27-L29), [LybraWstETHVault.sol#L41-L43](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWstETHVault.sol#L41-L43), [LybraEUSDVaultBase.sol#L82-L84](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L82-L84), [LybraPeUSDVaultBase.sol#L65-L68](https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L65-L68).
````solidity
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
212    function _withdraw(address _provider, address _onBehalfOf, uint256 _amount) internal {
213            require(depositedAsset[_provider] >= _amount, "Withdraw amount exceeds deposited amount.");
214            depositedAsset[_provider] -= _amount;
215            collateralAsset.transfer(_onBehalfOf, _amount);
216:           if (getBorrowedOf(_p

File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
212    function _withdraw(address _provider, address _onBehalfOf, uint256 _amount) internal {
213            require(depositedAsset[_provider] >= _amount, "Withdraw amount exceeds deposited amount.");
214            depositedAsset[_provider] -= _amount;
215            collateralAsset.transfer(_onBehalfOf, _amount);
216:           if (getBorrowedOf(_provider) > 0) {
217:               _checkHealth(_provider, getAssetPrice());
218:           }

// Code block 1 (solidity):
File: contracts/lybra/pools/LybraRETHVault.sol

27    function depositEtherToMint(uint256 mintAmount) external payable override {
28            require(msg.value >= 1 ether, "DNL");
29            uint256 preBalance = collateralAsset.balanceOf(address(this));
30            rkPool.deposit{value: msg.value}();
31            uint256 balance = collateralAsset.balanceOf(address(this));
32            depositedAsset[msg.sender] += balance - preBalance;
33
34:           if (mintAmount > 0) {
35:               _mintPeUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
36:           }

LybraRETHVault.sol#L27-L39, LybraStETHVault.sol#L47-L49, LybraWbETHVault.sol#L27-L29, LybraWstETHVault.sol#L41-L43, LybraEUSDVaultBase.sol#L82-L84, LybraPeUSDVaultBase.sol#L65-L68

LybraEUSDVaultBase.sol, LybraWstETHVault.sol, LybraRETHVault.sol, LybraStETHVault.sol, LybraWbETHVault.sol, LybraPeUSDVaultBase.sol


b0g0 Q

## [L-01] Signature verification function does not prevent signature malleability

## Issue Description
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/SigningTools.sol#L11

A common attack vector when it comes to using signatures is their malleability. In simple terms this allows an already used signature to be modified without changing it's signed data and reusing it a second time ([article](https://medium.com/draftkings-engineering/signature-malleability-7a804429b14a)). The way to prevent this is to check that s value of the signature is in the lower half order, and the v value to be either 27 or 28. However the `_verifySignature` inside `SigningTools.sol` library doesn't do this and allows such forged signatures to be accepted. This is used during voting to start the exchange and could lead to double votes

```
function _verifySignature(
        bytes32 messageHash,
        bytes memory signature
    ) internal pure returns (bool) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        //@audit [L] -> does not check for upper/lower value of the signature

        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := mload(add(signature, 0x41))
        }

        address recoveredAddress = ecrecover(messageHash, v, r, s);

        return (recoveredAddress == EXPECTED_SIGNER);
    }
```

## Recommended Mitigation Steps
Consider using OpenZeppelin ECDSA library (which handles this) instead of implementing it yourself -> https://docs.openzeppelin.com/contracts/2.x/api/cryptography#ECDSA-recover-bytes32-bytes-

## [L-02] BoostrapBallot does not use `deadline` in the signature when verifying  votes in the `vote()` method

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/BootstrapBallot.sol#L48

## Issue Description
Using deadlines

function finalizeBallot() external nonReentrant {
        require(!ballotFinalized, "Ballot has already been finalized");
        require(
            block.timestamp >= completionTimestamp,
            "Ballot is not yet complete"
        );

        if (startExchangeYes > startExchangeNo) {
            exchangeConfig.initialDistribution().distributionApproved();
            //@audit - there is a nested nasty loop here- can deployment go out of gas for too many whitelisted pools
            exchangeConfig.dao().pools().startExchangeApproved();

            startExchangeApproved = true;
        }

        emit BallotFinalized(startExchangeApproved);

        ballotFinalized = true;
    }

// Code block 1 (unknown):
function _verifySignature(
        bytes32 messageHash,
        bytes memory signature
    ) internal pure returns (bool) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        //@audit [L] -> does not check for upper/lower value of the signature

        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := mload(add(signature, 0x41))
        }

        address recoveredAddress = ecrecover(messageHash, v, r, s);

        return (recoveredAddress == EXPECTED_SIGNER);
    }


Shawon G

# Don't store data in a variable if it is used only once.

``` solidity
file: utils/LRTDeposit.sol

81 uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);
            unchecked {
                ++i;
            }
        }
    }
```
The value of `nodeDelegatorQueue.length` is used only once and caching it inside a uint256 variable would only use memory storage hence,
increasing the gas price.

Insdead, do this:

``` solidity
for (uint256 i; i < nodeDelegatorQueue.length;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);
            unchecked {
                ++i;
            }
        }
    }
```

// Code block 1 (unknown):
The value of `nodeDelegatorQueue.length` is used only once and caching it inside a uint256 variable would only use memory storage hence,
increasing the gas price.

Insdead, do this:


Stormreckson Q

1- https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/core/RdpxV2Core.sol#L894-L899

In the `bond` function,the `_to` parameter represents the recipient of the bond receipt, it wouldbe rational for `_to` to be `msg.sender`.
Setting `_to` as `msg.sender` would ensure that the bond receipt is sent directly to the caller of the function who is making the bond with the delegates.

i. Setting `_to` as msg.sender eliminates the need for the caller to provide an additional address parameter, simplifying the function call.

ii. By using msg.sender as the recipient, it avoids an additional storage write operation to store the recipient's address, resulting in potential gas savings.

iii.  Gas Efficiency: By using msg.sender as the recipient, it avoids an additional storage write operation to store the recipient's address, resulting in potential gas savings.
In order to make the functionality more intuitive and align it with the caller's intent, I propose updating the `bondWithDelegate` function to set the `_to` parameter as `msg.sender`. By doing so, the caller of the function will automatically become the recipient of the bond receipt.

Modified Function:

     function bondWithDelegate(
         uint256[] memory _amounts,
         uint256[] memory _delegateIds,
         uint256 rdpxBondId
      ) public returns (uint256
     receiptTokenAmount, uint256[]
     memory delegateReceiptTokenAmounts)

The change only adjusts the default behavior so that the caller becomes the recipient of the bond receipt. It improves usability and saves users from the need to explicitly provide their own address as the receipt recipient.

2- there are some confusing function descriptions in `RdpxV2core.sol` contract.
It states only the `owner` can call certain functions whereas the function `Roles` points to the `Admin`, for clearity purpose I suggest changing them to follow the function implementations.

https://github.com

  /**
   * @notice Pauses the vault for emergency cases
   * @dev    Can only be called by the owner
   **/
  function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {

  /**
   * @notice Unpauses the vault
   * @dev    Can only be called by the owner
   **/
  function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {

  /**
   * @notice Transfers all funds to msg.sender
   * @dev    Can only be called by the owner
   * @param  tokens The list of erc20 tokens to withdraw
   **/
  function emergencyWithdraw(
    address[] calldata tokens
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {

    for (uint256 i = 0; i < optionIds.length; i++) {
      uint256 strike = optionPositions[optionIds[i]].strike;
      uint256 amount = optionPositions[optionIds[i]].amount;


Grzegorz Q

# The LybraConfigurator.setBadCollateralRatio function emits incorrect event. It should emit BadCollaterialRatioChanged instead of SafeCollateralRatioChanged.
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L126C1-L130
```
    function setBadCollateralRatio(address pool, uint256 newRatio) external onlyRole(DAO) {
        require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
        vaultBadCollateralRatio[pool] = newRatio;
        emit SafeCollateralRatioChanged(pool, newRatio);
    }
```

# The EUSDMiningIncentives.notifyRewardAmount function call can be used to significantly reduce rewardRatio.
1. Let's assume duration=1_000_000
 Admin calls EUSDMiningIncentives.notifyRewardAmount(1_000_000)
rewardRatio = 1_000_000 / 1_000_000 = 1
2. After half of the duration admin calls EUSDMiningIncentives.notifyRewardAmount(2)
remainingRewards = 500_000
rewardRatio = ((1_000_000 - 500_000) + 2) / 1_000_000 = 0.502

// Code block 1 (unknown):
function setBadCollateralRatio(address pool, uint256 newRatio) external onlyRole(DAO) {
        require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
        vaultBadCollateralRatio[pool] = newRatio;
        emit SafeCollateralRatioChanged(pool, newRatio);
    }


0xhacksmithh G

### [Gas-01] Struct can be packed
*instances(16 structs)*
```
File: Ethos-Core/contracts/CollateralConfig.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L27-L32
````
```
File: Ethos-Core/contracts/BorrowerOperations.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L47-L64
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L66-L78
```
```
File: Ethos-Core/contracts/TroveManager.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L129-L138
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L140-L144
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L146-L158
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L160-L170
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L172-L182
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1-L1
```
```
File: Ethos-Core/contracts/ActivePool.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L220-L237
```
```
File: Ethos-Core/contracts/StabilityPool.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L214
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L223
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L646-L655
```
```
File: Ethos-Core/contracts/LUSDToken.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L58
```
```
File: Ethos-Vault/contracts/ReaperVaultV2.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L25-L32
https://github.com/code-423n4/2023-02-ethos/blob/main

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

// Code block 2 (unknown):
File: Ethos-Core/contracts/BorrowerOperations.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L47-L64
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L66-L78

// Code block 3 (unknown):
File: Ethos-Core/contracts/TroveManager.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L129-L138
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L140-L144
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L146-L158
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L160-L170
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L172-L182
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol#L1-L1

// Code block 4 (unknown):
File: Ethos-Core/contracts/ActivePool.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L220-L237

// Code block 5 (unknown):
File: Ethos-Core/contracts/StabilityPool.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L214
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L223
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L646-L655

// Code block 6 (unknown):
File: Ethos-Core/contracts/LUSDToken.sol
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L58

CollateralConfig.sol#L27-L32, BorrowerOperations.sol#L47-L64, BorrowerOperations.sol#L66-L78, TroveManager.sol#L129-L138, TroveManager.sol#L140-L144, TroveManager.sol#L146-L158, TroveManager.sol#L160-L170, TroveManager.sol#L172-L182, TroveManager.sol#L1, ActivePool.sol#L220-L237, StabilityPool.sol#L214, StabilityPool.sol#L223, StabilityPool.sol#L646-L655, LUSDToken.sol#L58, ReaperVaultV2.sol#L25-L32

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


Kaysoft G

## [GAS-1] The Ownable library is unnecessary on the `USDS.sol` contract.
There is 1 instance of this
- https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/USDS.sol#L14

The `onlyOwner` modifier of the `ownable` contract was used only once in the  function that is to be executed only once at deployment, then ownership is renounced in the same function.

Since the below function is the only function where `onlyOwner` modifier is used and it is executed only once at deployment according to the comment above it, the code in the `setCollateralAndLiquidity(...)` can be implemented in the constructor since the constructor is run only once and can only be by the deployer.

```
File: src/stable/USDS.sol
28: // This will be called only once - at deployment time
29:	function setCollateralAndLiquidity( ICollateralAndLiquidity _collateralAndLiquidity ) external onlyOwner
30:		{
31:		collateralAndLiquidity = _collateralAndLiquidity; //@audit set this in the constructor since it will be called at deployment.

32:		// setCollateralAndLiquidity can only be called once
33:		renounceOwnership();//@audit ownable is unnecessary.
34:		}
```

Impact:
Reduces deployment gas cost because the `Ownable` contract code is removed.

Recommendation:
Implement the code in the `setCollateralAndLiquidity(...)` in the constructor of the contract this way:

```
constructor(ICollateralAndLiquidity _collateralAndLiquidity) ERC20( "USDS", "USDS" ) {
   collateralAndLiquidity = _collateralAndLiquidity;// also emit event.
}
```

The above will remove the need for the `setCollateralAndLiquidity(...)` and the `Ownable` contract codes.

// Code block 1 (unknown):
File: src/stable/USDS.sol
28: // This will be called only once - at deployment time
29:	function setCollateralAndLiquidity( ICollateralAndLiquidity _collateralAndLiquidity ) external onlyOwner
30:		{
31:		collateralAndLiquidity = _collateralAndLiquidity; //@audit set this in the constructor since it will be called at deployment.

32:		// setCollateralAndLiquidity can only be called once
33:		renounceOwnership();//@audit ownable is unnecessary.
34:		}

// Code block 2 (unknown):
constructor(ICollateralAndLiquidity _collateralAndLiquidity) ERC20( "USDS", "USDS" ) {
   collateralAndLiquidity = _collateralAndLiquidity;// also emit event.
}


Brenzee Q

## L-01 `removeAssetFromTokenReserves` function does not remove assets correctly from the `reserveTokens` array

[Link to the code](https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L287)

`removeAssetFromtokenReserves` function removes tokens from `reserveAsset`, `reserveIndex`, and `reserveTokens`. But from the `reserveTokens` array it is not removed correctly - function removes **last** token no matter what token is intented to be removed.

```solidity
    reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();
```

### Recommended solution
Update the code to the following

```solidity
    reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    string memory lastElement = reserveTokens[reserveTokens.length - 1];
    reservesIndex[lastElement] = index;
    reserveTokens[index] = lastElement;

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();
```

// Code block 1 (solidity):
reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();

// Code block 2 (solidity):
reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    string memory lastElement = reserveTokens[reserveTokens.length - 1];
    reservesIndex[lastElement] = index;
    reserveTokens[index] = lastElement;

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();

reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    reserveAsset[index] = reserveAsset[reserveAsset.length - 1];

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();

reservesIndex[reserveTokens[reserveTokens.length - 1]] = index;

    // update the index of reserveAsset with the last element
    string memory lastElement = reserveTokens[reserveTokens.length - 1];
    reservesIndex[lastElement] = index;
    reserveTokens[index] = lastElement;

    // remove the last element
    reserveAsset.pop();
    reserveTokens.pop();


Breeje G

## Gas Optimizations

| |Issue|Instances|
|-|:-|:-:|
| [GAS-1](#GAS-1) | ++I/I++ SHOULD BE UNCHECKED{++I}/UNCHECKED{I++} WHEN IT IS NOT POSSIBLE FOR THEM TO OVERFLOW | 12 |
| [GAS-2](#GAS-2) | SPLITTING REQUIRE() STATEMENTS THAT USE && SAVES GAS | 3 |
| [GAS-3](#GAS-3) | NOT USING THE NAMED RETURN VARIABLES WHEN A FUNCTION RETURNS, WASTES DEPLOYMENT GAS | 1 |

###  [GAS-1] ++I/I++ SHOULD BE UNCHECKED{++I}/UNCHECKED{I++} WHEN IT IS NOT POSSIBLE FOR THEM TO OVERFLOW

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop.

*Instances (12)*:
```solidity
File: cash/CashManager.sol

750:      for (uint256 i = 0; i < size; ++i) {

786:      for (uint256 i = 0; i < size; ++i) {

933:      for (uint256 i = 0; i < size; ++i) {

961:      for (uint256 i = 0; i < exCallData.length; ++i) {

```

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

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

```

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

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

```

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

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

```

```solidity
File: cash/kyc/KYCRegistry.sol

163:      for (uint256 i = 0; i < length; i++) {

180:      for (uint256 i = 0; i < length; i++) {

```

```solidity
File: lending/CompoundLens.sol

85:      for (uint i = 0; i < cTokenCount; i++) {

135:     for (uint i = 0; i < cTokenCount; i++) {

167:     for (uint i = 0; i < cTokenCount; i++) {

```

###  [GAS-2] SPLITTING REQUIRE() STATEMENTS THAT USE && SAVES GAS

Saves 3 gas per instance.

*Instances (3)*:
```solidity
File: lending/OndoPriceOracleV2.sol

293:         (answeredInRound >= roundId) &&

```

```solidity
File: lending/tokens/cCash/CTokenCash.sol

46:          accrualBlockNumber == 0 && borrowIndex == 0,

```

```solidity
File: lending/tokens/cCash/CTokenModif

// Code block 1 (solidity):
File: cash/CashManager.sol

750:      for (uint256 i = 0; i < size; ++i) {

786:      for (uint256 i = 0; i < size; ++i) {

933:      for (uint256 i = 0; i < size; ++i) {

961:      for (uint256 i = 0; i < exCallData.length; ++i) {

// Code block 2 (solidity):
File: cash/factory/CashFactory.sol

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

// Code block 3 (solidity):
File: cash/factory/CashKYCSenderFactory.sol

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

// Code block 4 (solidity):
File: cash/factory/CashKYCSenderReceiverFactory.sol

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

// Code block 5 (solidity):
File: cash/kyc/KYCRegistry.sol

163:      for (uint256 i = 0; i < length; i++) {

180:      for (uint256 i = 0; i < length; i++) {

// Code block 6 (solidity):
File: lending/CompoundLens.sol

85:      for (uint i = 0; i < cTokenCount; i++) {

135:     for (uint i = 0; i < cTokenCount; i++) {

167:     for (uint i = 0; i < cTokenCount; i++) {

// Code block 7 (solidity):
File: lending/OndoPriceOracleV2.sol

293:         (answeredInRound >= roundId) &&

// Code block 8 (solidity):
File: lending/tokens/cCash/CTokenCash.sol

46:          accrualBlockNumber == 0 && borrowIndex == 0,

File: cash/CashManager.sol

750:      for (uint256 i = 0; i < size; ++i) {

786:      for (uint256 i = 0; i < size; ++i) {

933:      for (uint256 i = 0; i < size; ++i) {

961:      for (uint256 i = 0; i < exCallData.length; ++i) {

File: cash/factory/CashFactory.sol

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

File: cash/factory/CashKYCSenderFactory.sol

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

File: cash/factory/CashKYCSenderReceiverFactory.sol

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

File: cash/kyc/KYCRegistry.sol

163:      for (uint256 i = 0; i < length; i++) {

180:      for (uint256 i = 0; i < length; i++) {

File: lending/CompoundLens.sol

85:      for (uint i = 0; i < cTokenCount; i++) {

135:     for (uint i = 0; i < cTokenCount; i++) {

167:     for (uint i = 0; i < cTokenCount; i++) {

File: lending/OndoPriceOracleV2.sol

293:         (answeredInRound >= roundId) &&

File: lending/tokens/cCash/CTokenCash.sol

46:          accrualBlockNumber == 0 && borrowIndex == 0,


SM3_SS G

# Gas Optimizations Summary

|  NO  | 	Issue  |  Instances  |
|------|-----------|-------------|
|[G-01]| Cache external calls outside of loop to avoid re-calling function on each iteration   | 6 |
|[G-02]| Combine events to save 2 Glogtopic (375 gas)  | 6 |
|[G-03]| Use calldata instead of memory for function arguments that do not get mutated   | 10 |
|[G-04]| Avoid emitting storage values  | 16 |
|[G-05]| A modifier used only once and not being inherited should be inlined to save gas  | 1 |
|[G-06]| If statements that use && can be refactored into nested if statements   | 7 |
|[G-07]| Use assembly to write address storage values   | 14 |
|[G-08]| Using > 0 costs more gas than != 0 when used on a uint in a require() statement   | 9 |
|[G-09]| Do not calculate constants   | 8 |
|[G-10]| Use hardcode address instead address(this)   | 19 |
|[G-11]| Don’t compare boolean expressions to boolean literals   | 6 |
|[G-12]| Assigning keccak operations to constant variables results in extra gas costs   | 5 |
|[G-13]| With assembly, .call (bool success) transfer can be done gas-optimized   | 1 |
|[G-14]| Unnecessary look up in if condition   | 2 |
|[G-15]| State variables can be packed into fewer storage slots   | 2 |
|[G-16]| Use shift Right/Left instead of division/multiplication if possible   | 10 |
|[G-17]| Amounts should be checked for 0 before calling a transfer   | 5 |
|[G-18]| internal functions not called by the contract should be removed to save deployment gas   | 3 |
|[G-19]| Using a positive conditional flow to save a NOT opcode   | 8 |
|[G-20]| Use do while loops instead of for loops   | 5 |
|[G-21]| Failure to check the zero address in the constructor causes the contract to be deployed again   | 3 |
|[G-22]| Inverting the condition of an if-else-statement wastes gas   | 8 |
|[G-23]| Use assembly to emit events   | 72 |
|[G-24]| Create immutable variable to avoid redundant external calls   | 1 |
|[G-25]| Forgo internal function to save 1

file:   contracts/core/VaultController.sol

255         for (uint256 _i; _i < _tokenAddresses.length;) {
      _tokenId = _oldVaultController.tokenId(_tokenAddresses[_i]);
      if (_tokenId == 0) revert VaultController_WrongCollateralAddress();
      _tokensRegistered++;

      CollateralInfo memory _collateral = _oldVaultController.tokenCollateralInfo(_tokenAddresses[_i]);
      _collateral.tokenId = _tokensRegistered;
      _collateral.totalDeposited = 0;

      enabledTokens.push(_tokenAddresses[_i]);
      tokenAddressCollateralInfo[_tokenAddresses[_i]] = _collateral;

      unchecked {
        ++_i;
      }
    }

file:   contracts/core/Vault.sol

169         for (uint256 _i; _i < _tokenAddresses.length;) {
      IVaultController.CollateralInfo memory _collateralInfo = CONTROLLER.tokenCollateralInfo(_tokenAddresses[_i]);
      if (_collateralInfo.tokenId == 0) revert Vault_TokenNotRegistered();
      if (_collateralInfo.collateralType != IVaultController.CollateralType.CurveLPStakedOnConvex) {
        revert Vault_TokenNotCurveLP();
      }

188           for (uint256 _j; _j < _extraRewards;) {
        IVirtualBalanceRewardPool _virtualReward = _rewardsContract.extraRewards(_j);
        IERC20 _rewardToken = _virtualReward.rewardToken();
        uint256 _earnedReward = _virtualReward.earned(address(this));
        if (_earnedReward != 0) {
          _virtualReward.getReward();
          _rewardToken.transfer(_msgSender(), _earnedReward);
          emit ClaimedReward(address(_rewardToken), _earnedReward);
        }
        unchecked {
          ++_j;
        }
      }
      unchecked {
        ++_i;
      }
    }

254        for (_i; _i < _rewardsAmount;) {
      IVirtualBalanceRewardPool _virtualReward = _rewardsContract.extraRewards(_i);
      IERC20 _rewardToken = _virtualReward.rewardToken();
      uint256 _earnedReward = _virtualReward.earned(address(this));
      _rewards[_i + 2] = Reward(_rewardToken, _earnedReward);

      unchecked {
        ++_i;
      }
    }


charlesjhongc G

# Gas Optmization Findings

## Replace i++ in for loop
Could be replace with ++i or unchecked math block
```solidity
// option1
for (uint256 i = 0; i < derivativeCount; ++i)
{
    // ...
}
```
```solidity
// option2
for (uint256 i = 0; i < derivativeCount;)
{
    // ...
    unchecked{
        ++i;
    }
}
```
Occurrences
- 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

## totalWeight calculation could be further optimized
The `adjustWeight` and `addDerivative` function will result in `totalWeight` change. But instead of re-calcualting it by for loop iterating, the `totalWeight` could be updated by adding the weight of new derivative (in `addDerivative` case) or the difference between new and old weight (in `adjustWeight` case). It could save multiple storage reads.
```solidity
function adjustWeight(
    uint256 _derivativeIndex,
    uint256 _weight
) external onlyOwner {
    uint256 originalWeight = weights[_derivativeIndex];
    totalWeight = totalWeight + _weight - originalWeight;
    weights[_derivativeIndex] = _weight;
    emit WeightChange(_derivativeIndex, _weight);
}
```
```solidity
function addDerivative(
    address _contractAddress,
    uint256 _weight
) external onlyOwner {
    derivatives[derivativeCount] = IDerivative(_contractAddress);
    weights[derivativeCount] = _weight;
    derivativeCount++;

    totalWeight += _weight;
    emit DerivativeAdded(_contractAdd

// Code block 1 (solidity):
// option1
for (uint256 i = 0; i < derivativeCount; ++i)
{
    // ...
}

// Code block 2 (solidity):
// option2
for (uint256 i = 0; i < derivativeCount;)
{
    // ...
    unchecked{
        ++i;
    }
}

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

// option1
for (uint256 i = 0; i < derivativeCount; ++i)
{
    // ...
}

// option2
for (uint256 i = 0; i < derivativeCount;)
{
    // ...
    unchecked{
        ++i;
    }
}

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

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


Tripathi Q

### Low Risk Issues

| |Issue|Instances|
|-|:-|:-:|
| [L‑01] | `_mintEUSD ` emits incorrect parameter in `Mint` event | 1 |

### [L‑01] `_mintEUSD ` emits incorrect parameter in `Mint` event
`_mintEUSD ` is a internal function in pool/base contracts which emit an event after sucessfull minting of eUSD.
Currently first parameter is `msg.sender` which should be replaced by `_provider`. The fact that, everywhere `msg.sender` is same as `_provider` in current implementation is not creating any problem .But this will emit incorrect event if `msg.sender` and `_provider` are different in any case.

```solidity
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol

268:                emit Mint(msg.sender, _onBehalfOf, _mintAmount, block.timestamp);

```
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L268C5-L268C74

// Code block 1 (solidity):
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol

268:                emit Mint(msg.sender, _onBehalfOf, _mintAmount, block.timestamp);


0xOmer Q

## Missing check for wallets to not be the same addresses
`ManagedWallet` contract introduces 2 wallets `mainWallet` and `confirmationWallet`. It is possible to pick them as same addresses. Since they have different roles they must not be same. Consider adding relevant checks both in `constructor` and `proposeWallets()` to block the logic fail.

## ProposedConfirmationWallet can be overwritten
According to the [comment](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/ManagedWallet.sol#L48), overwriting a proposal should not allowed. Though the mentioned check exist for [proposedMainWallet](https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/ManagedWallet.sol#L49), there isn't for `proposedConfirmationWallet`.

## `approve()` should be replaced with `safeIncreaseAllowance()` / `safeDecreaseAllowance()`
`approve` is subject to known front-running attacks and does not handle non-standard ERC20 behaviors. Consider using `safeIncreaseAllowance` and `safeDecreaseAllowance` instead:

```ruby
src/staking/Liquidity.sol:
   86          if (swapAmountA > 0) {
   87:             tokenA.approve(address(pools), swapAmountA);
   88

  100          else if (swapAmountB > 0) {
  101:             tokenB.approve(address(pools), swapAmountB);
  102

  154          // Approve the liquidity to add
  155:         tokenA.approve(address(pools), maxAmountA);
  156:         tokenB.approve(address(pools), maxAmountB);
  157
```
Keep in mind that safeApprove() is deprecated by OZ and recommended using their `safeIncreaseAllowance()` and `safeDecreaseAllowance()`

See this discussion: [SafeERC20.safeApprove() Has unnecessary and unsecure added behavior](https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2219)

**NOTE:** In bot report this issue is mentioned but it recommends to use `safeApprove()` which is deprecated and not safe to use.

## Missing zero address check in [`PriceAggregator.

// Code block 1 (ruby):
src/staking/Liquidity.sol:
   86          if (swapAmountA > 0) {
   87:             tokenA.approve(address(pools), swapAmountA);
   88

  100          else if (swapAmountB > 0) {
  101:             tokenB.approve(address(pools), swapAmountB);
  102

  154          // Approve the liquidity to add
  155:         tokenA.approve(address(pools), maxAmountA);
  156:         tokenB.approve(address(pools), maxAmountB);
  157


ch0bu Q

## 1. For modern and more readable code; update import usages

Solidity code is also cleaner in another way that might not be noticeable: the struct Point. We were importing it previously with global import but not using it. The Point struct `polluted the source code` with an unnecessary object we were not using because we did not need it.

This was breaking the rule of modularity and modular programming: `only import what you need` Specific imports with curly braces allow us to apply this rule better.

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

## 2. Missing event for critical parameter change

Events help non-contract tools to track changes, and events prevent users from being surprised by changes

```
58	function setMaxSlippage(uint256 _slippage) external onlyOwner {
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol
```
51	function setMaxSlippage(uint256 _slippage) external onlyOwner {
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol
```
48	function setMaxSlippage(uint256 _slippage) external onlyOwner {
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol

## 3. Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)

Use (e.g. 1e6) rather than decimal literals (e.g. 1000000), for better code readability.

```
44	maxSlippage = (1 * 10 ** 16); // 1%
171	uint rethPerEth = (10 ** 36) / poolPrice();
173	uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
174	((10 ** 18 - maxSlippage))) / 10 ** 18);
214	RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
215	else return (poolPrice() * 10 ** 18) / (10 ** 18);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol
```
54	minAmount = 5 * 10 ** 17;
55	maxAmount = 200 * 10 ** 18;
75	10 ** 18;
80	preDepositPrice = 10 ** 18; // initializes with a price of 1
81	el

// Code block 1 (unknown):
58	function setMaxSlippage(uint256 _slippage) external onlyOwner {

// Code block 2 (unknown):
51	function setMaxSlippage(uint256 _slippage) external onlyOwner {

// Code block 3 (unknown):
48	function setMaxSlippage(uint256 _slippage) external onlyOwner {

// Code block 4 (unknown):
44	maxSlippage = (1 * 10 ** 16); // 1%
171	uint rethPerEth = (10 ** 36) / poolPrice();
173	uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
174	((10 ** 18 - maxSlippage))) / 10 ** 18);
214	RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
215	else return (poolPrice() * 10 ** 18) / (10 ** 18);


JP_Courses Q

0. QA/LOW: RdpxV2Core - Likely incorrect/misleading comment regarding precision for state variable `slippageTolerance`, or precision hasn't been added to variable value yet.

https://github.com/code-423n4/2023-08-dopex/blob/e96aaa5ea21f11b29d828dbe2d0745974cd046ed/contracts/core/RdpxV2Core.sol#L99-L100

L100:
`slippageTolerance = 5e5` but comment mentions 1e8 precision as per below:

```solidity
  /// @notice The slippage tolernce in swaps in 1e8 precision
  uint256 public slippageTolerance = 5e5; // 0.5%
```

Recommendation:

Fix the comment or fix the assignment value, depending on which one is incorrect.

If assignment incorrect, then:

```solidity
uint256 public slippageTolerance = 5e5 * 1e8; // 0.5%
```

1. QA: RdpxV2Core - `liquiditySlippageTolerance` state variable not used in this contract.

https://github.com/code-423n4/2023-08-dopex/blob/e96aaa5ea21f11b29d828dbe2d0745974cd046ed/contracts/core/RdpxV2Core.sol#L102-L103

```solidity
  /// @notice Liquidity slippage tolerance
  uint256 public liquiditySlippageTolerance = 5e5; // 0.5%
```

Recommendation:

Remove the variable to save on gas costs during deployments, or comment it out if planning to use later, also consider its storage slot.

// Code block 1 (solidity):
/// @notice The slippage tolernce in swaps in 1e8 precision
  uint256 public slippageTolerance = 5e5; // 0.5%

// Code block 2 (solidity):
uint256 public slippageTolerance = 5e5 * 1e8; // 0.5%

// Code block 3 (solidity):
/// @notice Liquidity slippage tolerance
  uint256 public liquiditySlippageTolerance = 5e5; // 0.5%

/// @notice The slippage tolernce in swaps in 1e8 precision
  uint256 public slippageTolerance = 5e5; // 0.5%

uint256 public slippageTolerance = 5e5 * 1e8; // 0.5%

/// @notice Liquidity slippage tolerance
  uint256 public liquiditySlippageTolerance = 5e5; // 0.5%


Jorgect G

#GAS OPTMIZATION

## [G-01] CREATING A NEW MODIFIER CAN SAVE GAS
the next owner validation is used in 3 different function, can crate a modifier to save gas:

```
file: ajna-core/src/RewardsManager.sol
if (msg.sender != stakeInfo.owner) revert NotOwnerOfDeposit();
```
https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/RewardsManager.sol#L120

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/RewardsManager.sol#L143

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-core/src/RewardsManager.sol#L275

consider add the next modifier:

```
modifier isOwner(uint256 id) {
if (msg.sender != stakes[id].owner) revert NotOwnerOfDeposit();
_;
}
```

// Code block 1 (unknown):
file: ajna-core/src/RewardsManager.sol
if (msg.sender != stakeInfo.owner) revert NotOwnerOfDeposit();

// Code block 2 (unknown):
modifier isOwner(uint256 id) {
if (msg.sender != stakes[id].owner) revert NotOwnerOfDeposit();
_;
}


0xblackskull Q

1- Missing events in rescueTokens and zero address check
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L322
```
  function rescueTokens(address _token) external onlyOwner {
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
  }
```

2- Emit should be in next line after uint256 tokensAmount as a solidity good practices
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L421
```
  function transferShares(
    address _recipient,
    uint256 _sharesAmount
  ) public returns (uint256) {
    _transferShares(msg.sender, _recipient, _sharesAmount);
    emit TransferShares(msg.sender, _recipient, _sharesAmount);
    uint256 tokensAmount = getRUSDYByShares(_sharesAmount);
    emit Transfer(msg.sender, _recipient, tokensAmount);
    return tokensAmount;
  }
```

3- Need approve before transferFrom() in  wrap()
  ```
function wrap(uint256 _USDYAmount) external whenNotPaused {
    require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
    _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
+   usdy.approve(address(this), _USDYAmount);
    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
    emit TransferShares(address(0), msg.sender, _USDYAmount);
  }
```

// Code block 1 (unknown):
function rescueTokens(address _token) external onlyOwner {
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
  }

// Code block 2 (unknown):
function transferShares(
    address _recipient,
    uint256 _sharesAmount
  ) public returns (uint256) {
    _transferShares(msg.sender, _recipient, _sharesAmount);
    emit TransferShares(msg.sender, _recipient, _sharesAmount);
    uint256 tokensAmount = getRUSDYByShares(_sharesAmount);
    emit Transfer(msg.sender, _recipient, tokensAmount);
    return tokensAmount;
  }

// Code block 3 (unknown):
function wrap(uint256 _USDYAmount) external whenNotPaused {
    require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
    _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
+   usdy.approve(address(this), _USDYAmount);
    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
    emit TransferShares(address(0), msg.sender, _USDYAmount);
  }


0xbrett8571 Analysis

## Introduction

Salty.IO is an Ethereum-based decentralized exchange (DEX) that aims to provide zero swap fees, yield from automatic arbitrage, and a native stablecoin (USDS) backed by WBTC/WETH collateral.

In this report, I analyze the Salty.IO smart contract architecture and identifies strengths, risks, and recommendations in key areas. Focus is placed on token safety, access controls, reward mechanisms, price oracles, and the USDS stablecoin model. Both systematic risks and specific vulnerabilities are covered.

**Evaluation Approach**

The analysis focused on auditing the critical Salty.IO contracts:

- [Pools.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol) - Core AMM and arbitrage logic
- [DAO.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/DAO.sol) - Governance and protocol owned liquidity
- [Upkeep.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/Upkeep.sol) - Rewards distribution and emissions
- [PriceAggregator.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/PriceAggregator.sol) - Redundant price feed
- [USDS.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/USDS.sol) + [CollateralAndLiquidity.sol](https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol) - Stablecoin model

Manual review was supplemented by unit tests, debugging sessions, and code instrumentation to validate assumptions and behavior.

Key areas of attention included:

- Access control and trust minimization
- Token tracking and arithmetic safety
- Protocol incentives and sustainability
- Price feed redundancy and manipulation resistance
- Stablecoin collateral ratios and liquidity

**Here is an explanation of some of the main contracts in Salty.IO, along with a brief analysis, risks, and recommendations for each:**

[**Pools.sol**](https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol)

This is the core contract that

┌──────────────────────┐
│                      │
│      DAO.sol         │
│      Governance      │
│                      │
└───────────┬──────────┘
           │
┌───────────┴──────────┐
│       Pools.sol      │
│    AMM + Arbitrage   │
└───────────┬──────────┘
           │
┌───────────┴┐┌────────┴─────────┐
│ Upkeep.sol ││ Collateral.sol  │
│  Rewards   ││   Stablecoin    │
│Distribution││     Model       │
└───────────┬┘└────────┬────────┘
           │         │
           │         │
┌───────────┴────────┐│
│ PriceAggregator.sol││
│    Redundant       ││
│     Price Feed     ││
└────────────────────┘│
                      │
 ┌─────────────────────┴────────┐
 │            Peripheral Contracts
 │  Staking.sol, RewardsEmitter.sol, etc
 └─────────────────────────────────┘

Pools.sol, DAO.sol, Upkeep.sol, PriceAggregator.sol, USDS.sol, CollateralAndLiquidity.sol

Pools.sol, DAO.sol, CollateralAndLiquidity.sol, Upkeep.sol, USDS.sol, PriceAggregator.sol


Lef Q

1)  Unused function parameters should be commented out as a better and declarative way to silence runtime warning messages. As an example, the following functions may have these parameters refactored to:

https://github.com/code-423n4/2023-03-asymmetry/blob/d016bef40dc745b6a16e18d83bd4f7d776245903/contracts/SafEth/derivatives/SfrxEth.sol#L111

Recommendation :

```
  function ethPerDerivative(uint256 _amount) public view returns (uint256) {

  change to

  function ethPerDerivative() public view returns (uint256) {
```

same for :

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

```
 function ethPerDerivative(uint256 _amount) public view returns (uint256) {

 change to

 function ethPerDerivative() public view returns (uint256) {
```

2) Inconsistent use of uint and uint256 in events

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

```
event has uint256   (uint256 indexed index, uint256 slippage);
function has uint   (uint _derivativeIndex, uint _slippage)
```
Same for events :

    event Staked(address indexed recipient, uint ethIn, uint safEthOut);
    event Unstaked(address indexed recipient, uint ethOut, uint safEthIn);
    event WeightChange(uint indexed index, uint weight);
    event DerivativeAdded(address indexed contractAddress,uint weight,uint index);

// Code block 1 (unknown):
function ethPerDerivative(uint256 _amount) public view returns (uint256) {

  change to

  function ethPerDerivative() public view returns (uint256) {

// Code block 2 (unknown):
function ethPerDerivative(uint256 _amount) public view returns (uint256) {

 change to

 function ethPerDerivative() public view returns (uint256) {

// Code block 3 (unknown):
event has uint256   (uint256 indexed index, uint256 slippage);
function has uint   (uint _derivativeIndex, uint _slippage)

SfrxEth.sol#L111, WstEth.sol#L86, SafEth.sol#L207, SafEth.sol#L25


KmanOfficial Q

### Low Risk / QA Issues

### [01] Unnecessary emit of PointsAdded event for the Merging Pool when a fight has been won but the stakeAtRisk is more than 0.

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/RankedBattle.sol#L416-L500

```
if (battleResult == 0) {
            /// If the user won the match

            /// If the user has no NRNs at risk, then they can earn points
            if (stakeAtRisk == 0) {
                points = stakingFactor[tokenId] * eloFactor;
            }

            /// Divert a portion of the points to the merging pool
            uint256 mergingPoints = (points * mergingPortion) / 100;
            points -= mergingPoints;
            _mergingPoolInstance.addPoints(tokenId, mergingPoints);
...
}
```

If stakeAtRisk is 0, it means the user is eligible to gain some points based on how much they have staked, but initially points is initialised to 0 within the function  so there is no need to do calculate mergingPoints if points will be 0. It leads to unnecessary execution of `_mergingPoolInstance.addPoints` which under all circumstances emits an pointsAdded event which is state data that doesn't need to be indexed as no points were actually added to the mergingPool.

Recommendation: Wrap the merging points calculation points and addition code within an if statement - `if(points > 0)`.

--

### [02] Total Battles is incorrectly updated twice for the same battle.
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/RankedBattle.sol#L348

The totalBattles value globally tracks the number of fights that has taken place. A fight between two users is one battle. `RankedBattle.UpdateBattleRecord` is called twice for one fight. Once for the initiator and once for the opponent. The code incorrectly increments total battles by 1 for each call for the respective users in the fight. This would give the indication that two separate battles took place, when it's the same battle.

Recommendation: Move `totalBattles += 1;` to wit

// Code block 1 (unknown):
if (battleResult == 0) {
            /// If the user won the match

            /// If the user has no NRNs at risk, then they can earn points
            if (stakeAtRisk == 0) {
                points = stakingFactor[tokenId] * eloFactor;
            }

            /// Divert a portion of the points to the merging pool
            uint256 mergingPoints = (points * mergingPortion) / 100;
            points -= mergingPoints;
            _mergingPoolInstance.addPoints(tokenId, mergingPoints);
...
}


Rickard G

# [G-01] Setting the constructor to `payable`
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor `payable`. Making the constructor payable eliminates the need for an initial check of `msg.value == 0` and saves 13 gas on deployment with no security risks.
## Context
4 results - 4 files
````solidity
contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {
````
[https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L24](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L24)
````solidity
contracts/SafEth/derivatives/Reth.sol
33:     constructor() {
````
[https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L33](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L33)
````solidity
contracts/SafEth/derivatives/SfrxEth.sol
27:     constructor() {
````
[https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L27](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L27)
````solidity
contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {
````
[https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L24](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L24)
## Recommended mitigation steps
Set the constructor to `payable`.

# [G-02] `<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables (`-=` too)
Using the addition operator instead of plus-equals saves [113 gas](https://gist.github.com/IllIllI000/cbbfb267425b898e5be734d4008d4fe8). Subtractions act the same way.

There are 10 instances of this issue.

# [G-03] Functions guaranteed to revert when called by normal users can be marked `payable`
If a function modifier such as `onlyOwner` is use

// Code block 1 (solidity):
contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {

// Code block 2 (solidity):
contracts/SafEth/derivatives/Reth.sol
33:     constructor() {

// Code block 3 (solidity):
contracts/SafEth/derivatives/SfrxEth.sol
27:     constructor() {

// Code block 4 (solidity):
contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {

contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {

contracts/SafEth/derivatives/Reth.sol
33:     constructor() {

contracts/SafEth/derivatives/SfrxEth.sol
27:     constructor() {

contracts/SafEth/derivatives/WstEth.sol
24:     constructor() {


0xMAKEOUTHILL Q

**Low** - There will always be **1** unused NRN token, because of the following statement in `mint` in `Neuron.sol`:

```
function mint(address to, uint256 amount) public virtual {

        //@audit this won't mint the last token of the MAX_SUPPLY
@>>>    require(totalSupply() + amount < MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have minter role to mint");
        _mint(to, amount);
    }
```

**Dummy values**

**MAX_SUPPLY** = 100
**totalSupply** - 99

So when we try to call `mint` with `amount` = 1, the `require` will revert because
`99 + 1 < 100`. Fix the check the following way:

`require(totalSupply() + amount <= MAX_SUPPLY, "Trying to mint more than the max supply");
`

-----------------------------------------------------------------------------------

**Informational** - In `AiArenaHelper.sol` in the constructor, when we add attribute probabilities via `addAttributeProbabilities`, then when we iterate over the attributes, we again add the same probabilities to the same attributes. Remove the second adding of probabilities.

```
constructor(uint8[][] memory probabilities) {

        _ownerAddress = msg.sender;

        // Initialize the probabilities for each attribute
        addAttributeProbabilities(0, probabilities);

        uint256 attributesLength = attributes.length;

        for (uint8 i = 0; i < attributesLength; i++) {

             //@audit this is not needed, remove it.
@>>>>        attributeProbabilities[0][attributes[i]] = probabilities[i];

            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];

        }
    }
```

-----------------------------------------------------------------------------------

**Informational** - In `FighterFarm.sol`, `redeemMintPass` the natspec above the function says:

**"/// @dev This function requires the length of all input arrays to be equal."**.

However this is not the case, as `uint8[] calldata

// Code block 1 (unknown):
function mint(address to, uint256 amount) public virtual {

        //@audit this won't mint the last token of the MAX_SUPPLY
@>>>    require(totalSupply() + amount < MAX_SUPPLY, "Trying to mint more than the max supply");
        require(hasRole(MINTER_ROLE, msg.sender), "ERC20: must have minter role to mint");
        _mint(to, amount);
    }

// Code block 2 (unknown):
constructor(uint8[][] memory probabilities) {

        _ownerAddress = msg.sender;

        // Initialize the probabilities for each attribute
        addAttributeProbabilities(0, probabilities);

        uint256 attributesLength = attributes.length;

        for (uint8 i = 0; i < attributesLength; i++) {

             //@audit this is not needed, remove it.
@>>>>        attributeProbabilities[0][attributes[i]] = probabilities[i];

            attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];

        }
    }


SpicyMeatball Q

### Redundant check
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/LiquidationLibrary.sol#L863
`_redistributeDebt` will never be called if debt = 0
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/LiquidationLibrary.sol#L525-L527
### BaseMath is not used
PriceFeed contract inherits from BaseMath but doesn't use it's single constant DECIMAL_PRECISION, so it's possible to remove it.
### Unused modifier
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/EBTCToken.sol#L323
this modifier is not used in `EBTCTokem.sol`
### Double checking `recipient != address(0)`
Before transferring tokens we invoke `_requireValidRecipient`, where among many checks we check that recipient != address(0),
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/EBTCToken.sol#L297
same check is made inside `_transfer` function
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/EBTCToken.sol#L248
### Enforce beta is not zero
Authorized users can change `beta` value
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManager.sol#L830
if it's set to zero. we'll divide by zero here
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManager.sol#L624

### `arrayIndex` value is not deleted when we remove cpd
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L269-L274
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/CdpManagerStorage.sol#L471
When we remove cpd, we zero all it's values, but `arrayIndex` remains.
### UpdateManager?
https://github.com/code-423n4/2023-10-badger/blob/main/packages/contracts/contracts/BorrowerOperations.sol#L963
`Borrower` can approve operations to the `PositionManager`, `UpdateManager` is not mentioned anywhere in the contract. `_requireBorrowerOrP

      keccak256(
            "PermitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)"
        );

    function syncGlobalAccounting() external {
        _requireCallerIsBorrowerOperations();
        _syncGlobalAccounting();
    }

LiquidationLibrary.sol#L863, LiquidationLibrary.sol#L525-L527, EBTCToken.sol#L323, EBTCToken.sol#L297, EBTCToken.sol#L248, CdpManager.sol#L830, CdpManager.sol#L624, CdpManagerStorage.sol#L269-L274, CdpManagerStorage.sol#L471, BorrowerOperations.sol#L963

CdpManagerStorage.sol, BorrowerOperations.sol, CdpManager.sol, EBTCToken.sol, LiquidationLibrary.sol


MatricksDeCoder G

#### GAS-1 Optimize Address Storage Value Management with assembly

Assembly storing leads to gas savings as it has less overheads compared to abstractions

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L71

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L72

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/Neuron.sol#L87

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L152

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L153

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L169

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L186

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L194

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L42

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/AiArenaHelper.sol#L63

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L107

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L108

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L109

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L122

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L96

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1

struct GameItemAttributes {
        string name;
        bool finiteSupply;
        bool transferable;
        uint256 itemsRemaining;
        uint256 itemPrice;
        uint256 dailyAllowance;
    }

function setValue(uint256 _value) external onlyOwner {
    value = _value;
}
Can be rewritten
function setValue(uint256 _value) external onlyOwner {
    assembly {
       sstore(value.slot,_value);
    }
}

Neuron.sol#L71, Neuron.sol#L72, Neuron.sol#L87, RankedBattle.sol#L152, RankedBattle.sol#L153, RankedBattle.sol#L169, RankedBattle.sol#L186, RankedBattle.sol#L194, AiArenaHelper.sol#L42, AiArenaHelper.sol#L63, FighterFarm.sol#L107, FighterFarm.sol#L108, FighterFarm.sol#L109, FighterFarm.sol#L122, GameItems.sol#L96

GameItems.sol, Neuron.sol, RankedBattle.sol, FighterFarm.sol, AiArenaHelper.sol


0xhacksmithh Q

### [Low-01] Code is contradicting comments
In `decreaseAmount()` according to comment it decreases bond amount (rdpxAmount) associated with corresponding `bondId`

But in code instead of substracting `decreaseAmount` from bond's rdpxAmount it directly set that variable('rdpxAmount') to inputed decreaseAmount.

There also absence of bond existance check before making operations on it.
```solidity
  /// @notice Decreases the bond amount
  /// @dev Can only be called by the rdpxV2Core
  /// @param bondId id of the bond to decrease
  /// @param amount amount to decrease
  function decreaseAmount(
    uint256 bondId,
    uint256 amount
  ) public onlyRole(RDPXV2CORE_ROLE) {
    _whenNotPaused();
-   bonds[bondId].rdpxAmount = amount;

+  bonds[bondId].rdpxAmount = bonds[bondId].rdpxAmount - amount; // @audit underflow by default reverted
  }
```
```
file: contracts/decaying-bonds/RdpxDecayingBonds.sol
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/decaying-bonds/RdpxDecayingBonds.sol#L139-L145
```

### [Low-02] While locking Collateral via `lockCollateral()` it doesn't check that locked amount is exceeding totalCollateral or not
`lockCollateral()` simply increase `_activeCollateral` via `amount` that may lead `_activeCollateral` value to exceed `totalCollateral` in that case `totalAvailableCollateral()` call always failed.

So while increasing locked amount there should be a check that ensure increased `_activeCollateral` will never exceed `totalCollateral`

```solidity
function lockCollateral(uint256 amount) public onlyPerpVault {
    _activeCollateral += amount;
  }
```
*Instances(1)*
```
File: contracts/perp-vault/PerpetualAtlanticVaultLP.sol
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L180-L182
```

### [Low-03] Functions like `mint()`, `burn()`, `burnFrom()` not respecting `pause` or `unpause` functionality
These `mint()`, `burn()`, `burnFrom()` 3 functions are `public` and only call

// Code block 1 (solidity):
/// @notice Decreases the bond amount
  /// @dev Can only be called by the rdpxV2Core
  /// @param bondId id of the bond to decrease
  /// @param amount amount to decrease
  function decreaseAmount(
    uint256 bondId,
    uint256 amount
  ) public onlyRole(RDPXV2CORE_ROLE) {
    _whenNotPaused();
-   bonds[bondId].rdpxAmount = amount;

+  bonds[bondId].rdpxAmount = bonds[bondId].rdpxAmount - amount; // @audit underflow by default reverted
  }

// Code block 2 (unknown):
file: contracts/decaying-bonds/RdpxDecayingBonds.sol
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/decaying-bonds/RdpxDecayingBonds.sol#L139-L145

// Code block 3 (solidity):
function lockCollateral(uint256 amount) public onlyPerpVault {
    _activeCollateral += amount;
  }

// Code block 4 (unknown):
File: contracts/perp-vault/PerpetualAtlanticVaultLP.sol
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L180-L182

/// @notice Decreases the bond amount
  /// @dev Can only be called by the rdpxV2Core
  /// @param bondId id of the bond to decrease
  /// @param amount amount to decrease
  function decreaseAmount(
    uint256 bondId,
    uint256 amount
  ) public onlyRole(RDPXV2CORE_ROLE) {
    _whenNotPaused();
-   bonds[bondId].rdpxAmount = amount;

+  bonds[bondId].rdpxAmount = bonds[bondId].rdpxAmount - amount; // @audit underflow by default reverted
  }

function lockCollateral(uint256 amount) public onlyPerpVault {
    _activeCollateral += amount;
  }


lukris02 G

# Gas Optimizations Report for Ethos Reserve contest
## Overview
During the audit, 2 gas issues were found.

№ | Title | Instance Count
--- | --- | ---
G-1 | Use unchecked blocks for subtractions where underflow is impossible | 14
G-2 | Using ```storage``` pointer to ```struct```/```array```/```mapping``` is cheaper than using ```memory``` | 2

## Gas Optimizations Findings(2)
### G-1. Use unchecked blocks for subtractions where underflow is impossible
##### Description
In Solidity 0.8+, there’s a default overflow and underflow check on unsigned integers. When an overflow or underflow isn’t possible (after require or if-statement), some gas (~35 per instance) can be saved by using [unchecked blocks](https://docs.soliditylang.org/en/v0.8.17/control-structures.html#checked-or-unchecked-arithmetic).
##### Instances
- [```return -int256(stratCurrentAllocation - stratMaxAllocation);```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L235)
- [```uint256 remaining = value - vaultBalance;```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L384)
- [```token.safeTransfer(vars.stratAddr, vars.credit - vars.freeWantInStrat);```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L526)
- [```token.safeTransferFrom(vars.stratAddr, address(this), vars.freeWantInStrat - vars.credit);```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L528)
- [```lockedProfit = vars.lockedProfitBeforeLoss - vars.loss;```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L535)
- [```return tvlCap - balance();```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultERC4626.sol#L82)
- [```return convertToShares(tvlCap - balance());```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultERC4626.so

##### Description
When you copy a storage ```struct```/```array```/```mapping``` to a memory variable, you are copying each member by reading it from the storage, which is expensive, but when you use the ```storage``` keyword, you are just storing a pointer to the storage, which is much cheaper.
##### Instances
- [```Snapshots memory snapshots = depositSnapshots[_depositor];```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L687)
- [```Snapshots memory snapshots = depositSnapshots[_depositor];```](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L722)

##### Recommendation
For example, change:

ReaperVaultV2.sol#L235, ReaperVaultV2.sol#L384, ReaperVaultV2.sol#L526, ReaperVaultV2.sol#L528, ReaperVaultV2.sol#L535, ReaperVaultERC4626.sol#L82, StabilityPool.sol#L687, StabilityPool.sol#L722


EvanW Q

[QA-01] **NatSpec comments provide rich code documentation. The following functions are some examples that miss the `@param` and/or `@return` comments.**

```solidity
contracts/SafEth/derivativrs/WstEth.sol
48: function setMaxSlippage(uint256 _slippage) external onlyOwner {
56: function withdraw(uint256 _amount) external onlyOwner {
86: function ethPerDerivative(uint256 _amount) public view returns (uint256) {

contracts/SafEth/derivativrs/SfrxEth.sol
51: function setMaxSlippage(uint256 _slippage) external onlyOwner {
111: function ethPerDerivative(uint256 _amount) public view returns (uint256) {

contracts/SafEth/derivativrs/Reth.sol
103: function withdraw(uint256 amount) external onlyOwner {
```

// Code block 1 (solidity):
contracts/SafEth/derivativrs/WstEth.sol
48: function setMaxSlippage(uint256 _slippage) external onlyOwner {
56: function withdraw(uint256 _amount) external onlyOwner {
86: function ethPerDerivative(uint256 _amount) public view returns (uint256) {

contracts/SafEth/derivativrs/SfrxEth.sol
51: function setMaxSlippage(uint256 _slippage) external onlyOwner {
111: function ethPerDerivative(uint256 _amount) public view returns (uint256) {

contracts/SafEth/derivativrs/Reth.sol
103: function withdraw(uint256 amount) external onlyOwner {