Snowman Merkle Airdrop

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

buySnow uses incorrect precision math, inflating the cost of tokens by 1e18 and making purchases impossible

Root + Impact

Description

Normal behavior expects that buySnow(amount) will allow a user to purchase a given amount of Snow tokens at the rate of 5 WETH/ETH per 1 full token (1e18 wei).

However, the cost calculation multiplies the raw amount parameter by s_buyFee. Since both s_buyFee (5 * 10^18) and amount (e.g., 10^18 for 1 token) are heavily scaled by 18 decimals, the resulting formula s_buyFee * amount generates an astronomical required payment instead of scaling correctly.

// In DeploySnow.s.sol, FEE is 5
// In Snow.sol constructor: s_buyFee = _buyFee * PRECISION = 5e18
function buySnow(uint256 amount) external payable canFarmSnow {
@> if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
@> i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}

Risk

Likelihood:

  • This will occur every single time any user attempts to buy Snow tokens legitimately.

Impact:

  • The purchasing mechanism is fundamentally broken. To buy just 1 true Snow token (1e18 wei), a user would be required to pay 5e18 * 1e18 = 5e36 wei of ETH/WETH (5 quintillion ETH), which is mathematically impossible.

  • A user can currently only afford to buy dust amounts (a few wei of Snow for 5 full ETH each wei).

Proof of Concept

This Proof of Concept demonstrates the mathematical overcharge. If Alice wants to buy 1 full Snow token, amountToBuy must be set to 1 ether (1e18) to match the ERC20 decimal standard. But because the contract does not divide the fee by PRECISION, the calculated fee becomes 5,000,000,000,000,000,000 ETH. Given the astronomical fee, buying tokens is literally impossible.

function test_MathPrecisionOvercharge() public {
uint256 amountToBuy = 1 ether; // Trying to buy 1 full token (1e18 wei)
// What does the contract calculate as the cost?
uint256 fee = snow.s_buyFee() * amountToBuy;
// Fee equals 5e36 instead of 5e18!
assertEq(fee, 5000000000000000000000000000000000000);
console2.log("Cost required for 1 SNOW:", fee);
}

Recommended Mitigation

Recommended Mitigation: Scale the fee cost calculation down by substituting the formula with (s_buyFee * amount) / PRECISION so the math checks out perfectly.

function buySnow(uint256 amount) external payable canFarmSnow {
- if (msg.value == (s_buyFee * amount)) {
+ uint256 cost = (s_buyFee * amount) / PRECISION;
+ if (msg.value == cost) {
_mint(msg.sender, amount);
} else {
- i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
+ i_weth.safeTransferFrom(msg.sender, address(this), cost);
_mint(msg.sender, amount);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 4 days 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!