The Snow::buySnow() function incorrectly resets the global s_earnTimer variable, which is used in earnSnow() to enforce a one-week cool down for free token claims. As a result, each time buySnow() is called, it resets the timer, preventing users from calling earnSnow() even though a week has not passed. This creates a Denial-of-service as it prevents earning free tokens.
The variable s_earnTimer is used to enforce a 1-week period where free tokens can be claimed. This variable is correctly used in the earnSnow function and incorrectly used in the buySnow function.
The result of the incorrect use in buySnow is each time a user buys Snow tokens, the variable is reset with the current block timestamp which has the effect of setting this cdode in earnSnow to be true triggering a revert:
The result is, the s_earnTimer is always pushed forward to a time in the future ; preventing users from claiming free Snow tokens even though a "off-chain week" has technically elasped. This is a form of Denial of Service.
This can issue can be exploited:
intentionally by an attacker but will require investment
through normal use of the contract.
Affected Areas
The severity of this issue is High for the following reasons:
Impact: High - It breaks a core functionality of the protocol whereby users are supposed to be able to earn free Snow tokens after a week has passed.
Likelihood: High - This issue will occur every time a user calls buySnow successfully; meaning the timer will always be pushed a week into the future.
As proof of the validity of this issue, I have created a runnable PoC to demonstrate the issue.
Description
A user - Ashley - claims a free Snow token
A week elapses allowing her to be able to claim another free Snow token.
Before she can claim, another user - Victory - buys a snow token resetting the s_earnTimer to a time in the future.
Ashley tries to claim but transaction reverts as the week period has been moved forward.
Code
Run with: forge test --mt testS_earnTimerResetAfterBuyingSnow
The recommended mitigation for this issue is to remove the line s_earner = block.timestamp in buySnow as it has no purpose. There are no protocol restrictions on buySnow.
When buySnow is successfully called, the global timer is reset. This inadvertently affects the earning of snow as that particular action also depends on the global timer.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.