Raisebox Faucet

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

Revert prevents the subsequent dailyClaimCount from being reset to zero

Root + Impact

Description

  • When the dailyClaimCount reaches its limit, all subsequent calls will be immediately reverted—including those that would have automatically reset the next day.

function claimFaucetTokens() public {
// Checks
faucetClaimer = msg.sender;
// (lastClaimTime[faucetClaimer] == 0);
if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
revert RaiseBoxFaucet_ClaimCooldownOn();
}
if (faucetClaimer == address(0) || faucetClaimer == address(this) || faucetClaimer == Ownable.owner()) {
revert RaiseBoxFaucet_OwnerOrZeroOrContractAddressCannotCallClaim();
}
if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
if (dailyClaimCount >= dailyClaimLimit) {
// @>When dailyClaimCount = dailyClaimLimit, the function is directly reverted at the beginning, and dailyClaimCount will never be reset to 0
revert RaiseBoxFaucet_DailyClaimLimitReached();
}

Risk

Likelihood:

  • When dailyClaimCount = dailyClaimLimit, the function is directly reverted at the beginning.The dailyClaimCount will never be reset to 0;

Impact:

  • The faucet is "locked";

    All subsequent claims will always fail;

    Unless a new contract is manually deployed

Proof of Concept

/// @notice Test dailyClaimCount reset logic error
/// @dev Prove that when dailyClaimCount reaches dailyClaimLimit, it is never reset to 0
function testDailyClaimCountResetBug() public {
console.log("=== Test dailyClaimCount reset logic error ===");
// Get initial state
uint256 initialClaimCount = raiseBoxFaucet.dailyClaimCount();
uint256 claimLimit = raiseBoxFaucet.dailyClaimLimit();
console.log("Initial dailyClaimCount:", initialClaimCount);
console.log("dailyClaimLimit:", claimLimit);
// Create enough users to reach the daily limit
address[] memory users = new address[](claimLimit);
for (uint256 i = 0; i < claimLimit; i++) {
users[i] = makeAddr(string(abi.encodePacked("user", i)));
}
// Let each user claim until the daily limit is reached
for (uint256 i = 0; i < claimLimit; i++) {
vm.prank(users[i]);
raiseBoxFaucet.claimFaucetTokens();
uint256 currentCount = raiseBoxFaucet.dailyClaimCount();
console.log("user", i + 1, "dailyClaimCount after claiming:", currentCount);
}
// Verify that the daily limit has been reached
uint256 finalClaimCount = raiseBoxFaucet.dailyClaimCount();
assertEq(finalClaimCount, claimLimit, "should have reached the daily limit");
// Now try to let the next user claim - should fail
address nextUser = makeAddr("nextUser");
vm.prank(nextUser);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_DailyClaimLimitReached.selector);
raiseBoxFaucet.claimFaucetTokens();
console.log("=== Problem Analysis ===");
console.log("Problem: When dailyClaimCount reaches dailyClaimLimit, ");
console.log("The function directly reverts in lines 179-181, never executing the reset logic in lines 220-223");
// Now 24 hours have passed, theoretically dailyClaimCount should be reset.
vm.warp(block.timestamp + 24 hours + 1 second);
// Try to let the user claim - still fails because dailyClaimCount hasn't been reset!
vm.prank(nextUser);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_DailyClaimLimitReached.selector);
raiseBoxFaucet.claimFaucetTokens();
console.log("BUG: Even after 24 hours, dailyClaimCount remains", raiseBoxFaucet.dailyClaimCount());
console.log("This means the contract will never be able to resume normal claiming!");
// Verify that dailyClaimCount has not been reset
assertEq(raiseBoxFaucet.dailyClaimCount(), claimLimit, "BUG: dailyClaimCount has not been reset");
console.log("Impact: Once the daily limit is reached, the contract will never be able to process new claiming requests");
}

Recommended Mitigation

function claimFaucetTokens() public {
faucetClaimer = msg.sender;
// Step 1: Each call first checks whether it spans multiple days, and clears it if necessary
if (block.timestamp > lastFaucetDripDay + 1 days) {
lastFaucetDripDay = block.timestamp;
dailyClaimCount = 0;
}
// Step 2: Limit determination
if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
Updates

Lead Judging Commences

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