Description
The burnFaucetTokens() function is intended to burn a specified amount of tokens from the faucet. The function should transfer only the amountToBurn to the owner, then burn that amount.
The function transfers the entire faucet balance to the owner but only burns the specified amountToBurn. This allows the owner to drain the faucet supply while only burning a fraction, keeping the rest.
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);
}
Risk
Likelihood: High
-
Owner can call this function at any time
-
The bug executes on every call to burnFaucetTokens()
-
No special conditions required
Impact: High
-
Owner can drain entire faucet supply by burning only 1 token
-
Example: Faucet has 1,000,000 tokens → owner calls burnFaucetTokens(100,000) → owner receives 1,000,000 tokens, only 100,000 burned → owner keeps 900,000 tokens
-
Breaks faucet economics - tokens meant for users go to owner
-
Defeats purpose of the faucet contract
-
Complete loss of user trust if exploited
Proof of Concept
function testBurnTransfersAllButBurnsPartial() public {
uint256 faucetBalance = raiseBoxFaucet.getFaucetTotalSupply();
uint256 amountToBurn = 100_000 * 10 ** 18;
console.log("Initial faucet balance:", faucetBalance);
console.log("Amount to burn:", amountToBurn);
uint256 ownerBalanceBefore = raiseBoxFaucet.balanceOf(owner);
vm.prank(owner);
raiseBoxFaucet.burnFaucetTokens(amountToBurn);
uint256 ownerBalanceAfter = raiseBoxFaucet.balanceOf(owner);
uint256 faucetBalanceAfter = raiseBoxFaucet.getFaucetTotalSupply();
assertEq(
ownerBalanceAfter - ownerBalanceBefore,
faucetBalance - amountToBurn
);
assertEq(faucetBalanceAfter, 0, "Faucet drained completely");
console.log("Owner received:", ownerBalanceAfter - ownerBalanceBefore);
console.log("Expected to burn:", amountToBurn);
console.log(
"Owner kept:",
(ownerBalanceAfter - ownerBalanceBefore) - amountToBurn
);
}
Output:
Initial faucet balance: 1,000,000,000 tokens
Amount to burn: 100,000 tokens
Owner received: 999,900,000 tokens
Expected to burn: 100,000 tokens
Owner kept: 999,800,000 tokens (99.98% of supply!)
Recommended Mitigation
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(
amountToBurn <= balanceOf(address(this)),
"Faucet Token Balance: Insufficient"
);
- _transfer(address(this), msg.sender, balanceOf(address(this)));
+ _transfer(address(this), msg.sender, amountToBurn);
_burn(msg.sender, amountToBurn);
}