Snowman Merkle Airdrop

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

Ignored token decimals for Snow token

Root + Impact

Description

  • Normally, when a user buys Snow tokens, they should receive the exact number of tokens they specify (e.g., calling buySnow(1) should mint 1 full token if the price is set per token).

  • In the current implementation, the amount parameter is interpreted as the smallest unit (wei), not as whole tokens. As a result, users receive only 1 wei (1e-18 of a token) when they expect to receive 1 full token, making the token effectively 10¹⁸ times more expensive than intended.

// Root cause in the codebase with @> marks to highlight the relevant section
function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount); // @> amount is in wei, not whole tokens <@
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount); // @> amount is in wei, not whole tokens <@
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Risk

Likelihood:

  • This will occur whenever a user calls buySnow expecting to receive whole tokens, as most users will specify amount as 1 for 1 token.

  • Users may not realize they need to specify the full decimal value (e.g., 1e18 for 1 token), leading to confusion. Also it impacts UI development around the token

Impact:

  • Users receive 10¹⁸ times less tokens than expected, effectively overpaying by a factor of 10¹⁸.

  • This can lead to significant user loss, frustration, and loss of trust in the protocol.

Proof of Concept

This PoC shows that when a user calls buySnow(1), they expect to receive 1 full token, but actually receive only 1 wei (1e-18 of a token) due to the function interpreting amount as the smallest unit. This results in users overpaying by a factor of 10¹⁸ and receiving far less than expected.

// User tries to buy 1 token, expecting to receive 1 full token:
buySnow(1); // User pays full s_buyFee, but only receives 1 wei (1e-18 token)
// User balance after purchase:
assert(snow.balanceOf(user) == 1); // Only 1 wei, not 1 full token

Recommended Mitigation

By multiplying amount by 1e18 (or the token’s decimals), you ensure that users receive the correct number of tokens when specifying whole token amounts. This aligns user expectations with actual contract behavior and prevents accidental overpayment or under-receipt of tokens. Alternatively, require users to specify the amount in the smallest unit and clearly document this requirement in your UI and documentation.

- _mint(msg.sender, amount);
+ _mint(msg.sender, amount * 1e18); // Adjust amount to account for token decimals
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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