Snowman Merkle Airdrop

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

earnSnow() mints 1 wei instead of 1 token — free Snow claims are 10^18× too small

Root + Impact

The Snow contract's weekly free-claim function earnSnow() is supposed to mint one full Snow token (10¹⁸ wei) to the caller every week. Instead it mints the bare integer 1, which is a single wei — a billion billion times smaller than intended. Every user who calls earnSnow() gets essentially nothing, breaking the entire free-Snow feature of the protocol.

// Snow.sol — line 64
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;
}
​

The @> line mints exactly 1 wei. The constructor and buySnow() both work with values scaled to 10 ** 18 (see s_buyFee = _buyFee * PRECISION), so a user who buys 1 Snow token receives 10 ** 18 wei but a user who claims free receives only 1 wei — a 10¹⁸ discrepancy.

Risk

Likelihood: High — every earnSnow() call triggers it; no special conditions needed.

Impact: High — the free weekly Snow feature is the protocol's core acquisition loop; it is rendered useless.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
​
import {Test} from "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
​
contract PoC is Test {
Snow snow;
address user = address(0xB0B);
​
function setUp() public {
snow = new Snow(address(new MockWETH()), 5, address(0xC0LL));
}
​
function test_earnSnow_mints_1_wei_not_1_token() public {
snow.earnSnow();
uint256 balance = snow.balanceOf(user);
​
// EXPECTED: 10 ** 18 (one full Snow token)
// ACTUAL: 1 wei — the free claim is 10^18 times too small
assertEq(balance, 1, "got 1 wei, expected 1e18");
// Demonstrate the intended value:
assertEq(balance, 10 ** 18); // this reverts → bug proven
}
}
​

The PoC deploys a fresh Snow contract, calls earnSnow() once, and shows the balance is 1 wei. The final assertion against 10 ** 18 reverts, proving the bug.

Recommended Mitigation

- _mint(msg.sender, 1);
+ _mint(msg.sender, 10 ** 18);
​

Replace the bare 1 with 10 ** 18 (one full token). This matches the scale used everywhere else in the contract (s_buyFee = _buyFee * PRECISION, PRECISION = 10 ** 18). No other code needs to change — buySnow() already mints amount in token units, so earnSnow() minting 10 ** 18 wei is consistent.


.

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!