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
[G‑02] Using calldata instead of memory for read-only arguments in external functions saves gas
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. Note that even if an interface defines a function as having memory arguments, it's still valid for implementation contracs to use calldata arguments instead.
If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it's still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
Note that I've also flagged instances where the function is public but can be marked as external since it's not called by the contract, and cases where a constructor is involved
[G‑03] Avoid contract existence checks by using solidity version 0.8.10 or later
Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (700 gas), to check for contract existence for external calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value
[G‑05] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement
require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }
There are 2 instances of this issue:
File: contracts/ethregistrar/ETHRegistrarController.sol
/// @audit require() on line 197/// @audit if-condition on line 203204: payable(msg.sender).transfer(msg.value- price.base);
[G‑07] ++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-loops
The 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
There are 7 instances of this issue:
File: contracts/dnssec-oracle/BytesUtils.sol
266: for(uint i =0; i < len; i++) {
313: for(uint256 idx = off; idx < off + len; idx++) {
[G‑08] require()/revert() strings longer than 32 bytes cost extra gas
Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 26 instances of this issue:
File: contracts/ethregistrar/ETHRegistrarController.sol
99require(
100 resolver !=address(0),
101"ETHRegistrarController: resolver is required when data is supplied"102: );
137require(
138msg.value>= (price.base + price.premium),
139"ETHRegistrarController: Not enough ether provided"140: );
196require(
197msg.value>= price.base,
198"ETHController: Not enough Ether provided for renewal"199: );
232require(
233 commitments[commitment] + minCommitmentAge <=block.timestamp,
234"ETHRegistrarController: Commitment is not valid"235: );
238require(
239 commitments[commitment] + maxCommitmentAge >block.timestamp,
240"ETHRegistrarController: Commitment has expired"241: );
242: require(available(name), "ETHRegistrarController: Name is unavailable");
259require(
260 txNamehash == nodehash,
261"ETHRegistrarController: Namehash on record do not match the name being registered"262: );
File: contracts/registry/ReverseRegistrar.sol
41require(
42 addr ==msg.sender||43 controllers[msg.sender] ||44 ens.isApprovedForAll(addr, msg.sender) ||45ownsContract(addr),
46"ReverseRegistrar: Caller is not a controller or authorised by address or the address itself"47: );
52require(
53address(resolver) !=address(0),
54"ReverseRegistrar: Resolver address must not be 0"55: );
File: contracts/wrapper/ERC1155Fuse.sol
60require(
61 account !=address(0),
62"ERC1155: balance query for the zero address"63: );
85require(
86 accounts.length== ids.length,
87"ERC1155: accounts and ids length mismatch"88: );
107require(
108msg.sender!= operator,
109"ERC1155: setting approval status for self"110: );
176: require(to !=address(0), "ERC1155: transfer to the zero address");
177require(
178 from ==msg.sender||isApprovedForAll(from, msg.sender),
179"ERC1155: caller is not owner nor approved"180: );
195require(
196 ids.length== amounts.length,
197"ERC1155: ids and amounts length mismatch"198: );
199: require(to !=address(0), "ERC1155: transfer to the zero address");
200require(
201 from ==msg.sender||isApprovedForAll(from, msg.sender),
202"ERC1155: transfer caller is not owner nor approved"203: );
215require(
216 amount ==1&& oldOwner == from,
217"ERC1155: insufficient balance for transfer"218: );
249: require(newOwner !=address(0), "ERC1155: mint to the zero address");
250require(
251 newOwner !=address(this),
252"ERC1155: newOwner cannot be the NameWrapper contract"253: );
290require(
291 amount ==1&& oldOwner == from,
292"ERC1155: insufficient balance for transfer"293: );
322: revert("ERC1155: ERC1155Receiver rejected tokens");
327: revert("ERC1155: transfer to non ERC1155Receiver implementer");
354: revert("ERC1155: ERC1155Receiver rejected tokens");
359: revert("ERC1155: transfer to non ERC1155Receiver implementer");
public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.
[G‑12] Splitting require() statements that use && saves gas
See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas
[G‑13] Using private rather than public for constants, saves gas
If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
[G‑14] require() or revert() statements that check input arguments should be at the top of the function
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.
There are 4 instances of this issue:
File: contracts/ethregistrar/ETHRegistrarController.sol
/// @audit expensive op on line 244246: require(duration >= MIN_REGISTRATION_DURATION);
File: contracts/wrapper/ERC1155Fuse.sol
/// @audit expensive op on line 196199: require(to !=address(0), "ERC1155: transfer to the zero address");
/// @audit expensive op on line 248249: require(newOwner !=address(0), "ERC1155: mint to the zero address");
/// @audit expensive op on line 248250require(
251 newOwner !=address(this),
252"ERC1155: newOwner cannot be the NameWrapper contract"253: );
File: contracts/ethregistrar/ETHRegistrarController.sol
99require(
100 resolver !=address(0),
101"ETHRegistrarController: resolver is required when data is supplied"102: );
137require(
138msg.value>= (price.base + price.premium),
139"ETHRegistrarController: Not enough ether provided"140: );
196require(
197msg.value>= price.base,
198"ETHController: Not enough Ether provided for renewal"199: );
232require(
233 commitments[commitment] + minCommitmentAge <=block.timestamp,
234"ETHRegistrarController: Commitment is not valid"235: );
238require(
239 commitments[commitment] + maxCommitmentAge >block.timestamp,
240"ETHRegistrarController: Commitment has expired"241: );
242: require(available(name), "ETHRegistrarController: Name is unavailable");
259require(
260 txNamehash == nodehash,
261"ETHRegistrarController: Namehash on record do not match the name being registered"262: );
File: contracts/registry/ReverseRegistrar.sol
41require(
42 addr ==msg.sender||43 controllers[msg.sender] ||44 ens.isApprovedForAll(addr, msg.sender) ||45ownsContract(addr),
46"ReverseRegistrar: Caller is not a controller or authorised by address or the address itself"47: );
52require(
53address(resolver) !=address(0),
54"ReverseRegistrar: Resolver address must not be 0"55: );
File: contracts/wrapper/BytesUtil.sol
28: require(offset == self.length-1, "namehash: Junk at end of name");
42: require(idx < self.length, "readLabel: Index out of bounds");
File: contracts/wrapper/ERC1155Fuse.sol
60require(
61 account !=address(0),
62"ERC1155: balance query for the zero address"63: );
85require(
86 accounts.length== ids.length,
87"ERC1155: accounts and ids length mismatch"88: );
107require(
108msg.sender!= operator,
109"ERC1155: setting approval status for self"110: );
176: require(to !=address(0), "ERC1155: transfer to the zero address");
177require(
178 from ==msg.sender||isApprovedForAll(from, msg.sender),
179"ERC1155: caller is not owner nor approved"180: );
195require(
196 ids.length== amounts.length,
197"ERC1155: ids and amounts length mismatch"198: );
199: require(to !=address(0), "ERC1155: transfer to the zero address");
200require(
201 from ==msg.sender||isApprovedForAll(from, msg.sender),
202"ERC1155: transfer caller is not owner nor approved"203: );
215require(
216 amount ==1&& oldOwner == from,
217"ERC1155: insufficient balance for transfer"218: );
248: require(owner ==address(0), "ERC1155: mint of existing token");
249: require(newOwner !=address(0), "ERC1155: mint to the zero address");
250require(
251 newOwner !=address(this),
252"ERC1155: newOwner cannot be the NameWrapper contract"253: );
290require(
291 amount ==1&& oldOwner == from,
292"ERC1155: insufficient balance for transfer"293: );
[G‑16] Functions guaranteed to revert when called by normal users can be marked payable
If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
There are 14 instances of this issue:
File: contracts/registry/ReverseRegistrar.sol
51: function setDefaultResolver(addressresolver) publicoverride onlyOwner {
Summary
Gas Optimizations
calldata
instead ofmemory
for read-only arguments inexternal
functions saves gasinternal
functions only called once can be inlined to save gasunchecked {}
for subtractions where the operands cannot underflow because of a previousrequire()
orif
-statement<array>.length
should not be looked up in every loop of afor
-loop++i
/i++
should beunchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used infor
- andwhile
-loopsrequire()
/revert()
strings longer than 32 bytes cost extra gasbool
s for storage incurs overhead++i
costs less gas thani++
, especially when it's used infor
-loops (--i
/i--
too)require()
statements that use&&
saves gasprivate
rather thanpublic
for constants, saves gasrequire()
orrevert()
statements that check input arguments should be at the top of the functionrevert()
/require()
strings to save gaspayable
Total: 138 instances over 16 issues
Gas Optimizations
[G‑01] Calculation re-calculated in same function
The calculation is repeated multiple times in the function. Instead, the value should be cache and re-used
There are 2 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/4dfb0e32f586bff3db486349523a93480e3ddfba/contracts/ethregistrar/ETHRegistrarController.sol#L182
[G‑02] Using
calldata
instead ofmemory
for read-only arguments inexternal
functions saves gasWhen a function with a
memory
array is called externally, theabi.decode()
step has to use a for-loop to copy each index of thecalldata
to thememory
index. Each iteration of this for-loop costs at least 60 gas (i.e.60 * <mem_array>.length
). Usingcalldata
directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as havingmemory
arguments, it's still valid for implementation contracs to usecalldata
arguments instead.If the array is passed to an
internal
function which passes the array to another internal function where the array is modified and thereforememory
is used in theexternal
call, it's still more gass-efficient to usecalldata
when theexternal
function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length oneNote that I've also flagged instances where the function is
public
but can be marked asexternal
since it's not called by the contract, and cases where a constructor is involvedThere are 4 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L46
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSEC.sol#L17
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L539-L550
[G‑03] Avoid contract existence checks by using solidity version 0.8.10 or later
Prior to 0.8.10 the compiler inserted extra code, including
EXTCODESIZE
(700 gas), to check for contract existence for external calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return valueThere are 3 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L182
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L311
[G‑04]
internal
functions only called once can be inlined to save gasNot inlining costs 20 to 40 gas because of two extra
JUMP
instructions and additional stack operations needed for function calls.There are 14 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L110
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L226-L229
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L181
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L741-L744
[G‑05] Add
unchecked {}
for subtractions where the operands cannot underflow because of a previousrequire()
orif
-statementrequire(a <= b); x = b - a
=>require(a <= b); unchecked { x = b - a }
There are 2 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L204
[G‑06]
<array>.length
should not be looked up in every loop of afor
-loopThe overheads outlined below are PER LOOP, excluding the first loop
MLOAD
(3 gas)CALLDATALOAD
(3 gas)Caching the length changes each of these to a
DUP<N>
(3 gas), and gets rid of the extraDUP<N>
needed to store the stack offsetThere are 4 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L93
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L256
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L92
[G‑07]
++i
/i++
should beunchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used infor
- andwhile
-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 loopThere are 7 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/BytesUtils.sol#L266
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L93
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L256
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/StringUtils.sol#L14
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L92
[G‑08]
require()
/revert()
strings longer than 32 bytes cost extra gasEach extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 26 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L99-L102
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L41-L47
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/Controllable.sol#L17
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L60-L63
[G‑09] Optimize names to save gas
public
/external
function names andpublic
member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shiftedThere are 17 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/algorithms/Algorithm.sol#L6
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/digests/Digest.sol#L6
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L16
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSEC.sol#L5
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/Owned.sol#L6
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L17
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/IBaseRegistrar.sol#L5
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/IETHRegistrarController.sol#L5
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ENS.sol#L3
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/IReverseRegistrar.sol#L3
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L8
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/resolvers/Resolver.sol#L20
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/Controllable.sol#L6
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L14
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/INameWrapper.sol#L18
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/INameWrapperUpgrade.sol#L4
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L27
[G‑10] Using
bool
s for storage incurs overheadhttps://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use
uint256(1)
anduint256(2)
for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing fromfalse
totrue
, after having beentrue
in the pastThere are 2 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/Controllable.sol#L7
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L19
[G‑11]
++i
costs less gas thani++
, especially when it's used infor
-loops (--i
/i--
too)Saves 5 gas per loop
There are 7 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/BytesUtils.sol#L266
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/DNSSECImpl.sol#L93
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/RRUtils.sol#L235
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L256
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/StringUtils.sol#L14
[G‑12] Splitting
require()
statements that use&&
saves gasSee this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas
There are 3 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/BytesUtils.sol#L268
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L215-L218
[G‑13] Using
private
rather thanpublic
for constants, saves gasIf needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There are 3 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L21
[G‑14]
require()
orrevert()
statements that check input arguments should be at the top of the functionChecks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.
There are 4 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L246
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L199
[G‑15] Use custom errors rather than
revert()
/require()
strings to save gasCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 26 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/dnssec-oracle/RRUtils.sol#L307
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/ethregistrar/ETHRegistrarController.sol#L99-L102
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L41-L47
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/BytesUtil.sol#L28
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/Controllable.sol#L17
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/ERC1155Fuse.sol#L60-L63
[G‑16] Functions guaranteed to revert when called by normal users can be marked
payable
If a function modifier such as
onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function aspayable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided areCALLVALUE
(2),DUP1
(3),ISZERO
(3),PUSH2
(3),JUMPI
(10),PUSH1
(3),DUP1
(3),REVERT
(0),JUMPDEST
(1),POP
(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment costThere are 14 instances of this issue:
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/registry/ReverseRegistrar.sol#L51
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/Controllable.sol#L11
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L105-L107
The text was updated successfully, but these errors were encountered: