How to create a Solidity function for a single free ERC721 token mint per user?

I’m working on a smart contract for an NFT project. I want to let people mint one free NFT each as part of a promo. How can I make a function that lets anyone mint just one token without paying (except gas fees)?

Here’s what I’ve got so far:

contract FreeNFTMint is ERC721, ERC721Enumerable, Ownable {
  bool public freeClaimOpen = false;
  uint256 public freeTokensLeft = 100;

  function claimFreeNFT() public {
    require(freeClaimOpen, "Free claim is not open yet");
    require(freeTokensLeft > 0, "No more free tokens left");
    // Need help here to limit one per user
    uint256 tokenId = totalSupply();
    _safeMint(msg.sender, tokenId);
    freeTokensLeft--;
  }
}

Right now, anyone can mint multiple times. How do I change it so each address can only mint once? Also, is there a way to do this without centralized tracking? Any tips or code examples would be great. Thanks!

To implement a single free mint per user, you can utilize a mapping to track claimed addresses. Here’s how you could modify your contract:

mapping(address => bool) public hasClaimed;

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

This approach ensures each address can only mint once. It’s simple and gas-efficient for smaller projects. However, for larger-scale distributions, consider exploring more advanced techniques like merkle proofs to optimize gas costs.

hey climbing mountain! cool project ur working on :slight_smile:

i was wondering, have u considered using an alternative approach like a merkle tree for whitelisting addresses? it could be more gas-efficient for larger free mint lists.

also, what’s ur plan for preventing bots from snatching up all the free mints? maybe add a small time delay or captcha?

btw, love the idea of a free promo mint! what kind of artwork are u planning for the nfts?

keep us updated on how it goes!

Hey ClimbingMountain, here’s a quick fix:

use a mapping to track minted addresses:

mapping(address => bool) public hasClaimed;

function claimFreeNFT() public {
  require(!hasClaimed[msg.sender], \"Already claimed\");
  // rest of ur code
  hasClaimed[msg.sender] = true;
}

this’ll do the trick! lmk if u need anything else