csanuragjain Q

## No_change will be chosen incorrectly

Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L372-L380

Impact
In case of tie between Increase and Decrease, No change will be chosen

Proof of Concept
1. The winning parameter is chosen using:

```
function winningParameterVote( uint256 ballotID ) external view returns (Vote)
		{
		mapping(Vote=>uint256) storage votes = _votesCastForBallot[ballotID];

		uint256 increaseTotal = votes[Vote.INCREASE];
		uint256 decreaseTotal = votes[Vote.DECREASE];
		uint256 noChangeTotal = votes[Vote.NO_CHANGE];

		if ( increaseTotal > decreaseTotal )
		if ( increaseTotal > noChangeTotal )
			return Vote.INCREASE;

		if ( decreaseTotal > increaseTotal )
		if ( decreaseTotal > noChangeTotal )
			return Vote.DECREASE;

		return Vote.NO_CHANGE;
		}
```

2. Let's say INCREASE and DECREASE are both 100 and NO_CHANGE is 0. In this case below flow happens:

```
increaseTotal > decreaseTotal which is 100>100 which is false
decreaseTotal > increaseTotal which is 100>100 which is false
So Vote.NO_CHANGE is returned
```

3. So in this case No_change is selected even though noone voted for NO_CHANGE and all votes were for Increase and Decrease

Recommendation
Document this behavior so that Users are aware

## Unfair Ballot selection

Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L431

Impact
Ballot with highest yes vote won't be selected right away, for token whitelisting under certain circumstances. Ballot which are created early are given preference which might not be fair always

Proof of Concept
1. Lets say 3 ballots exists for token whitelisting
2. Below are the param used

```md
quorum=100

Ballot 1:
yesVotes=80
noVotes=30

Ballot 2:
yesVotes=80
noVotes=20

Ballot 3:
yesVotes=70
noVotes=30

```

3. Now once Ballots are finalized for whitelisting, below call sequence occurs:

```
_finalizeTokenWhitelisting -> proposals.tokenWhitelistingBallotWithTheMostVotes();

// Code block 1 (unknown):
function winningParameterVote( uint256 ballotID ) external view returns (Vote)
		{
		mapping(Vote=>uint256) storage votes = _votesCastForBallot[ballotID];

		uint256 increaseTotal = votes[Vote.INCREASE];
		uint256 decreaseTotal = votes[Vote.DECREASE];
		uint256 noChangeTotal = votes[Vote.NO_CHANGE];

		if ( increaseTotal > decreaseTotal )
		if ( increaseTotal > noChangeTotal )
			return Vote.INCREASE;

		if ( decreaseTotal > increaseTotal )
		if ( decreaseTotal > noChangeTotal )
			return Vote.DECREASE;

		return Vote.NO_CHANGE;
		}

// Code block 2 (unknown):
increaseTotal > decreaseTotal which is 100>100 which is false
decreaseTotal > increaseTotal which is 100>100 which is false
So Vote.NO_CHANGE is returned

// Code block 3 (md):
quorum=100

Ballot 1:
yesVotes=80
noVotes=30

Ballot 2:
yesVotes=80
noVotes=20

Ballot 3:
yesVotes=70
noVotes=30


ether_sky Q

[L-1] The `ManagedWallet` is not functioning as anticipated.

The proposed `mainWallet` can confirm the change only after the `30` days timelock.
However, it can be skipped.

- The `confirmationWallet` is capable of sending `0.05` ether before suggesting a new `main wallet`.
Subsequently, the `activeTimelock` is updated.
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/ManagedWallet.sol#L66
```
receive() external payable {
    if ( msg.value >= .05 ether )
        activeTimelock = block.timestamp + TIMELOCK_DURATION;
}
```
- After `30` days, the main wallet proposes a new wallet.
- The newly proposed main wallet can confirm the change immediately since `30` days have elapsed.
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/ManagedWallet.sol#L77
```
function changeWallets() external {
    require( msg.sender == proposedMainWallet, "Invalid sender" );
    require( block.timestamp >= activeTimelock, "Timelock not yet completed" );
}
```

[L-2] The parameter order in the `proposeCallContract` function is incorrect.

- In the `possiblyCreateProposal` function, the parameter `string2` is associated with the `description`.
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/Proposals.sol#L109
```
function _possiblyCreateProposal( string memory ballotName, BallotType ballotType, address address1, uint256 number1, string memory string1, string memory string2 ) internal returns (uint256 ballotID) {
    ballots[ballotID] = Ballot( ballotID, true, ballotType, ballotName, address1, number1, string1, string2, ballotMinimumEndTime );
}
```
- However, in the `proposeCallContract` function, the `description` is inserted as parameter `string1`.
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/Proposals.sol#L218
```
function proposeCallContract( address contractAddress, uint256 number, string calldat

// Code block 1 (unknown):
receive() external payable {
    if ( msg.value >= .05 ether )
        activeTimelock = block.timestamp + TIMELOCK_DURATION;
}

// Code block 2 (unknown):
function changeWallets() external {
    require( msg.sender == proposedMainWallet, "Invalid sender" );
    require( block.timestamp >= activeTimelock, "Timelock not yet completed" );
}

// Code block 3 (unknown):
function _possiblyCreateProposal( string memory ballotName, BallotType ballotType, address address1, uint256 number1, string memory string1, string memory string2 ) internal returns (uint256 ballotID) {
    ballots[ballotID] = Ballot( ballotID, true, ballotType, ballotName, address1, number1, string1, string2, ballotMinimumEndTime );
}

ManagedWallet.sol#L66, ManagedWallet.sol#L77, Proposals.sol#L109, Proposals.sol#L218


Tomio G

1.
Title: empty `constructor`

Proof of Concept:
[CCashDelegate.sol#L15](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cCash/CCashDelegate.sol#L15)
[CTokenDelegate.sol#L15](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/tokens/cToken/CTokenDelegate.sol#L15)

Recommended Mitigation Steps:
Remove if unused for gas saving
________________________________________________________________________

2.
Title: Expression for `constant` values such as a call to `keccak256()`, should use `immutable` rather than `constant`

Proof of Concept:
[Cash.sol#L22](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/Cash.sol#L22)
[CashKYCSender.sol#L26](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSender.sol#L26)
[CashKYCSenderReceiver.sol#L26](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/cash/token/CashKYCSenderReceiver.sol#L26)

Recommended Mitigation Steps:
Change from `constant` to `immutable`
reference: [here](https://github.com/ethereum/solidity/issues/9232)
________________________________________________________________________

3.
Title: function getUnderlyingPrice() gas improvement on returning value

Proof of Concept:
[OndoPriceOracleV2.sol#L95](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L95)

Recommended Mitigation Steps:
by set `prices` in returns L#94 and delete L#95 can save gas

```
) external view override returns (uint256 prices) { { //@audit-info: set here
```
________________________________________________________________________

4.
Title: Using multiple `require` instead `&&` can save gas

Proof of Concept:
[OndoPriceOracleV2.sol#L292-L296](https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/OndoPriceOracleV2.sol#L292-L296)

Recommended Mitigation Steps:
Change to:

```
	 require((answeredInRound >= roundId),"Chainlink oracle price is stale");

         require((upda

	 require((answeredInRound >= roundId),"Chainlink oracle price is stale");

         require((updatedAt >= block.timestamp - maxChainlinkOracleTimeDelay),"Chainlink oracle price is stale");

// Code block 1 (unknown):
) external view override returns (uint256 prices) { { //@audit-info: set here

CCashDelegate.sol#L15, CTokenDelegate.sol#L15, Cash.sol#L22, CashKYCSender.sol#L26, CashKYCSenderReceiver.sol#L26, OndoPriceOracleV2.sol#L95, OndoPriceOracleV2.sol#L292-L296

CashKYCSenderReceiver.sol, CTokenDelegate.sol, CashKYCSender.sol, OndoPriceOracleV2.sol, Cash.sol, CCashDelegate.sol


0x11singh99 G

# Gas Optimizations

| Issue                                                                                | Total Gas Saved |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------: |
| [[G-01] **In `DAO.sol` refactor `finalizeBallot` function , `_finalizeParameterBallot`, `_finalizeApprovalBallot` and `_finalizeTokenWhitelisting` internal functions to avoid extra external calls.(Total Gas Saved ~7000 GAS)**](#g-01-in-daosol-refactor-finalizeballot-function--_finalizeparameterballot-_finalizeapprovalballot-and-_finalizetokenwhitelisting-internal-functions-to-avoid-extra-external-callstotal-gas-saved-7000-gas) |    **~7000**    |
| [[G-02] **State variables can be packed into fewer storage slots (saves ~30000 Gas)**](#g-02-state-variables-can-be-packed-into-fewer-storage-slots-saves-30000-gas)                                                                                |   **~30000**    |
| [[G-03] **Change the order of `modifier` check in `functions` can save upto 23.9k gas per instance half of the time(Total Total Gas Saved ~119500)**](#g-03-change-th

File : src/dao/DAO.sol

113:    function _finalizeParameterBallot( uint256 ballotID ) internal
		{
		Ballot memory ballot = proposals.ballotForID(ballotID);

219:    function _finalizeApprovalBallot( uint256 ballotID ) internal
		{
		if ( proposals.ballotIsApproved(ballotID ) )
			{
			Ballot memory ballot = proposals.ballotForID(ballotID);

235:	function _finalizeTokenWhitelisting( uint256 ballotID ) internal
		{
		if ( proposals.ballotIsApproved(ballotID ) )
			{
			// The ballot is approved. Any reversions below will allow the ballot to be attemped to be finalized later - as the ballot won't be finalized on reversion.
			Ballot memory ballot = proposals.ballotForID(ballotID);

278:  function finalizeBallot( uint256 ballotID ) external nonReentrant
		{
		// Checks that ballot is live, and minimumEndTime and quorum have both been reached
		require( proposals.canFinalizeBallot(ballotID), "The ballot is not yet able to be finalized" );

		Ballot memory ballot = proposals.ballotForID(ballotID);

		if ( ballot.ballotType == BallotType.PARAMETER )
			_finalizeParameterBallot(ballotID);
		else if ( ballot.ballotType == BallotType.WHITELIST_TOKEN )
			_finalizeTokenWhitelisting(ballotID);
		else
			_finalizeApprovalBallot(ballotID);
		}

## [G-02] State variables can be packed into fewer storage slots (saves ~30000 Gas)

The EVM works with 32 byte words. Variables less than 32 bytes can be declared next to each other in storage and this will pack the values together into a single 32 byte storage slot (if values combined are <= 32 bytes). If the variables packed together are retrieved together in functions (more likely with structs), we will effectively save ~2000 gas with every subsequent SLOAD for that storage slot. This is due to us incurring a Gwarmaccess (100 gas) versus a Gcoldsload (2100 gas).

### Total `SAVE: ~30000 GAS, 15 Storage SLOT`

### `bootstrappingRewards`, `percentPolRewardsBurned`, `baseBallotQuorumPercentTimes1000`, `ballotMinimumDuration`, `requiredProposalPercentStakeTimes1000`, `maxPendingTokensForWhitelisting`, `arbitrageProfitsPercentPOL` and `upkeepRewardPercent` can be packed in single SLOT :`SAVES 14000 GAS`, `7 SLOT`

1. **`bootstrappingRewards`** This can be reduced to uint96(can hold up to **~7.9e28**) because `bootstrappingRewards` maximum range is between 50k-500k ether (50e18 - 500e18) so `uint96` is more than sufficient to hold this value.

2. **`percentPolRewardsBurned`** This can be reduced to uint8(max value is 255) because `percentPolRewardsBurned` max value is 50 so `uint8` is more than sufficient to hold this value.

3. **`baseBallotQuorumPercentTimes1000`** This can be reduced to uint16(max value is 65535) because max value of `baseBallotQuorumPercentTimes1000` can be 10000 so `uint16` is more than sufficient to hold this value.

4. **`ballotMinimumDuration`** This can be reduced to uint32(max value is 4,294,967,295) because max value of `ballotMinimumDuration` can be 14 days(12,09,600 in seconds) so `uint32` is more than sufficient to hold this value.

5. **`requiredProposalPercentStakeTimes1000`**: This can be reduced to uint16(max value is 65535) because max value of `requiredProposalPercentStakeTimes1000` can be 2000 so `uint16` is more than sufficient t


0xBinChook Q

# QA Report for Salty.io

| Id              | Issue                                                                              |
|-----------------|------------------------------------------------------------------------------------|
| [L-1](#l-1)     | Staking configuration changes only applies to new unstake after finalization       |
| [L-2](#l-2)     | hitelisting, un-whitelisting then re-whitelisting to game bootstrap rewards        |
| [L-3](#l-3)     | Price feed proposal are not check to ensure feed uniqueness                        |
| [L-4](#l-4)     | Rounding down prevents proposals when there is between 1 and 999 xSALT             |
| [L-5](#l-5)     | When xSalt is low, there is a risk of proposal spam                                |
| [L-6](#l-6)     | `SigningTools::_verifySignature` allows malleable (non-unique) signature           |
| [L-7](#l-7)     | Salt from unclaimed airdrops will be trapped                                       |
| [L-8](#l-8)     | `ManagedWallet` is an trap ETH                                                     |
| [L-9](#l-9)     | Control of `proposedConfirmationWallet` should be confirmed                        |
| [L-10](#l-10)   | Both proposed wallets can be the same                                              |
| [L-11](#l-11)   | Upkeep has a race condition that creates an economic disincentive                  |
| [NC-1](#nc-1)   | When a user recasts their vote emit an event                                       |
| [NC-2](#nc-2)   | `Proposals::userHasActiveProposal()` will be incorrect for the DAO                 |
| [NC-3](#nc-3)   | `Proposals::openBallots()` returns an unbounded set                                |
| [NC-4](#nc-4)   | Inconsistent quorum tiers between `minimumQuorum` and `requiredQuorum`             |
| [NC-5](#nc-5)   | Integer rounding may leave dust in `Airdrop`                                       |
| [NC-6](#nc-6)   | Hardcoded ETH amount for confirmation signal is brittle

    uint256 requiredXSalt = ( totalStaked * daoConfig.requiredProposalPercentStakeTimes1000() ) / ( 100 * 1000 );

    require( requiredXSalt > 0, "requiredXSalt cannot be zero" );

    uint256 totalStaked = staking.totalShares(PoolUtils.STAKED_SALT);
    uint256 requiredXSalt = ( totalStaked * daoConfig.requiredProposalPercentStakeTimes1000() ) / ( 100 * 1000 );


t4sk Q

# Lock solidity version
```
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
```

# V3Oracle._requireMaxDifference incorrect comparison

```solidity
        if (differenceX10000 >= maxDifferenceX10000) {
            revert PriceDifferenceExceeded();
        }
```

`maxDifferencex1000` is max allowed price difference

Here the code will revert when the price difference is exactly equal to the max allowed difference.

Inequality should be changed to `differenceX10000 > maxDifferenceX10000`

https://github.com/code-423n4/2024-03-revert-lend/blob/457230945a49878eefdc1001796b10638c1e7584/src/V3Oracle.sol#L147

https://github.com/code-423n4/2024-03-revert-lend/blob/457230945a49878eefdc1001796b10638c1e7584/src/V3Oracle.sol#L200

// Code block 1 (unknown):
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

// Code block 2 (solidity):
if (differenceX10000 >= maxDifferenceX10000) {
            revert PriceDifferenceExceeded();
        }


BlueAlder Q

## [L-01] Unable to remove derivatives

There is no functionality of the contract to remove a derivative from the index
fund. In the case where an incorrect derivative is deployed or an underlying
derivative is compromised and is no longer wanted to be used there is no way to
remove this from the contract.

Although this can be mitigated by setting the weight of the derivative to 0 this
will become useless storage on the contract and will increase the overall cost
of operations on the contract when it has to iterate through all derivatives.

### Mitigation

Implement a removeDerivative() function which will remove the derivative from
the contract. This will have implications on how derivatives are added the
contract since it uses the `derivativeCount` variable to create new entries in
the derivative mapping.

## [L-02] Gas Griefing is possible on external calls

Gas griefing is possible on external calls as the return data has to be stored
even if it is omitted, due to the EVM architecture:

```sol
(bool sent,) = address(msg.sender).call{value: ethAmountToWithdraw}("");
```

To mitigate this, perform a low level call with out and outdata size set to 0
so that the return data is not stored.

```
@@ -108,7 +108,9 @@ contract SafEth is Initializable, ERC20Upgradeable, OwnableUpgradeable, SafEthSt
         uint256 ethAmountAfter = address(this).balance;
         uint256 ethAmountToWithdraw = ethAmountAfter - ethAmountBefore;
         // solhint-disable-next-line
-        (bool sent,) = address(msg.sender).call{value: ethAmountToWithdraw}("");
+        assembly {
+            sent := call(gas(), caller(), ethAmountToWithdraw, 0, 0, 0, 0)
+        }
         require(sent, "Failed to send Ether");
         emit Unstaked(msg.sender, ethAmountToWithdraw, _safEthAmount);
     }
```

Check this tweet for more details:
https://twitter.com/pashovkrum/status/1607024043718316032?t=xs30iD6ORWtE2bTTYsCFIQ&s=19

## [L-03] Use Ownable2StepUpgradeable instead of OwnableUpgradeable

// Code block 1 (sol):
(bool sent,) = address(msg.sender).call{value: ethAmountToWithdraw}("");

// Code block 2 (unknown):
@@ -108,7 +108,9 @@ contract SafEth is Initializable, ERC20Upgradeable, OwnableUpgradeable, SafEthSt
         uint256 ethAmountAfter = address(this).balance;
         uint256 ethAmountToWithdraw = ethAmountAfter - ethAmountBefore;
         // solhint-disable-next-line
-        (bool sent,) = address(msg.sender).call{value: ethAmountToWithdraw}("");
+        assembly {
+            sent := call(gas(), caller(), ethAmountToWithdraw, 0, 0, 0, 0)
+        }
         require(sent, "Failed to send Ether");
         emit Unstaked(msg.sender, ethAmountToWithdraw, _safEthAmount);
     }


btk Q

### Total L issues

| Number | Issues Details                                                                     | Context |
|--------|------------------------------------------------------------------------------------|---------|
| [L-01] | Avoid shadowing inherited state variables                                          |   4     |
| [L-02] | `initialize()` function can be called by anybody                                   |   3     |
| [L-03] | No Storage Gap for Upgradeable contracts                                           |   6     |
| [L-04] | Low level calls with solidity version 0.8.14 and lower can result in optimiser bug |   3     |
| [L-05] | Loss of precision due to rounding                                                  |   1     |

### Total NC issues

| Number  | Issues Details                                                          | Context       |
|---------|-------------------------------------------------------------------------|---------------|
| [NC-01] | Use `require()` instead of `assert()`                                   | 3             |
| [NC-02] | Critical changes should use-two step procedure                          | 3             |
| [NC-03] | Include `@return` parameters in NatSpec comments                        | 14            |
| [NC-04] | Function writing does not comply with the `Solidity Style Guide`   | All Contracts |
| [NC-05] | Solidity compiler optimizations can be problematic                      | 1             |
| [NC-06] | Non-usage of specific imports                                           | All Contracts |
| [NC-07] | Mark visibility of `initialize()` functions as external                 | 6             |
| [NC-08] | Add address(0) check for the critical changes                           | 3             |

## [L-01] Avoid shadowing inherited state variables

In `CashKYCSenderReceiver.sol` there is a local variables named `name` `symbol` `kycRegistry` `kycRequirementGroup`, but there is functions and sta

  function initialize(
    string memory name,
    string memory symbol,
    address kycRegistry,
    uint256 kycRequirementGroup
  ) public initializer {
    __ERC20PresetMinterPauser_init(name, symbol);
    __KYCRegistryClientInitializable_init(kycRegistry, kycRequirementGroup);
  }


0xSolus G

[L#271](https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L271) - Redundant code: `buy()` calculates `_getRoyalty()` twice unnecessarily as the
operation can be done in the first loop, avoiding the second one in L#272 make sure to optimize.
Gas costs old: avg -> 70884, median -> 74037, max -> 158864.
Gas costs new: avg -> 70311 , median -> 73894 , max -> 147884.
Recommendation:
```
function buy(uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof)
  public
  payable
returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount)
{
  // ~~~ Checks ~~~ //

  // calculate the sum of weights of the NFTs to buy
  uint256 weightSum = sumWeightsAndValidateProof(tokenIds, tokenWeights, proof);

  // calculate the required net input amount and fee amount
  (netInputAmount, feeAmount, protocolFeeAmount) = buyQuote(weightSum);

  // check that the caller sent 0 ETH if the base token is not ETH
  if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();

  // ~~~ Effects ~~~ //

  // update the virtual reserves
  virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
  virtualNftReserves -= uint128(weightSum);

  // ~~~ Interactions ~~~ //

  // calculate the sale price (assume it's the same for each NFT even if weights differ)
  uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length;
  uint256 royaltyFeeAmount = 0;
  for (uint256 i = 0; i < tokenIds.length; i++) {
    // transfer the NFT to the caller
    ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);

    if (payRoyalties) {
      // get the royalty fee for the NFT
      (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);

      // transfer the royalty fee to the recipient if it's greater than 0
      if (royaltyFee > 0 && recipient != address(0)) {
        if (baseToken != address(0)) {
          ERC20(baseToken

function buy(uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof)
  public
  payable
returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount)
{
  // ~~~ Checks ~~~ //

  // calculate the sum of weights of the NFTs to buy
  uint256 weightSum = sumWeightsAndValidateProof(tokenIds, tokenWeights, proof);

  // calculate the required net input amount and fee amount
  (netInputAmount, feeAmount, protocolFeeAmount) = buyQuote(weightSum);

  // check that the caller sent 0 ETH if the base token is not ETH
  if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();

  // ~~~ Effects ~~~ //

  // update the virtual reserves
  virtualBaseTokenReserves += uint128(netInputAmount - feeAmount - protocolFeeAmount);
  virtualNftReserves -= uint128(weightSum);

  // ~~~ Interactions ~~~ //

  // calculate the sale price (assume it's the same for each NFT even if weights differ)
  uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length;
  uint256 royaltyFeeAmount = 0;
  for (uint256 i = 0; i < tokenIds.length; i++) {
    // transfer the NFT to the caller
    ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);

    if (payRoyalties) {
      // get the royalty fee for the NFT
      (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice);

      // transfer the royalty fee to the recipient if it's greater than 0
      if (royaltyFee > 0 && recipient != address(0)) {
        if (baseToken != address(0)) {
          ERC20(baseToken).safeTransfer(recipient, royaltyFee);
        } else {
          recipient.safeTransferETH(royaltyFee);
        }
      }
      // add the royalty fee to the total royalty fee amount
      royaltyFeeAmount += royaltyFee;
    }
  }

  // add the royalty fee amount to the net input aount
  netInputAmount += royaltyFeeAmount;

  if (baseToken != address(0)) {
    // transfer the base token from the caller to the contr


mahdirostami Q

# 1. Not considering Strategy deposit limit
## Summary
The eigenlayer strategy contracts have deposit limitations:
```solidity
    /// The maximum deposit (in underlyingToken) that this strategy will accept per deposit
    uint256 public maxPerDeposit;

    /// The maximum deposits (in underlyingToken) that this strategy will hold
    uint256 public maxTotalDeposits;
```
Which currently are [100000000000000000000000](https://etherscan.io/address/0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc#readProxyContract#F3), the problem here is NodeDelegator doesn't consider this and send the whole balance to strategies which may cause revert.
```solidity
        address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

        uint256 balance = token.balanceOf(address(this));//@audit add amount instead of balance

        emit AssetDepositIntoStrategy(asset, strategy, balance);

        IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);
```
## Recommendations
use amount as an argument instead of the balance of the contract
# 2. Not considering token decimals
## Summary
in the share calculation, the protocol assumes that all assets have 18 decimals and oracles give a price in 18 decimals as well, for now, it doesn't have any concern, but it couldn't be true in the future, so assets with different decimals or prices with different decimals could result in wrong share calculation.
```solidity
        rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
```
```solidity
        return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset);
```
## Recommendations
add checks, to check token decimals.

// Code block 1 (solidity):
/// The maximum deposit (in underlyingToken) that this strategy will accept per deposit
    uint256 public maxPerDeposit;

    /// The maximum deposits (in underlyingToken) that this strategy will hold
    uint256 public maxTotalDeposits;

// Code block 2 (solidity):
address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

        uint256 balance = token.balanceOf(address(this));//@audit add amount instead of balance

        emit AssetDepositIntoStrategy(asset, strategy, balance);

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

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

// Code block 4 (solidity):
return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset);

/// The maximum deposit (in underlyingToken) that this strategy will accept per deposit
    uint256 public maxPerDeposit;

    /// The maximum deposits (in underlyingToken) that this strategy will hold
    uint256 public maxTotalDeposits;

address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);

        uint256 balance = token.balanceOf(address(this));//@audit add amount instead of balance

        emit AssetDepositIntoStrategy(asset, strategy, balance);

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

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

return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset);


martin G

# Biconomy

## Gas Optimizations

### G-01 Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

It is expected that the value should be converted into a constant value at compile time. But actually the expression is re-calculated each time the constant is referenced.

```solidity
File: /smart-contract-wallet/SmartAccount.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

347: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

416: return keccak256(enc

430: keccak256(

435: keccak256(_tx.data),
```

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

```solidity
File: /smart-contract-wallet/SmartAccountFactory.sol

34: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

70: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

71: bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256
(code)));
```

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

```solidity
File: /smart-contract-wallet/Proxy.sol

16: assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));
```

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

```solidity
File: /smart-contract-wallet/SmartAccountNoAuth.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

342: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

406: return keccak256(encodeTransactionData(_tx, refundInfo, _nonce));

420: keccak256(

425: keccak256(_tx.data),
```

https://github.com/code

// Code block 1 (solidity):
File: /smart-contract-wallet/SmartAccount.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

347: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

416: return keccak256(enc

430: keccak256(

435: keccak256(_tx.data),

// Code block 2 (solidity):
File: /smart-contract-wallet/SmartAccountFactory.sol

34: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

70: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

71: bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256
(code)));

// Code block 3 (solidity):
File: /smart-contract-wallet/Proxy.sol

16: assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

// Code block 4 (solidity):
File: /smart-contract-wallet/SmartAccountNoAuth.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

342: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

406: return keccak256(encodeTransactionData(_tx, refundInfo, _nonce));

420: keccak256(

425: keccak256(_tx.data),

File: /smart-contract-wallet/SmartAccount.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

347: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

416: return keccak256(enc

430: keccak256(

435: keccak256(_tx.data),

File: /smart-contract-wallet/SmartAccountFactory.sol

34: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

70: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));

71: bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256
(code)));

File: /smart-contract-wallet/Proxy.sol

16: assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

File: /smart-contract-wallet/SmartAccountNoAuth.sol

136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

217: txHash = keccak256(txHashData);

342: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

406: return keccak256(encodeTransactionData(_tx, refundInfo, _nonce));

420: keccak256(

425: keccak256(_tx.data),


GalloDaSballo G

# Executive Summary

There's an abundant amount of opportunity for refactorings, the below report offers extremely high savings opportunity with minimal work by reducing SSTOREs and SLOADs

All benchmarks are approximations based on counting storage slots.

Savings for Core are in the tens of thousands

Savings for Vault are in the hundreds of thousands

# Default Pool

## Gas: Can be refactored to avoid giving allowance, and savings tens of thousands of gas - 20k+
https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/DefaultPool.sol#L90-L91

```solidity
        IERC20(_collateral).safeIncreaseAllowance(activePool, _amount);
        IActivePool(activePoolAddress).pullCollateralFromBorrowerOperationsOrDefaultPool(_collateral, _amount);
```

Can just transfer, saving 20k gas per instance

# ActivePool

## Pack generator, amount and treshold into one struct by using u16 - 4.2k per balance
https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/ActivePool.sol#L246

By packing them into one struct you can save 4.2k per rebalance (2 cold SLOADs)

# RedemptionHelper

## Gas - By packing you can avoid doing 2 SLOADs and calls - 2.1k+
Also have a function "getConfig" or smth

https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/RedemptionHelper.sol#L120-L121

```solidity
        totals.collDecimals = collateralConfigCached.getCollateralDecimals(_collateral);
        totals.collMCR = collateralConfigCached.getCollateralMCR(_collateral);
```

## Gas - Cache token `lusdToken` - 100 gas
https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/RedemptionHelper.sol#L124-L128

```solidity
        _requireLUSDBalanceCoversRedemption(lusdToken, _redeemer, _LUSDamount);

        totals.totalLUSDSupplyAtStart = getEntireSystemDebt(_collateral);
        // Confirm re

// Code block 1 (solidity):
IERC20(_collateral).safeIncreaseAllowance(activePool, _amount);
        IActivePool(activePoolAddress).pullCollateralFromBorrowerOperationsOrDefaultPool(_collateral, _amount);

// Code block 2 (solidity):
totals.collDecimals = collateralConfigCached.getCollateralDecimals(_collateral);
        totals.collMCR = collateralConfigCached.getCollateralMCR(_collateral);

IERC20(_collateral).safeIncreaseAllowance(activePool, _amount);
        IActivePool(activePoolAddress).pullCollateralFromBorrowerOperationsOrDefaultPool(_collateral, _amount);

totals.collDecimals = collateralConfigCached.getCollateralDecimals(_collateral);
        totals.collMCR = collateralConfigCached.getCollateralMCR(_collateral);

DefaultPool.sol#L90-L91, ActivePool.sol#L246, RedemptionHelper.sol#L120-L121, RedemptionHelper.sol#L124-L128


squeaky_cactus Q

Anja Protocol - QA Report

Just a quick read through of the code, picking up a few NC issues.

Non-Critical

Code

Typographical Correctness

```
ajna-core/src/PositionManager.sol:352

352: function reedemPositions(
```
Misspelling of redeem; ``reedemPositions``-> ``redeemPositions``

NatSpec Comments

Code Conflict

```
ajna-core/src/PositionManager.sol:54

51:    /// @dev Mapping of `token id => ajna pool address` for which token was minted.
52:    mapping(uint256 => address) public override poolKey;
53:
54:    /// @dev Mapping of `token id => ajna pool address` for which token was minted.
55:    mapping(uint256 => mapping(uint256 => Position)) internal positions;
```
The comment on line 54 copies line 51, however it does not correctly describe the purpose of the field `positions`
on the following line.

Suggestion: ``/// @dev Mapping of `token id => position by index`.``

```
ajna-grants/src/ExtraOrdinaryFunding.sol:180

180:    /********************************/
181:    /*** Internal View Functions ****/
182:    /********************************/
```
An internal view function is above the comment block.

Suggestion: move the function ``_extraordinaryProposalSucceeded`` to below the comment.

```
ajna-grants/src/IFunding.sol:7

7:  * @title Ajna Grant Coordination Fund Extraordinary Proposal flow.
```
Title does not match purpose of the interface. This is the same contain in ``IExtordinaryFunding.sol``.

Suggestion: remove interface level comment block.

Typographical Correctness

```
ajna-core/src/PositionManager.sol:83

83:        uint256 depositTime;      // lender deposit time in from bucekt
```
Misspelling of bucket; ``bucekt``-> ``bucket``

```
ajna-core/src/RewardsManager.sol:189

189:        // update to bucket list exchange rates, from buckets are aready updated on claim
```
Misspelling of already; ``aready``-> ``already``

```
ajna-grants/src/ExtraOrdinaryFunding.sol:69

69:    // check

// Code block 1 (unknown):
ajna-core/src/PositionManager.sol:352

352: function reedemPositions(

// Code block 2 (unknown):
ajna-core/src/PositionManager.sol:54

51:    /// @dev Mapping of `token id => ajna pool address` for which token was minted.
52:    mapping(uint256 => address) public override poolKey;
53:
54:    /// @dev Mapping of `token id => ajna pool address` for which token was minted.
55:    mapping(uint256 => mapping(uint256 => Position)) internal positions;

// Code block 3 (unknown):
ajna-grants/src/ExtraOrdinaryFunding.sol:180

180:    /********************************/
181:    /*** Internal View Functions ****/
182:    /********************************/

// Code block 4 (unknown):
ajna-grants/src/IFunding.sol:7

7:  * @title Ajna Grant Coordination Fund Extraordinary Proposal flow.

// Code block 5 (unknown):
ajna-core/src/PositionManager.sol:83

83:        uint256 depositTime;      // lender deposit time in from bucekt

// Code block 6 (unknown):
ajna-core/src/RewardsManager.sol:189

189:        // update to bucket list exchange rates, from buckets are aready updated on claim


Ryonen Q

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/MergingPool.sol#L139-L167

## Impact

I was thinking that perhaps `claimRewards` could potentially generate issues for users who win rewards in a fairly **advanced roundId**, as claimRewards starts with a `lowerBound == 0` for new users. Additionally, it contains a loop that checks if `msg.sender == winnerAddresses[currentRound][j]`. Therefore, I created a test to understand how problematic this function can be:

## POC

```
        function testClaimRewardsAdvancedRoundIdWithNewUser() public {

        address winner = makeAddr("winner");
        address player3 = makeAddr("player3");
        address player4 = makeAddr("player4");
        address player5 = makeAddr("player5");
        address player6 = makeAddr("player6");
        address player7 = makeAddr("player7");

        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_DELEGATED_ADDRESS);
        _mintFromMergingPool(winner);
        _mintFromMergingPool(player3);
        _mintFromMergingPool(player4);
        _mintFromMergingPool(player5);
        _mintFromMergingPool(player6);
        _mintFromMergingPool(player7);

        _mergingPoolContract.updateWinnersPerPeriod(7);

        string[] memory _modelURIs = new string[](2);
        _modelURIs[0] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m";
        _modelURIs[1] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m";
        string[] memory _modelTypes = new string[](2);
        _modelTypes[0] = "original";
        _modelTypes[1] = "original";
        uint256[2][] memory _customAttributes = new uint256[2][](2);
        _customAttributes[0][0] = uint256(1);
        _customAttributes[0][1] = uint256(80);
        _customAttributes[1][0] = uint256(1);
        _customAttributes[1][1] = uint256(80);

        uint256[] memory _winners = new uint256[](7);

        _winners[0] = 0;
        _winners[1] = 1;
        _winners[2] = 2;
        _winne

        function testClaimRewardsAdvancedRoundIdWithNewUser() public {

        address winner = makeAddr("winner");
        address player3 = makeAddr("player3");
        address player4 = makeAddr("player4");
        address player5 = makeAddr("player5");
        address player6 = makeAddr("player6");
        address player7 = makeAddr("player7");

        _mintFromMergingPool(_ownerAddress);
        _mintFromMergingPool(_DELEGATED_ADDRESS);
        _mintFromMergingPool(winner);
        _mintFromMergingPool(player3);
        _mintFromMergingPool(player4);
        _mintFromMergingPool(player5);
        _mintFromMergingPool(player6);
        _mintFromMergingPool(player7);

        _mergingPoolContract.updateWinnersPerPeriod(7);

        string[] memory _modelURIs = new string[](2);
        _modelURIs[0] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m";
        _modelURIs[1] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m";
        string[] memory _modelTypes = new string[](2);
        _modelTypes[0] = "original";
        _modelTypes[1] = "original";
        uint256[2][] memory _customAttributes = new uint256[2][](2);
        _customAttributes[0][0] = uint256(1);
        _customAttributes[0][1] = uint256(80);
        _customAttributes[1][0] = uint256(1);
        _customAttributes[1][1] = uint256(80);

        uint256[] memory _winners = new uint256[](7);

        _winners[0] = 0;
        _winners[1] = 1;
        _winners[2] = 2;
        _winners[3] = 3;
        _winners[4] = 4;
        _winners[5] = 5;
        _winners[6] = 6;

        uint256[] memory _previousWinners = new uint256[](7);

        _previousWinners[0] = 0;
        _previousWinners[1] = 1;
        _previousWinners[2] = 3;
        _previousWinners[3] = 4;
        _previousWinners[4] = 5;
        _previousWinners[5] = 6;
        _previousWinners[6] = 7;

        for(uint256 i = 0; i < 6000; i++){
            _mergingPoolContract.pickWinner(_previousWinners);


Tomio G

1.
Title: Using bytes constant is more gas efficient

Reference: [Here](https://ethereum.stackexchange.com/questions/3795/why-do-solidity-examples-use-bytes32-type-instead-of-string)

Proof of Concept:
[BorrowerOperations.sol#L21](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L21)
[ActivePool.sol#L30](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L30)
[CommunityIssuance.sol#L19](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L19)
[LQTYStaking.sol#L23](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L23)

Recommended Mitigation Steps:
Change it to `bytes(1..32) constant`
________________________________________________________________________

2.
Title: Gas savings for using solidity 0.8.10

impact:
Use a solidity version of at least 0.8 to get default underflow/overflow checks, use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

Proof of Concept:
All contract in scope.
by using version 0.8+ we can avoid using library safeMath in [ActivePool.sol#L27](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/ActivePool.sol#L27) , [CommunityIssuance.sol#L15](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L15) , [LQTYStaking.sol#L20](https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L20) , [LUSDToken.sol#L29](https://github.com/code-423n4/2023-02-ethos/blob/main/Etho

function _getCompoundedDepositFromSnapshots(
        uint initialDeposit,
        Snapshots memory snapshots
    )
        internal
        view
        returns (uint compoundedDeposit) //@audit-info: set here

	require(_maxFeePercentage >= BORROWING_FEE_FLOOR,
                "Max fee percentage must be between 0.5% and 100%");
	require(_maxFeePercentage <= DECIMAL_PRECISION,
                "Max fee percentage must be between 0.5% and 100%");

BorrowerOperations.sol#L21, ActivePool.sol#L30, CommunityIssuance.sol#L19, LQTYStaking.sol#L23, ActivePool.sol#L27, CommunityIssuance.sol#L15, LQTYStaking.sol#L20

BorrowerOperations.sol, ActivePool.sol, CommunityIssuance.sol, LQTYStaking.sol


oualidpro Q

## Summary

### Low Risk Issues

| |Issue|Instances|
|-|:-|:-:|
| [[L‑1](#l1-vulnerable-versions-of-packages-are-being-used)] | Vulnerable versions of packages are being used | 1 |
| [[L‑2](#l2-missing-checks-for-address-0-when-assigning-values-to-address-state-variables)] | Missing checks for `address(0)` when assigning values to address state variables | 27 |
| [[L‑3](#l3-for-loops-in-public-or-external-functions-should-be-avoided-due-to-high-gas-costs-and-possible-dos)] | For loops in public or external functions should be avoided due to high gas costs and possible DOS | 11 |
| [[L‑4](#l4-missing-checks-in-constructor-initialize)] | Missing checks in constructor/initialize | 1 |
| [[L‑5](#l5-external-calls-in-an-un-bounded-loop-may-result-in-a-dos)] | `external` calls in an un-bounded loop may result in a DOS | 14 |
| [[L‑6](#l6-internal-function-calls-within-for-loops)] | `internal` Function calls within for loops | 17 |
| [[L‑7](#l7-loss-of-precision)] | Loss of precision | 13 |
| [[L‑8](#l8-require-should-be-used-instead-of-assert)] | `require()` should be used instead of `assert()` | 1 |
| [[L‑9](#l9-consider-using-openzeppelin-s-safecast-library-to-prevent-unexpected-overflows-when-casting-from-various-type-int-uint-values)] | Consider using OpenZeppelin’s SafeCast library to prevent unexpected overflows when casting from various type int/uint values | 5 |
| [[L‑10](#l10-setters-should-have-initial-value-check)] | Setters should have initial value check | 10 |
| [[L‑11](#l11-sweeping-may-break-accounting-if-tokens-with-multiple-addresses-are-used)] | Sweeping may break accounting if tokens with multiple addresses are used | 3 |
| [[L‑12](#l12-int-casting-block-timestamp-can-reduce-the-lifespan-of-a-contract)] | Int casting `block.timestamp` can reduce the lifespan of a contract | 1 |
| [[L‑13](#l13-unused-empty-receive-fallback-function)] | Unused/empty `re

File: package.json

<details><summary>Vulnerabilities related to `@openzeppelin/contracts`:</summary>

- [CVE-2023-34459](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p) :When the verifyMultiProof, verifyMultiProofCalldata, processMultiProof, or processMultiProofCalldata functions are in use, it is possible to construct merkle trees that allow forging a valid multiproof for an arbitrary set of leaves.
A contract may be vulnerable if it uses multiproofs for verification and the merkle tree that is processed includes a node with value 0 at depth 1(just under the root).This could happen inadvertently for balanced trees with 3 leaves or less, if the leaves are not hashed.This could happen deliberately if a malicious tree builder includes such a node in the tree.
A contract is not vulnerable if it uses single- leaf proving(verify, verifyCalldata, processProof, or processProofCalldata), or if it uses multiproofs with a known tree that has hashed leaves.Standard merkle trees produced or validated with the @openzeppelin/merkle-tree library are safe.

- [CVE-2023-34234](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-5h3x-9wvq-w4m2) :By frontrunning the creation of a proposal, an attacker can become the proposer and gain the ability to cancel it. The attacker can do this repeatedly to try to prevent a proposal from being proposed at all.
This impacts the Governor contract in v4.9.0 only, and the GovernorCompatibilityBravo contract since v4.3.0.

- [CVE-2023-30541](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-mx2q-35m2-x2rh) :A function in the implementation contract may be inaccessible if its selector clashes with one of the proxy's own selectors. Specifically, if the clashing function has a different signature with incompatible ABI encoding, the proxy could revert while attempting to decode the arguments from calldata.
The probability of an accidental clash

File: packages/contracts/contracts/ActivePool.sol

53:         borrowerOperationsAddress = _borrowerOperationsAddress;

54:         cdpManagerAddress = _cdpManagerAddress;

56:         collSurplusPoolAddress = _collSurplusAddress;

57:         feeRecipientAddress = _feeRecipientAddress;


0x11singh99 Q

# Low-Severity

## [L-01] Check address `to` for `address(0)` before transferring ethers (*Note: Instance missed by bot*)

It's indeed a good practice to check if the destination address (`to` address) is not the zero address before transferring `ethers`. This is to ensure that accidental transfers to the zero address do not occur, which could result in the loss of funds since transactions to the zero address are typically irreversible.

```solidity
File : src/automators/Automator.sol

123:   function withdrawETH(address to) external {
           if (msg.sender != withdrawer) {
               revert Unauthorized();
            }

        uint256 balance = address(this).balance;
        if (balance > 0) {
            (bool sent,) = to.call{value: balance}("");
            if (!sent) {
                revert EtherSendFailed();
            }

```
[123-133](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/automators/Automator.sol#L123C4-L133C14)

# Non-critical

## [N-01] Do not use shadowed variable `owner` in `withdraw` function

This can lead to confusion when referencing the variable `owner` within the `withdraw` function because the parameter `owner` will take precedence over the inherited state variable `owner`. Shadowing can lead to bugs if the developer mistakenly references the wrong variable within the function. Shadowing can make the code less clear and readable.

```solidity
File : src/V3Vault.sol

372:     function withdraw(uint256 assets, address receiver, address owner) external override returns (uint256) {

```
[372](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L372)

## [N-02] Changing the visibility of functions from `public` to `external` when it's not intended to be called within the contract itself (*Note: Instances missed by bot*)

Declaring a function as `external` clearly communicates to developers that the function is intended to be called from outside the contract. This can improve code readability

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

123:   function withdrawETH(address to) external {
           if (msg.sender != withdrawer) {
               revert Unauthorized();
            }

        uint256 balance = address(this).balance;
        if (balance > 0) {
            (bool sent,) = to.call{value: balance}("");
            if (!sent) {
                revert EtherSendFailed();
            }

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

372:     function withdraw(uint256 assets, address receiver, address owner) external override returns (uint256) {

File : src/automators/Automator.sol

123:   function withdrawETH(address to) external {
           if (msg.sender != withdrawer) {
               revert Unauthorized();
            }

        uint256 balance = address(this).balance;
        if (balance > 0) {
            (bool sent,) = to.call{value: balance}("");
            if (!sent) {
                revert EtherSendFailed();
            }

File : src/V3Vault.sol

372:     function withdraw(uint256 assets, address receiver, address owner) external override returns (uint256) {


0xgrbr G

## 1. Use calldata instead of memory for function parameters that don’t change
When you specify a data location as memory, that value will be copied into memory. When you specify the location as calldata, the value will stay static within calldata. If the value is a large, complex type, using memory may result in extra memory expansion costs.

Total Instances: 8

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/amo/UniV3LiquidityAmo.sol#L156

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

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

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

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

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

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

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

Change function arguments from memory to calldata.

## 2. Reorder structure layout
The following structures could be optimized moving the position of certain values in order to save a slot:

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L67
```
  /// @notice Token address that dpxEth is pegged to
  address public weth;
```
Move to:
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L116
```
....
  /// @notice Token address that dpxEth is pegged to
  address public weth;

  /// @notice Whether reLP is active or not
  bool public isReLPActive;

  /// @notice Whether put options are requred
  bool public putOptionsRequired;
....
```

## 3. Fetch variable before the loop

The current code calls getUnderlyingPrice() inside the loop, which is inefficient and consumes more gas for each iteration o

// Code block 1 (unknown):
/// @notice Token address that dpxEth is pegged to
  address public weth;

// Code block 2 (unknown):
....
  /// @notice Token address that dpxEth is pegged to
  address public weth;

  /// @notice Whether reLP is active or not
  bool public isReLPActive;

  /// @notice Whether put options are requred
  bool public putOptionsRequired;
....

UniV3LiquidityAmo.sol#L156, RdpxV2Core.sol#L244, RdpxV2Core.sol#L273, RdpxV2Core.sol#L768, RdpxV2Core.sol#L824, RdpxV2Core.sol#L825, PerpetualAtlanticVault.sol#L316, PerpetualAtlanticVault.sol#L406, RdpxV2Core.sol#L67, RdpxV2Core.sol#L116


osmanozdemir1 Q

### Summary

* \[L-01\] `LRTConfig::updateAssetDepositLimit()` function doesn't check if the current deposits are greater than the new limit

* \[L-02\] `LRTDepositPool::addNodeDelegatorContractToQueue()` function should check if the inputted array includes the same addresses

* \[L-03\] `LRTDepositPool::updateMaxNodeDelegatorCount` should check the new count is not below the current delegator count

* \[L-04\] Deposited amounts in the EigenLayer strategy should be checked before updating the strategy for the asset

* \[L-05\] `ChainlinkPriceOracle::getAssetPrice()` function should check the stale price

* \[NC-01\] `ChainlinkPriceOracle.sol` contract should use AggregatorV3Interface instead of AggregatorInterface

* \[NC-02\] It would be better to check the decimal precision of the returned values from oracles

---

### \[L-01\] `LRTConfig::updateAssetDepositLimit()` function doesn't check if the current deposits are greater than the new limit

[https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L94C5-L104C6](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTConfig.sol#L94C5-L104C6)

Every asset has a deposit limit and the contract managers can update these limits. The issue here is that the `updateAssetDepositLimit()` function doesn't check the current total asset deposits.

It is possible to set a new limit below the current deposits which leads to a broken invariant.

**Recommendation**: I would recommend checking the current deposits before assigning the new limit.

```solidity
    function updateAssetDepositLimit(
        address asset,
        uint256 depositLimit
    )
        external
        onlyRole(LRTConstants.MANAGER)
        onlySupportedAsset(asset)
    {
+       uint256 currentDepositAmount = lrtDepositPool.getTotalAssetDeposits(asset);
+       if (depositLimit < currentDepositAmount) {
+           revert
+       }

    function updateAssetDepositLimit(
        address asset,
        uint256 depositLimit
    )
        external
        onlyRole(LRTConstants.MANAGER)
        onlySupportedAsset(asset)
    {
+       uint256 currentDepositAmount = lrtDepositPool.getTotalAssetDeposits(asset);
+       if (depositLimit < currentDepositAmount) {
+           revert
+       }
        depositLimitByAsset[asset] = depositLimit;
        emit AssetDepositLimitUpdate(asset, depositLimit);
    }

    function getAssetDistributionData(address asset) {
        // skipped for brevity

        uint256 ndcsCount = nodeDelegatorQueue.length;
        for (uint256 i; i < ndcsCount;) {
            assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]); //@audit - it the array has the same delegator address in it, calculated balance will be greater than the actual deposit
            assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i]).getAssetBalance(asset);
            unchecked {
                ++i;
            }
        }
    }


nonseodion Q

## L-1 _PERMIT_POSITION_MANAGER_TYPEHASH hash generation and comment discrepancy

https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/BorrowerOperations.sol#L31-L35

// keccak256("permitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)");
    bytes32 private constant _PERMIT_POSITION_MANAGER_TYPEHASH =
        keccak256(
            "PermitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)"
        );

The comment for the typehash of the position manager and the actual code are different. The keccack256 hash in the comment and the hash in the code act on different arguments which will cause the output to be different. The difference in the arguments is in the capitalisation of the first letter.

### Recommended Mitigation
Change the hash argument to "PermitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)".

## L-2 Chainlink feed timeouts are immutable in PriceFeed but can be changed.
https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/PriceFeed.sol#L31-L33

Based on a [conversation](https://x.com/NonseOdion/status/1710950601301893320?s=20) with [ChainlinkGod](https://twitter.com/ChainLinkGod), a prominent advocate of Chainlink, on Twitter, the timeout (heartbeat) of price feeds can be changed. If this occurs it means the PriceFeed contract will be using invalid timeout values and it'll remain in that state since they are immutable. Thus it won't be able to know the right time to put the Chainlink price feeds in frozen states.

### Recommended Mitigation
Allow the heartbeats to be set by Governance. Since they aren't values enforced at the contract level.

## L-3 The status values for the PriceFeed status enums are used wrongly according to their dictionar

When the public `status` variable of PriceFeed is called, and it returns any status starting with "using", it should mean that the price returned by the PriceFeed was from the oracle mentioned after `using` while the other is untrusted or frozen. E.g if it returns `usingFallbackChainlinkFrozen`, it should mean the price returned was from Fallback while Chainlink is frozen. But sometimes it returns the lastGoodPrice instead of the latest price from that oracle.

E.g

https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/PriceFeed.sol#L120C3-L123


KrisApostolov Q

# QA report

## Disclaimer

Some of the examples may contain truncated versions of the original code. There may also be `@audit` tags, which denote the actual place affected

# Table of contents

### Low severity findings

|  | Issue | Instances |
| --- | --- | --- |
| [L-1] | Lack of balance check allows the unstake function to be called without a corresponding balance or amount | 1 |
| [L-02] | Not following the CEI pattern can lead to a reentrancy | 1 |
| [L-03] | Slight precision loss occurring in the calculations | 2 |

### [L-01] - Lack of balance check allows the `unstake` function to be called without a corresponding balance or amount

The **unstake** function in the **[SafEth](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol)** contract does not implement any balance checks whatsoever, which enables anyone to call the function with a **`_safEthAmount` = 0** without any reverts. The function will execute everything like normal thus wasting gas and opening the contract to other potential vulnerabilities.

### Instances

**File** **[./contracts/SafEth/SafEth.sol](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol):**

**[[1]](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L108-L129) - `unstake()`**

### Recommendations

Consider adding 2 types of checks in the **unstake** function:

1. **A** **`require(_safEthAmount ≠ 0, “error message”);` statement or similar.**
2. **A `require(balanceOf(msg.sender) ≥ _safEthAmount, “error message”)`** **statement**  **or similar.**

### [L-02] - Not following the CEI pattern can lead to a reentrancy

The **unstake** function in the **[SafEth](https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol)** contract does not follow the checks-effects-interactions pattern and goes straight to calling an external contract (a derivative) to withdraw amount the user wants before burning the user’s

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";


Collinsoden G

# Gas Optimization

## Gas 01: In contract/BorrowOperations.sol
[Link to code](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/BorrowerOperations.sol#L564C3-L569)
> Gas can be optimized if the call `_requireSufficientEbtcTokenBalance` is made first before `getCdpCollShares` and `getCdpLiquidatorRewardShares` as those two values may not be needed until after the check. If the `_requireSufficientEbtcTokenBalance` call fails, the gas spent on making those calls will be lost. This can save about 82,864 gas if the check fails.
```
       uint256 collShares = cdpManager.getCdpCollShares(_cdpId);
       uint256 debt = cdpManager.getCdpDebt(_cdpId);
       uint256 liquidatorRewardShares = cdpManager.getCdpLiquidatorRewardShares(_cdpId);

       _requireSufficientEbtcTokenBalance(msg.sender, debt)
```

> The code can be optimized thus:
```
  function decreaseSystemDebt(uint256 _amount) external override {
       _requireCallerIsBOorCdpM();
       uint256 debt = cdpManager.getCdpDebt(_cdpId);

       _requireSufficientEbtcTokenBalance(msg.sender, debt)

       uint256 collShares = cdpManager.getCdpCollShares(_cdpId);
       uint256 liquidatorRewardShares = cdpManager.getCdpLiquidatorRewardShares(_cdpId);

```

## Gas 02: In contracts/ActivePool.sol
[Link to code](https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/ActivePool.sol#L207-L214)
> In the code below, assigning systemDebt as `systemDebt -= _amount` would save gas and a variable.

> So this is recommended:
```
function decreaseSystemDebt(uint256 _amount) external override {
   _requireCallerIsBOorCdpM();
   systemDebt -= _amount
   emit ActivePoolEBTCDebtUpdated(systemDebt);
```

Instead of this:
```
   function decreaseSystemDebt(uint256 _amount) external override {
       _requireCallerIsBOorCdpM();

       uint256 cachedSystemDebt = systemDebt - _amount;

// Code block 1 (unknown):
uint256 collShares = cdpManager.getCdpCollShares(_cdpId);
       uint256 debt = cdpManager.getCdpDebt(_cdpId);
       uint256 liquidatorRewardShares = cdpManager.getCdpLiquidatorRewardShares(_cdpId);

       _requireSufficientEbtcTokenBalance(msg.sender, debt)

// Code block 2 (unknown):
function decreaseSystemDebt(uint256 _amount) external override {
       _requireCallerIsBOorCdpM();
       uint256 debt = cdpManager.getCdpDebt(_cdpId);

       _requireSufficientEbtcTokenBalance(msg.sender, debt)

       uint256 collShares = cdpManager.getCdpCollShares(_cdpId);
       uint256 liquidatorRewardShares = cdpManager.getCdpLiquidatorRewardShares(_cdpId);

// Code block 3 (unknown):
function decreaseSystemDebt(uint256 _amount) external override {
   _requireCallerIsBOorCdpM();
   systemDebt -= _amount
   emit ActivePoolEBTCDebtUpdated(systemDebt);


BRONZEDISC Q

## QA
---

### Layout Order [1]

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

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

```solidity
1437:  modifier nonReentrant() {
```

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

```solidity
// events coming after functions
74:  event Transfer(address indexed from, address indexed to, uint256 amount);
75:  event Approval(
```

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

```solidity
// events coming after functions
76:  event Transfer(address indexed from, address indexed to, uint256 amount);
77:  event Approval(
```

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

```solidity
// modifier coming after functions
1434:  modifier nonReentrant() {
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/compound/Ownable.sol

```solidity
// modifier coming after functions
54:  modifier onlyOwner() {
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/compound/Comptroller.sol

```solidity
// state variables should come before events
114:  uint224 public constant compInitialIndex = 1e36;
117:  uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05
120:  uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9
123:  uint internal constant collateralFactorMaxMantissa = 0.98e18; // 0.98
778:  struct AccountLiquidityLocalVars {
```

https://github.com/code-423n4/2023-01-ondo/blob/main/contracts/lending/compound/uniswap/UniswapAnchoredView.sol

```solidity
// state variables should come before events
81:  bytes32 constant ethHash = keccak256(abi.encodePacked("ETH"));
```

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

// Code block 1 (solidity):
1437:  modifier nonReentrant() {

// Code block 2 (solidity):
// events coming after functions
74:  event Transfer(address indexed from, address indexed to, uint256 amount);
75:  event Approval(

// Code block 3 (solidity):
// events coming after functions
76:  event Transfer(address indexed from, address indexed to, uint256 amount);
77:  event Approval(

// Code block 4 (solidity):
// modifier coming after functions
1434:  modifier nonReentrant() {

// Code block 5 (solidity):
// modifier coming after functions
54:  modifier onlyOwner() {

// Code block 6 (solidity):
// state variables should come before events
114:  uint224 public constant compInitialIndex = 1e36;
117:  uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05
120:  uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9
123:  uint internal constant collateralFactorMaxMantissa = 0.98e18; // 0.98
778:  struct AccountLiquidityLocalVars {

// Code block 7 (solidity):
// state variables should come before events
81:  bytes32 constant ethHash = keccak256(abi.encodePacked("ETH"));

1437:  modifier nonReentrant() {

// events coming after functions
74:  event Transfer(address indexed from, address indexed to, uint256 amount);
75:  event Approval(

// events coming after functions
76:  event Transfer(address indexed from, address indexed to, uint256 amount);
77:  event Approval(

// modifier coming after functions
1434:  modifier nonReentrant() {

// modifier coming after functions
54:  modifier onlyOwner() {

// state variables should come before events
114:  uint224 public constant compInitialIndex = 1e36;
117:  uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05
120:  uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9
123:  uint internal constant collateralFactorMaxMantissa = 0.98e18; // 0.98
778:  struct AccountLiquidityLocalVars {

// state variables should come before events
81:  bytes32 constant ethHash = keccak256(abi.encodePacked("ETH"));

CTokenModified.sol, EIP20NonStandardInterface.sol, EIP20Interface.sol, CTokenCash.sol, Ownable.sol, Comptroller.sol, UniswapAnchoredView.sol

Ownable.sol, EIP20Interface.sol, CTokenCash.sol, CTokenModified.sol, Comptroller.sol, UniswapAnchoredView.sol, EIP20NonStandardInterface.sol


atharvasama G

# Gas Optimizations list

| Number | Details                                                                                | Instances |
| ------ | ------------------------------------------------------------------------------------------------------------ | --------- |
| 1      | ```x += y``` COSTS MORE GAS THAN ```x = x + y``` FOR STATE VARIABLES                                         | 15        |
| 2      | CAN DECLARE VARIABLE OUTSIDE LOOP TO SAVE GAS                                                                | 23        |
| 3      | ++i/i++ CAN BE UNCHECKED                                                                                | 13        |
| 4      | MULTIPLE ADDRESS/ID MAPPINGS CAN BE COMNBINED INTO A SINGLE MAPPING TO A STRUCT                              | 21        |
| 5      | USE A MORE RECENT VERSION OF SOLIDITY                                                                        | 12        |
| 6      | AVOID CONTRACT EXISTENCE CHECKS BY USING LOW LEVEL CALLS                                                     | 6         |
| 7      | USE ```require``` INSTEAD OF ```assert```                                                                    | 20        |
| 8      | PUBLIC FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL INSTEAD                              | 4         |
| 9      | USE ```bytes32``` INSTEAD OF ```string``` WHEREVER POSSIBLE                                                  | 11        |
| 10     | OPTIMIZE NAMES TO SAVE GAS                                                                                | 12        |
| 11     | INTERNAL FUNCTIONS ONLY CALLED ONCE CAN BE INLINED TO SAVE GAS                                               | 24        |
| 12     | STATE VARIABLES CAN BE PACKED INTO FEWER STORAGE SLOTS                                                       |  2         |
| 13     | ```require()``` OR ```revert()``` STATEMENTS THAT CHECK INPUT ARGUMENTS SHOULD BE AT T

168:         totalAllocBPS += _allocBPS;
194:         totalAllocBPS -= strategies[_strategy].allocBPS;
214:         totalAllocBPS -= strategies[_strategy].allocBPS;
395:                 strategies[stratAddr].allocated -= actualWithdrawn;
396:                 totalAllocated -= actualWithdrawn;
444:                 stratParams.allocBPS -= bpsChange;
445:                 totalAllocBPS -= bpsChange;
450:         stratParams.losses += loss;
451:         stratParams.allocated -= loss;
452:         totalAllocated -= loss;
505:             strategy.gains += vars.gain;
514:                 strategy.allocated -= vars.debtPayment;
515:                 totalAllocated -= vars.debtPayment;
520:             strategy.allocated += vars.credit;
521:             totalAllocated += vars.credit;

352:             address collateral = assets[i];
353:             uint amount = amounts[i];
398:             address collateral = assets[i];
399:             uint amount = amounts[i];
832:             address collateral = collaterals[i];
833:             uint currentS = epochToScaleToSum[currentEpochCached][currentScaleCached][collateral];
860:             address collateral = collaterals[i];
861:             uint price = priceFeed.fetchPrice(collateral);
862:             address lowestTrove = sortedTroves.getLast(collateral);
863:             uint256 collMCR = collateralConfig.getCollateralMCR(collateral);
864:             uint ICR = troveManager.getCurrentICR(lowestTrove, collateral, price);


0xhacksmithh Q

### [Low-01] No `minimum Amount(rsETH)` receive parameter absent in `depositAsset()`
Here we can see that User deposit asset via `depositAsset()` which take `asset address` and `asset depositAmount` as parameter
Then `rsethAmountMinted` calculated via `_mintRsETH()`
    - where it fetch corresponding asset price from oracle
    - then use formula (amount * assetPrice)/rsETHPrice to calculate `rsethAmountMinted`

Problem here is that if something go wrong with Oracle, then fetched price is mismatched with actual value
Then User may receive less amount than he intended, may be there a huge slippage.

```diff
    function depositAsset(
        address asset,
        uint256 depositAmount,
+       uint256 minAmount
    )
        external
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
    {
        // checks
        if (depositAmount == 0) {
            revert InvalidAmount();
        }
        if (depositAmount > getAssetCurrentLimit(asset)) {
            revert MaximumDepositLimitReached();
        }

        if (!IERC20(asset).transferFrom(msg.sender, address(this), depositAmount)) {
            revert TokenTransferFailed();
        }

        // interactions
-       uint256 rsethAmountMinted = _mintRsETH(asset, depositAmount);
+       uint256 rsethAmountMinted = _mintRsETH(asset, depositAmount, minAmount);

        emit AssetDeposit(asset, depositAmount, rsethAmountMinted);
    }
```

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

### Mitigation
To Overcome that `function` should also have a another parameter like `minAmountReceived` which inputed by user where User clearly says how much (slippage he can bear) `rsEth` he wanted to mint back.

And it should be check against `rsethAmountToMint` in `_mintRsETH()` before minting

```diff
-   function _mintRsETH(address _asset, uint256 _amount) private ret

### [Low-02] Attacker Can target a User for DOS in `depositAsset()`
According to `depositAsset()` the deposited Amount should not exceed asset Limit

// Code block 1 (diff):
function depositAsset(
        address asset,
        uint256 depositAmount,
+       uint256 minAmount
    )
        external
        whenNotPaused
        nonReentrant
        onlySupportedAsset(asset)
    {
        // checks
        if (depositAmount == 0) {
            revert InvalidAmount();
        }
        if (depositAmount > getAssetCurrentLimit(asset)) {
            revert MaximumDepositLimitReached();
        }

        if (!IERC20(asset).transferFrom(msg.sender, address(this), depositAmount)) {
            revert TokenTransferFailed();
        }

        // interactions
-       uint256 rsethAmountMinted = _mintRsETH(asset, depositAmount);
+       uint256 rsethAmountMinted = _mintRsETH(asset, depositAmount, minAmount);

        emit AssetDeposit(asset, depositAmount, rsethAmountMinted);
    }

// Code block 2 (unknown):
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L151-L157
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L119-L122


coryli G

# USE FUNCTIONS INSTEAD OF MODIFIERS
## Instances

- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L128

# FUNCTIONS GUARANTEED TO REVERT WHEN CALLED BY NORMAL USERS CAN BE MARKED PAYABLE
When using a function modifier such as `onlyOwner`, the function reverts when a normal user tries to pay the function. Marking the function `payable` lowers the gas cost for legitimate callers since the compiler will not include the extra opcodes that check for whether a payment was provided. It saves about `21` gas per call plus the extra deployment cost.
## Instances

- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L50
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/CollateralConfig.sol#L89
- https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/BorrowerOperations.sol#L110

# USE A MORE RECENT VERSION OF SOLIDITY

-- Use a Solidity version of at least `0.8.2` to get simple compiler automatic inlining.
-- Use a Solidity version of at least `0.8.3` to get better struct packing and cheaper multiple storage reads.
-- Use a Solidity version of at least `0.8.4` to get custom errors, which are cheaper at deployment than revert()/require() strings.
-- Use a Solidity version of at least `0.8.10` to have external calls skip contract existence checks if the external call has a return value.

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

for (uint256 i; i < _collateralsLength;) {
 // ...
 unchecked { ++i; }
}

CollateralConfig.sol#L128, CollateralConfig.sol#L50, CollateralConfig.sol#L89, BorrowerOperations.sol#L110, CollateralConfig.sol#L3, BorrowerOperations.sol#L3, TroveManager.sol#L3, ActivePool.sol#L3, StabilityPool.sol#L3

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


Bauchibred Q

# QA Report

## Table of Contents

| Issue ID                                                                                | Description                                                                               |
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [QA-01](#qa-01-a-user-cant-burn-their-tokens-as-they-see-fit)                                             | A user can't burn their tokens as they see fit                                            |
| [QA-02](#qa-02-an-in-game-user-should-in-all-instances-have-access-to-the-amount-of-their-current-points) | An in-game user should in all instances have access to the amount of their current points |
| [QA-03](#qa-03-insecure-spender-approval-mechanism)                                                       | Insecure spender approval mechanism                                                       |
| [QA-04](#qa-04-introduce-protection-against-json-attacks-on-users)                                        | Introduce protection against JSON attacks on users                                        |
| [QA-05](#qa-05-somewhat-flawed-ownership-transfer-in-voltagemanager-sol)                                  | Somewhat flawed ownership transfer in VoltageManager.sol                                  |
| [QA-06](#qa-06-somewhat-flawed-ownership-transfer-in-voltagemanager-sol)                                  | `Mergingpool.sol` currently does not use the VRF which limits the randomization           |

## QA-01 A user can't burn their tokens as they see fit

### Proof of Concept

Take a look at https://github.com/code-423n4/2024-02-ai-arena/blob/befe84a9ddccba0cd0251082046e57ecabffb0cf/src/GameItems.sol#L243-L246

```solidity
    function burn(address account, uint256 tokenId, uint256 amount) public {
        require(allowedBurningAddresses[msg

    function burn(address account, uint256 tokenId, uint256 amount) public {
        require(allowedBurningAddresses[msg.sender]);
        _burn(account, tokenId, amount);
    }

    function testBurnFromEligibleOwnerDoesNotWork() public {
        _fundUserWith4kNeuronByTreasury(_ownerAddress);
        _gameItemsContract.mint(0, 1);
        assertEq(_gameItemsContract.balanceOf(_ownerAddress, 0), 1);
        vm.expectRevert();
        _gameItemsContract.burn(_ownerAddress, 0, 1);
    }


RedTiger Q

# Low Risk and Non-Critical Issues
## L01 -  No part of the bounty is returned to the mining pool
According to Lybra [blog post](https://medium.com/@Lybra_Finance/lybra-finance-v1-v2-and-the-future-e458d541cc0b)    a percentage of the rewards bounty sold at a discount should be returned to the mining pool
" If the qualifier drops below the minimum 5% threshold, the user will become ineligible for subsequent esLBR emissions. Simultaneously, a bounty equal to the amount of emissions that the user has earned while ineligible will be placed. This bounty can be purchased by any user at a 50% discount in LBR.

3. The LBR received will then be distributed as follows: 10% will be burned, and the remaining 90% will be returned to the mining pool."

However, we can see in the code that 100% of the bounty is burned.
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/EUSDMiningIncentives.sol#L218
   ```solidity
 IesLBR(LBR).burn(msg.sender, biddingFee);
```

## L02-  Error in the comments of reStake() in ProtocolRewardsPool.sol
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L139C22-L140C8
 * @dev Convert unredeemed and converting ESLBR tokens back to LBR.
Should be
 * @dev Convert unredeemed and converting LBR tokens back to ESLBR

## L03 Wrong address in the comments for WBETH
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraWbETHVault.sol#L16
The address of the WBETH contract listed in the comments for WBETH is 0xae78736Cd615f374D3085123A210448E74Fc6393.
But this address is the goerli RETH address. This comment should be change to the WBETH goerli address. Or mainet WBETH address.

## L04 Allowance check could be improved
lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L131
The allowance check in the liq

// Code block 1 (solidity):
IesLBR(LBR).burn(msg.sender, biddingFee);


ReyAdmirado G

| | issue |
| ----------- | ----------- |
| 1 | [Structs can be packed into fewer storage slots](#1-structs-can-be-packed-into-fewer-storage-slots) |
| 2 | [state variables should be cached in stack variables rather than re-reading them from storage](#2-state-variables-should-be-cached-in-stack-variables-rather-than-re-reading-them-from-storage) |
| 3 | [Add `unchecked {}` for subtractions where the operands cannot underflow because of a previous `require()` or `if` statement](#3-add-unchecked--for-subtractions-where-the-operands-cannot-underflow-because-of-a-previous-require-or-if-statement) |
| 4 | [`<x> += <y>` costs more gas than `<x> = <x> + <y>` for state variables](#4-x--y-costs-more-gas-than-x--x--y-for-state-variables) |
| 5 | [can make the variable outside the loop to save gas](#5-can-make-the-variable-outside-the-loop-to-save-gas) |
| 6 | [usage of uint/int smaller than 32 bytes (256 bits) incurs overhead](#6-usage-of-uintint-smaller-than-32-bytes-256-bits-incurs-overhead) |
| 7 | [ Ternary over if ... else](#7-ternary-over-if--else) |
| 8 | [public functions not called by the contract should be declared external instead](#8-public-functions-not-called-by-the-contract-should-be-declared-external-instead) |
| 9 | [Use assembly to check for address(0)](#9-use-assembly-to-check-for-address0) |
| 10 | [Functions guaranteed to revert when called by normal users can be marked `payable`](#10-functions-guaranteed-to-revert-when-called-by-normal-users-can-be-marked-payable) |
| 11 | [Optimize names to save gas](#11-optimize-names-to-save-gas) |
| 12 | [Setting the constructor to payable](#12-setting-the-constructor-to-payable) |
| 13 | [part of the code can be pre calculated](#13-part-of-the-code-can-be-pre-calculated) |
| 14 | [Use selfbalance() instead of address(this).balance](#14-use-selfbalance-instead-of-addressthisbalance) |
| 15 | [Duplicated require()/revert() checks should be refactored to a modifier or function](#15-duplicated-requirerevert-checks-shou

        if (block.timestamp > deadline && deadline != 0) {
            revert DeadlinePassed();
        }


naman1778 Q

## [N-01] Lack of address(0) checks in the constructor

Zero-address check should be used in the constructors, to avoid the risk of setting smth as address(0) at deploying time.

There are 4 instances of this issue in 4 files:

    File: contracts/bridge/SourceBridge.sol

    40: constructor(
    41:   address _token,
    42:   address _axelarGateway,
    43:   address _gasService,
    44:   address owner
    45: ) {

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

    File: contracts/bridge/DestinationBridge.sol

    55: constructor(
    56:   address _token,
    57:   address _axelarGateway,
    58:   address _allowlist,
    59:   address _ondoApprover,
    60:   address _owner,
    61:   uint256 _mintLimit,
    62:   uint256 _mintDuration
    63: )

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

    File: contracts/usdy/rUSDYFactory.sol

    51: constructor(address _guardian) {
    52:   guardian = _guardian;
    53: }

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDYFactory.sol

    File: contracts/rwaOracles/RWADynamicOracle.sol

    30: constructor(
    31:   address admin,
    32:   address setter,
    33:   address pauser,
    34:   uint256 firstRangeStart,
    35:   uint256 firstRangeEnd,
    36:   uint256 dailyIR,
    37:   uint256 startPrice
    38: ) {

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

## [N-02] According to the syntax rules, use `=> mapping (` instead of `=> mapping(` using spaces as keyword

There are 2 instances of this issue in 2 files:

    File: contracts/bridge/DestinationBridge.sol

    45: mapping(bytes32 => mapping(uint256 => bool)) public isSpentNonce;

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

    File: contracts/usdy/rUSDY.sol

    79: mapping(address => mapping(address => uint256)) private allowances;

https://git

626: function _beforeTokenTransfer(
627:   address from,
628:   address to,
629:   uint256
630: ) internal view {

SourceBridge.sol, DestinationBridge.sol, rUSDYFactory.sol, RWADynamicOracle.sol

DestinationBridge.sol, rUSDYFactory.sol, RWADynamicOracle.sol, SourceBridge.sol


Raihan Q

# GAS OPTIMIZATION

# SUMMARY
|      |  issue  |  instance  |
|------|---------|------------|
|[G‑01]|Using storage instead of memory for structs/arrays saves gas|36|
|[G‑02]|Can Make The Variable Outside The Loop To Save Gas|4|
|[G‑03]|internal functions only called once can be inlined to save gas|18|
|[G‑04]|Optimize External Calls with Assembly for Memory Efficiency|112|
|[G‑05]|Use of emit inside a loop|1|
|[G‑06]|Do-While loops are cheaper than for loops|3|
|[G‑07]|Cache external calls outside of loop to avoid re-calling function on each iteration|1|
|[G‑08]|Use assembly to write address storage values|6|
|[G‑09]|Use nested if statements instead of &&|13|
|[G‑10]|Splitting  Require() Statements That Use  && Saves Gas|8|
|[G‑11]|Sort Solidity operations using short-circuit mode|2|
|[G‑12]|Expressions for constant values such as a call to¬†keccak256(), should use immutable rather than constant|4|
|[G‑13]|Use assembly to perform efficient back-to-back calls|11|
|[G‑14]|Avoid contract existence checks by using low level calls|18|
|[G‑15]|>= costs less gas than >|13|
|[G‑16]|Amounts should be checked for 0 before calling a transfer|6|
|[G‑17]|Avoid updating storage when the value hasn't changed|10|
|[G‑18]|abi.encode() is less efficient than abi.encodePacked()|2|
|[G‑19]|Do not calculate constants|1|
|[G‑20]|Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead|1|
|[G‑21]|Use hardcode address instead address(this)|15|
|[G‑22]|Structs can be packed to use fewer storage slots|3|
|[G‑23]|Use constants instead of type(uintx).max|17|
|[G‑24]|Duplicated require()/if() checks should be refactored to a modifier or function|1|
|[G‑25]|Not using the named return variables when a function returns, wastes deployment gas|6|
|[G‑26]|Unnecessary computation|3|
|[G‑27]|Multiple Address/id Mappings Can Be Combined Into A Single Mapping Of An Address/id To A Struct, Where Appropriate|2|
|[G‑28]|bytes constants are more eficient than string constans|11|
|[G‑29]|Use asse

File:  packages/contracts/contracts/BorrowerOperations.sol
427  MoveTokensParams memory _varMvTokens = MoveTokensParams(

760  function _processTokenMovesFromAdjustment(MoveTokensParams memory _varMvTokens) internal {

856  AdjustCdpLocals memory _vars

987  AdjustCdpLocals memory vars,

File:  packages/contracts/contracts/CdpManager.sol
136  SingleRedemptionInputs memory _redeemColFromCdp

150  CdpDebtAndCollShares memory _oldDebtAndColl = CdpDebtAndCollShares(

329   RedemptionTotals memory totals;

403   SingleRedemptionValues memory singleRedemption = _redeemCollateralFromCdp(


Diana G

----------

| S No. | Issue | Instances | Gas Savings (from provided tests) |
|-----|-----|-----|-----|
| [G-01] | x += y costs more gas than x = x + y for state variables (same applies for x -= y) | 22 | 2486
| [G-02] | Internal functions only called once can be inlined to save gas | 46 | 1840
| [G-03] | Using both named returns and a return statement isn't necessary | 18 | 54
| [G-04] | Increments can be unchecked | 15 | 600
| [G-05] | Require() or revert() strings longer than 32 bytes cost extra gas | 49 | 147
| [G-06] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 8 | -
| [G-07] | Splitting require() statements that use && saves gas | 4 | 12
| [G-08] | Use scientific notation rather than exponentiation | 1 | -
| [G-09] | Use a more recent version of solidity | 12 | -

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

## G-01 x += y costs more gas than x = x + y for state variables (same applies for x -= y)

Using the addition operator instead of plus-equals saves **[113 gas](https://gist.github.com/IllIllI000/cbbfb267425b898e5be734d4008d4fe8)**.

_There are 22 instances of this issue_

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol

```
File: Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol

133: toFree += profit;
141: roi -= int256(loss);
```

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol

```
File: Ethos-Vault/contracts/ReaperVaultV2.sol

168: totalAllocBPS += _allocBPS;
194: totalAllocBPS -= strategies[_strategy].allocBPS;
196: totalAllocBPS += _allocBPS;
214: totalAllocBPS -= strategies[_strategy].allocBPS;
390: value -= loss;
391: totalLoss += loss;
395: strategies[stratAddr].allocated -= actualWithdrawn;
396: totalAllocated -= actualWithdrawn;
444: stratParams.allocBPS -= bpsChange;
445: totalAllocBPS -= bpsChange;
450: stratParams.losses += loss;
451: stratParams.allocated -= loss;
452: totalAllocated -= l

File: Ethos-Vault/contracts/ReaperVaultV2.sol

168: totalAllocBPS += _allocBPS;
194: totalAllocBPS -= strategies[_strategy].allocBPS;
196: totalAllocBPS += _allocBPS;
214: totalAllocBPS -= strategies[_strategy].allocBPS;
390: value -= loss;
391: totalLoss += loss;
395: strategies[stratAddr].allocated -= actualWithdrawn;
396: totalAllocated -= actualWithdrawn;
444: stratParams.allocBPS -= bpsChange;
445: totalAllocBPS -= bpsChange;
450: stratParams.losses += loss;
451: stratParams.allocated -= loss;
452: totalAllocated -= loss;
505: strategy.gains += vars.gain;
514: strategy.allocated -= vars.debtPayment;
515: totalAllocated -= vars.debtPayment;
516: vars.debt -= vars.debtPayment;
520: strategy.allocated += vars.credit;
521: totalAllocated += vars.credit;

// Code block 1 (unknown):
File: Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol

133: toFree += profit;
141: roi -= int256(loss);


0xhacksmithh G

### [Gas-01] Multiply by 2 Should be performed using `bit shifting`
```solidity
require(assetAmount * 2 <= depositedAsset[onBehalfOf],
```
*Instances(2)*
```
File: LybraEUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L159
```
```
File: LybraPeUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L130
```

### [Gas-02] `_checkHealth()` could placed some step above, so that on function call fail on that step, will seve some gas of caller.

```solidity
    function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
        require(poolTotalPeUSDCirculation + _mintAmount <= configurator.mintVaultMaxSupply(address(this)), "ESL");
        _updateFee(_provider);

        try configurator.refreshMintReward(_provider) {} catch {}

        borrowed[_provider] += _mintAmount;
+       _checkHealth(_provider, _assetPrice);
        PeUSD.mint(_onBehalfOf, _mintAmount);
        poolTotalPeUSDCirculation += _mintAmount;
-       _checkHealth(_provider, _assetPrice);
        emit Mint(_provider, _onBehalfOf, _mintAmount, block.timestamp);
    }
```
*Instances(1)*
```
File: LybraPeUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L183
```

### [Gas-03] External Contract Calls Could Be Preformed With Low Level Calls, To Exclude Contract Exitance Check And Save Gas.

```solidity
rkPool.deposit{value: msg.value}();
```
*Instances(4)*
```
File: LybraRETHVault.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraRETHVault.sol#L30
```
```
File: LybraStETHVault.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L40
```
```
File: LybraWbETHVault.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraWbETHVault

// Code block 1 (solidity):
require(assetAmount * 2 <= depositedAsset[onBehalfOf],

// Code block 2 (unknown):
File: LybraEUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L159

// Code block 3 (unknown):
File: LybraPeUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L130

// Code block 4 (solidity):
function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
        require(poolTotalPeUSDCirculation + _mintAmount <= configurator.mintVaultMaxSupply(address(this)), "ESL");
        _updateFee(_provider);

        try configurator.refreshMintReward(_provider) {} catch {}

        borrowed[_provider] += _mintAmount;
+       _checkHealth(_provider, _assetPrice);
        PeUSD.mint(_onBehalfOf, _mintAmount);
        poolTotalPeUSDCirculation += _mintAmount;
-       _checkHealth(_provider, _assetPrice);
        emit Mint(_provider, _onBehalfOf, _mintAmount, block.timestamp);
    }

// Code block 5 (unknown):
File: LybraPeUSDVaultBase.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L183

// Code block 6 (solidity):
rkPool.deposit{value: msg.value}();

// Code block 7 (unknown):
File: LybraRETHVault.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraRETHVault.sol#L30

// Code block 8 (unknown):
File: LybraStETHVault.sol
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L40

require(assetAmount * 2 <= depositedAsset[onBehalfOf],

function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
        require(poolTotalPeUSDCirculation + _mintAmount <= configurator.mintVaultMaxSupply(address(this)), "ESL");
        _updateFee(_provider);

        try configurator.refreshMintReward(_provider) {} catch {}

        borrowed[_provider] += _mintAmount;
+       _checkHealth(_provider, _assetPrice);
        PeUSD.mint(_onBehalfOf, _mintAmount);
        poolTotalPeUSDCirculation += _mintAmount;
-       _checkHealth(_provider, _assetPrice);
        emit Mint(_provider, _onBehalfOf, _mintAmount, block.timestamp);
    }

rkPool.deposit{value: msg.value}();

LybraEUSDVaultBase.sol#L159, LybraPeUSDVaultBase.sol#L130, LybraPeUSDVaultBase.sol#L183, LybraRETHVault.sol#L30, LybraStETHVault.sol#L40

LybraEUSDVaultBase.sol, LybraStETHVault.sol, LybraPeUSDVaultBase.sol, LybraRETHVault.sol