Maia DAO Ecosystem - ReyAdmirado's results

Efficient liquidity renting and management across chains with Curvenized Uniswap V3.

General Information

Platform: Code4rena

Start Date: 30/05/2023

Pot Size: $300,500 USDC

Total HM: 79

Participants: 101

Period: about 1 month

Judge: Trust

Total Solo HM: 36

Id: 242

League: ETH

Maia DAO Ecosystem

Findings Distribution

Researcher Performance

Rank: 78/101

Findings: 1

Award: $62.33

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

62.3314 USDC - $62.33

Labels

bug
G (Gas Optimization)
grade-b
G-09

External Links

1. Struct can be packed into fewer storage slots

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

proposer#L109 can be put together with canceled and executed#L133 to reduce slots used for this struct by 1

2. state variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper.

liquidity#L39 can be put near tickLower#L48 to reduce slots used by 1. also all functions that access liquidity as well as tickLower or tickUpper or initialized will have reduced gas usage.

gaugeCycle#L36 can be put together with nextCycle#L45 to reduce slots used by 1. also there is access to gaugeCycle and state vars in the same slot twice so this will reduce gas used as well

_setup can be put near localBranchPortAddress to reduce slots needed by 1

change the size of _unlocked from uint256 to something small like uint8 (its only 1 or 2 so there will be no problem changing this) so it can be put together with a address state var like localBridgeAgentAddress because they are accessed in a function together 5 times so we can save 500 gas and a 20000 for Gsset

same instances as above here we can put it with other address type state vars if we make it smaller

3. expressions for constant values such as a call to keccak256(), should use immutable rather than constant

4. state variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

_assets[rewardsContract] can be cached before emit and use the cached version in emit and #L62. reduces one complex SLOAD

partnerIds[partnerManager] is being read twice in #L81 and #L82 so we can cache it bofore the if statement to reduce one complex SLOAD

same logic as above for vaultIds[vault]

gaugeFactoryIds[gaugeFactory] is being read twice in #L122 and #L125 cache it so we reduce one complex SLOAD

localPortAddress is being read twice in #L83 and #L93 cache it before to save 100 gas

rootPortAddress is being read twice in #L49 and #L54 we can cache it before to save 100 gas

rootPortAddress same as above but 3 times #L62 and #L66 and #L70 we can cache it before to save 200 gas

localChainId is being read twice in #L62 and #L66 we can cache it before to save 100 gas

isBridgeAgent[_bridgeAgent] is being read twice in #L367 and #L372 cache it before if statement and use that instead to reduce one complex SLOAD

coreRootRouterAddress can be cached before #L435 to reduce gas usage by 100

bridgeAgentAddress same here in #L120 #L123 and in #L148 and #L155 .saves 100 in each functions

localPortAddress is being read in both #L133 and #143 cache it before to save 100

same here

localPortAddress will be read twice one in #L164 and the other read will be inside if statement or else statement, in both case we will save 100 if we cache it before

same here

same here but second read is inside of the 2 elses or just inside the if

flywheelRewards in #L126 and #L128can be cached to save 100 gas

getDeposit[_depositNonce].tokens[0] is being read twice inside if statement so we can cache it at the start of if state ment to reduce one complex SLOAD. used in #L339 and #342

same as above in else #L354 and #357

uint8(getDeposit[_depositNonce].hTokens.length will be read twice if only uint8(getDeposit[_depositNonce].hTokens.length) == 1 is false. so we can cache it before the #L332 so we only risk losing 3 gas if the first if is true but save a lot of gas if be false. used in #L332 and #L365

we can cache userFeeInfo.gasToBridgeOut inside the if statement of #L750 because if that if statement be true and inside it runs userFeeInfo.gasToBridgeOut is gonna be read twice in #L751 and #754

if we cache liquidity before #L261 and we dont use -= in #L277 and instead use the cached version for the SLOAD of it we are gonna use less gas

cache pendingAdmin at start of the function and use the cached version for #L508 and #L513 and #L516 and in lines #L521 instead of admin use that cached version of pendingAdmin because they are the same and in #L522 just return address(0) instead of reading pendingAdmin. overall saves 400 gas

usually in real use of the project these if statements like #L75 and #L85 and #L95 conditions happens and the inside of them will happen. with knowing this partnerVault should be cached before the if steatements. risking losing 3 gas for each function but saving 100 if conition be true

gaugeIds[gauge] is being read twice cache it before to reduce one complex SLOAD. used in #L131 and #L132

5. can make the variable outside the loop to save gas

make the variable outside the loop and only give the value to variable inside

6. using > 0 costs more gas than != 0 when used on a uint in a require() statement

This change saves 6 gas per instance. The optimization works until solidity version 0.8.13 where there is a regression in gas costs.

13 instances in

13 instances in

12 instances in

7. splitting require() statements that use && saves gas

Instead of using operator && on a single require. Using a two require can save more gas.

8. require() or revert() statements that check input arguments should be at the top of the function

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.

require in #L43 is cheaper than the one on #L42 so swap the places

first require is the most expensive so do it last

the 2 first requires are most expensive so do them lsat

the 2 first requires are most expensive so do them lsat

swap the require places to do the cheaper one first

bring #L63 and #L64 requires to the end of the requires

bring #L123 and #L120 requires to the startof the requires because they are the cheapest

swap these

swap these

swap these

swap these

9. Avoid a SLOAD optimistically

There is a chance that the first part will be true so the second part doesn’t need to be checked, so it is better to use the part that we have first instead of the part that needs to be called.

depositOwner == address(0) is way cheaper than getSettlement[_depositNonce].status != SettlementStatus.Failed because getSettlement[_depositNonce].status should be read from storage. so we can swap the sides of || to optimistically save lots of gas

destinationId == id is way cheaper than destinationIds[address(destination)] != 0 because destinationIds[address(destination)] should be read from storage. so we can swap the sides of || to optimistically save lots of gas

same logics here for the sides of ||

10. abi.encode() is less efficient than abi.encodepacked()

11. 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

12. avoid an unnecessary sstore by not writing a default value for bools

13. public library function should be made private/internal

Changing from public will remove the compiler-introduced checks for msg.value and decrease the contract’s method ID table size

14. Ternary over if ... else

Using ternary operator instead of the if else statement saves gas.

15. should use arguments instead of state variable

This will save 100 gas because it gets rid of a storage read and instead uses a argument that we already have which gives the same result

use _localBridgeAgentAddress instead of localBridgeAgentAddress to save 100 gas

16. use existing cached version of state var

saves gas because of less SLOADs

use the _tokenId stack var instead of reading tokenId from storage. saves 100 gas

same here with more instances

use implementation_ in emit instead of reading it from storage. saves 100

instead of votingDelay use newVotingDelay to save 100 gas

same with votingPeriod and newVotingPeriod

same with proposalThreshold and newProposalThreshold

same with whitelistGuardian and account

17. Optimize names to save gas

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 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.

See more here. you can use this tool to get the optimized version for function and properties signitures

18. Use abi.encodeWithSelector instead of abi.encodeWithSignature

abi.encodeWithSelector is much cheaper than abi.encodeWithSignature because it doesn’t require to compute the selector from the string.

19. part of the code can be pre calculated

these parts of the code can be pre calculated and given to the contract as constants this will stop the use of extra operations even if its for code readability consider putting comments instead.

these can be pre calculated to reduce one operation usage

PARAMS_START_SIGNED + PARAMS_TKN_START can be pre calculated to reduce one operation usage

PARAMS_START + PARAMS_END_OFFSET

PARAMS_START + PARAMS_END_SIGNED_OFFSET

PARAMS_START + PARAMS_START

PARAMS_TKN_START + PARAMS_START

PARAMS_START_SIGNED + PARAMS_TKN_START

PARAMS_START_SIGNED + PARAMS_START

INCENTIVES_DURATION + INCENTIVES_OFFSET

these reduce 2 operations PARAMS_START_SIGNED + PARAMS_TKN_START + PARAMS_START

20. require() Should Be Used Instead Of assert()

#0 - c4-judge

2023-07-11T13:54:50Z

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