Raisebox Faucet

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

Incorrect Reset of Daily ETH Drip Counter (dailyDrips) on Skipped Sepolia ETH Transfers

Incorrect reset of RaiseBoxFaucet::dailyDrips() causes the faucet to lose accurate tracking of how much ETH has been distributed in the current day

Description

  • The amount of Sepolia ETH dripped for first-time claimers is set to certain maximum amount per day, and this reset to zero on the beginning of every new day - erasing previous day history.

  • Redundant elsestatement in RaiseBoxFaucet::claimFaucetTokens() resets the global daily ETH counter to zero, erasing previous drip history. This effectively disables the faucet's daily ETH cap enforcement, since every new claim by a returning user or while paused resets the counter.

function claimFaucetTokens() public {
.
.
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
// ... normal ETH drip logic ...
} else {
@> dailyDrips = 0;
}
.
.
}

Risk

Likelihood:

  • Triggered frequently whenever non-first-time claimers interact.

Impact:

  • Causes ETH over-distribution, depleting the faucet’s funds.

Proof of Concept

steps:

1) user1 claims - receives faucet token & 0.005 ETH.

2) user2 claims - receives faucet token & 0.005 ETH.

  1. user1 claims again after 3 days - receives only faucet token & resets the dailyDripsto zero.

  2. Repeat across users, the contract gives out unlimited ETH in oneday, because whenever a non-new user claims, dailyDripsresets to zero and whether dailySepEthCapis reached or not, ETH is send to first-time claimers despite earlier usage, bypassing the daily cap.

Add the following codes to RaiseBoxFaucet.t.sol and run forge test --mt testSkippedSepEthTransferResetsDailyDripToZero -vv :

function testSkippedSepEthTransferResetsDailyDripToZero() public {
// claim is suppose to send both faucet tokens and eth to claimers
vm.prank(user1);
raiseBoxFaucet.claimFaucetTokens();
assertEq(address(user1).balance, 0.005 ether, "user 1 should receive 0.005 ether");
assertEq(raiseBoxFaucet.dailyDrips(), 0.005 ether, "Today's ETH dripped should be = 0.005 ETH");
console.log("A: ", raiseBoxFaucet.dailyDrips());
vm.prank(user2);
raiseBoxFaucet.claimFaucetTokens();
assertEq(address(user2).balance, 0.005 ether, "user 2 should receive 0.005 ether");
assertEq(raiseBoxFaucet.dailyDrips(), 0.01 ether, "Today's ETH dripped should be = 0.010 ETH");
console.log("B: ", raiseBoxFaucet.dailyDrips());
vm.startPrank(user1);
advanceBlockTime(block.timestamp + 3 days);
raiseBoxFaucet.claimFaucetTokens();
vm.stopPrank();
assertEq(raiseBoxFaucet.dailyDrips(), 0.0 ether, "Today's ETH dripped should be reset to zero");
assertEq(address(user1).balance, 0.005 ether, "user 1 should have 0.005 ether");
assertEq(address(user2).balance, 0.005 ether, "user 2 should have 0.005 ether");
console.log("C: ", raiseBoxFaucet.dailyDrips());
}

Result:

Ran 1 test for test/RaiseBoxFaucet.t.sol:TestRaiseBoxFaucet
[PASS] testSkippedSepEthTransferResetsDailyDripToZero() (gas: 373328)
Logs:
A: 5000000000000000
B: 10000000000000000
C: 0
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 79.43ms (12.79ms CPU time)
Ran 1 test suite in 281.66ms (79.43ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

The vulnerability can be mitigated by removing dailyDrips = 0;and emitting an event with the reason for Sepolia ETH drip skip.

function claimFaucetTokens() public {
.
.
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
// ... normal ETH drip logic ...
} else {
- dailyDrips = 0;
+ emit SepEthDripSkipped(faucetClaimer, hasClaimedEth[faucetClaimer] ? "Not a new user" : "Sepolia ETH drip paused");
}
.
.
}
Updates

Lead Judging Commences

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