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
Rank: 78/101
Findings: 1
Award: $62.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Raihan
Also found by: 0x11singh99, 0xAnah, 0xSmartContract, 0xn006e7, Aymen0909, DavidGiladi, IllIllI, JCN, Jorgect, MohammedRizwan, Rageur, ReyAdmirado, Rickard, Rolezn, SAQ, SM3_SS, Sathish9098, TheSavageTeddy, hunter_w3b, kaveyjoe, lsaudit, matrix_0wl, naman1778, petrichor, shamsulhaq123, wahedtalash77
62.3314 USDC - $62.33
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
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
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
make the variable outside the loop and only give the value to variable inside
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
&&
saves gasInstead of using operator && on a single require. Using a two require can save more gas.
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
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 ||
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
Changing from public will remove the compiler-introduced checks for msg.value and decrease the contract’s method ID table size
Using ternary operator instead of the if else statement saves gas.
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
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
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
abi.encodeWithSelector is much cheaper than abi.encodeWithSignature because it doesn’t require to compute the selector from the string.
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
#0 - c4-judge
2023-07-11T13:54:50Z
trust1995 marked the issue as grade-b