Platform: Code4rena
Start Date: 25/01/2023
Pot Size: $36,500 USDC
Total HM: 11
Participants: 173
Period: 5 days
Judge: kirk-baird
Total Solo HM: 1
Id: 208
League: ETH
Rank: 120/173
Findings: 1
Award: $17.20
š Selected for report: 0
š Solo Findings: 0
š Selected for report: CodingNameKiki
Also found by: 0x1f8b, 0x4non, 0x5rings, 0xAgro, 0xMirce, 0xRobocop, 0xSmartContract, 0xackermann, AkshaySrivastav, ArmedGoose, Aymen0909, BClabs, Breeje, Dewaxindo, Diana, HollaDieWaldfee, IceBear, IllIllI, Iurii3, Josiah, Krayt, PaludoX0, Phenomana, PrasadLak, RaymondFam, Rolezn, SaeedAlipoor01988, SaharDevep, SleepingBugs, adriro, arialblack14, bin2chen, brevis, btk, carlitox477, carrotsmuggler, catellatech, chaduke, chrisdior4, cryptonue, cryptostellar5, csanuragjain, ddimitrov22, fellows, glcanvas, halden, hl_, horsefacts, jat, joestakey, kenta, libratus, lukris02, luxartvinsec, manikantanynala97, martin, matrix_0wl, nadin, nicobevi, oberon, peanuts, prestoncodes, rbserver, sakshamguruji, sashik_eth, sayan, seeu, thekmj, tnevler, trustindistrust, tsvetanovv, xAriextz, zadaru13, zaskoh
17.196 USDC - $17.20
L-N | Issue |
---|---|
[Lā01] | _safeMint is vulnerable to reentrancy |
[Lā02] | Single-step ownership transfer pattern is dangerous |
[Lā03] | Missing zero address check in withdrawRemainingTokens() |
Total: 3 issues
N-N | Issue |
---|---|
[Nā01] | Use latest Solidity version with a stable pragma statement |
[Nā02] | Constants should be defined rather than using magic numbers |
[Nā03] | Missing event emission on setter functions in QuestFactory |
[Nā04] | Typo in the comments |
Total: 4 issues
RabbitHoleReceipt:mint
function mint(address to_, string memory questId_) public onlyMinter { _tokenIds.increment(); uint newTokenID = _tokenIds.current(); questIdForTokenId[newTokenID] = questId_; timestampForTokenId[newTokenID] = block.timestamp; _safeMint(to_, newTokenID); }
When minting NFTs, the code uses _safeMint function of the OZ reference implementation. This function is safe because it checks whether the receiver can receive ERC721 tokens. The can prevent the case that a NFT will be minted to a contract that cannot handle ERC721 tokens. However, this external function call creates a security loophole. Specifically, the attacker can perform a reentrant call inside the onERC721Received callback. More detailed information why a reeentrancy can occur - https://blocksecteam.medium.com/when-safemint-becomes-unsafe-lessons-from-the-hypebears-security-incident-2965209bda2a.
Use the nonReentrant modifier in the functions where you use _safeMint.
Inheriting from OpenZeppelin's Ownable
contract means you are using a single-step ownership transfer pattern. If an admin provides an incorrect address for the new owner this will result in none of the onlyOwner
marked methods being callable again. The better way to do this is to use a two-step ownership transfer approach, where the new owner should first claim its new rights before they are transferred.
Use OpenZeppelin's Ownable2Step
instead of Ownable
withdrawRemainingTokens()
File: Erc1155Quest.sol
Checking addresses against zero-address during initialization or during setting is a security best-practice. The check is also missing in the constructor for param rewardTokenAddress_
function withdrawRemainingTokens(address to_) public override onlyOwner { super.withdrawRemainingTokens(to_); IERC1155(rewardToken).safeTransferFrom( address(this), to_, rewardAmountInWeiOrTokenId, IERC1155(rewardToken).balanceOf(address(this), rewardAmountInWeiOrTokenId), '0x00' ); }
Add zero address checks.
Using a floating pragma ^0.8.15 statement is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode. Also use latest Solidity version to get all compiler features, bugfixes and optimizations
File: Erc20Quest.sol
return (maxTotalRewards() * questFee) / 10_000;
return (receiptRedeemers() * rewardAmountInWeiOrTokenId * questFee) / 10_000;
QuestFactory
Setters should emit an event so that Dapps can detect important changes to storage. Some of the setter functions in RabbitHoleReceipt.sol
are missing event emission as well.
Example:
function setClaimSignerAddress(address claimSignerAddress_) public onlyOwner { claimSignerAddress = claimSignerAddress_; }
File: QuestFactory.sol
remave
-> remove
#0 - c4-sponsor
2023-02-08T14:52:43Z
GarrettJMU marked the issue as sponsor confirmed
#1 - c4-judge
2023-02-16T07:35:15Z
kirk-baird marked the issue as grade-b