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: 63/93
Findings: 2
Award: $45.77
๐ Selected for report: 0
๐ Solo Findings: 0
๐ 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.2759 USDC - $30.28
Avoid floating pragmas for non-library contracts.
While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations.
A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.
It is recommended to pin to a concrete compiler version.
IForgottenRunesWarriorsGuild.sol 5: pragma solidity ^0.8.6; IForgottenRunesWarriorsMinter.sol 5: pragma solidity ^0.8.6; interfaces/IWETH.sol 3: pragma solidity ^0.8.6; ForgottenRunesWarriorsGuild.sol 1: pragma solidity ^0.8.0; ForgottenRunesWarriorsMinter.sol 1: pragma solidity ^0.8.0;
Checking addresses against zero-address during initialization or during setting is a security best-practice. However, such checks are missing in address variable initializations/changes in many places. Given that zero-address is used as an indicator for BNB, there is a greater risk of using it accidentally.
Impact: Allowing zero-addresses will lead to contract reverts and force redeployments if there are no setters for such address variables.
ForgottenRunesWarriorsGuild.sol 136: 137: function setMinter(address newMinter) public onlyOwner { 138: minter = newMinter; 139: }
ForgottenRunesWarriorsMinter.sol 530: 531: /** 532: * @notice set the warriors token address 533: */ 534: function setWarriorsAddress( 535: IForgottenRunesWarriorsGuild _newWarriorsAddress 536: ) public onlyOwner { 537: warriors = _newWarriorsAddress; 538: } 539: 540: /** 541: * @notice set the weth token address 542: */ 543: function setWethAddress(address _newWethAddress) public onlyOwner { 544: weth = _newWethAddress; 545: }
๐ 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.491 USDC - $15.49
Uninitialized variables are assigned with the types default value.
Explicitly initializing a variable with it's default value costs unnecesary gas.
ForgottenRunesWarriorsGuild.sol 24: uint256 public numMinted = 0; ForgottenRunesWarriorsMinter.sol 162: for (uint256 i = 0; i < numWarriors; i++) { 220: for (uint256 i = 0; i < numWarriors; i++) { 259: for (uint256 i = 0; i < count; i++) { 355: for (uint256 i = startIdx; i < endIdx + 1; i++) {
Shortening revert strings to fit in 32 bytes will decrease gas costs for deployment and gas costs when the revert condition has been met.
If the contract(s) in scope allow using Solidity >=0.8.4, consider using Custom Errors as they are more gas efficient while allowing developers to describe the error in detail using NatSpec.
ForgottenRunesWarriorsGuild.sol 70: 'ERC721Metadata: URI query for nonexistent token' 116: 'ERC721Burnable: caller is not owner nor approved' ForgottenRunesWarriorsMinter.sol 142: 'You can summon no more than 20 Warriors at a time' 148: 'Ether value sent is not sufficient'
the consequences of using extra functions:
ForgottenRunesWarriorsGuild.sol
the functions setMinter and initialize are redundant remove one of the functions to simply set the newMinter in a single function
since the function already has the onlyOwner modifier, it is unnecessary to check if msg.sender is address(0)
ForgottenRunesWarriorsGuild.sol 174: require(address(msg.sender) != address(0));
Constant variables are replaced at compile time by their values. By using constants for values that do not change, we can avoid fees of an SLOAD operation when using these values.
ForgottenRunesWarriorsMinter.sol 56: /// @notice The start price of the DA 57: uint256 public startPrice = 2.5 ether; 58: 59: /// @notice The lowest price of the DA 60: uint256 public lowestPrice = 0.6 ether; 61: 62: /// @notice The length of time for the price curve in the DA 63: uint256 public daPriceCurveLength = 380 minutes; 64: 65: /// @notice The interval of time in which the price steps down 66: uint256 public daDropInterval = 10 minutes; 67: 68: /// @notice The final price of the DA. Will be updated when DA is over and then used for subsequent phases 69: uint256 public finalPrice = 2.5 ether; 90: /// @notice The total number of tokens reserved for the DA phase 91: uint256 public maxDaSupply = 8000; 92: 93: /// @notice Tracks the total count of NFTs sold (vs. freebies) 94: uint256 public numSold; 95: 96: /// @notice Tracks the total count of NFTs for sale 97: uint256 public maxForSale = 14190; 98: 99: /// @notice Tracks the total count of NFTs claimed for free 100: uint256 public numClaimed; 101: 102: /// @notice Tracks the total count of NFTs that can be claimed 103: /// @dev While we will have a merkle root set for this group, putting a hard cap helps limit the damage of any problems with an overly-generous merkle tree 104: uint256 public maxForClaim = 1100;
Prefix increments are cheaper than postfix increments, eg ++i rather than i++
ForgottenRunesWarriorsMinter.sol 162: for (uint256 i = 0; i < numWarriors; i++) { 220: for (uint256 i = 0; i < numWarriors; i++) { 259: for (uint256 i = 0; i < count; i++) { 355: for (uint256 i = startIdx; i < endIdx + 1; i++) {
require statements can be placed earlier to reduce gas usage on revert ie move require to the top of the function if possible
move the following require statement up above line 183 to save gas in the case where the require statement fails
ForgottenRunesWarriorsMinter.sol 187: require( 188: MerkleProof.verify(_merkleProof, mintlist1MerkleRoot, node) || 189: MerkleProof.verify(_merkleProof, mintlist2MerkleRoot, node), 190: 'Invalid proof' 191: );
for variables only used once, changing it to inline saves gas
ForgottenRunesWarriorsMinter.sol 284: uint256 dropPerStep = (startPrice - lowestPrice) / 285: (daPriceCurveLength / daDropInterval); 287: uint256 elapsed = block.timestamp - daStartTime; 288: uint256 steps = elapsed / daDropInterval; 389: uint256 totalCostOfMints = finalPrice * daNumMinted[minter]; 390: uint256 refundsPaidAlready = daAmountRefunded[minter];