Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 76/168
Findings: 3
Award: $115.46
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MEP
Also found by: 0xSky, CertoraInc, MiloTruck, PwnPatrol, R2, Tointer, V_B, __141345__, antonttc, azephiar, cccz, d3e4, datapunk, davidbrai, easy_peasy, hansfriese, minhtrng, neumo, pcarranzav, peritoflores, scaraven, teawaterwire, tonisives, unforgiven, wagmi, zkhorse, zzzitron
5.6134 USDC - $5.61
baseTokenId
calculationbaseTokenId
can be 100 or more. It is not clearly explained in the documentation but I believe, for the logic, this is not a expected behaviour.
Founders are recipients of some of the first 100 tokenId.
The following logic that updates baseTokenId
will not include the module in the update.
` (baseTokenId += schedule) % 100`;
[-] (baseTokenId += schedule) % 100`; [+] baseTokenId= (baseTokendId + schedule) % 100`;
#0 - GalloDaSballo
2022-09-26T17:13:57Z
Dup of #269
🌟 Selected for report: CertoraInc
Also found by: 0xSky, PwnPatrol, Tomo, V_B, antonttc, bin2chen, pcarranzav, peritoflores, rbserver, scaraven, wagmi, zzzitron
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L82 https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L108
When you call _addfounders()
you pass FounderParams[].
The variable foundersPct is defined as a uint256
uint256 founderPct = _founders[i].ownershipPct;
This variable is unsafely casted to calculate that the totalOwnership is never more that 100 percent
if ((totalOwnership += uint8(founderPct)) > 100) revert INVALID_FOUNDER_OWNERSHIP();
Then is ok when you assign
newFounder.ownershipPct = uint8(founderPct);
However, when you enter the loop to schedule minting you dont limit the loop to the casted value
That means for example if you set founderPct
to 256 then it is stored as
ownershipPct = 1. But the founder will be scheduling 256 tokens.
for (uint256 j; j < founderPct; ++j) {@audit high // Get the available token id baseTokenId = _getNextTokenId(baseTokenId); // Store the founder as the recipient tokenRecipient[baseTokenId] = newFounder; emit MintScheduled(baseTokenId, founderId, newFounder); // Update the base token id (baseTokenId += schedule) % 100; }
It is better to do one safecast at the begining
[-] uint256 founderPct = _founders[i].ownershipPct; [+] uint8 founderPct = SafeCast.toUint8(_founders[i].ownershipPct);
Even better, I believe, is just to change the variable length is the definition of the struct FounderParams
[-] uint256 ownershipPct; [+] uint8 ownershipPct;
#0 - GalloDaSballo
2022-09-26T19:20:46Z
Dup of #303
🌟 Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
60.7742 USDC - $60.77
Upgradeable contracts do not follow the conventions so upgradeability is less flexible.
Storage gaps are a convention so removing them means that you will not be able to add new variables in future upgrades, at least no in your parent contracts. For your final contract it is better to add the gap in case you decide to inherit them in the future.
Gaps are missing in the following contracts.
​ Add this line at the end of every upgradeable contract
[+] uint256[50] private __gap;
#0 - GalloDaSballo
2022-09-20T19:16:24Z
Dup of #358
#1 - GalloDaSballo
2022-09-26T12:45:23Z
L