Raisebox Faucet

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

Burn Function Logic Error : Entire Faucet Balance Transferred to Owner Instead of Burning Requested Amount

Author Revealed upon completion

Root + Impact

Description

Normal behavior:

The burnFaucetTokens() function should allow the owner to reduce the faucet’s total token supply by burning a specified amount (amountToBurn) from the faucet’s balance, while leaving the remaining tokens available for user claims.

Actual behavior:

Instead of only burning the requested amount, the function first transfers the faucet’s entire token balance to the owner, and then burns only amountToBurn from the owner’s wallet.

This results in all faucet tokens being moved to the owner’s wallet, even if only a small portion was meant to be burned.

// Root cause in the codebase with @> marks to highlight the relevant section
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
// @> BUG: This line transfers the entire faucet balance to the owner
_transfer(address(this), msg.sender, balanceOf(address(this)));
// @> BUG: Only amountToBurn is burned, not the full transferred amount
_burn(msg.sender, amountToBurn);
}

Risk

Likelihood:

1.This will occur whenever the owner calls burnFaucetTokens() with a valid amount smaller than the faucet’s total balance.

2.The function’s _transfer(address(this), msg.sender, balanceOf(address(this))) line ensures the entire faucet balance is moved, regardless of the intended burn size.

Impact:

1.The faucet will lose all available tokens, rendering it unable to serve claimers.

2.The owner could unintentionally or maliciously drain the faucet’s token pool, disrupting the entire faucet’s functionality.

3.Users will no longer be able to claim tokens or test the RaiseBox protocol.

Proof of Concept

In this proof of concept, the faucet is funded with tokens (e.g., 1,000,000 RBT), and the owner calls burnFaucetTokens(100 * 1e18) intending to burn only 100 tokens. After execution, the faucet’s balance becomes 0 while the owner’s balance increases by nearly the full faucet amount, minus the 100 burned. This occurs because the function transfers the entire faucet balance to the owner before burning only the requested portion, effectively draining all faucet tokens to the owner and leaving the faucet empty

// Assume faucet holds 1,000,000 tokens
uint256 faucetBalanceBefore = faucet.getFaucetTotalSupply(); // = 1,000,000 * 1e18
// Owner calls burnFaucetTokens(100 * 1e18)
vm.prank(owner);
faucet.burnFaucetTokens(100 ether);
// Check balances after
uint256 ownerBalanceAfter = token.balanceOf(owner);
uint256 faucetBalanceAfter = token.balanceOf(address(faucet));
assertEq(faucetBalanceAfter, 0, "Faucet drained");
assertEq(ownerBalanceAfter, faucetBalanceBefore - 100 ether, "Owner received all but 100 burned");

Recommended Mitigation

To fix this issue, the burnFaucetTokens function should directly burn tokens from the faucet’s own balance instead of transferring all tokens to the owner first. This can be done by replacing the transfer-and-burn sequence with a single _burn(address(this), amountToBurn) call, ensuring only the intended amount is burned and the faucet retains the remaining tokens for user claims.

- remove this code
_transfer(address(this), msg.sender, balanceOf(address(this)));
_burn(msg.sender, amountToBurn);
+ add this codefunction burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
uint256 faucetBalance = balanceOf(address(this));
require(amountToBurn <= faucetBalance, "Faucet: insufficient balance");
_burn(address(this), amountToBurn);
}

Support

FAQs

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