Snowman Merkle Airdrop

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

Front-running in mintSnowman() Allows NFT Token ID Sniping

Description

The mintSnowman() function assigns token IDs using a global counter s_TokenCounter, which is used before being incremented. This makes future token IDs fully predictable. If a legitimate user attempts to mint a token (e.g., tokenId = 10), an attacker can observe the transaction in the mempool, predict the exact tokenId, and front-run the transaction by sending their own with higher gas fees. As a result, the attacker receives the intended token ID before the victim's transaction is processed.

Risk

Likelihood: High

  • Exploitable by anyone monitoring the mempool.

  • No on-chain restrictions, no signature checks.

  • No additional contracts or setup required.

  • Can be automated with basic MEV bots or flashbots bundles.

Impact:

  • Token ID sniping becomes trivial — attacker steals the exact token(s) a user is attempting to mint.

  • If token IDs are linked to rarity, airdrops, metadata, or in-game value, the economic damage is significant.

  • This undermines trust and fairness in the minting process.

  • No contract interaction is needed — even basic mempool watchers can exploit this.

Proof of Concept

Flow:

  1. Alice submits tx to mint 1 NFT → assume - expectedTokenId = 10.

  2. Attacker sees her tx in mempool.

  3. Attacker copies calldata and sends tx with higher gas.

  4. Attacker gets tokenId 10, Alice gets 11.


Recommended Mitigation

Move the counter increment before the external _safeMint() call to avoid predictability and eliminate reentrancy risk :

  • Use randomized token assignment (e.g. keccak256-based shuffle).

  • Implement commit-reveal schemes for higher-value mints.

function mintSnowman(address receiver, uint256 amount) external {
for (uint256 i = 0; i < amount; i++) {
uint256 tokenId = s_TokenCounter;
// ✅ Effect first
+ s_TokenCounter++;
// ✅ Then Interaction
+ _safeMint(receiver, tokenId);
emit SnowmanMinted(receiver, tokenId);
}
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 1 month ago
yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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