Ethos Reserve contest - P-384'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: 142/154

Findings: 1

Award: $42.07

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Expressions for constant values such as a call to keccak256(), should use immutable rather than constant:

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.

https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L49 bytes32 public constant KEEPER = keccak256("KEEPER"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");

https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Vault/contracts/ReaperVaultV2.sol#L73 bytes32 public constant DEPOSITOR = keccak256("DEPOSITOR"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");

Use a more recent version of solidity:

Use a Solidity version of at least 0.8.12 to get string.concat() to be used instead of abi.encodePacked(<str>,<str>). https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/HintHelpers.sol#L178

<x> += <y> costs more gas than <x> = <x> + <y> for state variables:

E.g.: totalAllocBPS += _allocBPS; https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L168 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L194 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L196 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L214

stratParams.allocBPS -= bpsChange; https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L444

stratParams.losses += loss; stratParams.allocated -= loss; https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L450

totalAllocated -= loss; https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L452

require() strings longer than 32 bytes cost extra gas:

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

"Ammount" appears to be misspelt which if corrected to "Amount" will bring the require string back down to 32 bytes and save gas: require(_amount <= balanceOf(), "Ammount must be less than balance"); https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L98

Require string "Upgrade cooldown not initiated or still ongoing" is 47 bytes https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L191

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L150 "Cannot add strategy during emergency shutdown" is 45 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L321 "Cannot deposit during emergency shutdown" is 40 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L436 "Strategy loss cannot be greater than allocation" is 47 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L619 "Degradation cannot be more than 100%" is 36 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L136 "LUSD: Caller is not guardian or governance" is 42 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L350 "LUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address" is 84 bytes https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L356 "LUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps" is 87 bytes https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L371 "LUSD: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool" is 77 bytes

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/StabilityPool.sol#L865 "StabilityPool: Cannot withdraw while there are troves with ICR < MCR" is 68 bytes

Use assembly to check for address(0):

Saves 6 gas per instance: https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol#L167 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L151 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L629 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L312 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L321 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L329 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L337 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L348

Internal functions only called once can be inlined to save gas:

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Vault/contracts/ReaperStrategyGranarySupplyOnly.sol#L86 function _liquidatePosition(uint256 _amountNeeded) internal override returns (uint256 liquidatedAmount, uint256 loss) {

++i costs less gas than i++, especially when it’s used in for-loops:

Saves 5 gas per loop

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L206 line 206: for (uint i = 0; i < assets.length; i++) {

line 228: for (uint i = 0; i < collaterals.length; i++) {

line 240: for (uint i = 0; i < numCollaterals; i++) {

https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/ActivePool.sol line 108: for(uint256 i = 0; i < numCollaterals; i++) {

https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/TroveManager.sol line 608: for (vars.i = 0; vars.i < _n && vars.user != firstUser; vars.i++) {

line 882: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {

line 808: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {

In addition to the above unchecked block could also be used to increment i to further save gas e.g.: unchecked { ++i; }

Or the following could also be used, as used in /Ethos-Vault/contracts/ReaperVaultV2.sol#L128: i = i.uncheckedInc() https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L128

<array>.length should not be looked up in every loop of a for-loop:

The overheads outlined below are PER LOOP, excluding the first loop

storage arrays incur a Gwarmaccess (100 gas) memory arrays use MLOAD (3 gas) calldata arrays use CALLDATALOAD (3 gas) Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol line 206: for (uint i = 0; i < assets.length; i++) { line 228: for (uint i = 0; i < collaterals.length; i++) {

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/TroveManager.sol line 882: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) { line 808: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {

Using private rather than public for constants, saves gas:

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it’s used, and not adding another entry to the method ID table.

/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol uint256 public constant PERCENT_DIVISOR = 10_000; uint256 public constant UPGRADE_TIMELOCK = 48 hours; // minimum 48 hours for RF uint256 public constant FUTURE_NEXT_PROPOSAL_TIME = 365 days * 100; https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L23

Use bytes32 instead of string:

Use string for arbitrary-length string (UTF-8) data that's longer than 32 bytes, else use bytes. As a rule of thumb, use bytes for arbitrary-length raw byte data and string for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/LQTYStaking.sol#L23 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LUSDToken.sol#L32-L34 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Core/contracts/LQTY/CommunityIssuance.sol#L19 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/Proxy/StabilityPoolScript.sol#L10 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/Proxy/TroveManagerScript.sol#L10 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/Proxy/TokenScript.sol#L10 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/Proxy/BorrowerWrappersScript.sol#L25 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/CollSurplusPool.sol#L17 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/DefaultPool.sol#L25 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/PriceFeed.sol#L27 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/HintHelpers.sol#L13 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/ActivePool.sol#L30 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/SortedTroves.sol#L49 https://github.com/code-423n4/2023-02-ethos/blob/d46ede81e35c1ed86242c9ba3c5c57d2ca2b57d8/Ethos-Core/contracts/BorrowerOperations.sol#L21

#0 - c4-judge

2023-03-09T18:29:08Z

trust1995 marked the issue as grade-b

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