Allows zero-token claims wasting gas.
The PoC shows that claimFaucetTokens() proceeds when faucetDrip == 0, performing an internal _transfer(address(this), claimer, 0) that succeeds, emits a Transfer event with value 0, wastes gas, and produces misleading logs — instead of reverting or skipping the call.
pragma solidity ^0.8.17;
contract RaiseBoxFaucetMock {
string public name = "RaiseBoxMock";
string public symbol = "RBM";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value);
uint256 public faucetDrip;
address public owner;
constructor(uint256 initialContractBalance, uint256 _faucetDrip) {
owner = msg.sender;
faucetDrip = _faucetDrip;
_mint(address(this), initialContractBalance);
}
function _mint(address to, uint256 amount) internal {
require(to != address(0), "mint to zero");
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function _transfer(address from, address to, uint256 amount) internal {
require(from != address(0), "transfer from zero");
require(to != address(0), "transfer to zero");
require(balanceOf[from] >= amount, "insufficient balance");
balanceOf[from] -= amount;
balanceOf[to] += amount;
emit Transfer(from, to, amount);
}
function claimFaucetTokens() public {
address faucetClaimer = msg.sender;
require(faucetClaimer != address(0), "zero addr");
_transfer(address(this), faucetClaimer, faucetDrip);
}
function setFaucetDrip(uint256 _drip) external {
require(msg.sender == owner, "only owner");
faucetDrip = _drip;
}
function topUp(uint256 amount) external {
_mint(address(this), amount);
}
}
contract PoCClaimer {
RaiseBoxFaucetMock public faucet;
event Claimed(address indexed claimer, uint256 balanceBefore, uint256 balanceAfter);
constructor(address faucetAddress) {
faucet = RaiseBoxFaucetMock(faucetAddress);
}
function startClaim() external {
uint256 before = faucet.balanceOf(address(this));
faucet.claimFaucetTokens();
uint256 after = faucet.balanceOf(address(this));
emit Claimed(address(this), before, after);
}
}
Place it before the transfer logic to prevent the function from proceeding when there is no value to transfer.