Snowman Merkle Airdrop

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

High: Fee Validation Bypass in buySnow() - Zero-Cost Minting & Protocol Economics Collapse

Root + Impact

Description

  • Normal behavior:
    The buySnow() function should require users to pay either ETH or WETH equal to s_buyFee * amount in order to mint Snow tokens, ensuring proper fee collection per unit minted.

  • Issue:
    The function lacks safeguards for fee precision and zero-value fee settings. If s_buyFee == 0, minting can be performed for free, breaking protocol economics. Additionally, the comparison msg.value == totalFee lacks precision control and can be exploited with overflows or dust values, particularly on high-magnitude amount inputs.

// >>> Root cause: Fee logic does not handle precision or enforce minimums properly @>
function buySnow(uint256 amount) external payable canFarmSnow {
if (amount == 0) revert S__ZeroValue();
uint256 totalFee = s_buyFee * amount; // @> Precision not accounted for
if (msg.value == totalFee) { // @> Vulnerable comparison (equality instead of ≥ or range)
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), totalFee);
_mint(msg.sender, amount);
}
}

Risk

Likelihood:

  • High — Exploitable under common owner configurations, especially when fees are misconfigured or maliciously set to zero.

  • Reproducibility: Reliable under predictable state; exploits do not require edge conditions.

  • Ease of exploitation: Fee bypass is trivial when set to 0; overflow or dust ETH attack requires minimal technical effort.

Impact:

  • Unlimited minting: Owner or attacker can mint tokens without paying any ETH or WETH.

  • Tokenomics failure: Fee model collapses, undermining any value accrual mechanism.

  • Economic harm: Protocol utility and credibility are compromised by fundless inflation.

  • Indexing/log misrepresentation: External systems assume payment occurred due to emitted events and state changes.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import "../src/Snow.sol";
contract SnowExploit is Test {
Snow snow;
address owner = address(0x111);
address attacker = address(0x666);
function testFeeBypass() public {
// Owner deploys with zero fee
vm.prank(owner);
snow = new Snow(address(0x123), 0, owner);
// Attacker mints without paying anything
vm.prank(attacker);
snow.buySnow{value: 0}(1000);
assertEq(snow.balanceOf(attacker), 1000);
}
function testPrecisionExploit() public {
vm.prank(owner);
snow = new Snow(address(0x123), 1, owner);
// Fee overflow attempt via dust ETH
vm.prank(attacker);
snow.buySnow{value: 1}(type(uint256).max);
}
}

Explanation:

  • Fee Bypass: Owner initializes contract with s_buyFee = 0; attacker calls buySnow(1000) with msg.value = 0 and receives tokens.

  • Precision Exploit: Large amount input can overflow fee calculation or result in successful minting for insignificant payment (e.g., 1 wei).


Recommended Mitigation

Ensure s_buyFee > 0 and apply strong payment validation using explicit condition checks and transfer confirmations.

function buySnow(uint256 amount) external payable canFarmSnow {
if (amount == 0) revert S__ZeroValue();
uint256 totalFee = s_buyFee * amount;
+ // Prevent zero-fee bypass
+ if (s_buyFee == 0) revert S__FeeNotSet();
+ if (msg.value != 0) {
+ require(msg.value == totalFee, "Incorrect ETH payment");
+ _mint(msg.sender, amount);
+ } else {
+ uint256 wethBefore = i_weth.balanceOf(address(this));
+ i_weth.safeTransferFrom(msg.sender, address(this), totalFee);
+ uint256 wethAfter = i_weth.balanceOf(address(this));
+ require(wethAfter - wethBefore == totalFee, "Incorrect WETH transfer");
+ _mint(msg.sender, amount);
+ }
- if (msg.value == totalFee) {
- _mint(msg.sender, amount);
- } else {
- i_weth.safeTransferFrom(msg.sender, address(this), totalFee);
- _mint(msg.sender, amount);
- }
}

Explanation:

  • Fixes zero-fee minting: Ensures owner cannot accidentally or maliciously bypass payment enforcement.

  • Prevents overflow: Total fee calculated with minimal risk of overflow and validated precisely.

  • Safe WETH path: Confirms actual tokens were transferred using balance delta.

  • Maintains protocol integrity: Preserves value model and off-chain trust in the payment layer.

Severity Note:

This is a high-severity vulnerability due to its potential to destroy protocol economics by enabling free or inaccurate minting. Proper fee validation is fundamental to DeFi protocols. The proposed mitigation preserves backward compatibility and can be deployed with minimal disruption.

Verification confirms proper functionality:

function testBuySnowWithZeroFeeFails() public {
vm.prank(owner);
snow = new Snow(address(0x123), 0, owner);
vm.expectRevert(S__FeeNotSet.selector);
vm.prank(attacker);
snow.buySnow{value: 0}(1);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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