Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
Submission Details
Impact: low
Likelihood: medium

s_earnTimer Init Missing

Author Revealed upon completion

Root + Impact

Description

  • Expected : The earnSnow function should enforce a 1-week cooldown between consecutive claims, ensuring fair token distribution over time.

  • Bug : The s_earnTimer variable starts at 0, and the cooldown check s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks) skips the first claim . Users can claim tokens immediately on the first call, bypassing the cooldown logic.

// ❌ Vulnerable Code
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 :

  • Medium : The first claim can be exploited immediately, but subsequent claims enforce the cooldown.

Impact :

  • Low : Early actors can claim tokens faster than intended, leading to unfair reward distribution but not fund loss.

Proof of Concept

// Exploit contract demonstrating cooldown bypass
contract Exploit {
address public snowContract = 0xDeployedSnowAddress;
function attack() external {
// First call to earnSnow (no cooldown enforced)
Snow(snowContract).earnSnow();
}
}

Explanation :
When s_earnTimer == 0, the if condition in earnSnow evaluates to false, allowing the first claim to proceed immediately. This breaks the intended 1-week delay for initial claims.

Recommended Mitigation

- function earnSnow() external canFarmSnow {
+ 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;
+ // Initialize timer on first claim
+ if (s_earnTimer == 0) {
+ s_earnTimer = block.timestamp;
+ }
}

Steps :

Handle First Claim Separately : Initialize s_earnTimer on the first claim to enforce the cooldown for all future claims.
Add Explicit Initialization : Alternatively, set s_earnTimer = block.timestamp in the constructor to enforce the cooldown from deployment.
Rationale :
Initializing s_earnTimer ensures the 1-week cooldown applies consistently, preserving fair token distribution.

Support

FAQs

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