Impact - Exploiting this flaw allows an attacker to drain all faucet-held tokens and illegitimately mint or transfer large portions to arbitrary addresses, compromising the contract’s supply logic and trust model.
Normal behavior:
The burnFaucetTokens() function should allow the contract owner to burn a specified number of faucet tokens (amountToBurn) directly from the contract’s balance, thereby reducing total supply and maintaining consistency in the faucet’s token economy.
Issue:
The original implementation transferred the entire contract balance to the owner before burning only amountToBurn tokens from the owner’s wallet. This resulted in the faucet contract’s balance becoming zero while the owner retained unburned tokens that were unintentionally transferred, breaking supply logic and faucet token flow.
Likelihood:
High — The condition triggers every time burnFaucetTokens() is executed, since the full contract balance is always transferred to the owner.
Deterministic — This occurs whenever the owner calls the function, independent of external input or edge cases.
Impact:
Owner receives all faucet tokens each time, regardless of burn amount.
Token supply metrics become inaccurate, leading to loss of trust and broken faucet mechanics.
Could enable accidental or malicious draining of faucet tokens.
A simple Foundry test demonstrates the bug: calling burnFaucetTokens(amountToBurn) transfers the entire faucet balance to the owner and only burns amountToBurn from the owner's balance, leaving the owner with leftover tokens and the faucet drained to zero.
src/RaiseBoxFaucet.sol — vulnerable contract (original implementation)
test/BurnLogic.t.sol — Foundry test performing the PoC (owner calls burnFaucetTokens)
Run the PoC test with Foundry from project root:
After calling burnFaucetTokens(contractBalance / 2), the faucet’s token balance is 0 (drained), whereas the owner ends up with contractBalance / 2 leftover tokens beyond the burned amount — demonstrating that the full contract balance was moved to the owner and only a portion was burned.
This proves the function does not burn tokens from the contract as intended and instead transfers tokens to the owner before burning, enabling unintended token takeover.
The fix ensures that the burn operation happens directly from the contract’s own token balance instead of transferring tokens to the owner first.
In the original version, _transfer(address(this), msg.sender, balanceOf(address(this))) moved all tokens to the owner before burning only amountToBurn — effectively draining the faucet and leaving unburned tokens in the owner’s wallet.
The corrected implementation:
Removes the _transfer() call entirely (no tokens are moved to the owner).
Adds a safety check to confirm the contract holds at least amountToBurn tokens.
Burns the tokens from the contract’s address (_burn(address(this), amountToBurn)), ensuring only the intended amount is destroyed and total supply remains accurate.
This preserves faucet balance integrity, prevents unintentional token leakage, and keeps the burn logic consistent with standard ERC-20 burn behavior.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.