Raisebox Faucet

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

dailyClaimCount Does Not Reset, Leading to Permanent Faucet LockUp

Root + Impact

Description

  • The contract designed to allow a limited number of users (dailyClaimLimit) to claim ERC20 tokens.

  • The dailyClaimCout is intended to reset to zero at the beginning of each new day, allowing claims to resume.

  • The logic to reset dailyClaimCount is incorrectly placed after the check that enforces the daily limit.

  • If the limit is reached on day 1 ad no successfull claim transaction occurs on the susequent day, the reset condition is never reached. This permanently locks the faucet, as all future calls will revert at the daily limit check.

// Root cause in the codebase with @> marks to highlight the relevant section
function claimFaucetTokens() public {
// ...
@> if (dailyClaimCount >= dailyClaimLimit) {
@> revert RaiseBoxFaucet_DailyClaimLimitReached();
@> }
// ... lots of other logic ...
@> if (block.timestamp > lastFaucetDripDay + 1 days) {
@> lastFaucetDripDay = block.timestamp;
@> dailyClaimCount = 0;
@> }
// ...
}

Risk

Likelihood:

  • Reason 1: The daily claim limit is reached on any given day, which is expected behavior for a popular faucet.

  • Reason 2: The claimFaucetTokens() function is not successfully called by anyone during the entire following 24-hour period, which is a highly plausible scenario.

Impact:

  • Impact 1: The primary function of the faucet—dispensing ERC20 tokens—becomes permanently unavailable to all users after the conditions are met.

  • Impact 2: The contract fails to serve its core purpose, requiring a redeployment and potentially causing a loss of trust from the community relying on it.

Proof of Concept

A test case using Foundry's cheatcodes can concretely demonstrate the vulnerability. The test simulates the exact sequence of events that leads to the permanent lockup.

function test_permanentDos() public {
// Assume dailyClaimLimit is set to 1 for this test
faucet.adjustDailyClaimLimit(99, false); // Set limit to 1
// 1. Day 1: User 1 claims, maxing out the daily limit.
vm.prank(user1);
faucet.claimFaucetTokens();
assertEq(faucet.dailyClaimCount(), 1);
// 2. A transaction to claim now would fail, as expected.
vm.prank(user2);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_DailyClaimLimitReached.selector);
faucet.claimFaucetTokens();
// 3. Fast-forward time by 2 days, simulating a full day with no activity.
vm.warp(block.timestamp + 2 days);
// 4. Day 3: User 2 attempts to claim. The transaction still fails.
// The dailyClaimCount was never reset because no successful transaction occurred on Day 2.
// The faucet is now permanently locked.
vm.prank(user2);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_DailyClaimLimitReached.selector);
faucet.claimFaucetTokens();
}

Recommended Mitigation

The daily counter reset logic must be moved to the beginning of the function, before any checks are performed. This ensures that the first caller on a new day resets the counter, allowing claims to proceed.

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

Lead Judging Commences

inallhonesty Lead Judge 4 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.