Platform: Code4rena
Start Date: 16/02/2023
Pot Size: $144,750 USDC
Total HM: 17
Participants: 154
Period: 19 days
Judge: Trust
Total Solo HM: 5
Id: 216
League: ETH
Rank: 133/154
Findings: 1
Award: $42.07
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: c3phas
Also found by: 0x3b, 0x6980, 0x73696d616f, 0xSmartContract, 0xackermann, 0xhacksmithh, 0xsomeone, Bnke0x0, Bough, Budaghyan, Darshan, DeFiHackLabs, Deivitto, GalloDaSballo, JCN, LethL, Madalad, MiniGlome, Morraez, P-384, PaludoX0, Phantasmagoria, Praise, RHaO-sec, Rageur, RaymondFam, ReyAdmirado, Rickard, Rolezn, SaeedAlipoor01988, Saintcode_, Sathish9098, TheSavageTeddy, Tomio, Viktor_Cortess, abiih, arialblack14, atharvasama, banky, codeislight, cryptonue, ddimitrov22, dec3ntraliz3d, descharre, dharma09, emmac002, favelanky, hl_, hunter_w3b, kaden, kodyvim, matrix_0wl, oyc_109, pavankv, scokaf, seeu, yamapyblack
42.0697 USDC - $42.07
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 and get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>. 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. Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked (<str>,<str>)
LUSDToken.sol 3: pragma solidity 0.6.11;
ReaperVaultERC4626.sol 3: pragma solidity ^0.8.0;
Using the addition operator instead of plus-equals saves 113 gas.
For example:
File: 2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol 168: totalAllocBPS += _allocBPS; 196: totalAllocBPS += _allocBPS;
Saves deployment costs
File: 2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol 180: require(strategies[_strategy].activation != 0, "Invalid strategy address"); 193: require(strategies[_strategy].activation != 0, "Invalid strategy address");
Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.
File: 2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol 58: struct ChainlinkResponse { 59: uint80 roundId; 60: int256 answer; 61: uint256 timestamp; 62: bool success; 63: uint8 decimals; 64: }
File: 2023-02-ethos/Ethos-Core/contracts/PriceFeed.sol 66: struct TellorResponse { 67: bool ifRetrieve; 68: uint256 value; 69: uint256 timestamp; 70: bool success; 71: }
A division/multiplication by any number x being a power of 2 can be calculated by shifting to the right/left. While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas.
Furthermore, Solidity’s division operation also includes a division-by-0 prevention which is bypassed using shifting.
For example:
File: 2023-02-ethos/Ethos-Vault/contracts/ReaperVaultV2.sol L125: lockedProfitDegradation = (DEGRADATION_COEFFICIENT * 46) / 10**6; // 6 hours in blocks
Using a constant value does not require the Solidity compiler to calculate the maximum value at compile time, which can reduce the amount of bytecode generated and therefore, reduce the gas cost of deploying and executing the contract.
For example:
File: 2023-02-ethos/Ethos-Vault/contracts/ReaperVaultERC4626.sol 81: if (tvlCap == type(uint256).max) return type(uint256).max;
When using elements that are smaller than 32 bytes, the contracts gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
For example:
File: 2023-02-ethos/Ethos-Core/contracts/TroveManager.sol 1321: function _addTroveOwnerToArray(address _borrower, address _collateral) internal returns (uint128 index) {
It saves more gas. When having a require statement with 2 or more expressions needed, place the expression that cost less gas first.
For example:
File: 2023-02-ethos/Ethos-Vault/contracts/mixins/ReaperAccessControl.sol 39: require(specifiedRoleFound && senderHighestRoleFound, "Unauthorized access");
Using nested is cheaper than &&. It is also easier to read the code.
For example:
File: 2023-02-ethos/Ethos-Core/contracts/BorrowerOperations.sol 311: if (_isDebtIncrease && !isRecoveryMode) { 337: if (!_isDebtIncrease && _LUSDChange > 0) {
Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.
For example:
File: 2023-02-ethos/Ethos-Core/contracts/BorrowerOperations.sol 311: if (_isDebtIncrease && !isRecoveryMode) { 337: if (!_isDebtIncrease && _LUSDChange > 0) { 538: require(_collTopUp != 0 || _collWithdrawal != 0 || _LUSDChange != 0, "BorrowerOps: There must be either a collateral change or a debt change");
Contracts most called functions could save gas by function ordering via Method ID
.
Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID
) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
#0 - c4-judge
2023-03-09T13:59:21Z
trust1995 marked the issue as grade-b