Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

ETH needs to be pre-stored in the contract constructor

Root + Impact

Description

  • A certain amount of ETH must be attached to the contract when deploying.
    Otherwise, the contract will deploy successfully, but the "ETH drip" feature will be disabled.

constructor(
string memory name_,
string memory symbol_,
uint256 faucetDrip_,
uint256 sepEthDrip_,
uint256 dailySepEthCap_
) ERC20(name_, symbol_) Ownable(msg.sender) {
faucetDrip = faucetDrip_;
sepEthAmountToDrip = sepEthDrip_;
dailySepEthCap = dailySepEthCap_;
// @>Although _mint() has allocated an initial supply,Although _mint() has allocated the initial supply, no ETH is reserved for the contract when it is deployed.
_mint(address(this), INITIAL_SUPPLY);
}

Risk

Likelihood:

  • Although the contract can be deployed successfully, the "ETH drip" function (i.e. the first gas subsidy) will be completely ineffective

Impact:

  • After the contract is deployed, some users are unable to receive ETH;

    However, they can still receive ERC20 tokens normally.

    The "ETH Faucet" function can only be used after the balance is replenished.

Proof of Concept

/// @notice Test behavior when contract is deployed without ETH
/// @dev Prove that all first-time claimers will trigger SepEthDripSkipped event when contract has no ETH
function testNoEthDeploymentIssue() public {
console.log("=== Testing deployment without ETH issue ===");
// Create a new contract instance without any ETH balance
RaiseBoxFaucet noEthContract = new RaiseBoxFaucet(
"NoEthToken",
"NET",
1000 * 10 ** 18,
0.005 ether,
0.5 ether
);
// Verify the contract has no ETH balance
assertEq(address(noEthContract).balance, 0, "Contract should have no ETH balance");
console.log("Contract ETH balance:", address(noEthContract).balance);
// Test first-time claimer - should trigger SepEthDripSkipped event
vm.expectEmit(true, true, true, true);
emit RaiseBoxFaucet.SepEthDripSkipped(user1, "Faucet out of ETH");
vm.prank(user1);
noEthContract.claimFaucetTokens();
// Verify user still gets tokens but no ETH
assertEq(noEthContract.getBalance(user1), 1000 * 10 ** 18, "User should get tokens");
assertEq(address(user1).balance, 0, "User should get no ETH");
assertFalse(noEthContract.getHasClaimedEth(user1), "User should not be marked as claimed ETH");
console.log("User1 token balance:", noEthContract.getBalance(user1));
console.log("User1 ETH balance:", address(user1).balance);
console.log("User1 has claimed ETH:", noEthContract.getHasClaimedEth(user1));
// Test multiple first-time claimers - all should trigger SepEthDripSkipped
address[] memory users = [user2, user3, user4, user5];
for (uint256 i = 0; i < users.length; i++) {
vm.expectEmit(true, true, true, true);
emit RaiseBoxFaucet.SepEthDripSkipped(users[i], "Faucet out of ETH");
vm.prank(users[i]);
noEthContract.claimFaucetTokens();
// Verify each user gets tokens but no ETH
assertEq(noEthContract.getBalance(users[i]), 1000 * 10 ** 18, "User should get tokens");
assertEq(address(users[i]).balance, 0, "User should get no ETH");
assertFalse(noEthContract.getHasClaimedEth(users[i]), "User should not be marked as claimed ETH");
console.log("User", i + 2, "token balance:", noEthContract.getBalance(users[i]));
console.log("User", i + 2, "ETH balance:", address(users[i]).balance);
}
console.log("=== Problem Analysis ===");
console.log("Problem: Contract deployed without ETH balance");
console.log("All first-time claimers trigger SepEthDripSkipped event");
console.log("Users get tokens but no ETH for gas fees");
}

Recommended Mitigation

//Send ETH when deploying the contract
constructor(
string memory name_,
string memory symbol_,
uint256 faucetDrip_,
uint256 sepEthDrip_,
uint256 dailySepEthCap_
) ERC20(name_, symbol_) Ownable(msg.sender) payable {
require(msg.value >= 1 ether, "Must fund faucet with ETH");
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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