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: 70/160
Findings: 2
Award: $52.10
🌟 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.4386 USDC - $35.44
The ProposalCondensed
struct doesn't include the calldatas
, targets
, values
, and signatures
properties. The contract's proposals()
function used to retrieve existing proposals externally only returns a ProposalCondensed
. It makes it hard for someone to read the complete proposal externally.
In NounsDAOLogicV2._burnVetoPower()
there's a comment that's a left-over from a copy & pasting 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
16.6573 USDC - $16.66
++i
instead of i++
to save gasunchecked
when incrementing the loop iteratoraddress(0)
calldata
instead of memory
if you don't mutate a function parametermemory
instead of storage
when reading a state variable++i
instead of i++
to save gas./contracts/NounsDescriptor.sol:110: for (uint256 i = 0; i < newColors.length; i++) { ./contracts/NounsDescriptor.sol:120: for (uint256 i = 0; i < _backgrounds.length; i++) { ./contracts/NounsDescriptor.sol:130: for (uint256 i = 0; i < _bodies.length; i++) { ./contracts/NounsDescriptor.sol:140: for (uint256 i = 0; i < _accessories.length; i++) { ./contracts/NounsDescriptor.sol:150: for (uint256 i = 0; i < _heads.length; i++) { ./contracts/NounsDescriptor.sol:160: for (uint256 i = 0; i < _glasses.length; i++) { ./contracts/SVGRenderer.sol:111: for (uint256 i = 0; i < image.draws.length; i++) { ./contracts/libs/MultiPartRLEToSVG.sol:90: for (uint256 i = 0; i < image.rects.length; i++) { ./contracts/NounsArt.sol:131: for (uint256 i = 0; i < _backgrounds.length; i++) { ./contracts/NounsArt.sol:432: for (uint256 i = 0; i < len; i++) { ./contracts/governance/NounsDAOLogicV2.sol:292: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:330: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:357: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:382: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:281: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:319: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:346: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:371: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:226: proposalCount++; ./contracts/governance/NounsDAOLogicV1.sol:216: proposalCount++;
unchecked
when incrementing the loop iteratorThe iterator can't overflow in most cases because of the loop's condition. Using unchecked
will save gas.
for (uint i; i < length;) { unchecked {++i} }
./contracts/NounsDescriptor.sol:110: for (uint256 i = 0; i < newColors.length; i++) { ./contracts/NounsDescriptor.sol:120: for (uint256 i = 0; i < _backgrounds.length; i++) { ./contracts/NounsDescriptor.sol:130: for (uint256 i = 0; i < _bodies.length; i++) { ./contracts/NounsDescriptor.sol:140: for (uint256 i = 0; i < _accessories.length; i++) { ./contracts/NounsDescriptor.sol:150: for (uint256 i = 0; i < _heads.length; i++) { ./contracts/NounsDescriptor.sol:160: for (uint256 i = 0; i < _glasses.length; i++) { ./contracts/NounsArt.sol:131: for (uint256 i = 0; i < _backgrounds.length; i++) { ./contracts/NounsArt.sol:432: for (uint256 i = 0; i < len; i++) { ./contracts/governance/NounsDAOLogicV2.sol:292: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:330: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:357: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV2.sol:382: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:281: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:319: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:346: for (uint256 i = 0; i < proposal.targets.length; i++) { ./contracts/governance/NounsDAOLogicV1.sol:371: for (uint256 i = 0; i < proposal.targets.length; i++) {
It's just a waste of gas this the zero value is assigned by default.
address(0)
It's a waste to check whether msg.sender != address(0)
calldata
instead of memory
if you don't mutate a function parameter./contracts/governance/NounsDAOLogicV2.sol:185: address[] memory targets, ./contracts/governance/NounsDAOLogicV2.sol:186: uint256[] memory values, ./contracts/governance/NounsDAOLogicV2.sol:187: string[] memory signatures, ./contracts/governance/NounsDAOLogicV2.sol:188: bytes[] memory calldatas, ./contracts/governance/NounsDAOLogicV2.sol:189: string memory description ./contracts/governance/NounsDAOLogicV2.sol:308: string memory signature, ./contracts/governance/NounsDAOLogicV2.sol:309: bytes memory data, ./contracts/governance/NounsDAOLogicV2.sol:536: string memory reason
memory
instead of storage
when reading a state variable./contracts/governance/NounsDAOLogicV2.sol:413: Proposal storage p = _proposals[proposalId]; ./contracts/governance/NounsDAOLogicV2.sol:434: Proposal storage proposal = _proposals[proposalId]; ./contracts/governance/NounsDAOLogicV2.sol:463: Proposal storage proposal = _proposals[proposalId]; ./contracts/governance/NounsDAOLogicV2.sol:878: Proposal storage proposal = _proposals[proposalId];
Since Solidity v0.8.0 over- & underflow checks are built-in. Checking for that yourself is a waste of gas: