Platform: Code4rena
Start Date: 23/06/2023
Pot Size: $60,500 USDC
Total HM: 31
Participants: 132
Period: 10 days
Judge: 0xean
Total Solo HM: 10
Id: 254
League: ETH
Rank: 77/132
Findings: 1
Award: $80.43
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: JCN
Also found by: 0xAnah, DavidGiladi, MohammedRizwan, Rageur, Raihan, ReyAdmirado, Rolezn, SAAJ, SAQ, SM3_SS, Sathish9098, ayo_dev, dharma09, fatherOfBlocks, hunter_w3b, mgf15, mrudenko, naman1778, shamsulhaq123, souilos, turvy_fuzz
80.434 USDC - $80.43
Detect optimal variable order in contract storage layouts to decrease the number of slots used.
<details> <summary> There are 1 instances of this issue: </summary>Line: 17 abstract contract LybraEUSDVaultBase
original variable order (count: 13 slots)
optimized variable order (count: 12 slots)
The expressions 'x * 2' and 'x / 2' can be optimized for gas efficiency by utilizing bitwise operations. In Solidity, you can achieve the same results by using bitwise left shift (x << 1) for multiplication and bitwise right shift (x >> 1) for division.
Using bitwise shift operations (SHL and SHR) instead of multiplication (MUL) and division (DIV) opcodes can lead to significant gas savings. The MUL and DIV opcodes cost 5 gas, while the SHL and SHR opcodes incur a lower cost of only 3 gas.
By leveraging these more efficient bitwise operations, you can reduce the gas consumption of your smart contracts and enhance their overall performance.
<details> <summary> There are 2 instances of this issue: </summary>Line: 159 require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
instead 2
use bit shifting 1
Line: 130 require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
instead 2
use bit shifting 1
This detector identifies instances where there are consecutive external calls to the same contract, where the subsequent calls could use a low-level call to save gas.
Note: This detector only triggers if the function call does not return any value.
Prior to 0.8.10, the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent Solidity versions the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low-level calls never check for contract existence.
<details> <summary> There are 1 instances of this issue: </summary>Line: 205 try configurator.distributeRewards() {} catch {}
This call could be replaced with a low-level call because the contract Iconfigurator
has already been checked in <br>Line: 193 try configurator.refreshMintReward(_onBehalfOf) {} catch {}
<br>
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L205
Modulus operations should be unchecked to save gas since they cannot overflow or underflow. Execution of modulus operations outside unchecked
blocks adds nothing but overhead. Saves about 30 gas.
Line: 102 (block.timestamp - lidoRebaseTime) % 1 days
should be unchecked
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L102
</details>This detector identifies instances of computations being performed inside loops that could be performed outside the loop to save gas.
Unnecessary computation inside loops:
Any computation performed inside a loop that doesn't change with each iteration can be moved outside the loop to save gas.This detector identifies instances where the following unnecessary computations are performed inside loops:
1 Local variables are read inside loops but never modified within the same loop, indicating they could be cached outside the loop.
2 Computations involving constant expressions (literals) which result in the same output in every loop iteration.
By addressing these issues, gas usage can be optimized.
<details> <summary> There are 1 instances of this issue: </summary>Line: 141 pool.getVaultType() == 1
The following computations can be cached outside of loops for gas optimization pool.getVaultType() == 1
<br>.
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L141
When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct / array to be read from storage. This incurs a Gcoldsload (2100 gas) for each field of the struct / array. If the fields are read from the new memory variable, they incur an additional MLOAD, which is more expensive than a simple stack read.
Instead of declaring the variable with the memory keyword, a more cost-effective approach is to declare the variable with the storage keyword. In this case, you can cache any fields that need to be re-read in stack variables. This approach significantly reduces gas costs, as you only incur the Gcoldsload for the fields actually read.
The only scenario where it makes sense to read the whole struct / array into a memory variable is if the full struct / array is being returned by the function or passed to a function that requires memory. Additionally, if the array / struct is being read from another memory array / struct, using a memory variable may be appropriate.
By carefully considering the storage and memory usage in your contract, you can optimize gas costs and improve the efficiency of your smart contract operations.
<details> <summary> There are 1 instances of this issue: </summary>Line: 39 esLBRLockSetting memory _setting = esLBRLockSettings[id]
should be declared as storage
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/esLBRBoost.sol#L39
</details>State variables that are not updated following deployment should be declared constant to save gas.
<details> <summary> There are 2 instances of this issue: </summary>Line: 15 uint256 maxSupply = 100_000_000 * 1e18
should be constant
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/LBR.sol#L15
Line: 20 uint256 maxSupply = 100_000_000 * 1e18
should be constant
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/esLBR.sol#L20
</details>I reported issues that were overlooked by the winning bot. I reported only on instances that were missed
Setting the internal/private function that called once to inline would save gas
<details> <summary> There are 4 instances of this issue: </summary>Line: 244 function _min(uint256 x, uint256 y) private pure returns (uint256)
should be inline to save gas:
Line: 147 function _min(uint256 x, uint256 y) private pure returns (uint256)
should be inline to save gas:
Line: 307 nction _etherPrice() internal returns (uint256)
should be inline to save gas:
Line: 177 function _spendAllowance(address owner, address spender, uint256 amount) internal virtual
should be inline to save gas:
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/EUSD.sol#L177-L185
</details>I reported issues that were overlooked by the winning bot. I reported only on instances that were missed
This detector flags contracts that inefficiently use uint
or int
of sizes smaller than 32 bytes. The EVM operates on 32 bytes at a time, thus using elements smaller than this may cause your contract's gas usage to be higher. Refer to the Solidity documentation for more details: https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html.
Line: 30 uint8 immutable vaultType = 0
vaultType
use 256 bites instead of 8
Line: 18 uint8 immutable vaultType = 1
vaultType
use 256 bites instead of 8
When passing function parameters, using the calldata
area instead of memory
can improve gas efficiency. Calldata is a read-only area where function arguments and external function calls' parameters are stored.
By using calldata
for function parameters, you avoid unnecessary gas costs associated with copying data from calldata
to memory. This is particularly beneficial when the parameter is read-only and doesn't require modification within the contract.
Using calldata
for function parameters can help optimize gas usage, especially when making external function calls or when the parameter values are provided externally and don't need to be stored persistently within the contract.
Line: 15 address[] memory proposers
should be declared as calldata
instead
Line: 15 address[] memory executors
should be declared as calldata
instead
Line: 93 address[] memory _pools
should be declared as calldata
instead
Line: 33 esLBRLockSetting memory setting
should be declared as calldata
instead
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/esLBRBoost.sol#L33
</details>I reported issues that were overlooked by the winning bot. I reported only on instances that were missed
Assignment operations that involve calculations, like add-assigment (+=
), for state variables, should be replaced with the appropriate calculation operation, like add (+
), followed by an assignment operation (=
), to save gas. Avoids a Gwarmaccess
(100 gas).
Line: 43 totalDepositedAsset += msg.value
should use the appropriate calculation operation (+
), followed by an assignment operation (=
)
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L43
</details>I have identified issues that were overlooked by the winning bot, specifically focusing on instances where revert was missed.
This detector flags functions that use revert()/require() strings, which are less gas efficient than custom errors. Custom errors, available from Solidity version 0.8.4, save approximately 50 gas each time they're used by avoiding the need to allocate and store the revert string.
<details> <summary> There are 4 instances of this issue: </summary>Line: 254 revert('EL')
use custom error instead
Line: 292 revert("collateralRatio is Below safeCollateralRatio");
use custom error instead
Line: 227 revert("collateralRatio is Below safeCollateralRatio");
use custom error instead
Line: 27 revert("not authorized")
use custom error instead
https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/esLBR.sol#L27
</details>#0 - c4-pre-sort
2023-07-27T21:32:55Z
JeffCX marked the issue as high quality report
#1 - c4-judge
2023-07-27T23:42:29Z
0xean marked the issue as grade-a
#2 - c4-sponsor
2023-07-29T11:28:56Z
LybraFinance marked the issue as sponsor acknowledged