Snowman Merkle Airdrop

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

Finding H-01

Root + Impact

Description

The earnSnow() function is intended to allow users to mint 1 free SNOW token per week as a reward for protocol participation. The buySnow() function prices tokens at _buyFee * PRECISION (where PRECISION = 10**18), establishing that 1 SNOW token = 1e18 base units.

The earnSnow() function calls _mint(msg.sender, 1), which mints only 1 wei (0.000000000000000001 SNOW) — a value so small it is effectively zero and completely unusable within the protocol's economy.

// Snow.sol
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
@> _mint(msg.sender, 1); // Mints 1 wei, not 1e18 (1 full token)
s_earnTimer = block.timestamp;
}

Risk

Likelihood:

  • Every single call to earnSnow() triggers this behavior — there is no edge case or precondition required. The bug is unconditional.

  • The buySnow() function establishes the precedent of amount * PRECISION for minting, confirming that earnSnow was intended to mint 1 * PRECISION but the developer forgot to scale.Impact:

    Impact:

  • Users receive 0.000000000000000001 SNOW instead of 1.0 SNOW — a 1e18x shortfall. The earned tokens have no economic value.

  • The core "free earn" mechanic of the protocol is completely non-functional, breaking the protocol's token distribution model.

  • Users who rely on earnSnow() to accumulate tokens for the Snowman NFT airdrop claim can never accumulate enough to participate.

Proof of Concept

Explanation: The test below simulates a standard user (Jerry) calling earnSnow(). After the transaction executes successfully, we check Jerry's SNOW token balance. We assert that his balance is exactly 1 (which equals 1 wei), and explicitly prove that it does not equal 1e18 (1 full token). This demonstrates that the intended economic reward is fundamentally broken.

solidity
function testEarnSnowMintsOnlyOneWei() public {
vm.prank(jerry);
snow.earnSnow();
uint256 balance = snow.balanceOf(jerry);
assertEq(balance, 1); // User gets 1 wei
assertTrue(balance != 1e18); // 1 full token was never minted
}

Recommended Mitigation

Explanation: To fix this issue, we must multiply the hardcoded 1 by the PRECISION constant (which is set to 10**18) during the _mint() call. This ensures that when a user calls the function, they receive exactly 1 full SNOW token base unit rather than 1 wei

function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
- _mint(msg.sender, 1);
+ _mint(msg.sender, 1 * PRECISION);
s_earnTimer = block.timestamp;
}
Updates

Lead Judging Commences

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