Snowman Merkle Airdrop

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

Timestamp-Based Access Control Vulnerable to Miner Manipulation

Root + Impact

Description

The earnSnow() function implements a weekly cooldown mechanism using block.timestamp to prevent users from claiming
rewards more frequently than once per week. The vulnerability arises because block.timestamp can be manipulated by miners within a
limited range (typically up to 900 seconds in Ethereum mainnet conditions). While the impact is limited to a single token mint per
week, this creates a potential fairness issue where miners or validators could slightly accelerate their reward claims.

// Root cause in the codebase with @> marks to highlight the relevant section
@> 92 | function earnSnow() external canFarmSnow {
@> 93 | if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
@> 94 | revert S__Timer();
@> 95 | }
@> 96 | _mint(msg.sender, 1);
@> 97 |
@> 98 | s_earnTimer = block.timestamp;
@> 99 | }

Risk

Likelihood:

• Low: Miner manipulation of timestamps is typically limited to ~900 seconds on Ethereum mainnet, and the 1-week (604,800 seconds)
cooldown period significantly reduces the practical advantage
• Low: The incentive to manipulate is minimal since the reward is only 1 token per week per user

Impact:

• Unfair advantage: Miners/validators could claim rewards slightly earlier than intended
• Potential for minor economic distortion in reward distribution timing

Proof of Concept

// Conceptual Foundry test demonstrating timestamp manipulation
function testTimestampManipulation() public {
// Setup: User claims initial reward
vm.prank(user);
snow.earnSnow();
uint256 initialTimer = snow.s_earnTimer();
// Fast forward to 1 second before cooldown completes
vm.warp(initialTimer + 1 weeks - 1);
// Normal user would fail
vm.prank(user);
vm.expectRevert(S__Timer.selector);
snow.earnSnow();
// Miner manipulates timestamp by 2 seconds forward
vm.warp(initialTimer + 1 weeks + 1);
// Miner successfully claims reward 1 second earlier than intended
vm.prank(miner);
snow.earnSnow();
// Miner gained 1-second advantage over honest users
assertEq(snow.balanceOf(miner), 1);
}

Recommended Mitigation

- if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
+ if (s_earnTimer != 0 && block.number < (s_startBlock + (1 weeks / 12 seconds))) {
revert S__Timer();
}
_mint(msg.sender, 1);
- s_earnTimer = block.timestamp;
+ s_startBlock = block.number;
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours 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!