Snowman Merkle Airdrop

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

No Token Supply Cap

No Token Supply Cap

Description

  • Alright, here’s the scoop: mintSnowman is like a snow machine with no off switch. You can call it with any amount, and it’ll keep minting tokens until you run out of gas. Our PoC had an attacker mint 10,000 tokens, and the contract didn’t even blink! Without a supply cap, anyone could mint millions (if gas allowed), flooding the market and turning your rare snowmen into common snowflakes. This could tank their value, mess up the airdrop’s economics, and leave legit holders holding a snowball’s worth of nothing.

  • Why This Is Different from Other Issues
    I have reported some similar issues before right?, but this one is completely different:

  • Vs. Issue #1 (Missing Access Control): Issue #1 is about who can call mintSnowman—anyone can, which is bad. This current issue is about how many tokens they can mint—unlimited, which is worse! Even if we lock down access issue (by fixing Issue #1), an owner could accidentally or maliciously over-mint without a cap.

  • Vs. Issue #2 (Unbounded Loop Gas Limit): Issue #2 showed that minting too many tokens in one go crashes due to gas limits (like trying 1,000,000 tokens). This issue is about the long-term damage: an attacker could mint 10,000 tokens per transaction, over and over, inflating the supply without ever hitting the cap we set. Issue #2 limits single transactions; This issue is about fixing the no limit on total number of tokens.

function mintSnowman(address receiver, uint256 amount) external {
@> // No check to control/limit the amount of tokens that can be minted by the protocol
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

Risk

Likelihood: High

  • Thanks to Issue #1, anyone can call mintSnowman, making this super easy to exploit. An attacker just needs some ETH for gas to mint a ton of tokens. Even if you fix access control issue, an owner could goof and mint too many. The lack of a cap is like leaving the snow machine running with no one watching.

Severity: High

  • This is a game-changer. Unlimited minting could flood the market, crush the value of your NFTs, and make collectors ditch the project faster than a snowball melts in July. It’s economic chaos waiting to happen!

Impact:

  • Devalues the NFT collection by destroying scarcity, tanking prices, and hurting holders.

  • Disrupts the airdrop’s economics, making it impossible to control supply.

  • Could kill trust in the project, sending your rep into a deep freeze.

  • Made worse by Issue #1, as any random Joe can trigger this snowstorm.

Proof of Concept

please copy the following code and paste inside the TestSnowman.t.sol file and run it with
forge test --match-test testNoTokenSupplyCap -vv

function testNoTokenSupplyCap() public {
uint256 largeAmount = 10_000; // Large but feasible number of tokens
uint256 initialTokenCounter = nft.getTokenCounter();
// Simulate attacker minting a large number of tokens
vm.prank(attacker); // Note: Attacker can call due to Issue #1
nft.mintSnowman(attacker, largeAmount);
// Verify that tokens were minted to attacker
assertEq(nft.balanceOf(attacker), largeAmount, "Attacker should have minted 10,000 tokens");
assertEq(nft.ownerOf(initialTokenCounter), attacker, "First token should be owned by attacker");
assertEq(nft.ownerOf(initialTokenCounter + largeAmount - 1), attacker, "Last token should be owned by attacker");
assertEq(nft.getTokenCounter(), initialTokenCounter + largeAmount, "Token counter should increase by 10,000");
// Log for debugging
console2.log("Attacker balance after mint:", nft.balanceOf(attacker));
console2.log("Token counter after mint:", nft.getTokenCounter());
}

What Happened When I Ran It:

Ran 1 test for test/TestSnowman.t.sol:TestSnowman
[PASS] testNoTokenSupplyCap() (gas: 331369392)
Logs:
Attacker balance after mint: 10000
Token counter after mint: 10000
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 385.76ms

Recommended Mitigation

Here’s how to keep your supply in check:

Add a hard cap on the total number of tokens in mintSnowman. Here’s a slick code snippet to make it happen:

uint256 public constant MAX_SUPPLY = 10_000; // Cap at 10,000 snowmen
uint256 private s_totalSupply;
function mintSnowman(address receiver, uint256 amount) external {
require(s_totalSupply + amount <= MAX_SUPPLY, "Whoa, we’re out of snow for more snowmen!");
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
s_totalSupply++;
}
}
+ uint256 public constant MAX_SUPPLY = 10_000; // Cap at 10,000 snowmen
+ uint256 private s_totalSupply;
//inside the mintSnowman function, we insert this line
+ require(s_totalSupply + amount <= MAX_SUPPLY, "Whoa, we’re out of snow for more snowmen!");
Updates

Lead Judging Commences

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.