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: 97/173
Findings: 2
Award: $17.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0xRobocop, 0xmrhoodie, 0xngndev, AkshaySrivastav, ArmedGoose, Atarpara, Bauer, CodingNameKiki, ElKu, Garrett, HollaDieWaldfee, IllIllI, Iurii3, KIntern_NA, KmanOfficial, Lotus, M4TZ1P, MiniGlome, Ruhum, SovaSlava, bin2chen, bytes032, carrotsmuggler, cccz, chaduke, codeislight, cryptonue, doublesharp, evan, fs0c, glcanvas, gzeon, hansfriese, hihen, hl_, holme, horsefacts, ladboy233, lukris02, mahdikarimi, manikantanynala97, martin, mert_eren, mrpathfindr, omis, peakbolt, peanuts, prestoncodes, rbserver, rvierdiiev, sashik_eth, timongty, tnevler, trustindistrust, usmannk, wait, yixxas, zadaru13, zaskoh
0.7512 USDC - $0.75
https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L102 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Quest.sol#L76
modifier onlyAdminWithdrawAfterEnd() { if (block.timestamp < endTime) revert NoWithdrawDuringClaim(); _; }
multiple
times.function withdrawFee() public onlyAdminWithdrawAfterEnd { IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); }
Manual Review
Check that function withdrawFee() is called only once. It is not important who called it (admin or another user) because fee will be send to specified protocolFeeRecipient address.
#0 - c4-judge
2023-02-06T09:15:46Z
kirk-baird marked the issue as duplicate of #23
#1 - c4-judge
2023-02-16T06:30:30Z
kirk-baird marked the issue as satisfactory
🌟 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
Context:
function setClaimSignerAddress(address claimSignerAddress_) public onlyOwner {
L159function setProtocolFeeRecipient(address protocolFeeRecipient_) public onlyOwner {
L165function setRoyaltyRecipient(address royaltyRecipient_) public onlyOwner {
L71function setMinterAddress(address minterAddress_) public onlyOwner {
L83function setRoyaltyRecipient(address royaltyRecipient_) public onlyOwner {
L60function setMinterAddress(address minterAddress_) public onlyOwner {
L73Recommendation:
The best practice is to use two-step procedure for critical changes to make them less error-prone.
Context:
import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
L9
Description:
@openzeppelin/contracts/access/Ownable.sol' used in this project contract implements renounceOwnership() function. Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.
Recommendation:
You need to reimplement the function.
Context:
Recommendation:
Choose named return variable or return statement. It is unnecessary to use both.
Context:
bytes32 public constant CREATE_QUEST_ROLE = keccak256('CREATE_QUEST_ROLE');
L17Description:
According to official solidity documentation for a constant variable, the expression assigned to it is copied to all the places where it is accessed and also re-evaluated each time. It is recommended to use immutable instead.
Context:
claimSignerAddress = claimSignerAddress_;
L160protocolFeeRecipient = protocolFeeRecipient_;
L167rabbitholeReceiptContract = RabbitHoleReceipt(rabbitholeReceiptContract_);
L173rewardAllowlist[rewardAddress_] = allowed_;
L180questFee = questFee_;
L188ReceiptRendererContract = ReceiptRenderer(receiptRenderer_);
L66royaltyRecipient = royaltyRecipient_;
L72QuestFactoryContract = IQuestFactory(questFactory_);
L78minterAddress = minterAddress_;
L84royaltyFee = royaltyFee_;
L91TicketRendererContract = TicketRenderer(ticketRenderer_);
L55royaltyRecipient = royaltyRecipient_;
L61royaltyFee = royaltyFee_;
L67minterAddress = minterAddress_;
L74Recommendation:
Example how to fix require(_newOwner != owner, " Same address");
Context:
using CountersUpgradeable for CountersUpgradeable.Counter;
L26 (using for declaration can not go after event definition)modifier onlyAdminWithdrawAfterEnd() {
L76 (modifier definition can not go after private function)function withdrawRemainingTokens(address to_) public override onlyOwner {
L54 (public function can not go after internal function)Description:
According to official solidity documentation functions should be grouped according to their visibility and ordered:
constructor
receive function (if exists)
fallback function (if exists)
external
public
internal
private
Within a grouping, place the view and pure functions last.
Recommendation:
Put the functions in the correct order according to the documentation.
Context:
function initialize(
L37function initialize(
L43constructor(
L26function initialize(
L32constructor(
L17constructor(
L13function generateDataURI(
L40Context:
/// @dev set or remave a contract address to be used as a reward
L176 (Change remave to remove)Context:
return ReceiptRendererContract.generateTokenURI(tokenId_, questId, totalParticipants, claimed, rewardAmount, rewardAddress);
L172/// @dev Only the owner of the Quest can call this function. Also requires that the Quest has started (not by date, but by calling the start function)
L56/// @dev Only the owner of the Quest can call this function. Also requires that the Quest has started (not by date, but by calling the start function)
L62/// @dev Function that gets the maximum amount of rewards that can be claimed by all users. It does not include the protocol fee
L43/// @notice Starts the quest by marking it ready to start at the contract level. Marking a quest ready to start does not mean that it is live. It also requires that the start time has passed
L56/// @dev Requires that the balance of the rewards in the contract is greater than or equal to the maximum amount of rewards that can be claimed by all users and the protocol
L57/// @dev Every receipt minted should still be able to claim rewards (and cannot be withdrawn). This function can only be called after the quest end time
L79Description:
Maximum suggested line length is 120 characters.
#0 - c4-judge
2023-02-06T22:36:19Z
kirk-baird marked the issue as grade-b