Platform: Code4rena
Start Date: 20/01/2023
Pot Size: $90,500 USDC
Total HM: 10
Participants: 59
Period: 7 days
Judge: Picodes
Total Solo HM: 4
Id: 206
League: ETH
Rank: 53/59
Findings: 1
Award: $48.54
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0xackermann, Aymen0909, Beepidibop, IllIllI, Iurii3, Rageur, RaymondFam, ReyAdmirado, Rolezn, SaeedAlipoor01988, Udsen, Viktor_Cortess, W0RR1O, W_Max, atharvasama, c3phas, chaduke, descharre, fatherOfBlocks, kaden, matrix_0wl, shark
48.5424 USDC - $48.54
Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations.
_timeswapV2TokenPositions
and _timeswapV2TokenPositionIds
are used in the same function a bunch of times
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.
liquidityPosition.long0Fees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the liquidityPosition.long0Fees
variable before the if condition so it can be read one less time.
liquidityPosition.long1Fees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the liquidityPosition.long1Fees
variable before the if condition so it can be read one less time.
liquidityPosition.shortFees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the liquidityPosition.shortFees
variable before the if condition so it can be read one less time.
pool.liquidity
is being read twice. cache it at start of the function to save one extra read from storage
pool.sqrtInterestRate
is being read twice. cache it at start of the function to save one extra read from storage
because usually the if statement will be true we can cache pool.liquidity
and save lots of gas with a low risk of using only 3 more gas
pool.long0ProtocolFees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the pool.long0ProtocolFees
variable before the if condition so it can be read one less time.
pool.long1ProtocolFees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the pool.long1ProtocolFees
variable before the if condition so it can be read one less time.
pool.shortProtocolFees
is being read once inside the if check and if it passes its gonna be read again inside the if statement.if the condition fails its gonna be read inside the else statement instead. so we can cache the pool.shortProtocolFees
variable before the if condition so it can be read one less time.
pool.liquidity
is read twice in both if checks at lines L271 and L279
pool.liquidity
is being read three times. cache it at the start of the function. read once in L559 once in one of the if or else statements below that and once in L530. cache it to save 2 extra storage read
pool.liquidity
is being read twice and highly possibly 4 times cache it at the start of the function to save lots of gas.read once in L559 once in one of the if or else statements below that and highly possibly gonna be read in L639 and L653
pool.liquidity
is being read twice. L666 and once of the L697 or L724
option.totalLong0
is being read twice. firstly in L192 and in one of the if or elses below it. cache it to have one less storage read(also theres is -= which is wasting gas and is pointed out at another point of the report. after changing that there is gonna be one more storage read that can be prevented)
option.totalLong1
is being read twice. firstly in L192 and in one of the if or elses below it. cache it to have one less storage read.(also theres is -= which is wasting gas and is pointed out at another point of the report. after changing that there is gonna be one more storage read that can be prevented)
_feesPositions[id][msg.sender]
is being read twice. cache it before L199 and use the functions on the cached version of it on L199 and L216
<x> += <y>
costs more gas than <x> = <x> + <y>
for state variablesUsing the addition operator instead of plus-equals saves gas
the variables declared are never used so there is no need of declaring them
make the variable outside the loop and only give the value to variable inside
Use a solidity version of at least 0.8.10 to have external
calls skip contract existence checks if the external call has a return value
Use a solidity version of at least 0.8.12 to get string.concat() to be used instead of abi.encodePacked(<str>,<str>)
Use a solidity version of at least 0.8.13 to get the ability to use using for
with a list of free functions
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
if check
positionsthe if check used on the first checks uses higher gas, so we can swap the positions and sort them by gas usage so if a cheaper one will return error for us we dont have to check the other expensive ones
switch the position of the first if
to last because its the most expensive
bring the first to last because it uses a function
bring the first to last because it uses a function
#0 - c4-judge
2023-02-02T12:28:38Z
Picodes marked the issue as grade-b