Platform: Code4rena
Start Date: 12/07/2022
Pot Size: $75,000 USDC
Total HM: 16
Participants: 100
Period: 7 days
Judge: LSDan
Total Solo HM: 7
Id: 145
League: ETH
Rank: 83/100
Findings: 1
Award: $78.74
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xKitsune
Also found by: 0x040, 0x1f8b, 0x29A, 0xNazgul, 0xNineDec, 0xsam, 8olidity, Aussie_Battlers, Aymen0909, Bnke0x0, CRYP70, Ch_301, Chom, Deivitto, Dravee, ElKu, Fitraldys, Funen, GimelSec, IllIllI, JC, JohnSmith, Lambda, MiloTruck, Noah3o6, RedOneN, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, TomJ, Tomio, Waze, _Adam, __141345__, ajtra, ak1, arcoun, asutorufos, benbaessler, brgltd, bulej93, c3phas, cRat1st0s, cryptonue, delfin454000, durianSausage, fatherOfBlocks, gogo, hake, hyh, joestakey, karanctf, kyteg, lcfr_eth, lucacez, m_Rassska, rajatbeladiya, rbserver, robee, rokinot, sach1r0, sahar, samruna, sashik_eth, seyni, simon135, zuhaibmohd
78.7412 USDC - $78.74
unchecked{++i}
instead of i++
in loopsa = a + b
instead of a += b
a / 2^x, x > 0
MLOAD
<< SLOAD
require()
before some computations, if it makes senseInternal
functions can be inlinedprivate/internal
for constants/immutable
variables instead of public
type(uint256).max
uint
instead of uint8
, uint64
, if it's possibleexternal
instead of public
, if there are no internal callscalldataload
instead of mload
revert
or require
instead of assert
i++
, the compiler has to to create a temp variable to return i
(if there's a need) and then i
gets incremented.++i
, the compiler just simply returns already incremented value.Contracts:
------------------------------- | file: src/**/BytesUtils.sol | ------------------------------- // Initial version: // Lines: [266-266] for(uint i = 0; i < len; i++) {} // Proposed by m_Rassska: // Lines: [266-266] for(uint i = 0; i < len;) { // some code unchecked{ ++i; } } // Also occured in the following files: src/**/DNSSECImpl.sol src/**/RRUtils.sol src/**/StringUtils.sol src/**/ETHRegistrarController.sol src/**/ERC1155Fuse.sol ------------------------------- | file: src/**/RRUtils.sol | ------------------------------- // Initial version: // Lines: [58-58] count += 1; // Proposed by m_Rassska: // Lines: [58-58] unchecked{ ++i; }; // Also occured in the following files: src/**/StringUtils.sol
unchecked{++i};
instead of i++;
/++i;
in loops<a name="G-02"></a>Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default Therefore, unchecked
box can be used to prevent all unnecessary checks, if it's no a way to get a reversion.
There are ~1e80 atoms in the universe, so 2^256 is closed to that number, therefore it's no a way to be overflowed, because of the gas limit as well.
Contracts:
------------------------------- | file: src/**/BytesUtils.sol | ------------------------------- // Initial versions: // Lines: [56-56] for (uint idx = 0; idx < shortest; idx += 32) {} // Proposed by m_Rassska: // Lines: [266-266] for (uint idx = 0; idx < shortest;) { // some code unchecked{ idx = idx + 32; } } // Also occured in the following files: src/**/RRUtils.sol ------------------------------- | file: src/**/RRUtils.sol | ------------------------------- // Initial versions: // Lines: [24-24] idx += labelLen + 1; // Lines: [54-54] offset += labelLen + 1; // Lines: [58-58] count += 1; // Proposed by m_Rassska: // Lines: [24-24] idx = idx + ++labelLen // Lines: [54-54] unchecked {++labelLen;} offset = offset + labelLen; // Lines: [58-58] unchecked {++count}; // Also occured in the following files:
a = a + b
instead of a += b
<a name="G-03"></a>Contracts:
------------------------------- | file: src/**/RRUtils.sol | ------------------------------- // Initial versions: // Lines: [75-76] selfptr += 32; otherptr += 32; // Proposed by m_Rassska: // Lines: [75-76] selfptr = selfptr + 32; otherptr = otherptr + 32; // Also occured in the following files: src/**/RRUtils.sol src/**/StringUtils.sol src/**/SHA1.sol
onlyOwner
modifier will be reverted, if the user invoke following methods.Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [58-61] function setAlgorithm(uint8 id, Algorithm algo) public owner_only { algorithms[id] = algo; emit AlgorithmUpdated(id, address(algo)); } // Proposed by m_Rassska: // Lines: [58-61] function setAlgorithm(uint8 id, Algorithm algo) public payable owner_only { algorithms[id] = algo; emit AlgorithmUpdated(id, address(algo)); } // Also occured in the following files: src/**/Owned.sol src/**/ReverseRegistrar.sol src/**/NameWrapper.sol src/**/Controllable.sol ```
a / 2^x, x > 0
or a * 2^x, x > 0
<a name="G-05"></a>Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [316-316] uint unused = 256 - (data.length - i) * 8; // Proposed by m_Rassska: // Lines: [316-316] uint unused = 256 - (data.length - i) << 3; // Also occured in the following files: ```
MLOAD
<< SLOAD
<a name="G-06"></a>MLOAD
costs only 3 units of gas, SLOAD
(warm access) is about 100 units. Therefore, cache, when it's possible.Contracts:
------------------------------- | file: src/**/NameWrapper.sol | ------------------------------- // Initial versions: // Lines: [128-143] function setUpgradeContract(INameWrapperUpgrade _upgradeAddress) public onlyOwner { if (address(upgradeContract) != address(0)) { registrar.setApprovalForAll(address(upgradeContract), false); ens.setApprovalForAll(address(upgradeContract), false); } upgradeContract = _upgradeAddress; if (address(upgradeContract) != address(0)) { registrar.setApprovalForAll(address(upgradeContract), true); ens.setApprovalForAll(address(upgradeContract), true); } } // Proposed by m_Rassska: // Lines: [128-143] function setUpgradeContract(INameWrapperUpgrade _upgradeAddress) public onlyOwner { address upgradeAddressPrev = address(upgradeContract); if (upgradeAddressPrev != address(0)) { registrar.setApprovalForAll(upgradeAddressPrev , false); ens.setApprovalForAll(upgradeAddressPrev , false); } upgradeContract = _upgradeAddress; address upgradeAddressCurr = address(_upgradeAddress); if (upgradeAddressCurr != address(0)) { registrar.setApprovalForAll(upgradeAddressCurr , true); ens.setApprovalForAll(upgradeAddressCurr , true); } } // Also occured in the following files: src/**/ETHRegistrarController.sol ```
require() before some computations, if it makes sense
<a name="G-07"></a>require()
takes some gas for execution. Sometimes it's usefull to rearrange require
statements or place them before any computations.Contracts:
------------------------------- | file: src/**/ETHRegistrarController.sol | ------------------------------- // Initial versions: // Lines: [232-242] function _consumeCommitment( string memory name, uint256 duration, bytes32 commitment ) internal { // Require a valid commitment (is old enough and is committed) require( commitments[commitment] + minCommitmentAge <= block.timestamp, "ETHRegistrarController: Commitment is not valid" ); // If the commitment is too old, or the name is registered, stop require( commitments[commitment] + maxCommitmentAge > block.timestamp, "ETHRegistrarController: Commitment has expired" ); require(available(name), "ETHRegistrarController: Name is unavailable"); delete (commitments[commitment]); require(duration >= MIN_REGISTRATION_DURATION); } // Proposed by m_Rassska: // Lines: [232-242] function _consumeCommitment( string memory name, uint256 duration, bytes32 commitment ) internal { // Require a valid commitment (is old enough and is committed) require(available(name), "ETHRegistrarController: Name is unavailable"); require( commitments[commitment] + minCommitmentAge <= block.timestamp, "ETHRegistrarController: Commitment is not valid" ); // If the commitment is too old, or the name is registered, stop require( commitments[commitment] + maxCommitmentAge > block.timestamp, "ETHRegistrarController: Commitment has expired" ); delete (commitments[commitment]); require(duration >= MIN_REGISTRATION_DURATION); } // Also occured in the following files: src/**/BytesUtil.sol src/**/ERC1155Fuse.sol src/**/NameWrapper.sol
Internal
functions can be inlined<a name="G-08"></a>JUMP
s. Especially, if it takes as an arg bytes or arrays, it would be expensive. There is a tradeoff here between: code readability and optimization. Try to evalute it.Contracts:
------------------------------------- | file: src/**/ReverseRegistrar.sol | ------------------------------------- // Initial versions: // Lines: [181-187] function ownsContract(address addr) internal view returns (bool) { try Ownable(addr).owner() returns (address owner) { return owner == msg.sender; } catch { return false; } } // Proposed by m_Rassska: // Lines: [181-187] // Comment: Remove the internal function with a simple logic. // Also occured in the following files: src/**/DNSSECImpl.sol src/**/RRUtils.sol src/**/NameWrapper.sol src/**/ETHRegistrarController.sol src/**/BytesUtils.sol
private/internal
for constants/immutable
variables instead of public
<a name="G-09"></a>public
instance.Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [21-26] uint16 constant DNSCLASS_IN = 1; uint16 constant DNSTYPE_DS = 43; uint16 constant DNSTYPE_DNSKEY = 48; uint constant DNSKEY_FLAG_ZONEKEY = 0x100; // Proposed by m_Rassska: // Lines: [21-26] uint16 constant private DNSCLASS_IN = 1; uint16 constant private DNSTYPE_DS = 43; uint16 constant private DNSTYPE_DNSKEY = 48; uint constant private DNSKEY_FLAG_ZONEKEY = 0x100; // Also occured in the following files: src/**/RRUtils.sol src/**/DNSSEC.sol src/**/ReverseRegistrar.sol src/**/NameWrapper.sol src/**/ETHRegistrarController.sol src/**/Controllable.sol
type(uint256).max
<a name="G-10"></a>Contracts:
------------------------------- | file: src/**/BytesUtils.sol | ------------------------------- // Proposed by m_Rassska: // Lines: [67-67] mask = type(uint256).max; // Proposed by m_Rassska: // Lines: [67-67] mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // type(uint).max // Also occured in the following files: src/**/NameWrapper.sol
uint
instead of uint8
, uint64
, if it's possible<a name="G-11"></a>uint8
, uint64
leads to overheads.Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [21-26] uint16 constant DNSCLASS_IN = 1; uint16 constant DNSTYPE_DS = 43; uint16 constant DNSTYPE_DNSKEY = 48; uint constant DNSKEY_FLAG_ZONEKEY = 0x100; // Proposed by m_Rassska: // Lines: [21-26] uint constant DNSCLASS_IN = 1; uint constant DNSTYPE_DS = 43; uint constant DNSTYPE_DNSKEY = 48; uint constant DNSKEY_FLAG_ZONEKEY = 0x100; // Also occured in the following files: src/**/RRUtils.sol src/**/DNSSEC.sol src/**/ReverseRegistrar.sol src/**/NameWrapper.sol src/**/StringUtils.sol src/**/Controllable.sol src/**/ENS.sol src/**/Resolver.sol ```
external
instead of public
, if there are no internal calls<a name="G-12"></a>external
use call data to read arguments, where public
will first allocate in local memory and then load them.Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [58-58] function setAlgorithm(uint8 id, Algorithm algo) public owner_only {} // Lines: [69-69] function setDigest(uint8 id, Digest digest) public owner_only {} // Proposed by m_Rassska: // Lines: [58-58] function setAlgorithm(uint8 id, Algorithm algo) external owner_only {} // Lines: [69-69] function setDigest(uint8 id, Digest digest) external owner_only {} // Also occured in the following files: src/**/Owned.sol src/**/ReverseRegistrar.sol src/**/ERC1155Fuse.sol src/**/Controllable.sol src/**/NameWrapper.sol ```
calldataload
instead of mload
<a name="G-13"></a>calldataload
, or replace memory
with calldata
, if args are not supposed to be modified.Contracts:
------------------------------- | file: src/**/DNSSECImpl.sol | ------------------------------- // Initial versions: // Lines: [183-183] function verifySignature(bytes memory name, RRUtils.SignedSet memory rrset, RRSetWithSignature memory data, bytes memory proof) internal view {} // Proposed by m_Rassska: // Lines: [183-183] function verifySignature(bytes calldata name, RRUtils.SignedSet calldata rrset, RRSetWithSignature memory data, bytes memory proof) internal view {} // Also occured in the following files: src/**/BytesUtils.sol src/**/RRUtils.sol src/**/ETHRegistrarController.sol src/**/ModexpPrecompile.sol src/**/ReverseRegistrar.sol src/**/NameWrapper.sol src/**/Resolver.sol // looks nice, thanks! ```
revert
or require
instead of assert
<a name="G-14"></a>Contracts:
---------------------------- | file: src/**/RRUtils.sol | ---------------------------- // Initial versions: // Lines: [22-22] assert(idx < self.length); // Lines: [52-52] assert(offset < self.length); // Proposed by m_Rassska: // Lines: [22-22] require(idx < self.length); // Lines: [52-52] require(offset < self.length); // Also occured in the following files: ```