Raisebox Faucet

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

Daily ETH cap can be trivially reset

Author Revealed upon completion

# Daily ETH cap can be trivially reset

## Description

Inside claimFaucetTokens, when a claimer is **not** eligible for Sepolia ETH—because they have already claimed or drips are paused—the function executes `else { dailyDrips = 0; }`. That unconditional reset wipes the running total of ETH distributed for the day. As a result, any repeat claimer (or a claim during a pause) can zero the counter, erasing the record of prior distributions.

## Risk

  • **Severity:** High

  • The faucet’s daily Sepolia ETH budget, enforced via dailySepEthCap, can be bypassed. After the counter is reset, subsequent first-time claimers see dailyDrips == 0 and drain ETH even if the cap was already reached.

- An attacker can script alternating first-time and repeat claims to continually drain the faucet’s ETH reserves far beyond the intended daily cap.

## Proof of Concept

Run the following scenario:

```text

1. User A (first-time claimer) calls `claimFaucetTokens` → `dailyDrips = 0.005 ether`.

2. User B (repeat claimer) calls `claimFaucetTokens` → `dailyDrips` is set back to `0`.

3. User C (first-time) now calls `claimFaucetTokens` → faucet believes no ETH has been distributed today and sends another `0.005 ether`.

4. Repeat step 2 with any repeat claimer to keep resetting the counter, allowing unlimited first-time claims per day.

```

Under attack, the faucet loses its ETH buffer despite nominal caps.

## Recommended Mitigation

Track dailyDrips as cumulative ETH sent during the current day and reset it **only** when the day boundary rolls over:

```solidity

if (block.timestamp / 1 days > lastDripDay) {

lastDripDay = block.timestamp / 1 days;

dailyDrips = 0;

}

if (!hasClaimedEth[claimant] && !sepEthDripsPaused) {

uint256 newTotal = dailyDrips + sepEthAmountToDrip;

if (newTotal <= dailySepEthCap) {

dailyDrips = newTotal;

hasClaimedEth[claimant] = true;

(bool success, ) = claimant.call{value: sepEthAmountToDrip}("");

if (!success) revert RaiseBoxFaucet_EthTransferFailed();

} else {

emit SepEthDripSkipped(claimant, "Daily ETH cap reached");

}

}

```

  • Delete the branch that sets dailyDrips = 0 when ETH isn’t distributed.

  • Reset dailyDrips only when a new day starts.

- Always increment `dailyDrips` just before sending ETH so the cap reflects actual payouts.

Support

FAQs

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