Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
Submission Details
Impact: low
Likelihood: medium

`RaiseBoxFaucet:mintFaucetTokens` function has flawed logic for enforcing "minting is only allowed when protocol balance is insufficient for one claim"

Author Revealed upon completion

RaiseBoxFaucet:mintFaucetTokens function has flawed logic for enforcing "minting is only allowed when protocol balance is insufficient for one claim"

Description

  • Judging from the code content, the intended behavior is "minting is only allowed when protocol balance is insufficient for one claim".

  • However, in the current protocol, the faucetDrip passed in the constructor is the actual amount of tokens distributed per claim, not the magic number 1000 * 10 ** 18.

  • Therefore, when the single claim amount is greater than 1000 * 10 ** 18 and the protocol's token balance is nearly depleted, a scenario may occur where "new minting becomes impossible".

function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
if (to != address(this)) {
revert RaiseBoxFaucet_MiningToNonContractAddressFailed();
}
@> if (balanceOf(address(to)) > 1000 * 10 ** 18) {
revert RaiseBoxFaucet_FaucetNotOutOfTokens();
}
_mint(to, amount);
emit MintedNewFaucetTokens(to, amount);
}

Risk

Likelihood:

  • May occur when the single claim amount is greater than 1000 * 10 ** 18 and the protocol's token balance is nearly depleted.

Impact:

  • The administrator cannot effectively mint new tokens, causing the faucet tokens to be depleted.

  • This could have unpredictable negative impacts on future testing protocols.

Proof of Concept

  • Add the following in RaiseBoxFaucet.t.sol:

function test__mintFaucetTokens() public {
// To fully demonstrate the "admin cannot mint new test tokens" in one test function, change the single claim amount to `15000000 * 10 ** 18`
RaiseBoxFaucet testRaiseBoxContract = new RaiseBoxFaucet(
"raiseboxtoken",
"RB",
15000000 * 10 ** 18,
0.01 ether,
1 ether
);
for (uint i=0; i < 66; i++) {
address userTmp = vm.addr(i + 100);
vm.prank(userTmp);
testRaiseBoxContract.claimFaucetTokens();
vm.warp(block.timestamp + 1 days);
}
console.log("getBalance(FaucetSelf)", testRaiseBoxContract.getBalance(address(testRaiseBoxContract)));
console.log("faucetDrip", testRaiseBoxContract.faucetDrip());
vm.prank(owner);
vm.expectRevert(abi.encodeWithSelector(RaiseBoxFaucet.RaiseBoxFaucet_FaucetNotOutOfTokens.selector));
testRaiseBoxContract.mintFaucetTokens(address(testRaiseBoxContract), 15000000 * 10 ** 18);
console.log("getBalance(FaucetSelf)", testRaiseBoxContract.getBalance(address(testRaiseBoxContract)));
}

Recommended Mitigation

function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
if (to != address(this)) {
revert RaiseBoxFaucet_MiningToNonContractAddressFailed();
}
- if (balanceOf(address(to)) > 1000 * 10 ** 18) {
+ if (balanceOf(address(to)) > faucetDrip) {
revert RaiseBoxFaucet_FaucetNotOutOfTokens();
}
_mint(to, amount);
emit MintedNewFaucetTokens(to, amount);
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.