Snowman Merkle Airdrop

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

Incorrect payment logic in buySnow causes double payment when ETH amount is imprecise

Description:

The buySnow function in Snow.sol contains a critical logical flaw in its payment handling mechanism. The function uses a strict equality check (==) for ETH payments, causing any transaction with an incorrect ETH amount to fallback to WETH payment while retaining the sent ETH in the contract.

In Snow.sol:

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

When users send any ETH amount that doesn't exactly match s_buyFee * amount, the function executes the else branch, which attempts to collect WETH tokens while keeping the sent ETH trapped in the contract. This results in users paying both ETH and WETH for the same purchase.

Attack path:

  1. User calculates the required payment: s_buyFee * amount (e.g., 5 ETH for 1 token)

  2. User accidentally sends slightly more or less ETH:

  3. User calls buySnow{value: 6 ether}(1) instead of exactly 5 ETH

  4. Since msg.value (6 ETH) != s_buyFee * amount (5 ETH), the else branch executes

  5. The 6 ETH remains trapped in the contract

  6. Additional 5 WETH is transferred from user's account via safeTransferFrom

  7. User effectively pays 11 ETH worth of value (6 ETH + 5 WETH) for tokens worth 5 ETH

  8. The excess ETH has no mechanism for refund and can only be collected by the fee collector

Example scenarios:

  • User sends 5.1 ETH instead of 5.0 ETH → pays 5.1 ETH + 5.0 WETH = 10.1 ETH total

  • User sends 4.9 ETH instead of 5.0 ETH → pays 4.9 ETH + 5.0 WETH = 9.9 ETH total

Impact:

Users can lose up to double the intended payment amount through accidental overpayment

ETH sent with incorrect amounts becomes permanently locked in the contract

Recommended Mitigation:

Implement proper ETH amount validation with clear error messages and refund mechanism:

function buySnow(uint256 amount) external payable canFarmSnow {
uint256 totalCost = s_buyFee * amount;
if (msg.value > 0) {
// ETH payment path
require(msg.value >= totalCost, "Insufficient ETH sent");
// Refund excess ETH
if (msg.value > totalCost) {
uint256 refund = msg.value - totalCost;
(bool success,) = payable(msg.sender).call{value: refund}("");
require(success, "ETH refund failed");
}
_mint(msg.sender, amount);
} else {
// WETH payment path
i_weth.safeTransferFrom(msg.sender, address(this), totalCost);
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
3 months ago
yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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