Raisebox Faucet

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

Improper `burnFaucetTokens` Implementation Transfers Full Contract Balance to Owner, Enabling Faucet Depletion and Disruption of Token Distribution

Improper burnFaucetTokens Implementation Transfers Full Contract Balance to Owner, Enabling Faucet Depletion and Disruption of Token Distribution

Description

  • When the owner wants to burn faucet tokens, only the specified amount should be destroyed from the contract’s holdings, leaving the remainder available for users.

  • The function first transfers the entire token balance held by the faucet contract to the owner, then burns only amountToBurn from the owner. This lets the owner (or anyone controlling the owner key) drain all remaining faucet tokens into the owner account, burning just a portion.

/// @notice Burns faucet tokens held by the contract
/// @dev Transfers tokens to owner first, then burns from owner
/// @param amountToBurn Amount of tokens to burn
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)));
_burn(msg.sender, amountToBurn);
}

Risk

Likelihood:

  • Whenever the owner executes burnFaucetTokens, the entire faucet balance is transferred to the owner by design, even if the intent is to burn only a small amount.

  • In the event of owner key compromise or malicious governance, this becomes a single-transaction drain of the faucet’s token reserves.

Impact:

  • Faucet depletion: Users can no longer claim tokens because the faucet contract no longer holds them.

  • Privilege abuse / trust failure: Owner can accumulate a large token balance unintentionally or intentionally, breaking assumptions about fair distribution and test economics.

Proof of Concept

This test demonstrates that calling burnFaucetTokens(amountToBurn) moves the entire faucet token balance from the contract to the owner before burning. As a result, only amountToBurn is destroyed, while the remainder stays with the owner. The assertions show:

  1. The faucet contract’s balance becomes zero immediately after the call (because everything was transferred out), and,

  2. The owner’s final balance increases by faucetInitialBalance - amountToBurn, proving an unintended drain of all faucet tokens except the small portion burned.

function testOwnerDrainFaucetEntireBalanceWithBurn() public {
uint256 amountToBurn = 100e18;
uint256 ownerInitialBalance = raiseBoxFaucet.getBalance(owner);
uint256 faucetInitialBalance = raiseBoxFaucet.getBalance(raiseBoxFaucetContractAddress);
console.log("owner Initial Balance is ", ownerInitialBalance);
console.log("faucet Initial Balance is ", faucetInitialBalance);
vm.prank(owner);
raiseBoxFaucet.burnFaucetTokens(amountToBurn);
uint256 ownerFinalBalance = raiseBoxFaucet.getBalance(owner);
uint256 faucetFinalBalance = raiseBoxFaucet.getBalance(raiseBoxFaucetContractAddress);
console.log("owner final Balance is ", ownerFinalBalance);
console.log("faucet final Balance is ", faucetFinalBalance);
assertEq(ownerFinalBalance, faucetInitialBalance - amountToBurn);
}

Recommended Mitigation

Burn directly from the contract’s balance and do not transfer anything to the owner. Also consider emitting a dedicated event for clarity.

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)));
-
- _burn(msg.sender, amountToBurn);
+ require(amountToBurn > 0, "Amount must be > 0");
+ require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
+
+ // Burn directly from the faucet contract balance
+ _burn(address(this), amountToBurn);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 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.