Snowman Merkle Airdrop

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

Root + Impact

buySnow() mints raw amount instead of amount * PRECISION, giving buyers 1e18x fewer tokens than they paid for

Description

  • The buySnow() function charges users s_buyFee * amount (where s_buyFee = 5e18). A user calling buySnow(1) pays 5e18 wei of WETH/ETH. However, the function then calls _mint(msg.sender, amount) — minting only 1 wei of SNOW instead of 1e18 wei. The payment is scaled by PRECISION but the minting is not.

// Snow.sol
function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
@> _mint(msg.sender, amount); // Mints raw 'amount', not 'amount * PRECISION'
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
@> _mint(msg.sender, amount); // Same issue in WETH path
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Risk

Likelihood:

  • Every call to buySnow() triggers this. The minting amount is always unscaled regardless of the payment path (ETH or WETH).

  • The s_buyFee is set to _buyFee * PRECISION in the constructor, proving the developer intended precision-scaled economics.

Impact:

  • Users pay 5 ETH/WETH per unit but receive 0.000000000000000001 SNOW per unit — a loss of funds with a 1,000,000,000,000,000,000:1 ratio.

    Proof of Concept:

    Explanation: This test proves that a user who pays the full WETH price for 1 token via buySnow(1) ends up receiving exactly 1 wei of SNOW, resulting in a total loss of economic value.

    function testBuySnowMintsWrongAmount() public {
    uint256 FEE = snow.iBuyFee(); // 5e18
    weth.mint(jerry, FEE);
    vm.startPrank(jerry);
    weth.approve(address(snow), FEE);
    snow.buySnow(1); // pays 5e18 WETH
    vm.stopPrank();
    assertEq(snow.balanceOf(jerry), 1); // User got only 1 wei of SNOW
    }

Recommended Mitigation

Explanation: To fix this, we multiply the requested amount by PRECISION inside both _mint calls, ensuring the user receives the correct 18-decimal token scale they paid for.

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
- _mint(msg.sender, amount);
+ _mint(msg.sender, amount * PRECISION);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
- _mint(msg.sender, amount);
+ _mint(msg.sender, amount * PRECISION);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
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!