Array.length should not be looked up in every loop of a for-loop
Proof of concept
One of most the expensive opcodes is SLOAD. \
It is better to cache arr.length in a stack variable like uint256 arrLength in order to save gas upon each lookup of the for-loops.
Recommendation
Cache arrays' length in memory in order to save gas from SLOAD on every iteration.
Use ++i instead of i++, especially when it’s used in for-loops (--i/i-- too)
Proof of concept
++i costs less gas than i++
Recommendation
Replace each i++ with ++i, especially in for-loops
Do not initialize i counter in for-loops
Proof of concept
Uint256's default value is zero so it is useless and a waste of gas to initialize it to 0
Since Solidity version 0.8 overflow and underflow checks are implemented in Solidity compiler. \
So it is applied by default on every arithmetic operation and costs additional gas. \
In the case of for-loop i will always be less than the target (e.g. arr.length) so those checks are unnecessary.
Recommendation
Put each ++i in unchecked {} in for-loops
Example
- 556 for (uint256 i = 0; i < orders.length; ++i) {
+ 556 for (uint256 i = 0; i < orders.length;) {
+ 557 ...
+ 558 unchecked {
+ 559 ++i
+ 560 }
+ 561 }
Use external instead of public function visibility modifiers
Proof of concept
Functions that are not being called from inside the contract through its lifecycle should be marked as external.
External function parameters are located in calldata by default which saves gas comparing to the memory location in public functions.
Gas optimisations
For loop gas optimisations
Array.length should not be looked up in every loop of a for-loop
Proof of concept
One of most the expensive opcodes is SLOAD. \ It is better to cache arr.length in a stack variable like uint256 arrLength in order to save gas upon each lookup of the for-loops.
Recommendation
Cache arrays' length in memory in order to save gas from SLOAD on every iteration.
Use ++i instead of i++, especially when it’s used in for-loops (--i/i-- too)
Proof of concept
++i costs less gas than i++
Recommendation
Replace each i++ with ++i, especially in for-loops
Do not initialize
i
counter in for-loopsProof of concept
Uint256's default value is zero so it is useless and a waste of gas to initialize it to 0
Recommendation
Use
uint256 i;
instead ofuint256 i = 0;
Example
Put
++i
inunchecked {}
Proof of concept
Since Solidity version 0.8 overflow and underflow checks are implemented in Solidity compiler. \ So it is applied by default on every arithmetic operation and costs additional gas. \ In the case of for-loop
i
will always be less than the target (e.g.arr.length
) so those checks are unnecessary.Recommendation
Put each
++i
inunchecked {}
in for-loopsExample
Scope (for-loop gas optimisations)
In https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
Use custom errors instead of require with string expressions
Proof of concept
Custom errors introduced in Solidity version 0.8.4 cost less than require()/revert() because of the missing string messages.
Recommendation
Use
if
checks with descriptive custom errors instead of require expressions with string parameters.Example
In https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
Scope
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
Redundant return statements
Proof of concept
Use
else if
instead of return expressionsScope
In function fillOrder https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
Use return expression instead of naming a return variable
Proof of concept
There is no reason to use a named return varible instead of directly returned value in this cases:
Scope
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L689 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L719
Use calldata instead of memory location for reference type variable
Proof of concept
Storing function parameters is cheaper if they are located in calldata because copying them in memory cost gas
Recommendation
Store function parameters in calldata where possible and be careful
Scope
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L269 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L389 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L466 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L526 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L547 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L574 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L576 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L593 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L610 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L623 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L636 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L646 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L657 floorTokens only!
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L669
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L683
Use external instead of public function visibility modifiers
Proof of concept
Functions that are not being called from inside the contract through its lifecycle should be marked as external. External function parameters are located in calldata by default which saves gas comparing to the memory location in public functions.
Scope
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L577 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L466 \ https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L389