Snowman Merkle Airdrop

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

Rolling 7-day cooldown blocks week-boundary earns

Summary

Snow.sol currently enforces a rolling 7-day cooldown ("must wait 7 days since last earn"). This rolling cooldown can block earns that should be allowed under the intended week-boundary policy.

Root cause

The current implementation uses a rolling 7-day cooldown ("must wait 7 days since last earn"), which is different than the intended week-boundary policy. For example: a user who earned at the end of week N is still blocked early in week N+1 because 7 days haven't passed.

function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
// ...
}

Impact

This leads to longer-than-intended blocking windows.

Likelihood

Rolling cooldowns are common and easy to trigger with late-week earns.

PoC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test, console2} from "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
import {DeploySnow} from "../script/DeploySnow.s.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
contract TestSnow is Test {
Snow snow;
DeploySnow deployer;
MockWETH weth;
address collector;
uint256 FEE;
address jerry;
address victory;
address ashley;
function setUp() public {
deployer = new DeploySnow();
snow = deployer.run();
weth = deployer.weth();
collector = deployer.collector();
FEE = deployer.FEE();
jerry = makeAddr("jerry");
victory = makeAddr("victory");
ashley = makeAddr("ashley");
weth.mint(jerry, FEE);
deal(victory, FEE);
}
function testCannotEarnAtWeekStart() public {
vm.prank(ashley);
snow.earnSnow();
vm.warp(block.timestamp + 13 days);
vm.prank(ashley);
snow.earnSnow();
vm.warp(block.timestamp + 2 days);
vm.prank(ashley);
vm.expectRevert();
snow.earnSnow();
}
}

Mitigation

  • Switch from a rolling 7-day cooldown to a week-boundary policy: record the week index of last earn (e.g., lastEarnWeek = block.timestamp / 1 weeks).

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days 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!