Raisebox Faucet

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

[M-01] Minting should allow a dynamic threshold

Minting should allow a dynamic threshold

Description

  • The initial supply is 1,000,000,000 tokens (1 billion) tokens and when used um by 1 million claims (1k per claim) the owner is allowed to refill the faucet by executing the minting function.

  • Design issue: The minting function, protected by onlyOwner, holds a immutable magic number 1000 * 10 ** 18that protects minting tokens by owner if the faucet holds more than 1k $RB.
    In other words, with the initial/standard claim rate of 1000k$RB tokens per user every three days, the owner would only be able to refill for the next claimer until the faucet is dried up.

  • Further issues:

    • No need to have a parameter to if to is only allowed to be the contrat address address(this)

/// @notice Mints new faucet tokens to the contract
/// @dev Can only mint to the contract itself
/// @param to Address that will receive minted tokens (must be the contract itself)
/// @param amount Number of tokens to mint
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 (High):

  • Issue always occurs after deployment that owner cannot to refill the faucet until its dry

Impact (Medium):

  • Minting is impossible until faucets token balance is below 1k tokens

  • Owner would need to refill tokens immediately after a single claim to keep the faucet usable, just enough for the next claim

Proof of Concept

function test_mintFaucetTokens_OwnerCanOnlyMintForOneClaim() public {
// Initial token balance of the faucet
uint256 initialFaucetTokenBalance = raiseBoxFaucet.balanceOf(address(raiseBoxFaucet));
// Transfer tokens away to simulate claims
vm.prank(address(raiseBoxFaucet));
raiseBoxFaucet.transfer(owner, initialFaucetTokenBalance - 1001 * 10 ** 18); // Leave 1001 tokens
// Remaining token balance should be 1001
uint256 remainingFaucetTokenBalance = raiseBoxFaucet.balanceOf(address(raiseBoxFaucet));
assertEq(remainingFaucetTokenBalance, 1001 * 10 ** 18);
// Owner tries to mint more tokens - should fail since balance > 1000
vm.prank(owner);
vm.expectRevert(RaiseBoxFaucet.RaiseBoxFaucet_FaucetNotOutOfTokens.selector);
raiseBoxFaucet.mintFaucetTokens(address(raiseBoxFaucet), 1000 * 10 ** 18);
// Transfer one more token away to go below threshold
vm.prank(address(raiseBoxFaucet));
raiseBoxFaucet.transfer(owner, 501 * 10 ** 18); // Leave 500 tokens
remainingFaucetTokenBalance = raiseBoxFaucet.balanceOf(address(raiseBoxFaucet));
assertEq(remainingFaucetTokenBalance, 500 * 10 ** 18);
// Owner mints more tokens since balance < 1000
vm.prank(owner);
raiseBoxFaucet.mintFaucetTokens(address(raiseBoxFaucet), 1000 * 10 ** 18);
}

Recommended Mitigation

  • Remove toparameter

  • Remove magic number

+ uint256 public constant MIN_BALANCE_FOR_MINT = 1000 * 10 ** 18; // 1,000 tokens
/// @notice Mints new faucet tokens to the contract
/// @dev Can only mint to the contract itself
/// @param to Address that will receive minted tokens (must be the contract itself)
/// @param amount Number of tokens to mint
- function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
+ function mintFaucetTokens(uint256 amount) public onlyOwner {
- if (to != address(this)) {
- revert RaiseBoxFaucet_MiningToNonContractAddressFailed();
- }
- if (balanceOf(address(this)) > 1000 * 10 ** 18) {
+ if (balanceOf(address(this)) > MIN_BALANCE_FOR_MINT) {
revert RaiseBoxFaucet_FaucetNotOutOfTokens();
}
- _mint(to, amount);
+ _mint(address(this), amount);
- emit MintedNewFaucetTokens(to, amount);
+ emit MintedNewFaucetTokens(address(this), amount);
}

Higher threshold or dynamic threshold along with faucetDrip, e.g. as implemented here

+ uint256 public constant MINT_DRIP_MULTIPLIER = 100;
/// @notice Mints new faucet tokens to the contract
/// @dev Can only mint to the contract itself
/// @param to Address that will receive minted tokens (must be the contract itself)
/// @param amount Number of tokens to mint
- function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
+ function mintFaucetTokens(uint256 amount) public onlyOwner {
- if (to != address(this)) {
- revert RaiseBoxFaucet_MiningToNonContractAddressFailed();
- }
- if (balanceOf(address(this)) > 1000 * 10 ** 18) {
+ if (balanceOf(address(this)) > faucetDrip * MINT_DRIP_MULTIPLIER) {
revert RaiseBoxFaucet_FaucetNotOutOfTokens();
}
_mint(address(this), amount);
emit MintedNewFaucetTokens(address(this), amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 days ago
Submission Judgement Published
Validated
Assigned finding tags:

mintFaucetTokens is unusable due to logic/design mismatch with initial supply

Support

FAQs

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