Raisebox Faucet

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

Incorrect transfer in burnFaucetTokens drains entire faucet balance to owner

Root + Impact

Description

burnFaucetTokens() transfers the entire faucet balance to the owner before burning only a portion:

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

This logic sends all remaining tokens in the faucet to the owner, and then burns only amountToBurn.
Effectively, the owner can drain all tokens from the faucet with a single transaction.

function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
// transfer faucet balance to owner first before burning
// ensures owner has a balance before _burn (owner only function) can be called successfully
_transfer(address(this), msg.sender, balanceOf(address(this)));
_burn(msg.sender, amountToBurn);
}

Risk

Likelihood: -

Impact: Faucet tokens permanently drained to owner

Only a small portion actually burned

Faucet unusable for future claims

Proof of Concept

Proof of Concept
function test_burnDrainsFaucet() public {
uint256 total = raiseBoxFaucet.getFaucetTotalSupply();
vm.prank(owner);
raiseBoxFaucet.burnFaucetTokens(1000 * 10**18);
assertEq(raiseBoxFaucet.getFaucetTotalSupply(), 0); // drained
assertGt(raiseBoxFaucet.balanceOf(owner), 0); // owner keeps tokens
}
//Test
function test_BurnDrainsAllTokens() public {
uint256 faucetBalanceBefore = raiseBoxFaucet.getFaucetTotalSupply();
uint256 ownerBalanceBefore = raiseBoxFaucet.balanceOf(owner);
vm.startPrank(owner);
raiseBoxFaucet.burnFaucetTokens(1000 * 10 ** 18);
vm.stopPrank();
// Expect the faucet to have 0 tokens, owner gained everything except burned amount
uint256 faucetBalanceAfter = raiseBoxFaucet.getFaucetTotalSupply();
uint256 ownerBalanceAfter = raiseBoxFaucet.balanceOf(owner);
assertEq(faucetBalanceAfter, 0); // Faucet drained
assertGt(ownerBalanceAfter, ownerBalanceBefore);
}
//result
Ran 1 test for test/RaiseBoxFaucet.t.sol:TestRaiseBoxFaucet
[PASS] test_BurnDrainsAllTokens() (gas: 53296)
Traces:
[58096] TestRaiseBoxFaucet::test_BurnDrainsAllTokens()
├─ [2605] RaiseBoxFaucet::getFaucetTotalSupply() [staticcall]
│ └─ ← [Return] 1000000000000000000000000000 [1e27]
├─ [2917] RaiseBoxFaucet::balanceOf(TestRaiseBoxFaucet: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496]) [staticcall]
│ └─ ← [Return] 0
├─ [0] VM::startPrank(TestRaiseBoxFaucet: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496])
│ └─ ← [Return]
├─ [36498] RaiseBoxFaucet::burnFaucetTokens(1000000000000000000000 [1e21])
│ ├─ emit Transfer(from: RaiseBoxFaucet: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: TestRaiseBoxFaucet: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], value: 1000000000000000000000000000 [1e27])
│ ├─ emit Transfer(from: TestRaiseBoxFaucet: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], to: 0x0000000000000000000000000000000000000000, value: 1000000000000000000000 [1e21])
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [605] RaiseBoxFaucet::getFaucetTotalSupply() [staticcall]
│ └─ ← [Return] 0
├─ [917] RaiseBoxFaucet::balanceOf(TestRaiseBoxFaucet: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496]) [staticcall]
│ └─ ← [Return] 999999000000000000000000000 [9.999e26]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 47.18ms (6.80ms CPU time)
Ran 1 test suite in 95.91ms (47.18ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Insufficient");
- _transfer(address(this), msg.sender, balanceOf(address(this)));
- _burn(msg.sender, amountToBurn);
+ _transfer(address(this), msg.sender, amountToBurn);
+ _burn(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.