Snowman Merkle Airdrop

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

No zero checks for `buySnow(uint256 amount)`

Root + Impact

Description

The intended behavior of the buySnow function is to require users to purchase Snow tokens at least once in order to become eligible for periodic free rewards via earnSnow.

However, the function does not validate that the amount parameter is greater than zero. This allows a user to call buySnow(0) without paying any ETH or WETH, while still resetting the s_earnTimer. As a result, the user becomes eligible to repeatedly claim free Snow tokens via earnSnow without ever purchasing any tokens.

function buySnow(uint256 amount) external payable canFarmSnow {
@> // @audit no zero check for `amount`
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);
}

Risk

Likelihood: High

  • Any user can call buySnow(0) without providing ETH or WETH.

  • The call successfully updates s_earnTimer, enabling access to earnSnow.

Impact: High

  • Users can farm unlimited Snow tokens without spending any funds.

  • The token’s economic model and incentive structure are completely broken.

  • Honest users are diluted by attackers minting tokens at zero cost.


Proof of Concept

The following test demonstrates that a user can earn Snow tokens without spending any ETH or WETH:

function testCanEarnSnowWithZeroCost() public {
vm.startPrank(ashley);
snow.buySnow(0);
vm.warp(block.timestamp + 8 days);
snow.earnSnow();
vm.warp(block.timestamp + 8 days);
snow.earnSnow();
vm.stopPrank();
assert(snow.balanceOf(ashley) == 2);
}

Recommended Mitigation

Add a non-zero validation for the amount parameter at the beginning of buySnow:

function buySnow(uint256 amount) external payable canFarmSnow {
+ require(amount != 0, "amount cannot be zero");
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);
}

This ensures that users must pay at least once to become eligible for earning Snow tokens and prevents free token farming.

Updates

Lead Judging Commences

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