Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Unrestricted Access to Snowman::mintSnowman() Enables Infinite NFT Minting and Bypasses Protocol Rules.

[H-3] Unrestricted Access to Snowman::mintSnowman() Enables Infinite NFT Minting and Bypasses Protocol Rules.

Description: The Snowman contract implements the Snowman::mintSnowman() function that allows any external caller to mint an arbitrary number of NFTs to any address, without restrictions:

function mintSnowman(address receiver, uint256 amount) external {
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

There are no access controls, no token gating, and no maximum mint cap. This undermines the protocol’s intended design, where NFTs are supposed to be earned through staking Snow tokens or claimed via SnowmanAirdrop using a Merkle proof and valid signature.

Impact:

  • Any user can call Snowman::mintSnowman() directly and mint thousands of NFTs without owning any Snow tokens or passing a Merkle proof.

  • This bypasses all staking, signature validation, or Merkle-tree-based restrictions from SnowmanAirdrop.sol.

  • Severe inflation of the NFT supply becomes possible, ruining scarcity and trust in the system.

  • NFT marketplaces and indexers may be spammed with illegitimate tokens.

  • Potential economic exploitation if NFTs have value, utility, or claimable rewards.

Proof of Concept: Add this into your TestSnowman.t.sol:

function testAnyoneCanMintUnlimitedNFTs() public {
// Anyone can call mintSnowman without restrictions
snowman.mintSnowman(alice, 1000);
assertEq(snowman.balanceOf(alice), 1000);
}

Recommended Mitigation: Restrict mintSnowman() to only authorized contract SnowmanAirdrop.
i_airdrop would be a state variable in the Snowman.sol contract, and it should store the address of the trusted SnowmanAirdrop contract — the only contract allowed to call Snowman::mintSnowman().

modifier onlyAirdrop() {
require(msg.sender == i_airdrop, "Not authorized");
_;
}
function mintSnowman(address receiver, uint256 amount) external onlyAirdrop {
...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Unrestricted NFT mint function

The mint function of the Snowman contract is unprotected. Hence, anyone can call it and mint NFTs without necessarily partaking in the airdrop.

Support

FAQs

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