You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Shorten the revert strings to fit in 32 bytes. That will affect gas optimization.
C4-006 : Adding unchecked directive can save gas
Impact
For the arithmetic operations that will never over/underflow, using the unchecked directive (Solidity v0.8 has default overflow/underflow checks) can save some gas from the unnecessary internal over/underflow checks.
Proof of Concept
2022-03-joyn/core-contracts/contracts/CoreCollection.sol::205 => bytes(HASHED_PROOF).length == 0,
2022-03-joyn/core-contracts/contracts/CoreFactory.sol::75 => _collections.length > 0,
2022-03-joyn/core-contracts/contracts/CoreFactory.sol::79 => for (uint256 i; i < _collections.length; i++) {
Tools Used
None
Recommended Mitigation Steps
Consider applying unchecked arithmetic where overflow/underflow is not possible.
C4-007 : Free gas savings for using solidity 0.8.10+
Impact
Using newer compiler versions and the optimizer gives gas optimizations and additional safety checks are available for free.
Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist
All Contracts
Tools Used
None
Recommended Mitigation Steps
Consider to upgrade pragma to at least 0.8.10.
C4-008 : Use calldata instead of memory for function parameters
Impact
In some cases, having function arguments in calldata instead of
memory is more optimal.
Consider the following generic example:
contract C {
function add(uint[] memory arr) external returns (uint sum) {
uint length = arr.length;
for (uint i = 0; i < arr.length; i++) {
sum += arr[i];
}
}
}
In the above example, the dynamic array arr has the storage location
memory. When the function gets called externally, the array values are
kept in calldata and copied to memory during ABI decoding (using the
opcode calldataload and mstore). And during the for loop, arr[i]
accesses the value in memory using a mload. However, for the above
example this is inefficient. Consider the following snippet instead:
contract C {
function add(uint[] calldata arr) external returns (uint sum) {
uint length = arr.length;
for (uint i = 0; i < arr.length; i++) {
sum += arr[i];
}
}
}
In the above snippet, instead of going via memory, the value is directly
read from calldata using calldataload. That is, there are no
intermediate memory operations that carries this value.
Gas savings: In the former example, the ABI decoding begins with
copying value from calldata to memory in a for loop. Each iteration
would cost at least 60 gas. In the latter example, this can be
completely avoided. This will also reduce the number of instructions and
therefore reduces the deploy time cost of the contract.
In short, use calldata instead of memory if the function argument
is only read.
Note that in older Solidity versions, changing some function arguments
from memory to calldata may cause "unimplemented feature error".
This can be avoided by using a newer (0.8.*) Solidity compiler.
Examples
Note: The following pattern is prevalent in the codebase:
C4-010 : Changing function visibility from public to external can save gas
Impact
There is a function declared as public that are never called internally within the contract. It is best practice to mark such functions as external instead, as this saves gas (especially in the case where the function takes arguments, as external functions can read arguments directly from calldata instead of having to allocate memory).
C4-001 : There is no need to assign default values to variables
Impact - Gas Optimization
When a variable is declared solidity assigns the default value. In case the contract assigns the value again, it costs extra gas.
Example: uint x = 0 costs more gas than uint x without having any different functionality.
Proof of Concept
Tools Used
Code Review
Recommended Mitigation Steps
uint x = 0 costs more gas than uint x without having any different functionality.
C4-002 : Cache array length in for loops can save gas
Impact
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
Proof of Concept
Tools Used
None
Recommended Mitigation Steps
Consider to cache array length.
C4-003:
> 0 can be replaced with != 0 for gas optimization
Impact
!= 0
is a cheaper operation compared to> 0
, when dealing with uint.Proof of Concept
Tools Used
Code Review
Recommended Mitigation Steps
Use "!=0" instead of ">0" for the gas optimization.
C4-004: Immutable variables
Impact
'immutable' greatly reduces gas costs. There are variables that do not change so they can be marked as immutable to greatly improve the gas costs.
Proof of Concept
2022-03-joyn/core-contracts/contracts/CoreCollection.sol::227 => keccak256(abi.encodePacked("CoreCollection", block.number))
2022-03-joyn/core-contracts/contracts/CoreFactory.sol::148 => new CoreProxy{salt: keccak256(abi.encodePacked(_collection.id))}(
2022-03-joyn/core-contracts/contracts/ERC721Claimable.sol::98 => return keccak256(abi.encodePacked(who, claimableAmount));
Tools Used
Code Review
Recommended Mitigation Steps
Mark variables as immutable.
C4-005: Revert String Size Optimization
Impact
Shortening revert strings to fit in 32 bytes will decrease deploy time gas and will decrease runtime gas when the revert condition has been met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Proof of Concept
Revert strings > 32 bytes are here:
2022-03-joyn/core-contracts/contracts/CoreCollection.sol::47 => require(!initialized, "CoreCollection: Already initialized");
Tools Used
Manual Review
Recommended Mitigation Steps
Shorten the revert strings to fit in 32 bytes. That will affect gas optimization.
C4-006 : Adding unchecked directive can save gas
Impact
For the arithmetic operations that will never over/underflow, using the unchecked directive (Solidity v0.8 has default overflow/underflow checks) can save some gas from the unnecessary internal over/underflow checks.
Proof of Concept
Tools Used
None
Recommended Mitigation Steps
Consider applying unchecked arithmetic where overflow/underflow is not possible.
C4-007 : Free gas savings for using solidity 0.8.10+
Impact
Using newer compiler versions and the optimizer gives gas optimizations and additional safety checks are available for free.
Proof of Concept
Solidity 0.8.10 has a useful change which reduced gas costs of external calls which expect a return value: https://blog.soliditylang.org/2021/11/09/solidity-0.8.10-release-announcement/
Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist
All Contracts
Tools Used
None
Recommended Mitigation Steps
Consider to upgrade pragma to at least 0.8.10.
C4-008 : Use
calldata
instead ofmemory
for function parametersImpact
In some cases, having function arguments in calldata instead of
memory is more optimal.
Consider the following generic example:
In the above example, the dynamic array arr has the storage location
memory. When the function gets called externally, the array values are
kept in calldata and copied to memory during ABI decoding (using the
opcode calldataload and mstore). And during the for loop, arr[i]
accesses the value in memory using a mload. However, for the above
example this is inefficient. Consider the following snippet instead:
In the above snippet, instead of going via memory, the value is directly
read from calldata using calldataload. That is, there are no
intermediate memory operations that carries this value.
Gas savings: In the former example, the ABI decoding begins with
copying value from calldata to memory in a for loop. Each iteration
would cost at least 60 gas. In the latter example, this can be
completely avoided. This will also reduce the number of instructions and
therefore reduces the deploy time cost of the contract.
In short, use calldata instead of memory if the function argument
is only read.
Note that in older Solidity versions, changing some function arguments
from memory to calldata may cause "unimplemented feature error".
This can be avoided by using a newer (0.8.*) Solidity compiler.
Examples
Note: The following pattern is prevalent in the codebase:
Here, changing to bytes calldata will decrease the gas. The total
savings for this change across all such uses would be quite
significant.
Proof Of Concept
Examples:
Tools Used
None
Recommended Mitigation Steps
Change memory definition with calldata.
C4-009 : Non-strict inequalities are cheaper than strict ones
Impact
Strict inequalities add a check of non equality which costs around 3 gas.
Proof of Concept
Tools Used
Code Review
Recommended Mitigation Steps
Use >= or <= instead of > and < when possible.
C4-010 : Changing function visibility from public to external can save gas
Impact
There is a function declared as public that are never called internally within the contract. It is best practice to mark such functions as external instead, as this saves gas (especially in the case where the function takes arguments, as external functions can read arguments directly from calldata instead of having to allocate memory).
Proof of Concept
Tools Used
Code Review
Recommended Mitigation Steps
All of the public functions in the contract are not called internally, so access can be changed to external to reduce gas.
C4-011: Redundant Import
Impact
Constructor is redundant in the following code.
Proof of Concept
https://github.com/code-423n4/2022-03-joyn/blob/main/core-contracts/contracts/CoreCollection.sol#L37
Tools Used
Code Review
Recommended Mitigation Steps
Consider deleting the redundant code.
The text was updated successfully, but these errors were encountered: