Open code423n4 opened 2 years ago
Custom Errors instead of Revert Strings to save Gas
Agree but won't fix at this time. We use these in the market but not in collections. Unfortunately custom errors are still not as good of an experience for users (e.g. on etherscan). We used them in the market originally because we were nearing the max contract size limit and this was a good way to reduce the bytecode. We'll consider this in the future as tooling continues to improve.
!=0 instead of >0 for UINT in a require() statement
Invalid. We tested the recommendation and got the following results:
createNFTDropCollection gas reporter results:
using > 0 (current):
- 319246 · 319578 · 319361
using != 0 (recommendation):
- 319252 · 319584 · 319367
impact: +6 gas
An array’s length should be cached to save gas in for-loops
May be theoretically valid, but won't fix. I tested this: gas-reporter and our gas-stories suite is reporting a small regression using this technique. It also hurts readability a bit so we wouldn't want to include it unless it was a clear win.
Preincrement costs less gas as compared to Postincrement
Valid! Will Fix.
Reduce the size of error messages (Long revert Strings)
Agree but won't fix. We use up to 64 bytes, aiming to respect the incremental cost but 32 bytes is a bit too short to provide descriptive error messages for our users.
1. Custom Errors instead of Revert Strings to save Gas
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Starting from Solidity v0.8.4,there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");),but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Instances:
contracts/NFTDropCollection.sol
contracts/mixins/collections/SequentialMintCollection.sol
contracts/NFTCollectionFactory.sol
contracts/NFTCollection.sol
contracts/mixins/shared/ContractFactory.sol
contracts/mixins/roles/AdminRole.sol contracts/mixins/roles/MinterRole.sol contracts/libraries/AddressLibrary.sol
References:
https://blog.soliditylang.org/2021/04/21/custom-errors/
Remediation:
I suggest replacing revert strings with custom errors.
2. !=0 instead of >0 for UINT in a
require()
statementInstances
contracts/NFTDropCollection.sol:88 contracts/NFTDropCollection.sol:130 contracts/NFTDropCollection.sol:131
Reference:
https://twitter.com/gzeon/status/1485428085885640706
Remediation:
I suggest changing > 0 with != 0. Also, please enable the Optimizer.
3. An array’s length should be cached to save gas in for-loops
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.
Instances:
Links: MarketFees.sol: L126 L198 L484 L503
Remediation:
Here, I suggest storing the array’s length in a variable before the for-loop, and use it instead.
4. Preincrement costs less gas as compared to Postincrement :
++i costs less gas as compared to i++ for unsigned integer, as per-increment is cheaper(its about 5 gas per iteration cheaper)
i++ increments i and returns initial value of i. Which means
But ++i returns the actual incremented value:
In the first case, the compiler has create a temporary variable (when used) for returning 1 instead of 2.
Instances:
contracts/NFTCollectionFactory.sol:207 contracts/NFTCollectionFactory.sol:231
5. Reduce the size of error messages (Long revert Strings)
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Instances:
contracts/NFTDropCollection.sol
contracts/mixins/collections/SequentialMintCollection.sol
contracts/NFTCollectionFactory.sol
contracts/NFTCollection.sol
contracts/mixins/shared/ContractFactory.sol
contracts/mixins/roles/AdminRole.sol contracts/mixins/roles/MinterRole.sol contracts/libraries/AddressLibrary.sol
I suggest shortening the revert strings to fit in 32 bytes, or that using custom errors as described next.****
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source Custom Errors in Solidity: