code-423n4 / 2023-01-rabbithole-findings

1 stars 2 forks source link

Upgraded Q -> 2 from #619 [1675724510983] #692

Closed c4-judge closed 1 year ago

c4-judge commented 1 year ago

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

[L-02] The function mintReceipt should check if the quest has expired on-chain as well The main function mintReceipt responsible for minting receipts lacks an important check to ensure the quest end time hasn't finished yet. Considering the fact that on quest creation every quest is enforced with a startTime and endTime, which represents the quest starting time and ending time. Users should not be allowed to mint receipts after the quest is expired.

By the sponsor comment, the claimSignerAddress takes care of that on the off-chain side and won't issue hashes before the quest start or after the quest ends. But mistakes always can occur and it is recommended to have a check on the smart contract level as well.

contracts/QuestFactory.sol

219: function mintReceipt(string memory questId, bytes32 hash, bytes memory signature) public { 220: if (quests[questId].numberMinted + 1 > quests[questId].totalParticipants) revert OverMaxAllowedToMint(); 221: if (quests[questId].addressMinted[msg.sender] == true) revert AddressAlreadyMinted(); 222: if (keccak256(abi.encodePacked(msg.sender, questId)) != hash) revert InvalidHash(); 223: if (recoverSigner(hash, signature) != claimSignerAddress) revert AddressNotSigned(); 224: 225: quests[questId].addressMinted[msg.sender] = true; 226: quests[questId].numberMinted++; 227: emit ReceiptMinted(msg.sender, questId); 228: rabbitholeReceiptContract.mint(msg.sender, questId); 229: } Here is a recommended change, which takes care of this problem:

Add a storage variable in the struct Quest, which will hold the end time of the quest. struct Quest { mapping(address => bool) addressMinted; address questAddress; uint totalParticipants; uint numberMinted;

if (keccak256(abi.encodePacked(contractType)) == keccak256(abi.encodePacked('erc20'))) { if (rewardAllowlist[rewardTokenAddress] == false) revert RewardNotAllowed();

        Erc20Quest newQuest = new Erc20Quest(
            rewardTokenAddress_,
            endTime_,
            startTime_,
            totalParticipants_,
            rewardAmountOrTokenId_,
            questId_,
            address(rabbitholeReceiptContract),
            questFee,
            protocolFeeRecipient
        );

        emit QuestCreated(
            msg.sender,
            address(newQuest),
            questId_,
            contractType_,
            rewardTokenAddress_,
            endTime_,
            startTime_,
            totalParticipants_,
            rewardAmountOrTokenId_
        );
        quests[questId_].questAddress = address(newQuest);
        quests[questId_].totalParticipants = totalParticipants_;
c4-judge commented 1 year ago

kirk-baird marked the issue as duplicate of #22

c4-judge commented 1 year ago

kirk-baird marked the issue as satisfactory