Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Blocked Sepolia ETH Claims Due to Low Token Balance Check

Blocked Sepolia ETH Claims Due to Low Token Balance Check

Description

claimFaucetTokens reverts on low token balance (<= faucetDrip) before ETH drip logic, preventing first-timers from receiving ETH despite availability, and blocking returns for potential retry.

//@>
if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
//@>
// ETH drip logic follows, unreachable on revert

Risk

Likelihood:

  • Token balance depletes near end-of-supply.

  • ETH balance remains sufficient.

Impact:

  • First-timers denied ETH, must cooldown retry (risking depletion).

  • Prioritizes tokens over ETH, unfair to early callers.

Proof of Concept

function testEthClaimBlockedByLowTokenBalance() public {
uint256 drip = raiseBoxFaucet.faucetDrip(); // 1000e18
uint256 initialTokens = raiseBoxFaucet.getFaucetTotalSupply();
uint256 initialEth = raiseBoxFaucet.getContractSepEthBalance(); // 1 ether
// Burn to leave exactly drip
vm.startPrank(owner);
raiseBoxFaucet.burnFaucetTokens(initialTokens);
raiseBoxFaucet.mintFaucetTokens(address(raiseBoxFaucet), drip);
vm.stopPrank();
assertEq(raiseBoxFaucet.balanceOf(address(raiseBoxFaucet)), drip);
assertEq(raiseBoxFaucet.getContractSepEthBalance(), initialEth); // ETH available
// First-timer user1: reverts on tokens, no ETH despite available
vm.prank(user1);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_InsufficientContractBalance.selector);
raiseBoxFaucet.claimFaucetTokens();
assertEq(user1.balance, 0); // No ETH received
assertFalse(raiseBoxFaucet.getHasClaimedEth(user1)); // Still first-timer, but blocked
}

POC Explanation: Burns tokens to exact faucetDrip. User1 (first-timer) claim reverts on token check, despite ETH available, denying ETH and starting no cooldown, proving blocked access.

Recommended Mitigation

if (balanceOf(address(this)) < faucetDrip) { // Change <= to <
revert RaiseBoxFaucet_InsufficientContractBalance();
}
// For first-timers, separate token check if needed, but allow ETH if tokens low but ETH ok
// Or move token transfer after ETH, with separate low-token handling
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused && balanceOf(address(this)) >= faucetDrip) {
// ETH drip logic (allow even if tokens marginal, but revert tokens later if needed)
}

Mitigation Key Points: Use < for token check. Decouple ETH from token balance; allow ETH drip pre-transfer. Ensures first-timers get ETH if available, tokens only if sufficient.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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