Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
Submission Details
Severity: low
Valid

[M-01] - Incorrect comparison operator in `claimFaucetTokens` prevents valid claims

Author Revealed upon completion

Root + Impact

Description

The claimFaucetTokens function is designed to allow users to claim tokens from the faucet when sufficient balance exists. Users should be able to claim tokens as long as the faucet balance is greater than or equal to the drip amount.

The specific issue is an incorrect comparison operator where the function uses <= instead of <, preventing the last user from claiming when the faucet balance exactly equals the drip amount.

// Root cause in the codebase
function claimFaucetTokens() external {
// ... other checks ...
require(
faucetToken.balanceOf(address(this)) <= faucetDrip, // @> Should be <
"Faucet is empty"
);
// ... rest of function ...
}

Risk

Likelihood:

  • Occurs naturally when faucet balance equals drip amount

  • Happens to the last user attempting to claim

  • No special conditions required

  • Common scenario in normal operation

Impact:

  • Last user cannot claim their entitled tokens

  • Tokens remain locked in contract

  • Poor user experience and trust issues

  • Denial of service for legitimate claims

Proof of Concept

This test demonstrates how a valid claim is rejected when balance equals drip amount:

  1. Setup: We give the faucet exactly 100 tokens (equal to faucetDrip)

  2. Attempt: A user tries to claim tokens

  3. Result: The transaction reverts with "Faucet is empty" despite sufficient balance

The exploit works because:

  • The condition checks if balance <= faucetDrip

  • When balance == faucetDrip, the condition evaluates to true

  • This triggers the "Faucet is empty" revert

  • The user cannot claim despite having exactly enough tokens

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {RaiseBoxFaucet} from "../src/RaiseBoxFaucet.sol";
import {RaiseBoxToken} from "../src/RaiseBoxToken.sol";
contract InsufficientBalanceCheckTest is Test {
RaiseBoxFaucet faucet;
RaiseBoxToken token;
address owner = makeAddr("owner");
address user = makeAddr("user");
uint256 constant FAUCET_DRIP = 100 * 10**18;
function setUp() public {
vm.startPrank(owner);
token = new RaiseBoxToken();
faucet = new RaiseBoxFaucet(address(token));
// Mint exactly faucetDrip amount to faucet
token.mintFaucetTokens(address(faucet), FAUCET_DRIP);
vm.stopPrank();
}
function testCannotClaimWhenBalanceEqualsDrip() public {
uint256 faucetBalance = token.balanceOf(address(faucet));
assertEq(faucetBalance, FAUCET_DRIP); // Balance equals drip amount
// Attempt to claim should succeed but fails
vm.prank(user);
vm.expectRevert("Faucet is empty");
faucet.claimFaucetTokens();
// User cannot claim despite sufficient balance
assertEq(token.balanceOf(user), 0);
assertEq(token.balanceOf(address(faucet)), FAUCET_DRIP); // Tokens remain locked
}
}

Recommended Mitigation

Change the comparison operator from <= to < to allow claims when the balance exactly equals the drip amount. This ensures the last user can claim their entitled tokens when sufficient balance exists.

function claimFaucetTokens() external {
// ... other checks ...
require(
- faucetToken.balanceOf(address(this)) <= faucetDrip,
+ faucetToken.balanceOf(address(this)) < faucetDrip,
"Faucet is empty"
);
// ... rest of function ...
}
Updates

Lead Judging Commences

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

Off-by-one error in `claimFaucetTokens` prevents claiming when the balance is exactly equal to faucetDrip

Support

FAQs

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