Platform: Code4rena
Start Date: 22/08/2022
Pot Size: $50,000 USDC
Total HM: 4
Participants: 160
Period: 5 days
Judge: gzeon
Total Solo HM: 2
Id: 155
League: ETH
Rank: 28/160
Findings: 2
Award: $66.19
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0bi, 0x040, 0x1337, 0x1f8b, 0xDjango, 0xNazgul, 0xNineDec, 0xRajeev, 0xSky, 0xSmartContract, 0xbepresent, 0xkatana, 0xmatt, 8olidity, Aymen0909, Bjorn_bug, Bnke0x0, CertoraInc, Ch_301, Chom, CodingNameKiki, Deivitto, DevABDee, DimitarDimitrov, Dravee, ElKu, Funen, GalloDaSballo, GimelSec, Guardian, Haruxe, JC, JansenC, Jeiwan, JohnSmith, KIntern_NA, Lambda, LeoS, Noah3o6, Olivierdem, R2, RaymondFam, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Saintcode_, Sm4rty, SooYa, Soosh, TomJ, Tomo, Trabajo_de_mates, Waze, _Adam, __141345__, ajtra, android69, asutorufos, auditor0517, berndartmueller, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, catchup, cccz, csanuragjain, d3e4, delfin454000, dipp, djxploit, durianSausage, erictee, exd0tpy, fatherOfBlocks, gogo, hyh, ladboy233, lukris02, mics, mrpathfindr, natzuu, oyc_109, p_crypt0, pashov, pauliax, pfapostol, prasantgupta52, rajatbeladiya, rbserver, ret2basic, rfa, robee, rokinot, rvierdiiev, sach1r0, saian, seyni, shenwilly, sikorico, simon135, sryysryy, sseefried, throttle, tnevler, tonisives, wagmi, xiaoming90, yixxas, z3s, zkhorse, zzzitron
35.4387 USDC - $35.44
File Name | SHA-1 Hash |
---|---|
2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol | e0e939a91c5d5c3148ae744741646e3f440d1e3d |
2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol | 96223a722bf49513779653adff38b72db55fce3f |
2022-08-nounsdao/contracts/governance/NounsDAOInterfaces.sol | 798ddc7b42dff8950e968af112bba2df70a9efe6 |
2022-08-nounsdao/contracts/governance/NounsDAOProxy.sol | 8f1078c179bb62ed1da7eb176adea138571e9b6e |
2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol | 6cf98771a9206dda38a0900791b2d2e1f6556334 |
2022-08-nounsdao/contracts/base/ERC721Enumerable.sol | 0552a2f4170e2f3fad483ab0a90a0d9c50a92377 |
block.basefee
Member "basefee" not found or not visible after argument-dependent lookup in block.
uint256 gasPrice = min(tx.gasprice, block.basefee + MAX_REFUND_PRIORITY_FEE);
Change pragma to ^0.8.7
that block.basefee
was introduced.
None.
diff --git a/contracts/governance/NounsDAOLogicV1.sol b/contracts/governance/NounsDAOLogicV1.sol index 5654382..4dcdd72 100644 --- a/contracts/governance/NounsDAOLogicV1.sol +++ b/contracts/governance/NounsDAOLogicV1.sol @@ -101,7 +101,7 @@ contract NounsDAOLogicV1 is NounsDAOStorageV1, NounsDAOEvents { bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)'); /** - * @notice Used to initialize the contract during delegator contructor + * @notice Used to initialize the contract during delegator constructor * @param timelock_ The address of the NounsDAOExecutor * @param nouns_ The address of the NOUN tokens * @param vetoer_ The address allowed to unilaterally veto proposals @@ -643,7 +643,7 @@ contract NounsDAOLogicV1 is NounsDAOStorageV1, NounsDAOEvents { } /** - * @notice Burns veto priviledges + * @notice Burns veto privileges * @dev Vetoer function destroying veto power forever */ function _burnVetoPower() public { diff --git a/contracts/governance/NounsDAOLogicV2.sol b/contracts/governance/NounsDAOLogicV2.sol index ccd7432..90251a2 100644 --- a/contracts/governance/NounsDAOLogicV2.sol +++ b/contracts/governance/NounsDAOLogicV2.sol @@ -112,7 +112,7 @@ contract NounsDAOLogicV2 is NounsDAOStorageV2, NounsDAOEventsV2 { error UnsafeUint16Cast(); /** - * @notice Used to initialize the contract during delegator contructor + * @notice Used to initialize the contract during delegator constructor * @param timelock_ The address of the NounsDAOExecutor * @param nouns_ The address of the NOUN tokens * @param vetoer_ The address allowed to unilaterally veto proposals @@ -845,7 +845,7 @@ contract NounsDAOLogicV2 is NounsDAOStorageV2, NounsDAOEventsV2 { } /** - * @notice Burns veto priviledges + * @notice Burns veto privileges * @dev Vetoer function destroying veto power forever */ function _burnVetoPower() public {
VS Code
🌟 Selected for report: IllIllI
Also found by: 0x040, 0x1f8b, 0xDjango, 0xNazgul, 0xNineDec, 0xSmartContract, 0xbepresent, 0xc0ffEE, 0xkatana, 2997ms, ACai, Amithuddar, Aymen0909, Ben, BipinSah, Bjorn_bug, Bnke0x0, CertoraInc, Ch_301, Chom, CodingNameKiki, Deivitto, DevABDee, DimitarDimitrov, Diraco, Dravee, ElKu, EthLedger, Fitraldys, Funen, GalloDaSballo, GimelSec, Guardian, IgnacioB, JC, JohnSmith, Junnon, KIntern_NA, Lambda, LeoS, Noah3o6, Olivierdem, Polandia94, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, SaharAP, Saintcode_, SerMyVillage, Shishigami, Sm4rty, SooYa, TomJ, Tomio, Tomo, Waze, Yiko, _Adam, __141345__, a12jmx, ajtra, ak1, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, catchup, ch0bu, d3e4, delfin454000, djxploit, durianSausage, erictee, exolorkistis, fatherOfBlocks, francoHacker, gogo, hyh, ignacio, jag, joestakey, karanctf, ladboy233, lucacez, lukris02, m_Rassska, martin, medikko, mics, mrpathfindr, natzuu, newfork01, oyc_109, pauliax, peritoflores, pfapostol, prasantgupta52, rbserver, ret2basic, rfa, robee, rokinot, rotcivegaf, rvierdiiev, sach1r0, saian, samruna, seyni, shark, shr1ftyy, sikorico, simon135, sryysryy, tay054, tnevler, wagmi, zishansami
30.7513 USDC - $30.75
&&
abi.encodePacked()
File Name | SHA-1 Hash |
---|---|
2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol | e0e939a91c5d5c3148ae744741646e3f440d1e3d |
2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol | 96223a722bf49513779653adff38b72db55fce3f |
2022-08-nounsdao/contracts/governance/NounsDAOInterfaces.sol | 798ddc7b42dff8950e968af112bba2df70a9efe6 |
2022-08-nounsdao/contracts/governance/NounsDAOProxy.sol | 8f1078c179bb62ed1da7eb176adea138571e9b6e |
2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol | 6cf98771a9206dda38a0900791b2d2e1f6556334 |
2022-08-nounsdao/contracts/base/ERC721Enumerable.sol | 0552a2f4170e2f3fad483ab0a90a0d9c50a92377 |
Pre-increments cost less gas compared to post-increments.
proposalCount++;
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
proposalCount++;
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
Change i++
to ++i
.
VS Code
In Solidity 0.8+, there’s a default overflow check on unsigned integers.
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
One example is the code would go from:
for (uint256 i = 0; i < proposal.targets.length; i++) { queueOrRevertInternal( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta ); }
to:
for (uint256 i = 0; i < proposal.targets.length;) { queueOrRevertInternal( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta ); unchecked { i++; } }
VS Code
If a variable is not set/initialized, it is assumed to have the default value (0
, false
, 0x0
, etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
Do not initialize variables with default values.
VS Code
Storage arrays incur a Gwarmaccess (100 gas), memory arrays use MLOAD (3 gas) and calldata arrays use CALLDATALOAD (3 gas).
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
for (uint256 i = 0; i < proposal.targets.length; i++) {
Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset.
VS Code
If a variable is not set/initialized, it is assumed to have the default value (0
, false
, 0x0
, etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.
uint256 lower = 0;
uint8 public constant decimals = 0;
uint32 lower = 0;
Do not initialize variables with default values.
VS Code
Less expensive and able to use dynamic information in them.
Use custom errors.
VS Code
A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.
While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
uint256 center = upper - (upper - lower) / 2;
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.
VS Code
&&
Using multiple require statements can save gas.
require( votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 'NounsDAO::initialize: invalid voting period' );
require( votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 'NounsDAO::initialize: invalid voting delay' );
require( proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 'NounsDAO::initialize: invalid proposal threshold bps' );
require( targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, 'NounsDAO::propose: proposal function information arity mismatch' );
require( newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 'NounsDAO::_setVotingDelay: invalid voting delay' );
require( newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 'NounsDAO::_setVotingPeriod: invalid voting period' );
require( newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' );
require( newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND, 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' );
require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
require( votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 'NounsDAO::initialize: invalid voting period' );
require( votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 'NounsDAO::initialize: invalid voting delay' );
require( proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 'NounsDAO::initialize: invalid proposal threshold' );
require( quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS, 'NounsDAO::initialize: invalid proposal threshold' );
require( targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, 'NounsDAO::propose: proposal function information arity mismatch' );
require( newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 'NounsDAO::_setVotingDelay: invalid voting delay' );
require( newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 'NounsDAO::_setVotingPeriod: invalid voting period' );
require( newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 'NounsDAO::_setProposalThreshold: invalid proposal threshold' );
require( newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS, 'NounsDAO::_setProposalThreshold: invalid proposal threshold' );
require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
Use multiple require statements.
VS Code
abi.encodePacked()
abi.encode()
is less efficient than abi.encodePacked()
.
!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
Replace abi.encode()
with abi.encodePacked()
.
VS Code