Ethos Reserve contest - Viktor_Cortess's results

A CDP-backed stablecoin platform designed to generate yield on underlying assets to establish a sustainable DeFi stable interest rate.

General Information

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

Ethos Reserve

Findings Distribution

Researcher Performance

Rank: 39/154

Findings: 2

Award: $370.05

QA:
grade-b
Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

[NC] Outdated compiler version

pragma solidity 0.6.11;

\Ethos-Core\contracts\CollateralConfig.sol \Ethos-Core\contracts\BorrowerOperations.sol \Ethos-Core\contracts\TroveManager.sol \Ethos-Core\contracts\ActivePool.sol \Ethos-Core\contracts\StabilityPool.sol \Ethos-Core\contracts\LQTY\CommunityIssuance.sol \Ethos-Core\contracts\LQTY\LQTYStaking.sol \Ethos-Core\contracts\LUSDToken.sol

It's a best practice to use the latest compiler version. The specified minimum compiler version is not up to date. Older compilers might be susceptible to some bugs. We recommend changing the solidity version pragma to the latest version to enforce the use of an up-to-date compiler.

A list of known compiler bugs and their severity can be found here: https://etherscan.io/solcbuginfo

[NC] INTERCHANGEABLE USAGE OF UINT AND UINT256

Consider using only one approach throughout the codebase, e.g. only uint or only uint256.

[NC] USE TIME UNITS DIRECTLY

\Ethos-Core\contracts\TroveManager.sol

48: uint constant public SECONDS_IN_ONE_MINUTE = 60;

The value of 1 minute can be used directly.

[NC] NAMED IMPORTS CAN BE USED

It’s possible to name the imports to improve code readability for all files in scope.

[NC] EMIT KEYWORD IS MISSING BEFORE THE EVENT NAME.

\Ethos-Core\contracts\ActivePool.sol

194: ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]); 201: ActivePoolLUSDDebtUpdated(_collateral, LUSDDebt[_collateral]);

[NC] TYPO IN REQUIRE STATEMENT IN ReaperVaultV2 CONTRACT.

\Ethos-Vault\contracts\ReaperVaultV2.sol

155: require(_feeBPS <= PERCENT_DIVISOR / 5, "Fee cannot be higher than 20 BPS"); 181: require(_feeBPS <= PERCENT_DIVISOR / 5, "Fee cannot be higher than 20 BPS"); PERCENT_DIVISOR = 10000 = 100%, so 10000/5 = 2000 = 20%. In require we see: 20BPS = 0.2 %

[NC] CONSTANT VALUES SUCH AS A CALL TO KECCAK256(), SHOULD USED TO IMMUTABLE RATHER THAN CONSTANT

There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts. While it doesn’t save any gas because the compiler knows that developers often make this mistake, it’s still best to use the right tool for the task at hand. Constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

\Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol

bytes32 public constant KEEPER = keccak256("KEEPER"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");

\Ethos-Vault\contracts\ReaperVaultV2.sol

bytes32 public constant DEPOSITOR = keccak256("DEPOSITOR"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");

[NC] TEST ENVIRONMENT COMMENTS AND CODES SHOULD NOT BE IN THE MAIN VERSION

\Ethos-Core\contracts\TroveManager.sol

14: // import "./Dependencies/Ownable.sol"; 18: contract TroveManager is LiquityBase, /*Ownable,*/ CheckContract, ITroveManager { // string constant public NAME = "TroveManager"; 431: // if (_ICR >= _MCR && ( _ICR >= _TCR || singleLiquidation.entireTroveDebt > _LUSDInStabPool)) 539: // if !vars.recoveryModeAtStart

[L-01] – PRAGMA FLOATING

\Ethos-Vault\contracts\ReaperVaultV2.sol \Ethos-Vault\contracts\ReaperVaultERC4626.sol \Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol \Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol

pragma solidity ^0.8.0;

Recommendation Locking the pragma helps to ensure that contracts do not accidentally get deployed using an outdated compiler version.

Note that pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case of contracts in a library or a package.

[L-02] INITIALIZE() FUNCTION CAN BE CALLED BY ANYBODY

initialize() function can be called by anybody when the contract is not initialized.

\Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol

62: function initialize( address _vault, address[] memory _strategists, address[] memory _multisigRoles, IAToken _gWant ) public initializer { gWant = _gWant; want = _gWant.UNDERLYING_ASSET_ADDRESS(); __ReaperBaseStrategy_init(_vault, want, _strategists, _multisigRoles); rewardClaimingTokens = [address(_gWant)]; }

[L-03] REQUIRE() SHOULD BE USED INSTEAD OF ASSERT()

Before solidity version 0.8.0, hitting an assert consumes the remainder of the transaction’s available gas rather than returning it, as require()/revert() do. assert() should be avoided even past solidity version 0.8.0 as its documentation states that “The assert function creates an error of type Panic(uint256). … Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract that you should fix”.

\Ethos-Core\contracts\BorrowerOperations.sol

128: assert(MIN_NET_DEBT > 0); 197: assert(vars.compositeDebt > 0); 301: assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _collTopUp > 0 && _LUSDChange == 0)); 331: assert(_collWithdrawal <= vars.coll);

\Ethos-Core\contracts\StabilityPool.sol

526: assert(_debtToOffset <= _totalLUSDDeposits); 551: assert(_LUSDLossPerUnitStaked <= DECIMAL_PRECISION); 591: assert(newP > 0);

\Ethos-Core\contracts\TroveManager.sol

417: assert(_LUSDInStabPool != 0); 1225: assert(totalStakesSnapshot[_collateral] > 0); 1280: assert(closedStatus != Status.nonExistent && closedStatus != Status.active); 1343: assert(troveStatus != Status.nonExistent && troveStatus != Status.active); 1349: assert(index <= idxLast); 1415: assert(newBaseRate > 0); 1490: assert(decayedBaseRate <= DECIMAL_PRECISION);

[L-04] USE OF ECRECOVER IS SUSCEPTIBLE TO SIGNATURE MALLEABILITY

\Ethos-Core\contracts\LUSDToken.sol

The ecrecover function is used to verify and execute Meta transactions. The built-in EVM precompile ecrecover is susceptible to signature malleability (because of non-unique s and v values) which could lead to replay attacks. While this is not exploitable for replay attacks in the current implementation because of the use of nonces, this may become a vulnerability if used elsewhere.

Recommend considering using OpenZeppelin’s ECDSA library (which prevents this malleability) instead of the built-in function.

[L-05] ADD A TIMELOCK updateStrategyFeeBPS() FUNCTION

/Ethos-Vault/contracts/ReaperVaultV2.sol#L178-L184

It is a good practice to give time for users to react and adjust to critical changes. A timelock provides more guarantees and reduces the level of trust required, thus decreasing the risk for users. It also indicates that the project is legitimate. However, it appears that no timelock capabilities have been utilized, which could have a significant impact on multiple users, prompting them to respond or receive advance notifications.

[L-06] PREVENT DIVISION BY 0

On several locations in the code precautions are not being taken for not dividing by 0, this will revert the code. These functions can be called with a 0 value in the input, this value is not checked for being bigger than 0, which that means in some scenarios can potentially trigger a division by zero.

\Ethos-Vault\contracts\ReaperVaultV2.sol

347: shares = (_amount * totalSupply()) / freeFunds; 472: uint256 bpsChange = Math.min((loss * totalAllocBPS) / totalAllocated, stratParams.allocBPS); 499: uint256 shares = supply == 0 ? performanceFee : (performanceFee * supply) / _freeFunds();

[L-07] MISSING EVENTS FOR INITIALIZE AND CRITICAL CHANGES

\Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol

62: function initialize( address _vault, address[] memory _strategists, address[] memory _multisigRoles, IAToken _gWant ) public initializer { gWant = _gWant; want = _gWant.UNDERLYING_ASSET_ADDRESS(); __ReaperBaseStrategy_init(_vault, want, _strategists, _multisigRoles); rewardClaimingTokens = [address(_gWant)]; }

E:\audits\2023-02-ethos\Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol

168: function setEmergencyExit() external { _atLeastRole(GUARDIAN); emergencyExit = true; IVault(vault).revokeStrategy(address(this)); }

#0 - c4-judge

2023-03-10T10:13:12Z

trust1995 marked the issue as grade-b

#1 - trust1995

2023-03-10T10:13:18Z

Barely B.

#2 - c4-sponsor

2023-03-28T21:33:59Z

0xBebis marked the issue as sponsor acknowledged

[G] <X> += <Y> COSTS MORE GAS THAN <X> = <X> + <Y> FOR STATE VARIABLES

\Ethos-Vault\contracts\ReaperVaultV2.sol

168: totalAllocBPS += _allocBPS; 214: totalAllocBPS -= strategies[_strategy].allocBPS; 396: totalAllocated -= actualWithdrawn; 445: totalAllocBPS -= bpsChange; 452: totalAllocated -= loss;

[G] CHANGES IN RETURNS COULD SAVE SOME GAS

if you remove the return statement at the end of the function and instead return the variable directly from the function definition, it will save some gas. This is because you would be avoiding the unnecessary assignment operation and just directly returning the value.

\Ethos-Vault\contracts\ReaperVaultV2.sol

462: function _chargeFees(address strategy, uint256 gain) internal returns (uint256 + performanceFee ) { uint256 performanceFee = (gain * strategies[strategy].feeBPS) / PERCENT_DIVISOR; if (performanceFee != 0) { uint256 supply = totalSupply(); uint256 shares = supply == 0 ? performanceFee : (performanceFee * supply) / _freeFunds(); _mint(treasury, shares); } - return performanceFee; }

Similar cases:

659: function _cascadingAccessRoles() internal view override returns (bytes32[] memory) {

\Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol

230: function balanceOfPool() public view returns (uint256) {

\Ethos-Core\contracts\TroveManager.sol

353: return singleLiquidation; //can be omitted 436: return singleLiquidation; //can be omitted 913: return newTotals; //can be omitted 1046: function getNominalICR(address _borrower, address _collateral) public view override returns (uint) { 1059: ) public view override returns (uint) { 1067: function _getCurrentTroveAmounts(address _borrower, address _collateral) internal view returns (uint, uint) { 1124: function getPendingCollateralReward(address _borrower, address _collateral) public view override returns (unit) 1139: function getPendingLUSDDebtReward(address _borrower, address _collateral) public view override returns (uint) { 1202: function _updateStakeAndTotalStakes(address _borrower, address _collateral) internal returns (uint) { 1214: function _computeNewStake(address _collateral, uint _coll) internal view returns (uint) { 1333: return index; //can be omitted 1403: ) external override returns (uint) { 1423: return newBaseRate; //can be omitted 1449: function _calcRedemptionFee(uint _redemptionRate, uint _collateralDrawn) internal pure returns (uint) { 1568: function increaseTroveColl(address _borrower, address _collateral, uint _collIncrease) external override returns (uint) { 1575: function decreaseTroveColl(address _borrower, address _collateral, uint _collDecrease) external override returns (uint) { 1583: function increaseTroveDebt(address _borrower, address _collateral, uint _debtIncrease) external override returns (uint) { 1590: function decreaseTroveDebt(address _borrower, address _collateral, uint _debtDecrease) external override returns (uint) {

\Ethos-Core\contracts\StabilityPool.sol

439: function _computeLQTYPerUnitStaked(uint _LQTYIssuance, uint _totalLUSDDeposits) internal returns (uint) { 543: return (collGainPerUnitStaked, LUSDLossPerUnitStaked); //can be omitted 683: function getDepositorLQTYGain(address _depositor) public view override returns (uint) { 693: function _getLQTYGainFromSnapshots(uint initialDeposit, Snapshots memory snapshots) internal view returns (uint) { 718: function getCompoundedLUSDDeposit(address _depositor) public view override returns (uint) { 735: returns (unit)

\Ethos-Core\contracts\LQTY\LQTYStaking.sol

217: function _getPendingLUSDGain(address _user) internal view returns (uint) {

\Ethos-Core\contracts\BorrowerOperations.sol

432: function _getUSDValue(uint _coll, uint _price, uint256 _collDecimals) internal pure returns (uint) { 466: returns (uint, unit) 673: returns (unit) 695: returns (unit) 713: returns (uint, unit) 736: returns (uint)

[G] Setting a variable to zero is cheaper than delete

\Ethos-Vault\contracts\ReaperVaultV2.sol

263: delete withdrawalQueue;

\Ethos-Vault\contracts\ReaperStrategyGranarySupplyOnly.sol

162: delete steps;

for arrays, setting the length to zero (withdrawalQueue.length = 0) is cheaper than using delete withdrawalQueue because the delete keyword also clears the storage slot for the array, which is an additional gas cost.

[G] REQUIRE() OR REVERT() STATEMENTS THAT CHECK INPUT ARGUMENTS SHOULD BE AT THE TOP OF THE FUNCTION

By doing these checks first, the function is able to revert before gas in a function that may ultimately revert in the unhappy case.

\Ethos-Vault\contracts\ReaperVaultV2.sol

627: function updateTreasury(address newTreasury) external { _atLeastRole(DEFAULT_ADMIN_ROLE); require(newTreasury != address(0), "Invalid address"); //move require up to the first line of the function 637: function inCaseTokensGetStuck(address _token) external { _atLeastRole(ADMIN); require(_token != address(token), "!token"); //move require up to the first line of the function

\Ethos-Core\contracts\ActivePool.sol

93: require(_treasuryAddress != address(0), "Treasury cannot be 0 address"); //move require up to the first line of the function 127: require(_bps <= 10_000, "Invalid BPS value"); "); //move require up to the first line of the function

\Ethos-Core\contracts\CollateralConfig.sol

66: require(_MCRs[i] >= MIN_ALLOWED_MCR, "MCR below allowed minimum"); 69: require(_CCRs[i] >= MIN_ALLOWED_CCR, "CCR below allowed minimum");

\Ethos-Core\contracts\CollateralConfig.sol

94: require(_MCR >= MIN_ALLOWED_MCR, "MCR below allowed minimum"); 97: require(_CCR >= MIN_ALLOWED_CCR, "CCR below allowed minimum");

[G] COMBINE 2 REQUIRE STATEMENTS WITH THE SAME ERROR TO SAVE GAS

When we use two separate require statements, the EVM will execute two JUMPI and REVERT instructions if either of the conditions is not met. However, when you use require(condition1 || condition2, "error"), the EVM will only execute one JUMPI instruction, and then another JUMPI instruction to skip the next REVERT instruction if either of the conditions is met. This can save some gas compared to using two separate require statements.

\Ethos-Core\contracts\CollateralConfig.sol

53: - require(_MCRs.length == _collaterals.length, "Array lengths must match"); - require(_CCRs.length == _collaterals.length, "Array lenghts must match"); + require(_MCRs.length == _collaterals.length || _CCRs.length == _collaterals.length , "Array lengths must match");

\Ethos-Core\contracts\BorrowerOperations.sol

There are different error messages in require statements in the following code, but in theory, they could be combined the same way as in the previous example.

529: require(IERC20(_collateral).balanceOf(_user) >= _collAmount, "BorrowerOperations: Insufficient user collateral balance"); require(IERC20(_collateral).allowance(_user, address(this)) >= _collAmount, "BorrowerOperations: Insufficient collateral allowance");

[G] USING FIXED BYTES IS CHEAPER THAN USING STRING

If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

\Ethos-Core\contracts\ActivePool.sol

30: string constant public NAME = "ActivePool"

\Ethos-Core\contracts\BorrowerOperations.sol

21: string constant public NAME = "BorrowerOperations";

\Ethos-Core\contracts\LUSDToken.sol

32: string constant internal _NAME = "LUSD Stablecoin"; string constant internal _SYMBOL = "LUSD"; string constant internal _VERSION = "1";

\Ethos-Core\contracts\StabilityPool.sol

150: string constant public NAME = "StabilityPool";

\Ethos-Core\contracts\LQTY\CommunityIssuance.sol

19: string constant public NAME = "CommunityIssuance";

[G] SIMPLE IF STATEMENT CAN BE WRITTEN AS A TERNARY OPERATOR TO SAVE SOME GAS

\Ethos-Vault\contracts\ReaperVaultV2.sol

338: if (totalSupply() == 0) { shares = _amount; } else { shares = (_amount * totalSupply()) / freeFunds; // use "freeFunds" instead of "pool" }

Can be swapped with: shares = (totalSupply() == 0) ? _amount : (_amount * totalSupply()) / freeFunds;

[G] USING STORAGE INSTEAD OF MEMORY FOR STRUCTS/ARRAYS SAVES GAS

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, which 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 rather than a cheap stack read. Instead of declaring the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time 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, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct.

\Ethos-Core\contracts\ActivePool.sol

`240: LocalVariables_rebalance memory vars;`

\Ethos-Core\contracts\BorrowerOperations.sol

`ContractsCache memory contractsCache = ContractsCache(troveManager, activePool, lusdToken);` `LocalVariables_openTrove memory vars;`

\Ethos-Core\contracts\TroveManager.sol

`518: LocalVariables_OuterLiquidationFunction memory vars;` `LiquidationTotals memory totals;` 684: LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; 722: LocalVariables_OuterLiquidationFunction memory vars; LiquidationTotals memory totals; 797: LocalVariables_LiquidationSequence memory vars; 801: LiquidationValues memory singleLiquidation;

\Ethos-Vault\contracts\ReaperVaultV2.sol

494: LocalVariables_report memory vars;

[G] ASSIGNING A VALUE MSG.SENDER TO A VARIABLE IN SOLIDITY REQUIRES AN ADDITIONAL OPERATION COMPARED TO SIMPLY ACCESSING THE MSG.SENDER DIRECTLY.

Also if you use msg.sender several times within a function, only one CALLER opcode will be executed to set the value of msg.sender in memory, and subsequent references to msg.sender will simply read from that memory location, without incurring additional gas costs for a CALLER opcode.

\Ethos-Vault\contracts\ReaperVaultV2.sol

Before: 5128 gas

225: function availableCapital() public view returns (int256) { address stratAddr = msg.sender; if (totalAllocBPS == 0 || emergencyShutdown) { return -int256(strategies[stratAddr].allocated); } uint256 stratMaxAllocation = (strategies[stratAddr].allocBPS * balance()) / PERCENT_DIVISOR; uint256 stratCurrentAllocation = strategies[stratAddr].allocated;

After: 5114 gas

function availableCapital() public view returns (int256) { if (totalAllocBPS == 0 || emergencyShutdown) { return -int256(strategies[msg.sender].allocated); } uint256 stratMaxAllocation = (strategies[msg.sender].allocBPS * balance()) / PERCENT_DIVISOR; uint256 stratCurrentAllocation = strategies[msg.sender].allocated;

[G] OPTIMIZE FUNCTIONS’ NAMES TO SAVE SOME GAS

\Ethos-Vault\contracts\ReaperVaultV2.sol

Contracts most called functions could simply 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 is 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.

For example, during tests, I noticed that the function availableCapital() is the most often called function in the Vault contract. So, we can modify its name to save some gas.

The current function’s Signature is 199cb7d8 and as we know a function with 00 at the beginning of the signature will be the cheapest one to call.

So availableCapital() can be renamed to availableCapital_3(), then its signature will be 0094169d.

Similar optimization could be done for the most often callable functions from other contracts.

[G] UPGRADE SOLIDITY’S OPTIMIZER

Make sure Solidity’s optimizer is enabled. It reduces gas costs. If you want to gas optimize for contract deployment (costs less to deploy a contract) then set the Solidity optimizer at a low number. If you want to optimize for run-time gas costs (when functions are called on a contract) then set the optimizer to a high number.

Set the optimization value higher than 800 in your hardhat.config.js file.

[G] USE CONSTANTS INSTEAD OF TYPE(UINTX).MAX

type(uint120).max or type(uint112).max, etc. it uses more gas in the distribution process and also for each transaction than constant usage.

\Ethos-Vault\contracts\ReaperVaultV2.sol

624: updateTvlCap(type(uint256).max);

\Ethos-Vault\contracts\ReaperVaultERC4626.sol

81: if (tvlCap == type(uint256).max) return type(uint256).max; 124: if (tvlCap == type(uint256).max) return type(uint256).max;

\Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol

79: IERC20Upgradeable(want).safeApprove(vault, type(uint256).max);

[G] USE ASSEMBLY TO WRITE ADDRESS STORAGE VALUES

\Ethos-Vault\contracts\ReaperVaultV2.sol

111: constructor( address _token, string memory _name, string memory _symbol, uint256 _tvlCap, address _treasury, address[] memory _strategists, address[] memory _multisigRoles ) ERC20(string(_name), string(_symbol)) { token = IERC20Metadata(_token); constructionTime = block.timestamp; lastReport = block.timestamp; tvlCap = _tvlCap; - //treasury = _treasury; + assembly { sstore(treasury.slot, _treasury) } 663: function updateTreasury(address newTreasury) external { _atLeastRole(DEFAULT_ADMIN_ROLE); require(newTreasury != address(0), "Invalid address"); - treasury = newTreasury; + assembly { sstore(treasury.slot, newTreasury) } }

\Ethos-Vault\contracts\abstract\ReaperBaseStrategyv4.sol

63: function __ReaperBaseStrategy_init( address _vault, address _want, address[] memory _strategists, address[] memory _multisigRoles ) internal onlyInitializing { __UUPSUpgradeable_init(); __AccessControlEnumerable_init(); -// vault = _vault; -// want = _want; + assembly { sstore(vault.slot, _vault) sstore(want.slot, _want) } IERC20Upgradeable(want).safeApprove(vault, type(uint256).max); uint256 numStrategists = _strategists.length; for (uint256 i = 0; i < numStrategists; i = i.uncheckedInc()) { _grantRole(STRATEGIST, _strategists[i]); } require(_multisigRoles.length == 3, "Invalid number of multisig roles"); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(DEFAULT_ADMIN_ROLE, _multisigRoles[0]); _grantRole(ADMIN, _multisigRoles[1]); _grantRole(GUARDIAN, _multisigRoles[2]); clearUpgradeCooldown(); }

There are several similar cases in Ethos-Core, but with the current solidity version 0.6.11; it doesn’t work there.

[G] SETTING THE CONSTRUCTOR TO PAYABLE

You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check of msg.value == 0 and saves 13 gas on deployment with no security risks.

E:\audits\2023-02-ethos\Ethos-Vault\contracts\ReaperVaultERC4626.sol

16: constructor( address _token, string memory _name, string memory _symbol, uint256 _tvlCap, address _treasury, address[] memory _strategists, address[] memory _multisigRoles ) + payable ReaperVaultV2(_token, _name, _symbol, _tvlCap, _treasury, _strategists, _multisigRoles) {}

\Ethos-Core\contracts\TroveManager.sol

225: constructor() public { // makeshift ownable implementation to circumvent contract size limit owner = msg.sender; }

\Ethos-Core\contracts\LQTY\CommunityIssuance.sol

57: constructor() public { distributionPeriod = 14 days; }

#0 - c4-judge

2023-03-09T18:43:47Z

trust1995 marked the issue as grade-a

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter