Snowman Merkle Airdrop

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

Critical Precision Mismatch in `Snow.sol` Makes Protocol Unusable

Critical Precision Mismatch in Snow.sol Makes Protocol Unusable

Description

  • The Snow contract is an ERC20 token implementation that allows users to buy tokens at a fixed price by calling the buySnow(uint256 amount) function. Each unit of amount is intended to represent a single token.

  • The contract calculates the total cost at a rate of s_buyFee * amount, where s_buyFee is scaled to 18 decimals (1e18). However, the _mint function is called with the raw amount value, which results in minting a tiny fraction of a token (wei) instead of the full token unit.

// src/Snow.sol
73: s_buyFee = _buyFee * PRECISION; // PRECISION is 10**18
79: function buySnow(uint256 amount) external payable canFarmSnow {
80: if (msg.value == (s_buyFee * amount)) {
@> _mint(msg.sender, amount);
81: } else {
82: i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
@> _mint(msg.sender, amount);
83: }

Risk

Likelihood: High

  • Under the current logic, if a user wants to buy 1 full Snow token (which is 10**18 wei), they would need to pass amount = 10**18. The total cost would then be 1e18 * 1e18 = 1e36 wei, or 1,000,000,000,000,000,000 ETH.

  • This effectively makes the token impossible to purchase in any significant quantity, breaking the entire economic and staking model of the protocol.

Proof of Concept

The following PoC demonstrates that paying 1 full ETH (the price for 1 token) results in receiving only 1 wei of Snow, which is a negligible amount for an 18-decimal token.

// test/AuditPoC.t.sol
function test_precisionMismatch() public {
// 1. Setup a buyer with 10 ETH
address buyer = makeAddr("buyer");
vm.deal(buyer, 10 ether);
// 2. The buyer calls buySnow with amount = 1, paying 1 ETH
vm.prank(buyer);
snow.buySnow{value: 1 ether}(1);
// 3. Verify that the buyer received only 1 wei of Snow instead of 1 full token (1e18)
assertEq(snow.balanceOf(buyer), 1);
console2.log("Precision mismatch verified: 1 ETH bought 1 wei of Snow");
}

Recommended Mitigation

Adjust the _mint call to scale the amount by the token's precision (18 decimals) before minting.

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);
}

(Note: Ensure the earnSnow() function is also adjusted or that the Merkle tree allotment handles whole token units vs. wei consistently).

Updates

Lead Judging Commences

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