Raisebox Faucet

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

Inconsistent Daily Reset Mechanisms for ETH Drips and Token Claims

Root + Impact

Description

  • The contract should have a consistent mechanism for resetting daily counters to ensure predictable behavior at UTC midnight or 24-hour intervals.

  • The contract uses two different daily reset mechanisms: one based on day numbers (block.timestamp / 24 hours) for ETH drips and another based on time elapsed (block.timestamp > lastFaucetDripDay + 1 days) for token claims, causing the counters to reset at different times and creating inconsistent behavior.

// ETH drip reset (day-based, resets at UTC midnight)
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
// @> uint256 currentDay = block.timestamp / 24 hours;
// @> if (currentDay > lastDripDay) {
// @> lastDripDay = currentDay;
// @> dailyDrips = 0;
// @> }
// ...
}
// Token claim reset (time-elapsed based, resets 24h after last reset)
// @> if (block.timestamp > lastFaucetDripDay + 1 days) {
// @> lastFaucetDripDay = block.timestamp;
// @> dailyClaimCount = 0;
// @> }

Risk

Likelihood:

  • This inconsistency manifests on every single day of operation

  • The two counters will almost never reset at the same time

  • Creates unpredictable behavior that occurs continuously

Impact:

  • Users and operators cannot predict when limits reset

  • Daily ETH cap and daily claim limit reset at different times, causing confusion

  • Potential for gaming the system by exploiting the timing differences

  • Monitoring and debugging becomes difficult

  • Documentation cannot accurately describe reset behavior

Proof of Concept

function testInconsistentResetTiming() public {
// Day 1: First claim at timestamp 100,000
vm.warp(100_000);
vm.prank(user1);
raiseBoxFaucet.claimFaucetTokens();
// ETH drip day: 100,000 / 24 hours = 100,000 / 86,400 = day 1
// Token claim day: lastFaucetDripDay = 100,000
// 12 hours later (timestamp 143,200)
vm.warp(143_200);
// Check daily counters
uint256 ethDripDay = 143_200 / 86_400; // = day 1 (same day)
// Token claim check: 143,200 > 100,000 + 86,400? NO (same period)
// Move to next UTC midnight (timestamp 172,800 = exactly day 2)
vm.warp(172_800);
vm.prank(user2);
raiseBoxFaucet.claimFaucetTokens();
// ETH drip resets: 172,800 / 86,400 = day 2 > day 1 ✓
// Token claim DOES NOT reset: 172,800 > 100,000 + 86,400?
// 172,800 > 186,400? NO ✗
// Token claims won't reset until timestamp 186,400
// This creates a 13,600 second (3.78 hour) window of inconsistency!
}

Recommended Mitigation

function claimFaucetTokens() public {
// Checks
faucetClaimer = msg.sender;
if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
revert RaiseBoxFaucet_ClaimCooldownOn();
}
// ... other checks ...
+ // Unified daily reset logic using day-based calculation
+ uint256 currentDay = block.timestamp / 24 hours;
+
+ // Reset ETH drip counter
+ if (currentDay > lastDripDay) {
+ lastDripDay = currentDay;
+ dailyDrips = 0;
+ }
+
+ // Reset token claim counter
+ if (currentDay > lastFaucetDripDay) {
+ lastFaucetDripDay = currentDay;
+ dailyClaimCount = 0;
+ }
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
- uint256 currentDay = block.timestamp / 24 hours;
-
- if (currentDay > lastDripDay) {
- lastDripDay = currentDay;
- dailyDrips = 0;
- }
// ... ETH drip logic ...
}
- if (block.timestamp > lastFaucetDripDay + 1 days) {
- lastFaucetDripDay = block.timestamp;
- dailyClaimCount = 0;
- }
// Effects and Interactions ...
}
Updates

Lead Judging Commences

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

Inconsistent day calculation methods cause desynchronization between ETH and token daily resets.

Support

FAQs

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