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: 154/160
Findings: 1
Award: $16.66
๐ Selected for report: 0
๐ Solo Findings: 0
๐ 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
16.6573 USDC - $16.66
[1] 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.
There are 19 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #1 126: require( 127: votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 128: 'NounsDAO::initialize: invalid voting period' 129: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #2 130: require( 131: votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 132: 'NounsDAO::initialize: invalid voting delay' 133: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #3 134: require( 135: proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 136: 'NounsDAO::initialize: invalid proposal threshold' 137: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #4 138: require( 139: quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS, 140: 'NounsDAO::initialize: invalid proposal threshold' 141: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #5 191: require( 192: targets.length == values.length && 193: targets.length == signatures.length && 194: targets.length == calldatas.length, 195: 'NounsDAO::propose: proposal function information arity mismatch' 196: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #6 531: require( 532: newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 533: 'NounsDAO::_setVotingDelay: invalid voting delay' 534: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #7 547: require( 548: newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 549: 'NounsDAO::_setVotingPeriod: invalid voting period' 550: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #8 564: require( 565: newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 566: newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 567: 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 568: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #9 582: require( 583: newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS, 584: 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 585: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #10 617: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #11 137: require( 138: votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 139: 'NounsDAO::initialize: invalid voting period' 140: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #12 141: require( 142: votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 143: 'NounsDAO::initialize: invalid voting delay' 144: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #13 145: require( 146: proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 147: 'NounsDAO::initialize: invalid proposal threshold bps' 148: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #14 201: require( 202: targets.length == values.length && 203: targets.length == signatures.length && 204: targets.length == calldatas.length, 205: 'NounsDAO::propose: proposal function information arity mismatch' 206: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #15 623: require( 624: newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 625: 'NounsDAO::_setVotingDelay: invalid voting delay' 626: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #16 639: require( 640: newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 641: 'NounsDAO::_setVotingPeriod: invalid voting period' 642: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #17 656: require( 657: newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 658: newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 659: 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' 660: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #18 677: require( 678: newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && 679: newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND, 680: 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' 681: );
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #19 819: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
[2] <ARRAY>.LENGTH
SHOULD NOT BE LOOKED UP IN EVERY LOOP OF A FOR
-LOOP
The overheads outlined below are PER LOOP, excluding the first loop
-storage arrays incur a Gwarmaccess (100 gas) -memory arrays use MLOAD (3 gas) -calldata arrays use CALLDATALOAD (3 gas) 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.
There are 8 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #1 281: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #2 319: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #3 346: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #4 371: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #5 292: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #6 330: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #7 357: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #8 382: for (uint256 i = 0; i < proposal.targets.length; i++) {
[3] ABI.ENCODE()
IS LESS EFFICIENT THAN ABI.ENCODEPACKED()
There are 8 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #1 302: !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #2 480: abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol #3 482: bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #4 313: !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #5 572: abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol #6 574: bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 135: abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), getChainId(), address(this)) 137: bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
[4] USING PRIVATE
RATHER THAN PUBLIC
FOR CONSTANTS, SAVES GAS
If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table
There are 31 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 67: string public constant name = 'Nouns DAO'; #1 70: uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; // 1 basis point or 0.01% #2 73: uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; // 1,000 basis points or 10% #3 76: uint256 public constant MIN_VOTING_PERIOD = 5_760; // About 24 hours #4 79: uint256 public constant MAX_VOTING_PERIOD = 80_640; // About 2 weeks #5 82: uint256 public constant MIN_VOTING_DELAY = 1; #6 85: uint256 public constant MAX_VOTING_DELAY = 40_320; // About 1 week #7 88: uint256 public constant MIN_QUORUM_VOTES_BPS = 200; // 200 basis points or 2% #8 91: uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20% #9 94: uint256 public constant proposalMaxOperations = 10; // 10 actions #10 97: bytes32 public constant DOMAIN_TYPEHASH =keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); #11 101: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)'); #12
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 59: string public constant name = 'Nouns DAO'; #13 62: uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; #14 65: uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; #15 68: uint256 public constant MIN_VOTING_PERIOD = 5_760; #16 71: uint256 public constant MAX_VOTING_PERIOD = 80_640; #17 74: uint256 public constant MIN_VOTING_DELAY = 1; #18 77: uint256 public constant MAX_VOTING_DELAY = 40_320; #19 80: uint256 public constant MIN_QUORUM_VOTES_BPS_LOWER_BOUND = 200; #20 83: uint256 public constant MIN_QUORUM_VOTES_BPS_UPPER_BOUND = 2_000; #21 86: uint256 public constant MAX_QUORUM_VOTES_BPS_UPPER_BOUND = 6_000; #22 89: uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; #23 92: uint256 public constant proposalMaxOperations = 10; #24 95: uint256 public constant MAX_REFUND_PRIORITY_FEE = 2 gwei; #25 98: uint256 public constant REFUND_BASE_GAS = 36000; #26 101: bytes32 public constant DOMAIN_TYPEHASH = #27 105: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)'); #28
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 41: uint8 public constant decimals = 0; #29 59: bytes32 public constant DOMAIN_TYPEHASH = #30 63: bytes32 public constant DELEGATION_TYPEHASH = #31
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Checkpointable.sol#L41
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Checkpointable.sol#L59
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Checkpointable.sol#L63
[5] DUPLICATED REQUIRE()
/REVERT()
CHECKS SHOULD BE REFACTORED TO A MODIFIER OR FUNCTION
Saves deployment cost
There are 6 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 336: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 365: require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 123: require(msg.sender == admin, 'NounsDAO::initialize: admin only');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 134: require(msg.sender == admin, 'NounsDAO::initialize: admin only');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 347: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 377: require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer');
[6] ++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 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 8 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 281: for (uint256 i = 0; i < proposal.targets.length; i++) { 319: for (uint256 i = 0; i < proposal.targets.length; i++) { 346: for (uint256 i = 0; i < proposal.targets.length; i++) { 371: for (uint256 i = 0; i < proposal.targets.length; i++) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 292: for (uint256 i = 0; i < proposal.targets.length; i++) { 330: for (uint256 i = 0; i < proposal.targets.length; i++) { 357: for (uint256 i = 0; i < proposal.targets.length; i++) { 382: for (uint256 i = 0; i < proposal.targets.length; i++) {
[7] PUBLIC
FUNCTIONS NOT CALLED BY THE CONTRACT SHOULD BE DECLARED EXTERNAL
INSTEAD
Contracts are allowed to override their parentsโ functions and change the visibility from external
to public
and can save gas by doing so.
There are 10 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 113: function initialize( 114: address timelock_, 115: address nouns_, 116: address vetoer_, 117: uint256 votingPeriod_, 118: uint256 votingDelay_, 119: uint256 proposalThresholdBPS_, 120: uint256 quorumVotesBPS_ 121: ) public virtual {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 174: function propose( 175: address[] memory targets, 176: uint256[] memory values, 177: string[] memory signatures, 178: bytes[] memory calldatas, 179: string memory description 180: ) public returns (uint256) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 649: function _burnVetoPower() public {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 124: function initialize( 125: address timelock_, 126: address nouns_, 127: address vetoer_, 128: uint256 votingPeriod_, 129: uint256 votingDelay_, 130: uint256 proposalThresholdBPS_, 131: DynamicQuorumParams calldata dynamicQuorumParams_ 132: ) public virtual {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 184: function propose( 185: address[] memory targets, 186: uint256[] memory values, 187: string[] memory signatures, 188: bytes[] memory calldatas, 189: string memory description 190: ) public returns (uint256) {
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 851: function _burnVetoPower() public {
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 163: function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96) {
File : 2022-08-nounsdao/contracts/base/ERC721Enumerable.sol 54: function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { 61: function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { 76: function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Enumerable.sol#L54
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Enumerable.sol#L61
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Enumerable.sol#L76
[8] DONโT COMPARE BOOLEAN EXPRESSIONS TO BOOLEAN LITERALS
if (<x> == true)
=> if(<x>)
, if (<x> == false)
=> if (!<x>)
There are 2 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 505: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 597: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');
[9] USE ASSEMBLY TO CHECK FOR ADDRESS(0) to save gas.
There are 11 instances of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 124: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 125: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 364: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 485: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 617: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 135: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 136: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 375: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 577: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 819: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 140: require(signatory != address(0), 'ERC721Checkpointable::delegateBySig: invalid signature');
[10] USAGE OF UINTS/INTS
SMALLER THAN 32 BYTES (256 BITS) INCURS OVERHEAD
When using elements that are smaller than 32 bytes, your contractโs gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
There are 50 instances of this issue
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 41: uint8 public constant decimals = 0; 48: uint32 fromBlock; 49: uint96 votes; 130: uint8 v, 152: uint32 nCheckpoints = numCheckpoints[account]; 181: uint32 lower = 0; 182: uint32 upper = nCheckpoints - 1; 184: uint32 center = upper - (upper - lower) / 2; 205: uint96 amount = votesToDelegate(delegator); 213: uint96 amount 217: uint32 srcRepNum = numCheckpoints[srcRep]; 218: uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; 219: uint96 srcRepNew = sub96(srcRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount underflows'); 224: uint32 dstRepNum = numCheckpoints[dstRep]; 225: uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; 226: uint96 dstRepNew = add96(dstRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount overflows'); 234: uint32 nCheckpoints, 235: uint96 oldVotes, 236: uint96 newVotes 238: uint32 blockNumber = safe32( 264: uint96 a, 265: uint96 b, 268: uint96 c = a + b; 274: uint96 a, 275: uint96 b,
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/base/ERC721Checkpointable.sol#L41
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 101: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)'); 450: function castVote(uint256 proposalId, uint8 support) external { 462: uint8 support, 474: uint8 support 475: uint8 v 499: uint8 support 508: uint96 votes = nouns.getPriorVotes(voter, proposal.startBlock - votingDelay);
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 105: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)'); 503: function castRefundableVote(uint256 proposalId, uint8 support) external { 520: uint8 support, 535: uint8 support 539: uint96 votes = castVoteInternal(msg.sender, proposalId, support); 566: uint8 support 567: uint8 v, 591: uint8 support 592: internal returns (uint96) 600: uint96 votes = nouns.getPriorVotes(voter, proposalCreationBlock(proposal));
File : 2022-08-nounsdao/contracts/governance/NounsDAOInterfaces.sol 218: uint8 support; 220: uint96 votes; 322: uint8 support; 324: uint96 votes; 352: uint16 minQuorumVotesBPS; 354: uint16 maxQuorumVotesBPS; 357: uint32 quorumCoefficient; 363: uint32 fromBlock;
[11] USING STORAGE
INSTEAD OF MEMORY
FOR STRUCTS/ARRAYS SAVES GAS
When fetching data from a storage location, assigning the data to a memory
variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD
rather than a cheap stack read. Instead of declearing the variable with the memory
keyword, declaring the variable with the storage
keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory
variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory
, or if the array/struct is being read from another memory
array/struct
There is 4 instance of this issue:
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 185: Checkpoint memory cp = checkpoints[account][center];
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV1.sol 181: ProposalTemp memory temp;
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 191: ProposalTemp memory temp; 952: DynamicQuorumParamsCheckpoint memory cp = quorumParamsCheckpoints[center];
[12] DIVISION BY TWO SHOULD USE BIT SHIFTING
<x> / 2
is the same as <x> >>1
. The DIV
opcode costs 5 gas, whereas SHR
only costs 3 gas
There is 2 instance of this issue:
File : 2022-08-nounsdao/contracts/base/ERC721Checkpointable.sol 184: uint32 center = upper - (upper - lower) / 2;
File : 2022-08-nounsdao/contracts/governance/NounsDAOLogicV2.sol 951: uint256 center = upper - (upper - lower) / 2;
[13] INTERNAL FUNCTIONS ONLY CALLED ONCE CAN BE INLINED TO SAVE GAS
Not inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
There is 1 instance of this issue:
File : 2022-08-nounsdao/contracts/governance/NounsDAOProxy.sol 94: function delegateTo(address callee, bytes memory data) internal {
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOProxy.sol#L94