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: 13/93
Findings: 3
Award: $907.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: gzeon
Also found by: AuditsAreUS, BowTiedWardens, Ruhum, pedroais, shenwilly, teddav
The owner of ForgottenRunesWarriorsMinter.sol
contract can transfer all funds in the contract, including the refunds intended to be sent back to DA minters.
While it is unlikely for the owner to steal users' funds, the owner key can still be compromised.
setVaultAddress
, setting their address as the new vault.withdrawAll
, stealing all funds including refunds.setVaultAddress
function so fund can't be stolen even if key is compromised.#0 - KenzoAgada
2022-06-06T13:06:39Z
Duplicate of #178
#1 - gzeoneth
2022-06-18T18:30:22Z
Duplicate of #187
🌟 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.3871 USDC - $30.39
The code heavily relies on the owner to properly setup and manage the contract during and after the minting process. While the team is fully doxxed, there is still the risk of compromised key and faulty script deployment.
Consider removing setter functions that are unlikely to be called once deployment is done, and add more sanity checks when setting critical variables (ex: minimum/maximum values, zero-address checks).
In case of DA not minting out, setFinalPrice
need to be manually set by the owner.
If there's a problem that prevents the owner from setting the final price before the Mintlist or Public phase started, unaware users might mint at the highest price 2.5e
before owner sets the correct price, causing reputational risk for the team.
Consider adding a new variable isfinalPriceSet
to track whether finalPrice
has been changed (whether through DA minting out or manually set) and only allow mintlist/public mint when it is set to true.
// bidSummon() ... if (numSold == maxDaSupply) { // optimistic: save gas by not setting on every mint, but will // require manual `setFinalPrice` before refunds if da falls short finalPrice = currentPrice; isfinalPriceSet = true; }
function setFinalPrice(uint256 _newPrice) public onlyOwner { finalPrice = _newPrice; isfinalPriceSet = true; }
function mintlistStarted() public view returns (bool) { return isfinalPriceSet && block.timestamp > mintlistStartTime; } function publicStarted() public view returns (bool) { return isfinalPriceSet && block.timestamp > publicStartTime; }
newDaStartTime
input validationAs the DA phase is expected to launch first, there should be a check for this in setPhaseTimes in case of faulty deployment script setting the wrong value for newDaStartTime
.
Add the check for newDaStartTime
:
require( newMintlistStartTime >= newDaStartTime, 'Set mintlist after dutch auction' ); // or alternatively, a stricter check which includes the minimum DA duration: require( newMintlistStartTime >= newDaStartTime + daPriceCurveLength, 'Set mintlist after dutch auction' );
When refunding contract minters that didn't implement token transfers, the WETH will be stuck forever in the contract. Consider using a pull approach and let the minters claim the refund by themselves.
In case the contract didn't implement selfRefund
, it's still safe as team can withdraw the unclaimed refund and manually transfer it to the minter.
Consider using pull approach by reducing daAmountRefunded
on failed transfer and let them manually call selfRefund
instead.
function _safeTransferETHWithFallback(address to, uint256 amount) internal { if (!_safeTransferETH(to, amount)) { // IWETH(weth).deposit{value: amount}(); // IERC20(weth).transfer(to, amount); daAmountRefunded[to] -= amount; } }
🌟 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
ForgottenRunesWarriorsGuild.forwardERC20s
It's almost impossible for msg.sender
to be address zero so L174 can safely be removed.
ForgottenRunesWarriorsMinter.forwardERC20s
It's almost impossible for msg.sender
to be address zero so L628 can safely be removed.
ForgottenRunesWarriorsMinter.teamSummon
OpenZeppelin guards against minting to zero address so L258 can safely be removed.