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: 52/93
Findings: 2
Award: $47.39
🌟 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.8695 USDC - $30.87
According to the solidity document, the recommendation of naming of the state variable is using mixedCase.
https://docs.soliditylang.org/en/v0.8.11/style-guide.html#local-and-state-variable-names
Use mixedCase. Examples: totalSupply, remainingSupply, balancesOf, creatorAddress, isPreSale, tokenExchangeRate.
Currently, METADATA_PROVENANCE_HASH is a state variable, and it seems not following the recommendation by solidity.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L36
string public METADATA_PROVENANCE_HASH = '';
It should use mixedCase as the document suggests like this.
string public metadataProvenanceHash = '';
tokenURI function is defined in the code, but @openzeppelin/contracts/token/ERC721/ERC721.sol
already has full implementation.
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require( _exists(tokenId), 'ERC721Metadata: URI query for nonexistent token' ); return string(abi.encodePacked(baseTokenURI, tokenId.toString())); }
The above mentioned codes can be reduced while keeping the almost same functionality.
Here is a definition of the ERC721 codebase. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L93-L98
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; }
The difference is that the OZ's ERC721 checks if the baseURI is defined or not. This additional check seems to be a good check to keep.
msg.sender is used at following codes. https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L164 https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L174-L175
On the other hand, _msgSender() is used here. https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L101
The code should use either msg.sender or _msgSender() for the consistency.
These codes have parentheses wrapping currentPrice * numWarriors
or finalPrice * numWarriors
.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L147 https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L215
require( msg.value >= (currentPrice * numWarriors), 'Ether value sent is not sufficient' );
Technicaly these parentheses are not required. It can remove them like this:
require( msg.value >= currentPrice * numWarriors, 'Ether value sent is not sufficient' );
forwardERC20s
function at both ForgottenRunesWarriorsGuild and ForgottenRunesWarriorsMinter accepts any address. If the attacker sets the malicious ERC20 whose transfer
function has malicious code, it potentially steal WETH stored in ForgottenRunesWarriorsMinter contract. Only owner can call forwardERC20s
, so the owner should be careful which ERC20 token is used in this function.
🌟 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
16.5224 USDC - $16.52
address function used on msg.sender.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L174
require(address(msg.sender) != address(0));
The type of msg.sender is address, so there is no need to use address function on msg.sender. Removing address can reduce the gas fee.
tokenURI function is defined in the code, but @openzeppelin/contracts/token/ERC721/ERC721.sol
already has full implementation.
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require( _exists(tokenId), 'ERC721Metadata: URI query for nonexistent token' ); return string(abi.encodePacked(baseTokenURI, tokenId.toString())); }
The above mentioned codes can be reduced while keeping the almost same functionality.
Here is a definition of the ERC721 codebase. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L93-L98
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; }
Not implementing tokenURI again can reduce the deployment gas fee.
This part checks if msg.sender is not address(0).
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L174
require(address(msg.sender) != address(0));
I cannot think of a case where msg.sender becomes 0. In addition, this function is only called by owner, and owner will not be address(0), so this check itself seems not needed. Not having a check can reduce the gas cost.
numMinted is set to 0. The default value of uint256 is 0, so there is no need to set 0 at numMinted variable.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L174
uint256 public numMinted = 0;
It can be rewritten like this:
uint256 public numMinted;
This can remove the gas cost.
Since numMinted will not be greater than MAX_WARRIORS.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsGuild.sol#L104
require(numMinted < MAX_WARRIORS, 'All warriors have been summoned'); ... numMinted += 1;
Therefore, numMinted += 1
will not be overflown. So it can be rewritten like this to reduce the gas cost.
unchecked { numMinted += 1; }
!= 0
instead of > 0
to reduce gas costFollowing codes use > 0
on uint256 variable. uint256 will never be less than 0, so it can be replaced with != 0
.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L141
By using != 0
instead of > 0
, it can reduce gas cost.
= 0
for uint256 variableFollowing codes set uint256 i = 0
.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L162
The default value of uint256 variable is 0, so there is no need to set = 0
. It can be rewritten like this:
for (uint256 i; i < numWarriors; i++) { _mint(msg.sender); }
In the for loop, i++ is used at the following codes.
At each for loop, i < numWarriors
or i < count
or i < endIdx
is set as the end condition. numWarriors or count will not be overflown. Therefore, it can be rewritten like this to reduce gas cost.
for (uint256 i = 0; i < numWarriors;) { _mint(msg.sender); unchecked { i++; } }
This part checks if msg.sender is not address(0).
require(address(msg.sender) != address(0));
I cannot think of a case where msg.sender becomes 0. In addition, this function is only called by owner, and owner will not be address(0), so this check itself seems not needed. Not having a check can reduce the gas cost.
address function used on msg.sender or variable whose type is address at several places.
require(address(msg.sender) != address(0));
require(address(vault) != address(0), 'no vault');
The type of msg.sender is address, so there is no need to use address function on msg.sender. Removing address can reduce the gas fee. It can be written as follows:
require(msg.sender != address(0));
require(vault != address(0), 'no vault');
!
instead of == false
can reduce gas costFollowing codes use == false
.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L182
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L238
By using !
, it can reduce the gas cost.
require(!mintlistMinted[msg.sender], 'Already minted');
Following code can be wrapped by unchecked. Since the code has require(numSold + numWarriors <= maxDaSupply, 'Not enough remaining')
check, numSold + numWarriors
will not be overflown.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L154 https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L219
numSold += numWarriors;
It can write like this to reduce the gas cost.
unchecked { numSold += numWarriors; }
Following code can be wrapped by unchecked. Since the code has require(numSold < maxForSale, 'Sold out')
, it will not be overflown.
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L193
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L248
numSold += 1;
It can write like this to reduce the gas cost.
unchecked { numSold += 1; }