Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
Submission Details
Impact: high
Likelihood: high

Flawed Cooldown Logic Allows Bypassing the 3-Day Wait Period

Author Revealed upon completion

Root + Impact

Description

  • The claimFaucetTokens function is intended to enforce a 3-day cooldown period between claims for each user. However, the logic for resetting the dailyClaimCount to zero is flawed. It checks if (block.timestamp > lastFaucetDripDay + 1 days), which can be true even if less than three days have passed since the user's last claim. A user can claim once, wait just over 24 hours, and claim again. During this second claim, the dailyClaimCount is reset to 0, and then their claim increments it to 1. Since the cooldown check if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) is based on lastClaimTime, a third immediate claim will succeed, bypassing the intended 3-day wait.

// Root cause in the codebase with @> marks to highlight the relevant section// In claimFaucetTokens()
if (block.timestamp > lastFaucetDripDay + 1 days) {
lastFaucetDripDay = block.timestamp;
dailyClaimCount = 0; // This reset is the root of the problem
}
// Effects
lastClaimTime[faucetClaimer] = block.timestamp; // This is updated, but the check can be bypassed
dailyClaimCount++;

Risk

Likelihood:

  • This occurs when a user claims tokens for a second time just after the 24-hour daily reset period has passed.

  • A user then immediately calls claimFaucetTokens for a third time, before the 3-day cooldown from their second claim has elapsed.

Impact:

  • The core economic assumption of the faucet—a 3-day cooldown—is broken, allowing users to drain the faucet's token supply much faster than intended.

  • This undermines the fairness of the token distribution, giving an unfair advantage to users who understand and exploit this flaw.

Proof of Concept

// In claimFaucetTokens()
if (block.timestamp > lastFaucetDripDay + 1 days) {
lastFaucetDripDay = block.timestamp;
dailyClaimCount = 0; // This reset is the root of the problem
}
// Effects
lastClaimTime[faucetClaimer] = block.timestamp; // This is updated, but the check can be bypassed
dailyClaimCount++;

Recommended Mitigation

// In claimFaucetTokens function, before the "Effects" section
- if (block.timestamp > lastFaucetDripDay + 1 days) {
- lastFaucetDripDay = block.timestamp;
- dailyClaimCount = 0;
- }
+ uint256 currentDay = block.timestamp / 1 days;
+ if (currentDay > lastFaucetDripDay) {
+ lastFaucetDripDay = currentDay;
+ dailyClaimCount = 0;
+ }

Support

FAQs

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