Raisebox Faucet

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

burnFaucetTokens::RaiseBoxFaucet.sol transfers all faucet tokens to the owner before burning, draining the contract’s balance.

Root + Impact

Description:

The burnFaucetTokens function is intended to burn a specified amount of faucet tokens held by the contract. Instead, it first transfers the entire token balance from the contract to the owner, and only then burns amountToBurn from the owner’s balance. This leaves the contract with zero tokens, preventing future claims.

As a result, claimFaucetTokens() will revert with InsufficientContractBalance after any call to burnFaucetTokens, even if the intent was to burn just a small portion.

function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
_transfer(address(this), msg.sender, balanceOf(address(this))); <@ Transfers the entire contract balance to the owner
// Burns only amountToBurn, from the owner's balance
_burn(msg.sender, amountToBurn);
}

Risk

Likelihood:

  • When the owner performs a normal burn with a small amountToBurn, the function transfers all faucet tokens to the owner first, draining the faucet and halting claims.

  • Scheduled/admin scripts that call the burn will repeatedly drain the faucet’s balance, causing recurring outages even without operator mistakes.

Impact:

  • Contract’s ERC20 balance becomes 0 after any burn.


Users can no longer claim tokens (claimFaucetTokens reverts due to empty faucet).

  • Owner unintentionally receives the entire faucet supply instead of only moving the portion to be burned.

Proof of Concept (Foundry Test)

Paste this test function in your test file and run:

forge test -mt testBurnTransfersAllInsteadOfAmountToBurn -vvvv

function testBurnTransfersAllInsteadOfAmountToBurn() public {
// Arrange
uint256 initialContractBalance = raiseBoxFaucet.getBalance(address(raiseBoxFaucet));
uint256 amountToBurn = 2 * 10 ** 18; // burn only 2 tokens
uint256 ownerBalanceBefore = raiseBoxFaucet.getBalance(owner);
// Act
vm.startPrank(owner);
raiseBoxFaucet.burnFaucetTokens(amountToBurn);
vm.stopPrank();
// After burn, contract should be empty due to full transfer
uint256 contractBalanceAfter = raiseBoxFaucet.getBalance(address(raiseBoxFaucet));
uint256 ownerBalanceAfter = raiseBoxFaucet.getBalance(owner);
// Assert contract drained
assertEq(contractBalanceAfter, 0, "Contract balance should be 0 (drained)");
// Owner should have: previous owner balance + (all faucet tokens) - amount burned
assertEq(
ownerBalanceAfter,
ownerBalanceBefore + initialContractBalance - amountToBurn,
"Owner did not receive full faucet balance minus burned amount"
);
// Users can no longer claim because faucet is empty
vm.prank(user1);
vm.expectRevert(); // InsufficientContractBalance
raiseBoxFaucet.claimFaucetTokens();
}

Recommended Mitigation

Fix 1 --> Transfer the desired amount to the owner, and burn only what you intend.

Fix 2 --> Don’t transfer anything to the owner. Burn directly from the contract’s balance.

Fix 1:
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
// transfer faucet balance to owner first before burning
// ensures owner has a balance before _burn (owner only function) can be called successfully
- _transfer(address(this), msg.sender, balanceOf(address(this)));
+ _transfer(address(this), msg.sender, amountToBurn);
_burn(msg.sender, amountToBurn);
}
Fix 2:
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
- _transfer(address(this), msg.sender, balanceOf(address(this)));
- _burn(msg.sender, amountToBurn);
+ _burn(address(this), amountToBurn);
}
Updates

Lead Judging Commences

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

Unnecessary and convoluted logic in burnFaucetTokens

Support

FAQs

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