Snowman Merkle Airdrop

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

M-1 — Non-exact ETH payment strands ETH and still pulls WETH

Root + Impact

buySnow selects the ETH branch only on exact equality. Any other non-zero msg.value silently falls through to the WETH branch, which pulls the full fee in WETH and leaves the sent ETH in the contract with no refund. There is also no zero-amount guard.

Description

// src/Snow.sol
79: function buySnow(uint256 amount) external payable canFarmSnow {
@>80: if (msg.value == (s_buyFee * amount)) {
81: _mint(msg.sender, amount);
82: } else {
@>83: i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
84: _mint(msg.sender, amount);
@>85: } // msg.value is neither refunded nor rejected here

Risk

Likelihood: Medium — any user who overpays or under-sends ETH relative to the exact fee.

Impact: Medium — the buyer is charged twice (full WETH pull plus retained ETH); retained ETH is later swept to the collector by collectFee.

Proof of Concept

function test_PoC3b_PartialEthStranded() public {
_deploy();
address buyer = makeAddr("buyer");
uint256 feeWei = snow.s_buyFee();
​
vm.deal(buyer, feeWei);
weth.mint(buyer, feeWei);
vm.startPrank(buyer);
weth.approve(address(snow), feeWei);
snow.buySnow{value: feeWei - 1}(1); // not exact -> WETH branch
vm.stopPrank();
​
assertEq(weth.balanceOf(address(snow)), feeWei); // full fee pulled in WETH
assertEq(address(snow).balance, feeWei - 1); // ETH kept, no refund
}

Recommended Mitigation

Require amount > 0; allow exactly one payment asset per call; refund excess.

function buySnow(uint256 amount) external payable canFarmSnow {
+ if (amount == 0) revert S__ZeroValue();
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
- } else {
+ } else if (msg.value == 0) {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
+ } else {
+ revert S__ZeroValue(); // reject mixed/partial payments
}

Updates

Lead Judging Commences

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