Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Global `s_earnTimer` allow users to block each other from farming Snow tokens

# Global `s_earnTimer` allow users to block each other from farmning Snow tokens
## Description
* The `Snow` contract allows users to "earn" 1 Snow token every 1 week by calling `earnSnow()`. This feature is supposed to be individual to each user, enforcing a 1-week cooldown between claims.
* However, the `s_earnTimer` variable is implemented as a **single global timestamp**, not per-user. This means **all users share the same timer**, and one user's interaction affects everyone else. As a result, users can either block others from earning or front-run each other, depending on who calls the function first after the timer resets.
```javascript
// earnSnow function
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}
```
## Risk
**Likelihood**:
* Any time multiple users attempt to claim rewards using `earnSnow()`, they are subject to race conditions. This will **always** occur in production usage.
* A malicious user can grief others by calling `earnSnow()` repeatedly every week, effectively preventing others from earning tokens.
**Impact**:
* Users are unfairly blocked from accessing the rewards they are entitled to.
* Leads to centralization of token distribution and a poor user experience, possibly breaking economic assumptions of fairness.
## Proof of Concept
The following proves how one user can set the global timer and disallowing others from farming Snow Tokens.
```javascript
// Assume Alice calls earnSnow(), sets the global timer
vm.prank(alice);
snow.earnSnow(); // works
// Immediately after, Bob tries to call earnSnow()
vm.prank(bob);
snow.earnSnow(); // fails due to s_earnTimer not being expired
```
## Recommended Mitigation
- Use a mapping instead of a global variable
```diff
- uint256 private s_earnTimer;
+ mapping(address => uint256) private s_earnTimer;
```
```diff
- if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
+ if (s_earnTimer[msg.sender] != 0 && block.timestamp < (s_earnTimer[msg.sender] + 1 weeks)) {
revert S__Timer();
}
- s_earnTimer = block.timestamp;
+ s_earnTimer[msg.sender] = block.timestamp;
```
Updates

Lead Judging Commences

yeahchibyke Lead Judge
16 days ago
yeahchibyke Lead Judge 15 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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