Platform: Code4rena
Start Date: 03/03/2023
Pot Size: $90,500 USDC
Total HM: 4
Participants: 42
Period: 7 days
Judge: 0xean
Total Solo HM: 2
Id: 219
League: ETH
Rank: 17/42
Findings: 1
Award: $575.19
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: JCN
Also found by: 0x6980, 0xSmartContract, 0xnev, Madalad, Phantasmagoria, Rageur, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, atharvasama, descharre, hunter_w3b, matrix_0wl, saneryee, volodya, yongskiws
575.1851 USDC - $575.19
Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmaccess (100 gas) with a PUSH32 (3 gas).
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.
example
possible 97 gas save with risking 3 gas. if the check fails we save 97 for the second quitPeriod
read and if it passes we just lose 3 gas. cache it before this line
node
can be cached because it is used twice in L86 and L93. this will save 97 gas. cache it before L94
resolver
can be cached at the start of the function because it is used twice in L94 and L95. this will save 97 gas
possible gas save with only risking 3 gas. if the check passes (usually passes) we save gas for the second multisigSettings.minApprovals
read and if it passes we just lose 3 gas. cache it before the if statement
latestRelease
is being checked at least twice in this function(if the first if succeeds second use is inside it, and if it fails the second if statement check is the second use) so we should cache latestRelease
before L143 to reduce one storage read.
make the variable outside and only give the value to variable inside
++i/i++
should be unchecked{++i}/unchecked{i++}
when it is not possible for them to overflow, as is the case when used in for-loop and while-loopsThe unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP
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.
swap the position of the third require with the first one because its gonna be cheaper checks first
swap these two requires
Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls. following are the ones that makes sense to be inlined
When using elements that are smaller than 32 bytes, your contract’s 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. Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
buildsPerRelease
can be made into 256 bit. if the state var is not being put in the same slot with another small statevar to in one slot, it makes sense to make them into 256 bit variables so it doesnt waste gas when doing operations
same with latestRelease
Using ternary operator instead of the if else statement saves gas.
Contracts are allowed to override their parents’ functions and change the visibility from external to public and can save gas by doing so.
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
switch ens.resolver(_node)
with _ens.resolver(_node)
to save gas
before transfer we should check for amount being 0 so the function doesnt run when its not gonna do anything
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
many functions in MajorityVotingBase which makes it very wasteful if the names dont be optimzied.(its inherited many times too which makes it even more important)
In the EVM, there is no opcode for non-strict inequalities (>=, <=) and two operations are performed (> + = or < + =).
consider replacing >= with the strict counterpart > and add - 1
to the second side
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.
just pre calculate the answer of this part of code and use the answer inside the code instead to avoid unnecessary gas usage. use a comment to show what it was to the readers
RATIO_BASE - 1
can be calculated and defined as a constant to stop a operation each time the function runs.(2 instances in L528 and L530)
bytes4(keccak256("initialize(address,address,uint256)"))
can be pre calculated and saved as a constant or a immutable and used that way to save gas
same with bytes4(keccak256("setNewVariable(uint256)")
same here keccak256("EXECUTE_PERMISSION")
bytes4(keccak256("initialize(address,address,uint256)"))
keccak256("EXECUTE_PERMISSION")
Address x = address(0)
#0 - c4-judge
2023-03-12T18:26:30Z
0xean marked the issue as grade-a
#1 - c4-sponsor
2023-03-22T13:11:10Z
novaknole20 marked the issue as sponsor acknowledged