How to create a Solidity function for free public minting of a single ERC721 token?

Hey everyone! I’m trying to set up a function for a promotional giveaway where each person can mint one free NFT. I’m stuck on how to make this work for public users instead of just the contract owner. Here’s what I’ve got so far:

contract FreeNFTGiveaway is ERC721, ERC721Enumerable, Ownable {
    bool public giveawayActive = false;
    uint256 public freeTokensLeft = 100;

    function claimFreeNFT() public {
        require(giveawayActive, "Giveaway not active yet");
        require(freeTokensLeft > 0, "No more free tokens");
        
        uint256 tokenId = totalSupply();
        _safeMint(msg.sender, tokenId);
        freeTokensLeft--;
    }
}

This lets the owner mint multiple free NFTs, but I want each public user to mint just one. Any ideas on how to do this? Maybe using a mapping to track addresses? I’m open to any suggestions, even if it involves some off-chain stuff. Thanks!

I’ve dealt with a similar scenario before. You’re on the right track with using a mapping to track addresses. Here’s how you could modify your contract:

contract FreeNFTGiveaway is ERC721, ERC721Enumerable, Ownable {
    bool public giveawayActive = false;
    uint256 public freeTokensLeft = 100;
    mapping(address => bool) public hasClaimed;

    function claimFreeNFT() public {
        require(giveawayActive, "Giveaway not active yet");
        require(freeTokensLeft > 0, "No more free tokens");
        require(!hasClaimed[msg.sender], "Address has already claimed");
        
        uint256 tokenId = totalSupply();
        _safeMint(msg.sender, tokenId);
        freeTokensLeft--;
        hasClaimed[msg.sender] = true;
    }
}

This implementation ensures each address can only claim once. The hasClaimed mapping efficiently prevents duplicate claims by keeping track of addresses that have already minted their free NFT. Adjust further as needed for your specific project requirements.

hey kai, good idea with the giveaway! u could use a mapping like this:

mapping(address => bool) public hasClaimed;

then in ur function add:

require(!hasClaimed[msg.sender], “already claimed”);
hasClaimed[msg.sender] = true;

that should do the trick for one per address. lmk if u need anything else!

hey there! that’s a cool idea for a giveaway :smiley: i’m curious, have you thought about adding any extra features to make it more fun? like maybe a random element to the nft they get, or a time limit?

i know you asked about limiting to one per user, but what if you wanted to let people earn more somehow? like completing tasks or sharing on social media? could be a neat way to build some hype

anyway, just some random thoughts. let us know how it turns out!