Raisebox Faucet

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

Daily Limit Check Before Reset Causes Denial Of Service In Protocol

Root + Impact

Description

Normal Behavior

  • The function claimFaucetTokens() should reset dailyClaimCount before checking dailyClaimLimit, ensuring that dailyClaimCount properly resets after one day.

Issue

  • The dailyClaimCount resets happens after the dailyClaimLimit check, causing permanent lockout once limit is reached with in 1 day (24 hours).

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

Risk

Likelihood:

  • Once dailyClaimCount reaches dailyClaimLimit with in 24 hours.

Impact:

  • Once dailyClaimCount reaches dailyClaimLimit, the function will always revert forever for entirely new faucet claimers, even after 24 hours or 3 days.

Proof of Concept

To test this, include the following code in the RaiseBoxFaucetTest.t.sol file:

function test_DenialOfServiceAfterHundredUserClaimedFaucetWithInOneDay() public {
uint256 numberOfUser = 100;
for (uint160 i = 1; i <= numberOfUser; i++) {
address user = address(i+100);
vm.startPrank(user);
raiseBoxFaucet.claimFaucetTokens();
uint256 userBalance = raiseBoxFaucet.balanceOf(user);
assertEq(userBalance,raiseBoxFaucet.faucetDrip());
vm.stopPrank();
}
// After 100th user claimed withIn 24 Hour, The protocol completemely block the drips of token and sepolia Eth indeed the cooldown period had been passed, due to the dailyclaimlimit has not been reset.
advanceBlockTime(block.timestamp + 3 days);
vm.roll(200);
address user101 = makeAddr("USER-101");
vm.startPrank(user101);
vm.expectRevert();
raiseBoxFaucet.claimFaucetTokens();
}

Recommended Mitigation

In order to mitigate the DOS, move dailyClaimCount reset logic before dailyClaimLimit logic checks.

function claimFaucetTokens() public {
// Checks
faucetClaimer = msg.sender;
// (lastClaimTime[faucetClaimer] == 0);
if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
revert RaiseBoxFaucet_ClaimCooldownOn();
}
if (faucetClaimer == address(0) || faucetClaimer == address(this) || faucetClaimer == Ownable.owner()) {
revert RaiseBoxFaucet_OwnerOrZeroOrContractAddressCannotCallClaim();
}
if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
+ if (block.timestamp > lastFaucetDripDay + 1 days) {
+ lastFaucetDripDay = block.timestamp;
+ dailyClaimCount = 0;
+ }
if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
// drip sepolia eth to first time claimers if supply hasn't ran out or sepolia drip not paused**
// still checks
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
// dailyClaimCount = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
(bool success,) = faucetClaimer.call{value: sepEthAmountToDrip}("");
if (success) {
emit SepEthDripped(faucetClaimer, sepEthAmountToDrip);
} else {
revert RaiseBoxFaucet_EthTransferFailed();
}
} else {
emit SepEthDripSkipped(
faucetClaimer,
address(this).balance < sepEthAmountToDrip ? "Faucet out of ETH" : "Daily ETH cap reached"
);
}
} else {
dailyDrips = 0;
}
/**
*
* @param lastFaucetDripDay tracks the last day a claim was made
* @notice resets the @param dailyClaimCount every 24 hours
*/
- if (block.timestamp > lastFaucetDripDay + 1 days) {
- lastFaucetDripDay = block.timestamp;
- dailyClaimCount = 0;
- }
// Effects
lastClaimTime[faucetClaimer] = block.timestamp;
dailyClaimCount++;
// Interactions
_transfer(address(this), faucetClaimer, faucetDrip);
emit Claimed(msg.sender, faucetDrip);
}
Updates

Lead Judging Commences

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

dailyClaimCount Reset Bug

Support

FAQs

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