Snowman Merkle Airdrop

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

Claim function will fail for smart contract receivers lacking ERC721Receiver support

Summary

Smart contracts that acquire Snow tokens may be unable to claim Snowman NFTs if they do not implement the onERC721Received() function. This can cause Snow tokens to become trapped on such contracts, leading to potential loss of funds or denial of claim.

Description

Currently, anyone — including smart contracts — can call buySnow() and earnSnow() to acquire Snow tokens.
However, during claimSnowman(), the contract mints Snowman NFTs directly to the receiver address via:
i_snowman.mintSnowman(receiver, amount);
If receiver is a smart contract without an ERC721Receiver implementation, the mint operation will revert. This leaves the contract holding Snow tokens it cannot redeem for NFTs, and is unable to recover the SNOW tokens either.

POC

Put the BadReceiver contract in the test folder. And add testCannotClaim in the TestSnowmanAirdrop.
Here we are not even using the airdrop claim function we are just minting right away because mint function in Snowman contact is accessible to all so why not, dont even need a merkle tree proof for this POC. It demonstates that yes indeed a smart contract can buy and earn snow tokens and then be unable to do anything with them if they dont have onERC721Received function implemented.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Snow} from "../src/Snow.sol";
import {Snowman} from "../src/Snowman.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract BadReceiver {
Snow public immutable snow;
Snowman public immutable snowman;
constructor(address _snow, address _snowman) {
snow = Snow(_snow);
snowman = Snowman(_snowman);
}
// This contract is able to receive SNOW tokens
function buySnow(uint256 amount) payable external {
snow.buySnow{value: msg.value}(amount);
}
function claim(uint256 amount) external {
snowman.mintSnowman(address(this), amount);
}
// Notice: no onERC721Received() function implemented!
}
function testCannotClaim() public {
BadReceiver badReceiver = new BadReceiver(address(snow), address(nft));
uint256 amount = 1;
uint256 ethNeeded = snow.s_buyFee() * amount;
deal(address(badReceiver), ethNeeded);
badReceiver.buySnow{value: ethNeeded}(amount);
vm.expectRevert();
badReceiver.claim(amount);
uint256 snowBalance = snow.balanceOf(address(badReceiver));
// meanwhile bob can mint no problem
vm.prank(bob);
nft.mintSnowman(bob, 1);
}

Impact

Inability to claim Snowman NFTs by non-ERC721-compatible contracts.

Severity

HIGH

Likelihood

HIGH

Tools used

Manual review

Recommended mitigation

Restrict buySnow() and/or claimSnowman() to EOAs using:
if (msg.sender != tx.origin) revert OnlyEOA();
Alternatively, add interface checks to ensure receivers can handle ERC721 tokens before allowing claims.

Updates

Lead Judging Commences

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

Appeal created

0xsamuraijack Submitter
5 months ago
yeahchibyke Lead Judge
5 months ago
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.