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
Basket.sol::43 => for (uint256 i = 0; i < _tokens.length; i++) {
Basket.sol::70 => for (uint256 i = 0; i < _tokens.length; i++) {
Basket.sol::93 => for (uint256 i = 0; i < _tokens.length; i++) {
NibblVault.sol::506 => for (uint256 i = 0; i < _assetAddresses.length; i++) {
NibblVault.sol::525 => for (uint256 i = 0; i < _assets.length; i++) {
NibblVault.sol::547 => for (uint256 i = 0; i < _assets.length; i++) {
Recommendation
Store the array’s length in a variable before the for-loop.
[G-02] ++i costs less gas compared to i++ or i += 1
Impact
++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
Proof of Concept
Basket.sol::43 => for (uint256 i = 0; i < _tokens.length; i++) {
Basket.sol::70 => for (uint256 i = 0; i < _tokens.length; i++) {
Basket.sol::93 => for (uint256 i = 0; i < _tokens.length; i++) {
NibblVault.sol::506 => for (uint256 i = 0; i < _assetAddresses.length; i++) {
NibblVault.sol::525 => for (uint256 i = 0; i < _assets.length; i++) {
NibblVault.sol::547 => for (uint256 i = 0; i < _assets.length; i++) {
Recommendation
Use ++i instead of i++ to increment the value of an uint variable.
Same thing for --i and i--.
[G-03] Reduce the size of error messages (Long revert Strings).
Impact
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.
Proof of Concept
NibblVaultFactory.sol::48 => require(msg.value >= MIN_INITIAL_RESERVE_BALANCE, "NibblVaultFactory: Initial reserve balance too low");
NibblVaultFactory.sol::49 => require(IERC721(_assetAddress).ownerOf(_assetTokenID) == msg.sender, "NibblVaultFactory: Invalid sender");
NibblVaultFactory.sol::107 => require(basketUpdateTime != 0 && block.timestamp >= basketUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed");
NibblVaultFactory.sol::131 => require(feeToUpdateTime != 0 && block.timestamp >= feeToUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed");
NibblVaultFactory.sol::141 => require(_newFee <= MAX_ADMIN_FEE, "NibblVaultFactory: Fee value greater than MAX_ADMIN_FEE");
NibblVaultFactory.sol::149 => require(feeAdminUpdateTime != 0 && block.timestamp >= feeAdminUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed");
NibblVaultFactory.sol::166 => require(vaultUpdateTime != 0 && block.timestamp >= vaultUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed");
Recommendation
Shorten the revert strings to fit in 32 bytes, or use custom errors if >0.8.4.
[G-04] Use Custom Errors instead of Revert Strings.
Impact
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Gas
[G-01] Cache Array Length Outside of Loop.
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
Recommendation
Store the array’s length in a variable before the for-loop.
[G-02]
++i
costs less gas compared toi++
ori += 1
Impact
++i
costs less gas compared toi++
ori += 1
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.Proof of Concept
Recommendation
Use
++i
instead ofi++
to increment the value of an uint variable. Same thing for--i
andi--
.[G-03] Reduce the size of error messages (Long revert Strings).
Impact
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.
Proof of Concept
Recommendation
Shorten the revert strings to fit in 32 bytes, or use custom errors if >0.8.4.
[G-04] Use Custom Errors instead of Revert Strings.
Impact
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Recommendation
Use custom errors instead of revert strings.
Tools used
c4udit, manual, slither