Platform: Code4rena
Start Date: 03/05/2022
Pot Size: $30,000 USDC
Total HM: 6
Participants: 93
Period: 3 days
Judge: gzeon
Id: 118
League: ETH
Rank: 26/93
Findings: 3
Award: $292.27
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Kulk0
Also found by: 0x1f8b, 0xDjango, BowTiedWardens, Dinddle, broccolirob, defsec, dirk_y, hyh, rajatbeladiya, throttle, unforgiven
Currently it is possible for the team to mint new tokens at any point in the timeline with teamSummon
. This could potentially be a problem if the team mints more than totalSupply - maxForSale - maxForClaim
. In this instance it would mean that the publicSummon
(and also claimSummon
) would actually fail to mint, even though numSold < maxForSale
.
Although this situation is unlikely to occur deliberately since the team are assumed to be acting in good faith, it could occur accidentally and impact the whole success of the launch if this situation occurred since the community would have been expecting a certain guaranteed amount to be available to them.
I deem this to be of Medium severity since:
"Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions"
This situation can occur because teamSummon
does not have a counter or limit to how many tokens can be minted through this call. The only condition that has to be satisfied is that totalMinted < maxSupply
. See logic here.
VSCode
teamSummon
should also have a counter and a max cap, as with the other summon methods. I propose the following lines to be added to the method:
require(numTeam < maxForTeam); require(count > 0); require(numTeam + count <= maxForTeam); numTeam += count;
where maxForTeam
= totalSupply - maxForClaim - maxForSale
#0 - KenzoAgada
2022-06-06T05:39:24Z
Duplicate of #104.
🌟 Selected for report: defsec
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0x52, 0xDjango, 0xf15ers, 0xkatana, 0xliumin, AuditsAreUS, BowTiedWardens, CertoraInc, Cr4ckM3, Funen, GimelSec, Hawkeye, IllIllI, Kulk0, M0ndoHEHE, MaratCerby, Picodes, Ruhum, TerrierLover, TrungOre, VAD37, WatchPug, berndartmueller, broccolirob, catchup, cccz, cryptphi, csanuragjain, delfin454000, dirk_y, eccentricexit, ellahi, fatherOfBlocks, gzeon, hake, hansfriese, hickuphh3, horsefacts, hubble, hyh, ilan, joestakey, kebabsec, kenta, kenzo, leastwood, m9800, marximimus, minhquanym, oyc_109, p4st13r4, pauliax, pedroais, peritoflores, plotchy, rajatbeladiya, reassor, rfa, robee, rotcivegaf, samruna, shenwilly, shung, simon135, sorrynotsorry, sseefried, teddav, throttle, tintin, unforgiven, z3s
30.2807 USDC - $30.28
During my audit of this project I found 2 low priority findings:
Technically for all methods checking whether a phase has started (e.g. daStarted
, mintListStarted
etc.), the check should be (note the change of comparator):
return block.timestamp >= daStartTime;
When you say a phase is starting at a particular time, it should start on (not after) that time. In reality this isn't a problem though as it's only 1 second difference.
finalPrice
should have a flag that states whether it has been updatedCurrently, if the DA phase sells out, the finalPrice
is set to currentPrice
. However, if the DA phase does not sell out, it is the responsibility of the owner to call setFinalPrice
for the price calculations to be correct for the follow on phases. It would be safer to have an additional storage variable boolean called something like finalPriceUpdated
that can be set to true
when the final price has been updated (either automatically or by the owner). The other summon phases could then have an additional require check like:
require(finalPriceUpdated);
This would ensure subsequent phases all have the correct price and ensures that it is not possible for any of the community to mint at the incorrect price.
🌟 Selected for report: BowTiedWardens
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xProf, 0xc0ffEE, 0xf15ers, 0xkatana, 0xliumin, ACai, AlleyCat, CertoraInc, Cityscape, Cr4ckM3, DavidGialdi, Dinddle, FSchmoede, Funen, GimelSec, Hawkeye, IllIllI, Kulk0, M0ndoHEHE, MaratCerby, MiloTruck, Picodes, RoiEvenHaim, Tadashi, TerrierLover, TrungOre, VAD37, WatchPug, antonttc, catchup, defsec, delfin454000, dirk_y, eccentricexit, ellahi, fatherOfBlocks, gzeon, hake, hansfriese, hickuphh3, horsefacts, ilan, joestakey, kebabsec, kenta, kenzo, marximimus, minhquanym, noobie, oyc_109, p4st13r4, pauliax, rajatbeladiya, reassor, rfa, robee, rotcivegaf, saian, samruna, shenwilly, shung, simon135, slywaters, sorrynotsorry, throttle, unforgiven, z3s
15.4498 USDC - $15.45
Through this audit I found one area in particular in which gas could be optimised. In bidSummon
, every time this method is called the msg.sender
is appended to the daMinters
array, even if the user has already been added. This results in unnecessary storage usage, which costs gas.
Alternatively I would suggest using a mapping in tandem with this array. For instance you could have a mapping like:
mapping(address => bool) public hasDaMinted;
In bidSummon
you could then add the following logic:
if (!hasDaMinted(msg.sender)) { hasDaMinted(msg.sender) = true; daMinters.push(msg.sender); }
Although this logic uses slightly more gas during execution, overall I believe you should save gas from lower storage usage costs and in the refund logic (fewer items to iterate over in the array).