Raisebox Faucet

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

L01. Unintended Full Token Drain on Partial Burn

Description

  • Under normal circumstances, the burnFaucetTokens function should allow the contract owner to burn a specified portion of the faucet’s token supply. The contract should transfer only the amount intended for burning from the faucet contract to the owner, then burn that same amount.

  • However, the current implementation mistakenly transfers the entire contract token balance to the owner before burning only the specified portion. This causes the faucet to lose all remaining tokens, leaving only the burned portion removed from supply.

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

Risk

Likelihood:

  • This occurs whenever the owner calls burnFaucetTokens during normal operations, intending to burn a portion of tokens.

  • The function is part of the faucet’s administrative maintenance, making its usage expected and recurring.

Impact:

  • The faucet contract will be fully drained of tokens, disrupting its ability to distribute tokens to users.

  • The excess tokens remain in the owner’s wallet, effectively centralizing supply and breaking the faucet’s intended decentralized distribution mechanism.

Proof of Concept

function testBurnFaucetTokensDrain() public {
// Assume faucet holds 1,000,000 tokens
uint256 initialFaucetBalance = raiseBoxFaucet.balanceOf(address(raiseBoxFaucet));
assertEq(initialFaucetBalance, 1_000_000 ether);
// Owner attempts to burn only 10,000 tokens
uint256 amountToBurn = 10_000 ether;
vm.prank(owner);
raiseBoxFaucet.burnFaucetTokens(amountToBurn);
// Faucet is now completely drained
assertEq(raiseBoxFaucet.balanceOf(address(raiseBoxFaucet)), 0);
// Owner now holds all remaining tokens minus the burned amount
uint256 ownerBalance = raiseBoxFaucet.balanceOf(owner);
assertEq(ownerBalance, initialFaucetBalance - amountToBurn);
// Only 10,000 tokens were burned
assertEq(raiseBoxFaucet.totalSupply(), initialFaucetBalance - amountToBurn);
}

Explanation:
In this test, the faucet starts with 1,000,000 tokens. The owner calls burnFaucetTokens(10,000 ether) expecting only 10,000 tokens to be burned. Instead, the entire 1,000,000 tokens are transferred to the owner, and only 10,000 are burned. The faucet balance becomes zero, making future user claims impossible.

Recommended Mitigation

Transfer only amountToBurnor directly burn tokens from the contract, withou transferring them

- _transfer(address(this), msg.sender, balanceOf(address(this)));
+ _burn(address(this), amountToBurn);
- _transfer(address(this), msg.sender, balanceOf(address(this)));
+ _transfer(address(this), msg.sender, amountToBurn);
Updates

Lead Judging Commences

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