Snowman Merkle Airdrop

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

Double Payment Vulnerability in `buySnow` Function

Root + Impact

Description

  • Normal Behavior:
    The buySnow function should allow users to purchase tokens by paying either ETH (msg.value) or WETH (safeTransferFrom), but not both simultaneously.

  • Vulnerability:
    If a user approves a large WETH allowance to the contract and then calls buySnow with less or more ETH (failing the msg.value check), the function will execute the else branch, charging WETH while still keeping the sent ETH. This results in the user paying both ETH and WETH for a single token purchase.

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) { // @> Condition fails if ETH sent is less or more
_mint(msg.sender, amount);
} else { // @> Executes WETH transfer BUT DOES NOT REFUND ETH
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Risk

Likelihood: High

  • Reason 1: Occurs whenever a user with WETH approval sends non-matching ETH.

Impact:

  • Impact 1: Funds theft, users lose both ETH and WETH for a single transaction

Proof of Concept

// User approves max WETH allowance first
weth.approve(snowContract, type(uint256).max);
// Then calls buySnow with wrong ETH amount
snowContract.buySnow{value: 0.1 ether}(100);
// ETH (0.1 ether) is kept, AND WETH is transferred

Recommended Mitigation

function buySnow(uint256 amount) external payable canFarmSnow {
+ uint256 requiredPayment = s_buyFee * amount;
+ require(
+ msg.value == 0 || msg.value == requiredPayment,
+ "Invalid ETH payment"
+ );
if (msg.value == requiredPayment) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), requiredPayment);
_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.