Platform: Code4rena
Start Date: 25/01/2023
Pot Size: $90,500 USDC
Total HM: 3
Participants: 26
Period: 9 days
Judge: GalloDaSballo
Id: 209
League: ETH
Rank: 16/26
Findings: 1
Award: $131.98
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: descharre
Also found by: 0xA5DF, 0xSmartContract, Aymen0909, Deivitto, NoamYakov, ReyAdmirado, Rolezn, chaduke, cryptostellar5, matrix_0wl
131.9758 USDC - $131.98
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.
state.updateTime
can be cached because the require check will usually be true so we save 100 gas by risking 3 more gas use.
<x> += <y>
costs more gas than <x> = <x> + <y>
for state variablesUsing the addition operator instead of plus-equals saves gas
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
bool
for storage incurs overheadBooleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled. Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past
Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.
these functions can be inlined inside DripsHub.sol because its only used once there
_receiveDrips
_receivableDripsCycles
_squeezeDrips
_dripsState
_setDrips
_splittable
_splitResult
_split
_collectable
_collect
_give
_setSplits
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
_AMT_PER_SEC_EXTRA_DECIMALS
_cycleSecs
(also casting is done to change it to 256 bits in some places which costs extra gas)
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.
registerDriver
updateDriverAddress
nextDriverId
cycleSecs
receivableDripsCycles
receiveDripsResult
receiveDrips
squeezeDrips
squeezeDripsResult
splittable
splitResult
split
collectable
collect
dripsState
balanceAt
hashDrips
hashDripsHistory
splitsHash
hashSplits
mint
safeMint
collect
give
setDrips
emitUserMetadata
changeAdmin
grantPauser
revokePauser
allPausers
paused
pause
unpause
authorize
unauthorize
allAuthorized
callAs
callSigned
callBatched
before transfer we should check for amount being 0 with a require of if check so the function doesnt run when its not gonna do anything
keccak256(bytes(CALL_SIGNED_TYPE_NAME))
always has the same answer, so it can be pre calculated
_erc1967Slot()
function will basically do some operation on a string, all the operations can be pre calculated in these places to save gas
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.
delete
assigning to zero uses more gas than using delete
, and they both assign variables to default value. so it is encouraged if the data is no longer needed use delete instead.
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.
See more here. you can use this tool to get the optimized version for function and properties signatures
DripsHub.sol has 33 public functions that are gonna be used externally used, ordering them for Method ID will be a huge gas save
NFTDriver.sol has 16 public functions that are gonna be used externally used, ordering them for Method ID will be a huge gas save
Managed.sol has 9 public functions that are gonna be used externally used, ordering them for Method ID will be a nice gas save
#0 - GalloDaSballo
2023-02-24T11:00:38Z
100 SLOAD
500 from +=
300 from unchecked
900
#1 - c4-judge
2023-02-24T11:02:05Z
GalloDaSballo marked the issue as grade-b