Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

dailyClaimCount check in claimFaucetTokens blocks claims permanently after limit

dailyClaimCount check in claimFaucetTokens blocks claims permanently after limit

Description

  • Normal behavior: the contract should reset the per-day claim counter before enforcing the daily limit so users can claim again the next day.

  • Specific issue: the function enforces dailyClaimCount >= dailyClaimLimit before performing the daily reset. When dailyClaimCount has reached the limit, subsequent calls always revert at that check because the reset (which clears the counter for the new day) runs later in the same function. This breaks availability: even after a day boundary, claims are rejected until state is changed externally.

// @> the daily-claim reset happens after the daily-claim-limit check
// current code:
@> if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
// ...many lines later...
// @> reset is applied after checks/effects/interactions
@> if (block.timestamp > lastFaucetDripDay + 1 days) {
lastFaucetDripDay = block.timestamp;
dailyClaimCount = 0;
}

Risk

Likelihood: Medium

  • Normal faucet usage and maintenance will reach the daily limit frequently; time-based logic makes this likely to manifest.

Impact: High

  • Users who should be able to claim the next day are blocked; the faucet becomes unavailable. This is a denial-of-service.

Proof of Concept

This PoC shows that at exact dailyClaimLimit reached in a day, breaks protocol from working

function testClaimFaucetAfter100LimitInADayDoesntWorkNextTime() public {
for (uint256 i = 0; i < 100; i++) {
address faucetClaimer = makeAddr(string(abi.encodePacked(uint256(i))));
vm.prank(faucetClaimer);
raiseBoxFaucet.claimFaucetTokens();
console2.log("dailyClaim", raiseBoxFaucet.dailyClaimCount(), i);
}
advanceBlockTime(block.timestamp + 3 days);
address random = makeAddr("random");
vm.prank(random);
vm.expectRevert("RaiseBoxFaucet_DailyClaimLimitReached()");
raiseBoxFaucet.claimFaucetTokens();
// reverts even after 3 days, when dailyLimitCount should be reset
}

Run the test with:

forge test --match-test testClaimFaucetAfter100LimitInADayDoesntWorkNextTime -vvv

Recommended Mitigation

  • Move the reset block to execute before the daily-limit check:

function claimFaucetTokens() public {
// exisiting code...
+ if (block.timestamp > lastFaucetDripDay + 1 days) {
+ lastFaucetDripDay = block.timestamp;
+ dailyClaimCount = 0;
+ }
if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
// exisiting code...
- if (block.timestamp > lastFaucetDripDay + 1 days) {
- lastFaucetDripDay = block.timestamp;
- dailyClaimCount = 0;
- }
// exisiting code...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 days ago
Submission Judgement Published
Validated
Assigned finding tags:

dailyClaimCount Reset Bug

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.