Raisebox Faucet

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

Improper cooldown enforcement to SepEth

Root + Impact

Description

  • Users are intended to claim faucet tokens every 3 days and SepEth exactly once in their lifetime.


Current implementation enforces a single cooldown for the entire claimFaucetTokens() function.

  • If a user claims tokens but the SepEth is unavailable (e.g., contract balance too low or daily limit reached), the function still updates the token cooldown.

if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
@> revert RaiseBoxFaucet_ClaimCooldownOn();
}

Risk

Likelihood:

  • The cooldown for tokens is applied globally in the claim function, unintentionally blocking the user from claiming the available SepEth until the cooldown expires. Which will occur every time a user claim and the contract has no SepEth or has reached its limit.

  • This is prone to keep on repeating if even after the 3 days, the contract has reached limit or has no SepEth.

Impact:

  • The user cannot claim their one-time SepEth immediately after it becomes available.

  • They are forced to wait until the token cooldown expires (3 days) before they can claim the SepEth, violating the intended “claim SepEth any time once” behavior.

  • This can cause loss of user funds or frustration since the one-time SepEth may be missed due to timing.

Proof of Concept

For this POC, you need to modify the setUp() to set the dailly limit to 0.01 ether just for ease of reaching the daily limit.

function setUp() public {
owner = address(this);
raiseBoxFaucet = new RaiseBoxFaucet(
"raiseboxtoken",
"RB",
1000 * 10 ** 18,
0.005 ether,
0.01 ether
);
raiseBoxFaucetContractAddress = address(raiseBoxFaucet);
raiseBoxDeployer = new DeployRaiseboxContract();
vm.deal(raiseBoxFaucetContractAddress, 1 ether);
vm.deal(owner, 100 ether);
advanceBlockTime(3 days); // 3 days
}
function test_claimFaucetTokens_enforce_improper_global_cooldown() public {
// i modified the setUp() fucntion so only two user can receiver SepEth per day
vm.prank(user1);
raiseBoxFaucet.claimFaucetTokens();
vm.prank(user2);
raiseBoxFaucet.claimFaucetTokens();
vm.prank(user3);
raiseBoxFaucet.claimFaucetTokens();
// at this stage
// user1 has faucetToken and SepEth
// user2 hase faucetTokens and SepEth
// user3 has only faucetTokens
assertEq(user1.balance, 0.005 ether);
assertEq(user2.balance, 0.005 ether);
assertEq(user3.balance, 0);
// assume 1 day has passed so user3 must be able to claim SepEth
vm.warp(1 days);
// but this will revert because 3 days COOLDOW is enforced on SepEth and faucetTokens
vm.expectRevert();
vm.prank(user3);
raiseBoxFaucet.claimFaucetTokens();
}

Recommended Mitigation

  • Separate SepEth claim logic from token cooldown

  • Track SepEth claim status independently using a hasClaimedSepEth mapping

  • Only enforce the 3-day cooldown for token claims, not for SepEth.

+ function claimSepEth() public {
+ // here you only add the claim of SepEth logic
+ }
Updates

Lead Judging Commences

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

User can't retry for ETH without waiting 3 days, even though they never received it

Support

FAQs

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