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: 95/173
Findings: 1
Award: $18.70
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: carlitox477
Also found by: 0xRobocop, 0xbepresent, ArmedGoose, Atarpara, IllIllI, Tointer, UdarTeam, adriro, betweenETHlines, cryptojedi88, evan, glcanvas, gzeon, horsefacts, ladboy233, libratus, lukris02, luxartvinsec, manikantanynala97, minhquanym, mookimgo, p4st13r4, simon135, thekmj, trustindistrust
18.6976 USDC - $18.70
https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Quest.sol#L96 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Quest.sol#L99
Affected code:
The claim()
function of a Quest
calls into the RabbitHoleReceipt
contract to check which receipts belong to the msg.sender
for the given questId
, and then proceeds to pay the unclaimed rewards to the user.
An issue may arise for users that participated in many quests and thus have a lot of past receipts, because the getOwnedTokenIdsOfQuest()
function used in RabbitHoleReceipt
iterates through all the userโ past receipts, and then filters for the given questId
. If the list of receipts gets too big, then an user wonโt be able to be rewarded for all the present and future quests, since the claim()
function will run out of gas
This is a test added in test/Erc20Quest.spec.ts
, within the claim()
group of tests:
it.only('should run out of gas if you partecipated in too many quests', async () => { // Mint 6k "past" receipts const receiptsPromises = [] for (let i = 0; i < 6000; i++) { let randomQuestId = (Math.random() + 1).toString(36).substring(2) receiptsPromises.push(deployedRabbitholeReceiptContract.mint(owner.address, randomQuestId)) } await Promise.all(receiptsPromises) // Mint one additional receipt for current quest await deployedRabbitholeReceiptContract.mint(owner.address, questId) await deployedQuestContract.start() await ethers.provider.send('evm_increaseTime', [86400]) expect(await deployedSampleErc20Contract.balanceOf(owner.address)).to.equal(0) expect(await deployedQuestContract.isClaimed(1)).to.equal(false) // @audit: Will run out of gas await deployedQuestContract.claim() expect(await deployedSampleErc20Contract.balanceOf(owner.address)).to.equal(rewardAmount) await ethers.provider.send('evm_increaseTime', [-86400]) })
The test fails because claim()
exceeds 30M gas usage
Editor
Build a separate data structure in RabbitHoleReceipt
to help deal with already redeemed receipts
#0 - c4-judge
2023-02-05T05:15:33Z
kirk-baird marked the issue as duplicate of #135
#1 - c4-judge
2023-02-14T09:18:01Z
kirk-baird marked the issue as satisfactory