Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 67
Period: 5 days
Judge: Picodes
Total Solo HM: 7
Id: 172
League: ETH
Rank: 66/67
Findings: 1
Award: $25.96
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Jeiwan
Also found by: 0x1f8b, 0x4non, 0x5rings, 0xSmartContract, Awesome, Aymen0909, Bnke0x0, CodingNameKiki, Diana, DimSon, JC, JrNet, LeoS, RaymondFam, ReyAdmirado, Saintcode_, Shinchan, __141345__, berndartmueller, bharg4v, brgltd, carlitox477, ch0bu, chaduke, cryptostellar5, emrekocak, gogo, lukris02, martin, mcwildy, sakman, trustindistrust, zishansami
25.9629 USDC - $25.96
When a function with a memory
array is called externally, the abi.decode()
step has to use a for-loop to copy each index of the calldata
to the memory
index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length
). Using calldata
directly, obliviates the need for such a loop in the contract code and runtime execution.
When arguments are read-only on external functions, the data location should be calldata
16 Instances: -JB721GlobalGovernance.sol line 55 -JB721TieredGovernance.sol lines 147, 313 -JBTiered721Delegate.sol lines 205, 206, 208, 210, 211, 213, 264, 290, 386, 480, 598, 652. -JBTiered721DelegateDeployer.sol line 71
Reading array length at each iteration of the loop consumes more gas than necessary. In the best case scenario (length read on a memory variable), caching the array length in the stack saves around 3 gas per iteration. In the worst case scenario (external calls at each iteration), the amount of gas wasted can be massive.
Consider storing the array’s length in a variable before the for-loop, and use this new variable instead
3 Instances: -JBIpfsDecoder.sol lines 49, 76, 84
1 Instance: -JBTiered721DelegateStore.sol line 827
++I
INSTEAD OF I++
(OR USE ASSEMBLY WHEN APPLICABLE)Use ++i
instead of i++
. This is especially useful in for loops but this optimization can be used anywhere in your code.
4 Instances: -JBIpfsDecoder.sol lines 59, 68, 76, 84
If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address…). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
As an example: for (uint256 i = 0; i < numIterations; ++i) { should be replaced with for (uint256 i; i < numIterations; ++i) {
6 Instances: -JBIpfsDecoder.sol lines 47, 49, 51, 68, 76, 84
>=
COSTS LESS GAS THAN >
The compiler uses opcodes GT
and ISZERO
for solidity code that uses >
, but only requires LT
for >=
, which saves 3 gas
5 Instances: -JBTiered721Delegate.sol lines 240, 551 -JBTiered721DelegateStore.sol line 1254 -JB721Delegate.sol line 116 -JBIpfsDecoder.sol line 57
++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- 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
for (uint256 i = 0; i < orders.length; /** NOTE: Removed i++ **/ ) { // Do the thing // Unchecked pre-increment is cheapest unchecked { ++i; } }
5 Instances: -JBIpfsDecode.sol lines 49, 51, 68, 76, 84
#0 - c4-judge
2022-11-04T21:07:53Z
Picodes marked the issue as grade-b