Forgotten Runes Warrior Guild contest - IllIllI's results

16,000 Warrior NFTs sold in a phased Dutch Auction.

General Information

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

Forgotten Runes

Findings Distribution

Researcher Performance

Rank: 3/93

Findings: 5

Award: $1,878.54

๐ŸŒŸ Selected for report: 0

๐Ÿš€ Solo Findings: 0

Findings Information

๐ŸŒŸ Selected for report: VAD37

Also found by: AuditsAreUS, IllIllI, MaratCerby, rfa, sorrynotsorry

Labels

bug
duplicate
2 (Med Risk)
upgraded by judge

Awards

1116.8018 USDC - $1,116.80

External Links

Judge has assessed an item in Issue #47 as Medium risk. The relevant finding follows:

1. Unsafe use of transfer()/transferFrom() with IERC20

Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert. Use OpenZeppelinโ€™s SafeERC20's safeTransfer()/safeTransferFrom() instead

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

175           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L175

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

402               IERC20(weth).transfer(to, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L402

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

629           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L629

#0 - gzeoneth

2022-06-18T17:09:02Z

Duplicate of #70

Findings Information

๐ŸŒŸ Selected for report: pedroais

Also found by: AuditsAreUS, BowTiedWardens, GimelSec, IllIllI, WatchPug, defsec, leastwood

Labels

bug
duplicate
2 (Med Risk)

Awards

542.7657 USDC - $542.77

External Links

Lines of code

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsGuild.sol#L35-L36 https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsGuild.sol#L141-L147

Vulnerability details

The purpose of a provenance hash is to help ensure that the auction is fair - that the sellers aren't able to deterministically assign who gets what, after all of the money has been spent, and that the buyers don't know which NFTs to focus their energies on buying. The ability to set it after funds have been transferred, effectively allows the seller to steal from the buyers in an undetectable way: once the seller knows the IDs of the NFTs he/she has bought via various sybil accounts, the seller can generate a provenance hash that favors the NFTs in those accounts, leaving the less valuable ones for the other buyers. It is important for the hash to be set in stone before money changes hands.

Impact

The auction that buyers thought was fair was in fact unfair, and because of this, buyers spend more to acquire their NFTs than they otherwise would have chosen to had they known it was unfair.

Proof of Concept

The hash is not a constant nor is it immutable

File: contracts/ForgottenRunesWarriorsGuild.sol   #1

35       /// @notice The provenance hash
36       string public METADATA_PROVENANCE_HASH = '';

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsGuild.sol#L35-L36

The hash can be changed at any time. The team running the auction can 'forget' to set it during the auction, and once they determine which NFTs they now control, they can set the hash to favor their NFT holdings, choosing some of the most valuable ones for themselves. It need not be after the whole auction is done - it can be set once they've bought a sufficient number of NFTs.

File: contracts/ForgottenRunesWarriorsGuild.sol   #2

141       /**
142        * @dev Sets provenance hash
143        * @param newHash string of the new minter
144        */
145       function setProvenanceHash(string memory newHash) public onlyOwner {
146           METADATA_PROVENANCE_HASH = newHash;
147       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsGuild.sol#L141-L147

Tools Used

Code inspection

Ideally the METADATA_PROVENANCE_HASH would be immutable and set in the constructor. At the very least setProvenanceHash() should revert after the first user mints, and mint() should revert if the hash has not yet been set.

#0 - wagmiwiz

2022-05-05T10:17:38Z

acknowledged but won't change

#1 - KenzoAgada

2022-06-06T07:08:50Z

Duplicate of #49.

#2 - gzeoneth

2022-06-18T18:15:38Z

Duplicate of #38

Awards

48.5872 USDC - $48.59

Labels

bug
duplicate
2 (Med Risk)
upgraded by judge

External Links

Judge has assessed an item in Issue #47 as Medium risk. The relevant finding follows:

4. Don't use payable.transfer()/payable.send()

The use of payable.transfer() is heavily frowned upon because it can lead to the locking of funds. The transfer() call requires that the recipient is either an EOA account, or is a contract that has a payable callback. For the contract case, the transfer() call only provides 2300 gas for the contract to complete its operations. This means the following cases can cause the transfer to fail:

  • The contract does not have a payable callback
  • The contract's payable callback spends more than 2300 gas (which is only enough to emit something)
  • The contract is called through a proxy which itself uses up the 2300 gas Use OpenZeppelin's Address.sendValue() instead
File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

164           require(payable(msg.sender).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L164

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

610           require(payable(vault).send(_amount));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L610

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

618           require(payable(vault).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L618

#0 - gzeoneth

2022-06-18T19:19:25Z

Duplicate of #254

Low Risk Issues

1. Unsafe use of transfer()/transferFrom() with IERC20

Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert. Use OpenZeppelinโ€™s SafeERC20's safeTransfer()/safeTransferFrom() instead

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

175           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L175

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

402               IERC20(weth).transfer(to, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L402

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

629           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L629

2. Return values of transfer()/transferFrom() not checked

Not all IERC20 implementations revert() when there's a failure in transfer()/transferFrom(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually making a payment

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

175           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L175

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

402               IERC20(weth).transfer(to, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L402

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

629           token.transfer(msg.sender, amount);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L629

3. Missing checks for address(0x0) when assigning values to address state variables

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

138           minter = newMinter;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L138

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

528           vault = _newVaultAddress;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L528

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

544           weth = _newWethAddress;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L544

4. Don't use payable.transfer()/payable.send()

The use of payable.transfer() is heavily frowned upon because it can lead to the locking of funds. The transfer() call requires that the recipient is either an EOA account, or is a contract that has a payable callback. For the contract case, the transfer() call only provides 2300 gas for the contract to complete its operations. This means the following cases can cause the transfer to fail:

  • The contract does not have a payable callback
  • The contract's payable callback spends more than 2300 gas (which is only enough to emit something)
  • The contract is called through a proxy which itself uses up the 2300 gas Use OpenZeppelin's Address.sendValue() instead
File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

164           require(payable(msg.sender).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L164

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

610           require(payable(vault).send(_amount));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L610

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

618           require(payable(vault).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L618

5. selfRefundsStartTime not updated when the other values are updated

setPhaseTimes() does not have a variable for updating the selfRefundsStartTime variable, so it may be overlooked. After watching your explanation video, it seems like a better approach would be to have selfRefundsStartTime become selfRefundsDelay and have selfRefundsStarted() return whether the current timestamp is past publicStartTime + selfRefundsDelay. That way if you "get hit by a bus", as you said, users will always be able to self-refund.

File: contracts/ForgottenRunesWarriorsMinter.sol   #1

480       function setPhaseTimes(
481           uint256 newDaStartTime,
482           uint256 newMintlistStartTime,
483           uint256 newPublicStartTime,
484           uint256 newClaimsStartTime
485       ) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsMinter.sol#L480-L485

Non-critical Issues

1. The nonReentrant modifier should occur before all other modifiers

This is a best-practice to protect against reentrancy in other modifiers

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

353           nonReentrant

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L353

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

364       function refundAddress(address minter) public onlyOwner nonReentrant {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L364

2. require()/revert() statements should have descriptive reason strings

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

164           require(payable(msg.sender).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L164

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

174           require(address(msg.sender) != address(0));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L174

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

610           require(payable(vault).send(_amount));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L610

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

618           require(payable(vault).send(address(this).balance));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L618

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

628           require(address(msg.sender) != address(0));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L628

3. public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

52       function initialize(address newMinter) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L52

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

85       function exists(uint256 tokenId) public view returns (bool) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L85

File: /contracts/ForgottenRunesWarriorsGuild.sol   #3

145       function setProvenanceHash(string memory newHash) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L145

File: /contracts/ForgottenRunesWarriorsGuild.sol   #4

163       function withdrawAll() public payable onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L163

File: /contracts/ForgottenRunesWarriorsGuild.sol   #5

173       function forwardERC20s(IERC20 token, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L173

File: /contracts/ForgottenRunesWarriorsMinter.sol   #6

337       function numDaMinters() public view returns (uint256) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L337

File: /contracts/ForgottenRunesWarriorsMinter.sol   #7

350       function issueRefunds(uint256 startIdx, uint256 endIdx)
351           public
352           onlyOwner
353           nonReentrant

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L350-L353

File: /contracts/ForgottenRunesWarriorsMinter.sol   #8

364       function refundAddress(address minter) public onlyOwner nonReentrant {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L364

File: /contracts/ForgottenRunesWarriorsMinter.sol   #9

371       function selfRefund() public nonReentrant {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L371

File: /contracts/ForgottenRunesWarriorsMinter.sol   #10

427       function pause() public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L427

File: /contracts/ForgottenRunesWarriorsMinter.sol   #11

434       function unpause() public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L434

File: /contracts/ForgottenRunesWarriorsMinter.sol   #12

469       function setSelfRefundsStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L469

File: /contracts/ForgottenRunesWarriorsMinter.sol   #13

480       function setPhaseTimes(
481           uint256 newDaStartTime,
482           uint256 newMintlistStartTime,
483           uint256 newPublicStartTime,
484           uint256 newClaimsStartTime
485       ) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L480-L485

File: /contracts/ForgottenRunesWarriorsMinter.sol   #14

505       function setMintlist1MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L505

File: /contracts/ForgottenRunesWarriorsMinter.sol   #15

513       function setMintlist2MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L513

File: /contracts/ForgottenRunesWarriorsMinter.sol   #16

520       function setClaimlistMerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L520

File: /contracts/ForgottenRunesWarriorsMinter.sol   #17

550       function setStartPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L550

File: /contracts/ForgottenRunesWarriorsMinter.sol   #18

557       function setLowestPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L557

File: /contracts/ForgottenRunesWarriorsMinter.sol   #19

564       function setDaPriceCurveLength(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L564

File: /contracts/ForgottenRunesWarriorsMinter.sol   #20

571       function setDaDropInterval(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L571

File: /contracts/ForgottenRunesWarriorsMinter.sol   #21

579       function setFinalPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L579

File: /contracts/ForgottenRunesWarriorsMinter.sol   #22

586       function setMaxDaSupply(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L586

File: /contracts/ForgottenRunesWarriorsMinter.sol   #23

593       function setMaxForSale(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L593

File: /contracts/ForgottenRunesWarriorsMinter.sol   #24

600       function setMaxForClaim(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L600

File: /contracts/ForgottenRunesWarriorsMinter.sol   #25

608       function withdraw(uint256 _amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L608

File: /contracts/ForgottenRunesWarriorsMinter.sol   #26

616       function withdrawAll() public payable onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L616

File: /contracts/ForgottenRunesWarriorsMinter.sol   #27

627       function forwardERC20s(IERC20 token, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L627

4. constants should be defined rather than using magic numbers

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

141               numWarriors > 0 && numWarriors <= 20,

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L141

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

211               numWarriors > 0 && numWarriors <= 20,

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L211

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

416           (bool success, ) = to.call{value: value, gas: 30_000}(new bytes(0));

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L416

5. Redundant cast

The type of the variable is the same as the type to which the variable is being cast

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

258           require(address(recipient) != address(0), 'address req');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L258

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

609           require(address(vault) != address(0), 'no vault');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L609

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

617           require(address(vault) != address(0), 'no vault');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L617

6. Use a more recent version of solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>)

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L1

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L1

7. Variable names that consist of all capital letters should be reserved for const/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

36       string public METADATA_PROVENANCE_HASH = '';

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L36

8. Non-library/interface files should use fixed compiler versions, not floating ones

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L1

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L1

9. File does not contain an SPDX Identifier

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

0   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L0

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

0   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L0

10. NatSpec is incomplete

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

106       /**
107        * @dev Create the contract and set the initial baseURI
108        * @param _warriors address the initial warriors contract address
109        */
110       constructor(IForgottenRunesWarriorsGuild _warriors, address _weth) {

Missing: @param _weth https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L106-L110

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

386        * @param minter address the address of the account that wants a refund
387        */
388       function refundOwed(address minter) public view returns (uint256) {

Missing: @return https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L386-L388

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

410        * @param value uint256 how much ETH to send
411        */
412       function _safeTransferETH(address to, uint256 value)
413           internal
414           returns (bool)

Missing: @return https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L410-L414

11. Missing event for critical parameter change

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

129       function setBaseURI(string memory newBaseURI) public onlyOwner {
130           baseTokenURI = newBaseURI;
131       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L129-L131

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

137       function setMinter(address newMinter) public onlyOwner {
138           minter = newMinter;
139       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L137-L139

File: /contracts/ForgottenRunesWarriorsGuild.sol   #3

145       function setProvenanceHash(string memory newHash) public onlyOwner {
146           METADATA_PROVENANCE_HASH = newHash;
147       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L145-L147

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

441       function setDaStartTime(uint256 _newTime) public onlyOwner {
442           daStartTime = _newTime;
443       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L441-L443

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

448       function setMintlistStartTime(uint256 _newTime) public onlyOwner {
449           mintlistStartTime = _newTime;
450       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L448-L450

File: /contracts/ForgottenRunesWarriorsMinter.sol   #6

455       function setPublicStartTime(uint256 _newTime) public onlyOwner {
456           publicStartTime = _newTime;
457       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L455-L457

File: /contracts/ForgottenRunesWarriorsMinter.sol   #7

462       function setClaimsStartTime(uint256 _newTime) public onlyOwner {
463           claimsStartTime = _newTime;
464       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L462-L464

File: /contracts/ForgottenRunesWarriorsMinter.sol   #8

469       function setSelfRefundsStartTime(uint256 _newTime) public onlyOwner {
470           selfRefundsStartTime = _newTime;
471       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L469-L471

File: /contracts/ForgottenRunesWarriorsMinter.sol   #9

505       function setMintlist1MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {
506           mintlist1MerkleRoot = newMerkleRoot;
507       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L505-L507

File: /contracts/ForgottenRunesWarriorsMinter.sol   #10

513       function setMintlist2MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {
514           mintlist2MerkleRoot = newMerkleRoot;
515       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L513-L515

File: /contracts/ForgottenRunesWarriorsMinter.sol   #11

520       function setClaimlistMerkleRoot(bytes32 newMerkleRoot) public onlyOwner {
521           claimlistMerkleRoot = newMerkleRoot;
522       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L520-L522

File: /contracts/ForgottenRunesWarriorsMinter.sol   #12

527       function setVaultAddress(address _newVaultAddress) public onlyOwner {
528           vault = _newVaultAddress;
529       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L527-L529

File: /contracts/ForgottenRunesWarriorsMinter.sol   #13

534       function setWarriorsAddress(
535           IForgottenRunesWarriorsGuild _newWarriorsAddress
536       ) public onlyOwner {
537           warriors = _newWarriorsAddress;
538       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L534-L538

File: /contracts/ForgottenRunesWarriorsMinter.sol   #14

543       function setWethAddress(address _newWethAddress) public onlyOwner {
544           weth = _newWethAddress;
545       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L543-L545

File: /contracts/ForgottenRunesWarriorsMinter.sol   #15

550       function setStartPrice(uint256 _newPrice) public onlyOwner {
551           startPrice = _newPrice;
552       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L550-L552

File: /contracts/ForgottenRunesWarriorsMinter.sol   #16

557       function setLowestPrice(uint256 _newPrice) public onlyOwner {
558           lowestPrice = _newPrice;
559       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L557-L559

File: /contracts/ForgottenRunesWarriorsMinter.sol   #17

564       function setDaPriceCurveLength(uint256 _newTime) public onlyOwner {
565           daPriceCurveLength = _newTime;
566       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L564-L566

File: /contracts/ForgottenRunesWarriorsMinter.sol   #18

571       function setDaDropInterval(uint256 _newTime) public onlyOwner {
572           daDropInterval = _newTime;
573       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L571-L573

File: /contracts/ForgottenRunesWarriorsMinter.sol   #19

579       function setFinalPrice(uint256 _newPrice) public onlyOwner {
580           finalPrice = _newPrice;
581       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L579-L581

File: /contracts/ForgottenRunesWarriorsMinter.sol   #20

586       function setMaxDaSupply(uint256 _newSupply) public onlyOwner {
587           maxDaSupply = _newSupply;
588       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L586-L588

File: /contracts/ForgottenRunesWarriorsMinter.sol   #21

593       function setMaxForSale(uint256 _newSupply) public onlyOwner {
594           maxForSale = _newSupply;
595       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L593-L595

File: /contracts/ForgottenRunesWarriorsMinter.sol   #22

600       function setMaxForClaim(uint256 _newSupply) public onlyOwner {
601           maxForClaim = _newSupply;
602       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L600-L602

12. Inconsistent variable naming conventions

Sometimes the plural form of the word is used as in claimsStartTime, but other times the singular is: claimlistMerkleRoot. The comments have similar issues as well. The code should use the same form for the same word.

File: /contracts/ForgottenRunesWarriorsMinter.sol (various lines)   #1

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol

13. Typos

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

20       /// @notice The start timestamp for mintlisters

mintlisters https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L20

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

41       /// @dev Having a backup merkle root lets us atomically update the merkletree without downtime on the frontend

merkletree https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L41

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

44       /// @notice The claimslist Merkle root

claimslist https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L44

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

476        * @param newMintlistStartTime uint256 the mintlst phase start time

mintlst https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L476

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

518        * @notice set the merkle root for the claimslist phase

claimslist https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L518

14. Non-exploitable re-entrancies

Code should follow the best-practice of check-effects-interaction

Reentrancy in ForgottenRunesWarriorsGuild.mint(address) (contracts/ForgottenRunesWarriorsGuild.sol#94-106): #1 External calls: - _safeMint(recipient,tokenId) (contracts/ForgottenRunesWarriorsGuild.sol#103) - IERC721Receiver(to).onERC721Received(_msgSender(),from,tokenId,_data) (node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol#376-386) State variables written after the call(s): - numMinted += 1 (contracts/ForgottenRunesWarriorsGuild.sol#104)

15. Consider having pause() and unpause() keep track of how long things were paused, and automatically add that amount of time to the contract times

This will make the pause/unpause procedure less error-prone, and will ensure that the full duration of a phase is always maintained

File: contracts/ForgottenRunesWarriorsMinter.sol   #1

427       function pause() public onlyOwner {
428           _pause();
429       }
430   
431       /**
432        * @notice unpause the contract
433        */
434       function unpause() public onlyOwner {
435           _unpause();
436       }

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsMinter.sol#L427-L436

Gas Optimizations

For your specific request about gas savings during issueRefunds(), the best way to reduce costs is to get large gas refunds for deleting storage entries. Every storage slot deleted/set to zero results in a gas refund of ~12,000 gas (Rsclear(15000) - Gsreset(2900)), up to half of the total cost of the transaction. If instead of keeping around that data for users after they've been refunded, you delete them, you'll get this refund. Since you have five mappings per user, deleting all five would result in a refund of ~60,000 gas per user. If you are worried about losing old state and being unable to help users debug issues related to refunds, you can just emit events for every refund, which will cost at most 2,000 gas per user, and will be visible on all block explorers. If you store all five mappings as a single mapping of structs (as is mentioned below), deletes will be cheaper because you're only accessing one mapping storage slot rather than five, and the fetching of the data to delete will be cheaper because the booleans will be returned from a single storage slot. In addition, rather than iterating over the daMinters array forwards, you can go backwards and delete entries along the way, saving another ~12,000 gas per entry.

1. Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Deletion is a lot more efficient

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

76       mapping(address => uint256) public daAmountPaid;
77   
78       /// @notice Tracks the total amount refunded to a given address for the DA
79       mapping(address => uint256) public daAmountRefunded;
80   
81       /// @notice Tracks the total count of NFTs minted by a given address in the DA
82       mapping(address => uint256) public daNumMinted;
83   
84       /// @notice Tracks if a given address minted in the mintlist
85       mapping(address => bool) public mintlistMinted;
86   
87       /// @notice Tracks the total count of NFTs claimed by a given address
88       mapping(address => bool) public claimlistMinted;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L76-L88

2. State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching will replace each Gwarmaccess (100 gas) with a much cheaper stack read. Less obvious fixes/optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

102           uint256 tokenId = numMinted;

numMinted https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L102

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

287           uint256 elapsed = block.timestamp - daStartTime;

daStartTime https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L287

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

610           require(payable(vault).send(_amount));

vault https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L610

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

618           require(payable(vault).send(address(this).balance));

vault https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L618

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

402               IERC20(weth).transfer(to, amount);

weth https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L402

File: /contracts/ForgottenRunesWarriorsMinter.sol   #6

292           if (stepDeduction > startPrice) {

startPrice https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L292

File: /contracts/ForgottenRunesWarriorsMinter.sol   #7

295           uint256 currentPrice = startPrice - stepDeduction;

startPrice https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L295

File: /contracts/ForgottenRunesWarriorsMinter.sol   #8

293               return lowestPrice;

lowestPrice https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L293

File: /contracts/ForgottenRunesWarriorsMinter.sol   #9

296           return currentPrice > lowestPrice ? currentPrice : lowestPrice;

lowestPrice https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L296

File: /contracts/ForgottenRunesWarriorsMinter.sol   #10

296           return currentPrice > lowestPrice ? currentPrice : lowestPrice;

lowestPrice https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L296

File: /contracts/ForgottenRunesWarriorsMinter.sol   #11

285               (daPriceCurveLength / daDropInterval);

daPriceCurveLength https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L285

File: /contracts/ForgottenRunesWarriorsMinter.sol   #12

288           uint256 steps = elapsed / daDropInterval;

daDropInterval https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L288

File: /contracts/ForgottenRunesWarriorsMinter.sol   #13

137           require(numSold + numWarriors <= maxDaSupply, 'Not enough remaining');

maxDaSupply https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L137

File: /contracts/ForgottenRunesWarriorsMinter.sol   #14

137           require(numSold + numWarriors <= maxDaSupply, 'Not enough remaining');

numSold https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L137

File: /contracts/ForgottenRunesWarriorsMinter.sol   #15

156           if (numSold == maxDaSupply) {

numSold https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L156

File: /contracts/ForgottenRunesWarriorsMinter.sol   #16

208           require(numSold + numWarriors <= maxForSale, 'Not enough remaining');

numSold https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L208

File: /contracts/ForgottenRunesWarriorsMinter.sol   #17

208           require(numSold + numWarriors <= maxForSale, 'Not enough remaining');

maxForSale https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L208

3. <x> += <y> costs more gas than <x> = <x> + <y> for state variables

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

104           numMinted += 1;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L104

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

154           numSold += numWarriors;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L154

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

193           numSold += 1;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L193

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

219           numSold += numWarriors;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L219

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

248           numClaimed += 1;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L248

4. internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

399       function _safeTransferETHWithFallback(address to, uint256 amount) internal {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L399

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

412       function _safeTransferETH(address to, uint256 value)
413           internal
414           returns (bool)

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L412-L414

5. ++i/i++ should be unchecked{++i}/unchecked{++i} when it is not possible for them to overflow, as is the case when used in for- and while-loops

This saves 30-40 gas PER LOOP

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

162           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L162

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

220           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L220

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

259           for (uint256 i = 0; i < count; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L259

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

355           for (uint256 i = startIdx; i < endIdx + 1; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L355

6. require()/revert() strings longer than 32 bytes cost extra gas

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

68           require(
69               _exists(tokenId),
70               'ERC721Metadata: URI query for nonexistent token'
71           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L68-L71

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

114           require(
115               _isApprovedOrOwner(_msgSender(), tokenId),
116               'ERC721Burnable: caller is not owner nor approved'
117           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L114-L117

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

140           require(
141               numWarriors > 0 && numWarriors <= 20,
142               'You can summon no more than 20 Warriors at a time'
143           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L140-L143

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

146           require(
147               msg.value >= (currentPrice * numWarriors),
148               'Ether value sent is not sufficient'
149           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L146-L149

File: /contracts/ForgottenRunesWarriorsMinter.sol   #5

210           require(
211               numWarriors > 0 && numWarriors <= 20,
212               'You can summon no more than 20 Warriors at a time'
213           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L210-L213

7. Using bools for storage incurs overhead

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 Use uint256(1) and uint256(2) for true/false

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

85       mapping(address => bool) public mintlistMinted;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L85

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

88       mapping(address => bool) public claimlistMinted;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L88

8. Use a more recent version of solidity

Use a solidity version of at least 0.8.2 to get compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L1

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

1   pragma solidity ^0.8.0;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L1

9. It costs more gas to initialize variables to zero than to let the default of zero be applied

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

162           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L162

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

220           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L220

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

259           for (uint256 i = 0; i < count; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L259

10. ++i costs less gas than ++i, especially when it's used in for-loops (--i/i-- too)

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

162           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L162

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

220           for (uint256 i = 0; i < numWarriors; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L220

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

259           for (uint256 i = 0; i < count; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L259

File: /contracts/ForgottenRunesWarriorsMinter.sol   #4

355           for (uint256 i = startIdx; i < endIdx + 1; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L355

11. Splitting require() statements that use && saves gas

See this issue for an example

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

140           require(
141               numWarriors > 0 && numWarriors <= 20,
142               'You can summon no more than 20 Warriors at a time'
143           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L140-L143

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

210           require(
211               numWarriors > 0 && numWarriors <= 20,
212               'You can summon no more than 20 Warriors at a time'
213           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L210-L213

12. Using private rather than public for constants, saves gas

If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

21       uint256 public constant MAX_WARRIORS = 16000;

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L21

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

32       string public constant R =
33           "Old men forget: yet all shall be forgot, But he'll remember with advantages What feats he did that day: then shall our names Familiar in his mouth as household words. This story shall the good man teach his son From this day to the ending of the world";

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L32-L33

13. Don't compare boolean expressions to boolean literals

if (<x> == true) => if (<x>), if (<x> == false) => if (!<x>)

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

182           require(mintlistMinted[msg.sender] == false, 'Already minted');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L182

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

238           require(claimlistMinted[msg.sender] == false, 'Already claimed');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L238

14. Duplicated require()/revert() checks should be refactored to a modifier or function

Saves deployment costs

File: /contracts/ForgottenRunesWarriorsMinter.sol   #1

210           require(
211               numWarriors > 0 && numWarriors <= 20,
212               'You can summon no more than 20 Warriors at a time'
213           );

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L210-L213

File: /contracts/ForgottenRunesWarriorsMinter.sol   #2

207           require(numSold < maxForSale, 'Sold out');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L207

File: /contracts/ForgottenRunesWarriorsMinter.sol   #3

617           require(address(vault) != address(0), 'no vault');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L617

15. Empty blocks should be removed or emit something

The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the block is an empty if-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...} => if(!x){if(y){...}else{...}})

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

152       function uploadImage(bytes calldata s) external onlyOwner {}

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L152

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

157       function uploadAttributes(bytes calldata s) external onlyOwner {}

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L157

16. Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

52       function initialize(address newMinter) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L52

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

129       function setBaseURI(string memory newBaseURI) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L129

File: /contracts/ForgottenRunesWarriorsGuild.sol   #3

137       function setMinter(address newMinter) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L137

File: /contracts/ForgottenRunesWarriorsGuild.sol   #4

145       function setProvenanceHash(string memory newHash) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L145

File: /contracts/ForgottenRunesWarriorsGuild.sol   #5

152       function uploadImage(bytes calldata s) external onlyOwner {}

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L152

File: /contracts/ForgottenRunesWarriorsGuild.sol   #6

157       function uploadAttributes(bytes calldata s) external onlyOwner {}

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L157

File: /contracts/ForgottenRunesWarriorsGuild.sol   #7

163       function withdrawAll() public payable onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L163

File: /contracts/ForgottenRunesWarriorsGuild.sol   #8

173       function forwardERC20s(IERC20 token, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L173

File: /contracts/ForgottenRunesWarriorsMinter.sol   #9

257       function teamSummon(address recipient, uint256 count) external onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L257

File: /contracts/ForgottenRunesWarriorsMinter.sol   #10

350       function issueRefunds(uint256 startIdx, uint256 endIdx)
351           public
352           onlyOwner
353           nonReentrant

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L350-L353

File: /contracts/ForgottenRunesWarriorsMinter.sol   #11

364       function refundAddress(address minter) public onlyOwner nonReentrant {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L364

File: /contracts/ForgottenRunesWarriorsMinter.sol   #12

427       function pause() public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L427

File: /contracts/ForgottenRunesWarriorsMinter.sol   #13

434       function unpause() public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L434

File: /contracts/ForgottenRunesWarriorsMinter.sol   #14

441       function setDaStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L441

File: /contracts/ForgottenRunesWarriorsMinter.sol   #15

448       function setMintlistStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L448

File: /contracts/ForgottenRunesWarriorsMinter.sol   #16

455       function setPublicStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L455

File: /contracts/ForgottenRunesWarriorsMinter.sol   #17

462       function setClaimsStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L462

File: /contracts/ForgottenRunesWarriorsMinter.sol   #18

469       function setSelfRefundsStartTime(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L469

File: /contracts/ForgottenRunesWarriorsMinter.sol   #19

480       function setPhaseTimes(
481           uint256 newDaStartTime,
482           uint256 newMintlistStartTime,
483           uint256 newPublicStartTime,
484           uint256 newClaimsStartTime
485       ) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L480-L485

File: /contracts/ForgottenRunesWarriorsMinter.sol   #20

505       function setMintlist1MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L505

File: /contracts/ForgottenRunesWarriorsMinter.sol   #21

513       function setMintlist2MerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L513

File: /contracts/ForgottenRunesWarriorsMinter.sol   #22

520       function setClaimlistMerkleRoot(bytes32 newMerkleRoot) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L520

File: /contracts/ForgottenRunesWarriorsMinter.sol   #23

527       function setVaultAddress(address _newVaultAddress) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L527

File: /contracts/ForgottenRunesWarriorsMinter.sol   #24

534       function setWarriorsAddress(
535           IForgottenRunesWarriorsGuild _newWarriorsAddress
536       ) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L534-L536

File: /contracts/ForgottenRunesWarriorsMinter.sol   #25

543       function setWethAddress(address _newWethAddress) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L543

File: /contracts/ForgottenRunesWarriorsMinter.sol   #26

550       function setStartPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L550

File: /contracts/ForgottenRunesWarriorsMinter.sol   #27

557       function setLowestPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L557

File: /contracts/ForgottenRunesWarriorsMinter.sol   #28

564       function setDaPriceCurveLength(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L564

File: /contracts/ForgottenRunesWarriorsMinter.sol   #29

571       function setDaDropInterval(uint256 _newTime) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L571

File: /contracts/ForgottenRunesWarriorsMinter.sol   #30

579       function setFinalPrice(uint256 _newPrice) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L579

File: /contracts/ForgottenRunesWarriorsMinter.sol   #31

586       function setMaxDaSupply(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L586

File: /contracts/ForgottenRunesWarriorsMinter.sol   #32

593       function setMaxForSale(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L593

File: /contracts/ForgottenRunesWarriorsMinter.sol   #33

600       function setMaxForClaim(uint256 _newSupply) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L600

File: /contracts/ForgottenRunesWarriorsMinter.sol   #34

608       function withdraw(uint256 _amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L608

File: /contracts/ForgottenRunesWarriorsMinter.sol   #35

616       function withdrawAll() public payable onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L616

File: /contracts/ForgottenRunesWarriorsMinter.sol   #36

627       function forwardERC20s(IERC20 token, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsMinter.sol#L627

17. Don't use _msgSender() if not supporting EIP-2771

Use msg.sender if the code does not implement EIP-2771 trusted forwarder support

File: /contracts/ForgottenRunesWarriorsGuild.sol   #1

101           require(_msgSender() == minter, 'Not a minter');

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L101

File: /contracts/ForgottenRunesWarriorsGuild.sol   #2

115               _isApprovedOrOwner(_msgSender(), tokenId),

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3//contracts/ForgottenRunesWarriorsGuild.sol#L115

18. Calculate unchanging values only once

Before starting the for-loop, you should do unchecked { ++endIdx } so that you aren't doing the addition during every loop

File: contracts/ForgottenRunesWarriorsMinter.sol   #1

355           for (uint256 i = startIdx; i < endIdx + 1; i++) {

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsMinter.sol#L355

Calculate dropPerStep in a state variable whenever one of the constituent values changes, and use the state variable in this function

File: contracts/ForgottenRunesWarriorsMinter.sol   #2

284           uint256 dropPerStep = (startPrice - lowestPrice) /
285               (daPriceCurveLength / daDropInterval);

https://github.com/code-423n4/2022-05-runes/blob/060b4f82b79c8308fe65674a39a07c44fa586cd3/contracts/ForgottenRunesWarriorsMinter.sol#L284-L285

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax ยฉ 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter