Platform: Code4rena
Start Date: 20/05/2022
Pot Size: $1,000,000 USDC
Total HM: 4
Participants: 59
Period: 14 days
Judge: leastwood
Id: 128
League: ETH
Rank: 40/59
Findings: 1
Award: $474.36
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Dravee
Also found by: 0x1f8b, 0x29A, 0xalpharush, Chom, Czar102, Hawkeye, IllIllI, MaratCerby, MiloTruck, NoamYakov, OriDabush, RoiEvenHaim, Spearbit, Tadashi, TerrierLover, TomJ, asutorufos, cccz, cmichel, csanuragjain, defsec, delfin454000, djxploit, ellahi, foobar, gzeon, hake, hickuphh3, ignacio, ilan, joestakey, kaden, mayo, ming, oyc_109, peritoflores, rfa, sach1r0, sashik_eth, shung, sirhashalot, twojoy, zer0dot, zkhorse
474.3555 USDC - $474.36
If a variable is not set/initialized, it has the default value (0
for uint
, false
for bool
, etc.). Explicitly initializing a variable with its default value is an anti-pattern and wastes gas.
The OrderCombiner.sol contract has more than ten explicit initializations of default values. Other contract have more examples. Examples include:
To fix this, remove explicit initializations for default values.
Consider the following generic for loop:
for (uint i = 0; i < length; i++) { // do something that doesn't change the value of i }
In this example, the for loop post condition, i.e., i++
involves checked arithmetic, which is not required. This is because the value of i
is always strictly less than length <= 2**256 - 1
. Therefore, the theoretical maximum value of i
to enter the for-loop body is 2**256 - 2
. This means that the i++
in the for loop can never overflow. Regardless, the overflow checks are performed by the compiler.
Unfortunately, the Solidity optimizer is not smart enough to detect this and remove the checks. One can manually do this by:
for (uint i = 0; i < length; i = unchecked_inc(i)) { // do something that doesn't change the value of i } function unchecked_inc(uint i) returns (uint) { unchecked { return i + 1; } }
Above code and explanation from hrkrshnn.
The OrdrCombiner.sol contract has more than 5 loops that can be improved this way. Examples include:
To fix, use unchecked increment in loops.
== 0
The iszero opcode is useful when comparing variables to zero. t11s suggested overusing this improvement on twitter.
The Assertions.sol and OrderCombiner.sol contracts have more than 5 zero comparisons that can be improved this way. Examples include:
To fix, replace == 0
with iszero
.
Vyper contracts normally use less gas than their solidity counterparts. The possible savings was demonstrated for Seaport by doggo and a more general example was shown here. Seaport is written in solidity and yul, not vyper. Switching some or all of the contracts to vyper can result in gas savings.
To fix, replace solidity contracts with vyper code.
#0 - HardlyDifficult
2022-06-26T16:25:36Z
Unnecessary explicit initialization of variables with default values
In my testing these changes seem to make very little difference.
Use unchecked for increment
This will provide small savings.
iszero assembly should replace == 0
I like how you reference t11s own tweet here :) This could be a worthwhile change to consider including here.
Use vyper instead of solidity
Potentially. I saw 0age mention a working group exploring this more. Very curious to see if that concludes, would love a full featured comparison of Seaport in Solidity/Yul vs Vyper