Raisebox Faucet

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

# `RaiseBoxFaucet::claimFaucetTokens` resets `dailyDrips` incorrectly

RaiseBoxFaucet::claimFaucetTokens resets dailyDrips incorrectly

Description

claimFaucetTokens() should limit the amount of Sepolia ETH dripped per day using dailyDrips and dailySepEthCap, regardless of whether the claimer has already received ETH or if the drip is paused.

In the current implementation, when hasClaimedEth[faucetClaimer] is true or sepEthDripsPaused is true, the else branch resets dailyDrips to zero. This happens every time a user who has already received ETH claims again (even if they don't get more ETH), effectively setting the daily limit to zero and allowing more ETH to be distributed than intended.

if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
...
dailyDrips += sepEthAmountToDrip;
...
} else {
@> dailyDrips = 0;
}

Risk

Likelihood: High

  • Any subsequent claim by a user with hasClaimedEth = true triggers the else branch and resets the daily counter.

  • Normal usage (or an attacker) only needs to wait for the CLAIM_COOLDOWN to claim without ETH and reset the daily limit.

Impact: High

  • Resetting the counter to zero allows exceeding dailySepEthCap, so the faucet can distribute more Sepolia ETH than the daily budget.

  • The operational limit is no longer reliable, making controlled Sepolia ETH distribution difficult and compromising the faucet's sustainability.

Proof of Concept

  1. User A (new) claims tokens and ETH; dailyDrips increases by sepEthAmountToDrip.

  2. After the CLAIM_COOLDOWN (or exploiting the reentrancy bug), User A claims again: no ETH is received, but the else branch sets dailyDrips = 0.

  3. User B (new) claims immediately and receives ETH as if nothing had been spent from the daily budget.

  4. Repeating the pattern with a few accounts, the contract distributes ETH without respecting dailySepEthCap.

function test_DailyDripsResetOnRepeatClaim() public {
address userA = user1;
address userB = user2;
vm.prank(userA);
raiseBoxFaucet.claimFaucetTokens();
assertEq(raiseBoxFaucet.dailyDrips(), 0.005 ether, "dailyDrips increased by 0.005 ether");
vm.warp(block.timestamp + raiseBoxFaucet.CLAIM_COOLDOWN());
vm.prank(userA);
raiseBoxFaucet.claimFaucetTokens(); // resets dailyDrips
assertEq(raiseBoxFaucet.dailyDrips(), 0, "dailyDrips reset");
vm.prank(userB);
raiseBoxFaucet.claimFaucetTokens(); // receives ETH despite daily limit
}

Recommended Mitigation

Preserve dailyDrips while it's the same day and only modify the counter when ETH is actually sent. Remove the unconditional reset.

if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
...
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
...
} else {
emit SepEthDripSkipped(...);
}
- } else {
- dailyDrips = 0;
}
Updates

Lead Judging Commences

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

dailyDrips Reset Bug

Support

FAQs

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