Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Valid

buySnow function updates earn timer completely blocking free token distribution

Description:

The buySnow() function updates the global s_earnTimer variable, which is used to control the cooldown period for the earnSnow() function. This creates a critical flaw where purchasing tokens interferes with the free token distribution mechanism, effectively making free tokens unavailable to all users.

In Snow.sol - buySnow():

function buySnow(uint256 amount) external payable canFarmSnow {
// ... payment logic ...
_mint(msg.sender, amount);
s_earnTimer = block.timestamp; // @audit updates earn timer
emit SnowBought(msg.sender, amount);
}

And in earnSnow():

function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer(); // @audit blocked by buySnow() calls
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}

Since token purchases are likely to occur more frequently than once per week (normal market activity), the earnSnow() function becomes permanently inaccessible, converting the protocol into a purchase-only system.

Attack path:

  1. Natural market activity blocks free distribution:

    • Users regularly purchase Snow tokens via buySnow() (expected normal behavior)

    • Each purchase updates s_earnTimer = block.timestamp

    • Before a full week passes, another user makes a purchase

    • This resets the timer, preventing anyone from using earnSnow()

    • The cycle continues indefinitely with normal trading activity

  2. Intentional blocking of free tokens:

    • Attacker monitors blockchain for approaching 7-day periods since last buySnow()

    • Just before the week expires, attacker makes a minimal token purchase

    • This resets the timer and blocks all free token earning for another week

    • Attacker can maintain this with minimal cost (just gas fees and minimal token purchase)

Impact:

Normal market activity makes earnSnow() permanently unusable

The intended dual mechanism (buy vs earn) becomes purchase-only

Users cannot access the promised free token distribution method

Example scenario:

Day 1: Alice buys 5 tokens → s_earnTimer updated
Day 3: Bob buys 2 tokens → s_earnTimer updated
Day 5: Charlie buys 1 token → s_earnTimer updated
Day 7: Dave tries earnSnow() → BLOCKED (only 2 days since last buy)
Day 8: Eve buys 3 tokens → s_earnTimer updated again
Result: earnSnow() never becomes available

Recommended Mitigation:

Delete the timer mechanisms from buySnow() function

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
- s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

buying of snow resets global timer thus affecting earning of free snow

When buySnow is successfully called, the global timer is reset. This inadvertently affects the earning of snow as that particular action also depends on the global timer.

Support

FAQs

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