Snowman Merkle Airdrop

AI First Flight #10
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Global s_earnTimer prevents all users from earning Snow for 1 week after single user claims

Root + Impact
// @audit Global s_earnTimer variable causes DoS for all users
// The s_earnTimer is a single global variable instead of per-user mapping
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; // @audit This updates timer for ALL users
}

Description

The s_earnTimer variable is declared as a global state variable instead of a mapping(address => uint256). When any user calls earnSnow(), it updates the timer for ALL users, preventing anyone else from earning for 1 week.

// @audit Root cause: Global variable instead of per-user mapping
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; // @audit This updates timer for ALL users
}

Risk

Likelihood:High (5/5) - Any user calling earnSnow() will trigger this issue

Impact:High (5/5) - Prevents all other users from earning tokens for 1 week, effectively breaking the entire farming mechanism

Proof of Concept

// User A calls earnSnow() - succeeds
vm.prank(alice);
snow.earnSnow();
assertEq(snow.balanceOf(alice), 1, "Alice should have 1 token");
​
// Time advances 1 day (less than 1 week)
vm.warp(block.timestamp + 1 days);
​
// User B tries to earn - REVERTS due to global timer
vm.prank(bob);
vm.expectRevert(Snow.S__Timer.selector);
snow.earnSnow(); // This reverts because Alice updated the GLOBAL timer

Recommended Mitigation

mapping(address => uint256) private s_earnTimer;

function earnSnow() external canFarmSnow {
if (s_earnTimer[msg.sender] != 0 && block.timestamp < (s_earnTimer[msg.sender] + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer[msg.sender] = block.timestamp;
}

mapping(address => uint256) private s\_earnTimer;
​
function earnSnow() external canFarmSnow {
if (s\_earnTimer\[msg.sender] != 0 && block.timestamp < (s\_earnTimer\[msg.sender] + 1 weeks)) {
revert S\_\_Timer();
}
\_mint(msg.sender, 1);
s\_earnTimer\[msg.sender] = block.timestamp;
}
​
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!