Rageur G

## GAS-1: <X> * 2 costs more gas then <X> << 1

### Description

Using bitwize operator instead of '*' or '/' saves gas.

### Affected file

* LybraEUSDVaultBase.sol (Line: 159)
* LybraPeUSDVaultBase.sol (Line: 130)

## GAS-2: Add unchecked{} for subtractions where the operands cannot underflow because of a previous require() or if statement

### Description

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }
 if(a <= b); x = b - a => if(a <= b); unchecked { x = b - a }
 This will stop the check for overflow and underflow so it will save gas.

### Affected file

* LBR.sol (Line: 22)
* LybraPeUSDVaultBase.sol (Line: 200)
* LybraStETHVault.sol (Line: 75, 78)
* PeUSD.sol (Line: 14)
* PeUSDMainnetStableVision.sol (Line: 60)
* ProtocolRewardsPool.sol (Line: 201, 202, 203, 209, 211)

## GAS-3: Caching global variables is more expensive than using the actual variable

### Description

 It’s cheaper to use global variable as compared to caching.

### Affected file

* EUSDMiningIncentives.sol (Line: 240)
* LybraEUSDVaultBase.sol (Line: 297)
* LybraStETHVault.sol (Line: 43, 92)
* stakerewardV2pool.sol (Line: 143)

## GAS-4: Do not calculate constants

### Description

Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.

### Affected file

* GovernanceTimelock.sol (Line: 10, 11, 12, 13)
* LybraConfigurator.sol (Line: 76, 77, 78)

## GAS-5: Internal functions not called by the contract should be removed to save deployment gas

### Description

If the functions are required by an interface, the contract should inherit from that interface and use the override keyword.

### Affected file

* LBR.sol (Line: 49, 56, 61, 70)
* LybraEUSDVaultBase.sol (Line: 307)
* LybraGovernance.sol (Line: 59, 66, 76, 98)
* LybraPeUSDVaultBase.sol (Line: 244)
* PeUSD.sol (Line: 31, 38, 43, 52)
* PeUSDMainnetStableVision.sol (Line: 184, 19

assembly {
 if iszero(_addr) {
  mstore(0x00, "zero address")
  revert(0x00, 0x20)
 }
}


inzinko Q

## [L-01] Users can still vote after the ballot expiry is over

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/Proposals.sol#L259C2-L293C4

If the Ballot end time has been completed, users are not supposed to be able to cast vote again, but the cast vote does not check that the ballot end time has reached
```solidity
function castVote( uint256 ballotID, Vote vote ) external nonReentrant
		{
		Ballot memory ballot = ballots[ballotID];

		// Require that the ballot is actually live
		require( ballot.ballotIsLive, "The specified ballot is not open for voting" );

//@audit This check here that asks if the ballot is live or not does not prove that the ballot end time has been completed, because that value is only updated if the ballot has been finalized

...more code
		}
```

- Only on finalizeBallot does ballotIsLive gets updated
This means that before finalize ballot is called users can still cast their vote

Mitigation
Implement a check to see if The ballot duration has ended

## [L-02] Different amount of SALT rewards from Protocol Owned Liquidity to be burnt from the Docs and the Code Implementation

In the Docs of the project, it says that 45% of SALT rewards must be burnt, but this value is different in the code

the docs as shown below

> It generates yield for the DAO in the form of SALT.  By default 45% of this generated SALT is burned.

https://docs.salty.io/decentralization/protocol-owned-liquidity

But in the code of the `DAOConfig` The Value is 50%

as shown below

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/DAOConfig.sol#L26C2-L28C49

```solidity
// For rewards distributed to the DAO, the percentage of SALT that is burned with the remaining staying in the DAO for later use.
	// Range: 25% to 75% with an adjustment of 5%
    uint256 public percentPolRewardsBurned = 50;
```
## [L-03] Pools contract can contain Unwhitelisted Tokens

In the `Pools::dep

// Code block 1 (solidity):
function castVote( uint256 ballotID, Vote vote ) external nonReentrant
		{
		Ballot memory ballot = ballots[ballotID];

		// Require that the ballot is actually live
		require( ballot.ballotIsLive, "The specified ballot is not open for voting" );

//@audit This check here that asks if the ballot is live or not does not prove that the ballot end time has been completed, because that value is only updated if the ballot has been finalized

...more code
		}

// Code block 2 (solidity):
// For rewards distributed to the DAO, the percentage of SALT that is burned with the remaining staying in the DAO for later use.
	// Range: 25% to 75% with an adjustment of 5%
    uint256 public percentPolRewardsBurned = 50;

function castVote( uint256 ballotID, Vote vote ) external nonReentrant
		{
		Ballot memory ballot = ballots[ballotID];

		// Require that the ballot is actually live
		require( ballot.ballotIsLive, "The specified ballot is not open for voting" );

//@audit This check here that asks if the ballot is live or not does not prove that the ballot end time has been completed, because that value is only updated if the ballot has been finalized

...more code
		}

// For rewards distributed to the DAO, the percentage of SALT that is burned with the remaining staying in the DAO for later use.
	// Range: 25% to 75% with an adjustment of 5%
    uint256 public percentPolRewardsBurned = 50;


Rolezn G

## GAS Summary<a name="GAS Summary">

### Gas Optimizations
| |Issue|Contexts|Estimated Gas Saved|
|-|:-|:-|:-:|
| [GAS‑1](#GAS‑1) | `address(this)` can be stored in `state variable` that will ultimately cost less, than every time calculating this, specifically when it used multiple times in a `contract` | 5 | 250 |
| [GAS‑2](#GAS‑2) | Avoid repeating computations | 4 | - |
| [GAS‑3](#GAS‑3) | Consider using solady's 'FixedPointMathLib' | 63 | - |
| [GAS‑4](#GAS‑4) | Counting down in `for` statements is more gas efficient | 12 | 3084 |
| [GAS‑5](#GAS‑5) | Duplicated `require()`/`revert()` Checks Should Be Refactored To A Modifier Or Function | 13 | 364 |
| [GAS‑6](#GAS‑6) | Using `delete` statement can save gas | 6 | 48 |
| [GAS‑7](#GAS‑7) | Increments can be `unchecked` to save gas | 27 | 810 |
| [GAS‑8](#GAS‑8) | The result of a function call should be cached rather than re-calling the function | 26 | 1300 |
| [GAS‑9](#GAS‑9) | Usage of `uints`/`ints` smaller than 32 bytes (256 bits) incurs overhead | 2 | 12 |
| [GAS‑10](#GAS‑10) | Use assembly to validate `msg.sender` | 2 | 24 |
| [GAS‑11](#GAS‑11) | Using XOR (^) and AND (&) bitwise equivalents for gas optimizations | 117 | 1521 |
| [GAS‑12](#GAS‑12) | Using assembly to check for zero can save gas | 39 | 234 |
| [GAS‑13](#GAS‑13) | Using `constant`s instead of `enum` can save gas | 1 | - |
| [GAS‑14](#GAS‑14) | The more likely conditional `require` can be checked first | 3 | - |
| [GAS‑15](#GAS‑15) | Move `proposals.ballotForID(ballotID)` call after the conditional checks to save gas | 1 | - |
| [GAS‑16](#GAS‑16) | Move `uint256 minUnstakePercent = stakingConfig.minUnstakePercent();` call after the conditional checks to save gas | 1 | - |

Total: 379 contexts over 16 issues

## Gas Optimizati

File: DAO.sol

170: if ( exchangeConfig.salt().balanceOf(address(this)) >= ballot.number1 )

File: DAO.sol

246: uint256 saltBalance = exchangeConfig.salt().balanceOf( address(this) );