Raisebox Faucet

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

Incorrect Faucet Token Burn Transfer, Faucet has no token after transfer

Incorrect Faucet Token Burn Transfer, Faucet has no token after transfer

Description

  • Normally, burnFaucetTokens(amountToBurn) should transfer only the number of tokens that will be burned (amountToBurn) from the faucet contract to the caller (owner), and then burn that amountToBurn, leaving the remaining faucet tokens in the contract.

  • The issue is that the implementation transfers the contract's entire token balance to the caller before burning only amountToBurn. This causes the faucet to lose all tokens while only a portion is burned.

// Root cause in the codebase with @> marks to highlight the relevant section
// ./src/RaiseBoxFaucet.sol
function burnFaucetTokens(uint256 amountToBurn) external onlyOwner {
uint256 contractBalance = balanceOf(address(this));
// intended: transfer only amountToBurn to owner then burn amountToBurn
// @> bug: transfers full contract balance instead of amountToBurn
_transfer(address(this), msg.sender, balanceOf(address(this))); // @>
_burn(msg.sender, amountToBurn);
}

Risk

Likelihood:

  • Occurs when the owner calls burnFaucetTokens(amountToBurn) while the contract holds a token balance greater than amountToBurn.

  • Happens any time the owner executes the burn function for maintenance or supply management while tokens remain in the faucet contract.

Impact:

  • The faucet contract is drained of all its tokens, while only amountToBurn tokens are actually burned—owner receives the remaining tokens.

  • Allows the owner to unintentionally (or maliciously) appropriate leftover faucet tokens, breaking expected token distribution and trust assumptions.

Proof of Concept

// Pseudocode demonstration
// Setup:
// Contract holds 10,000 tokens
uint256 contractBalance = 10000 * 10**decimals;
// Call:
burnFaucetTokens(1000); // owner calls to burn 1000 tokens
// Execution steps in current implementation:
// 1. _transfer(address(this), msg.sender, balanceOf(address(this)));
// transfers 10,000 tokens to owner
// 2. _burn(msg.sender, 1000);
// burns 1000 tokens from the owner's balance
// Result:
// - Owner ends up with 9000 tokens (10,000 - 1,000 burned)
// - Faucet contract balance becomes 0
// - Only 1000 tokens were burned, but 10,000 were moved to the owner first

Recommended Mitigation

- // bug: transfer full contract balance to owner
- _transfer(address(this), msg.sender, balanceOf(address(this)));
- _burn(msg.sender, amountToBurn);
+ // fix: transfer only the amount to be burned, then burn it
+ _transfer(address(this), msg.sender, amountToBurn);
+ _burn(msg.sender, amountToBurn);
Updates

Lead Judging Commences

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