Raisebox Faucet

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

Uninitialized day tracker misaligns rate limits

Root + Impact

lastFaucetDripDay defaults to zero so the first claim always triggers a daily reset, producing inconsistent rate-limit behavior post deployment.

Description

  • The faucet expects lastFaucetDripDay to track the most recent day boundary so daily claim caps only reset when a full day passes.

  • Because the variable is never initialized, its zero value causes block.timestamp > lastFaucetDripDay + 1 days to evaluate true immediately after deployment, leading to unexpected resets and skewed counters during the first 24 hours.

uint256 public lastFaucetDripDay; // @> default 0
...
if (block.timestamp > lastFaucetDripDay + 1 days) {
@> lastFaucetDripDay = block.timestamp;
@> dailyClaimCount = 0;
}

Risk

Likelihood:

  • The constructor never sets lastFaucetDripDay, so the very first call to claimFaucetTokens after deployment inevitably hits the reset branch.

  • Any redeployments or forks repeat the pattern, meaning most production launches will encounter the inconsistency.

Impact:

  • Day-one analytics are wrong: counters reset midstream, making rate limiting dependent on launch timing rather than true 24-hour windows.

  • Attackers can schedule claims immediately after deployment to force resets and potentially combine with other timing bugs to exceed caps.

Proof of Concept

By claiming immediately after deployment the PoC proves the reset block triggers because lastFaucetDripDay starts at zero.

// Immediately after deployment
require(faucet.lastFaucetDripDay() == 0);
// First call triggers reset block even though no day has elapsed
faucet.claimFaucetTokens();
assert(faucet.dailyClaimCount() == 1); // reset happened before any real day transition

Recommended Mitigation

Initializing lastFaucetDripDay in the constructor aligns the first-day behavior with subsequent days and stabilizes rate limits.

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_;
+ lastFaucetDripDay = block.timestamp;
_mint(address(this), INITIAL_SUPPLY); // mint initial supply to contract on deployment
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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