Snowman Merkle Airdrop

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

Only one user can earn a free snow token per week

Description

The Snow::earnSnow function allows users to earn one free snow token per week. However, the current implementation restricts minting to only one user per week due to the use of a global s_earnTimer state variable. After the first user successfully calls earnSnow, the s_earnTimer is updated to the current block.timestamp, causing subsequent calls by other users to revert until one week has passed.

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: High. The issue occurs whenever multiple users attempt to call earnSnow within the same week.

Impact: High. Users are prevented from earnign their intended free snow token, which could lead to user dissatisfaction and reduced engagement. Additionally, front runners can monitor the Snow::earnSnow event and call the function as soon as the one-week restriction expires, potentially monopolizing token minting.

Proof of Concept

Add the following test to test/TestSnow.t.sol to confirm that only one user can earn a snow token per week.

function testOnlyOneUserCanEarnTokenPerWeek() public {
// Test that only one user can earn a snow token per week/
// Ashley earns her free snow token
vm.prank(ashley);
snow.earnSnow();
assert(snow.balanceOf(ashley) == 1);
// Jerry tries to earn his free token immediately after
vm.prank(jerry);
vm.expectRevert(); // reverts because 1 week has not passed
snow.earnSnow();
assert(snow.balanceOf(jerry) == 0);
}

Recommended Mitigation

To allow each user to earn a free snow token independently, track the timer per user using a mapping. The following diff shows the required changes:

+mapping(address => uint256) public s_earnTimer;
function earnSnow() external canFarmSnow {
- 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();
}
_mint(msg.sender, 1);
- s_earnTimer = block.timestamp;
+ s_earnTimer[msg.sender] = block.timestamp;
}
Updates

Lead Judging Commences

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

Support

FAQs

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