Snowman Merkle Airdrop

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

Global Timer Allows Griefing in earnSnow()

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

The earnSnow() function is designed to allow users to mint 1 $S token every 7 days, acting like a reward faucet.

  • Explain the specific issue or problem in one or more sentences

The function uses a single shared global timer (s_earnTimer) for all users. This means:

  • If any user calls earnSnow(), it resets the timer for everyone.

  • As a result, one malicious user can grief the entire network by calling earnSnow() just before others become eligible.

// Root cause in the codebase with @> marks to highlight the relevant section

Risk

Likelihood: High

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)


Any user can easily exploit this by interacting with earnSnow() repeatedly, preventing others from claiming their rewards.


Impact:


Medium–High

  • This undermines the fairness of the earning mechanism and can frustrate or drive away users.


Proof of Concept

Proof of Concept (PoC) — Shared Global Timer Allows Griefing

The original earnSnow() function uses a single s_earnTimer timestamp shared by all users. This allows griefing attacks where one user’s claim resets the timer globally, blocking others from earning Snow for a full week.


Original Vulnerable Code

uint256 private s_earnTimer;
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;
}

** Exploit Scenario**

  1. User A calls earnSnow() and succeeds.

  2. User B, attempting to call earnSnow() just a moment later, is blocked by the S__Timer revert — even though they’ve never earned before.

  3. This gives User A full control over when others can earn tokens, enabling griefing or even potential denial of rewards.


Fixed Code (Per-User Timer)

mapping(address => uint256) private s_earnTimers;
function earnSnow() external canFarmSnow {
if (block.timestamp < s_earnTimers[msg.sender] + 1 weeks) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimers[msg.sender] = block.timestamp;
}


Recommended Mitigation

Replace the global s_earnTimer with a per-user timer using a mapping, so that each user has their own cooldown period. This prevents any single user from griefing the reward system.


Explanation

This mitigation ensures that:

  • Each user earns independently.

  • One user’s activity no longer interferes with others.

  • The contract behaves more predictably, securely, and fairly for all participants.


- remove this code
+ add this code
Updates

Lead Judging Commences

yeahchibyke Lead Judge
3 months ago
yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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