How to Create a Solidity Function Allowing Users to Claim One Free ERC721 NFT Each?

I need help building a claim function that lets each wallet address mint exactly one NFT for free (only paying gas). This is for a promotional campaign where users can get their free token directly.

Right now my contract only allows the owner to distribute tokens, but I want regular users to be able to claim their own NFT. This would save me from paying all the gas fees when there are many tokens to distribute.

Here’s my current code using OpenZeppelin:

contract FreeNFTContract is ERC721, ERC721Enumerable, Ownable {

    bool public giveawayEnabled = false;
    uint256 public remainingFreeTokens = 5;

    function distributeFreeNFT(uint qty, address recipient) public onlyOwner {
        require(giveawayEnabled, "Giveaway is not enabled yet.");
        require(qty <= remainingFreeTokens, "Not enough tokens left.");
        require(qty > 0, "Quantity must be at least 1.");
        
        for (uint j = 0; j < qty; j++) {
            uint256 newTokenId = totalSupply();
            if (newTokenId < remainingFreeTokens) {
                _safeMint(recipient, newTokenId);
            }
        }

        remainingFreeTokens = remainingFreeTokens.sub(qty);
    }
}

Is there a way to modify this so users can call the function themselves while still limiting one NFT per address?

Oh this is interesting! I’ve been working on something similar and hit the same gas cost issues :sweat_smile:

You want users to mint their own NFTs instead of sending them manually, right? Smart move, especially with lots of claims expected.

Looking at your code, you’ll need a mapping to track claimed addresses and a new function users can call directly. Something like mapping(address => bool) public hasClaimed; works.

Few questions though - planning any whitelist for claims? Or truly open to anyone who finds the contract? What about people gaming it with multiple wallets - concern or acceptable risk?

Also, considered adding time limits? Start/end dates might prevent endless claiming :thinking:

What’s your target audience size? Hundreds or thousands of potential claimers?

Replace the onlyOwner modifier with public and add a mapping to track claims: mapping(address => bool) claimed. Check !claimed[msg.sender] before minting, then set claimed[msg.sender] = true after the mint or ppl will spam it. Your tokenId logic looks off too - totalSupply() will break if you burn tokens.