How to limit NFT minting to single token per Solidity contract

I’m working on a Solidity smart contract and need to create just one unique NFT instead of multiple tokens. Most guides show how to build collections using ERC721 inheritance, but I want something different.

Here’s my current approach:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract UniqueArtwork is ERC721 {
    uint256 public currentTokenId;
    
    constructor() ERC721("SingleArt", "ART") {
        currentTokenId = 0;
    }
    
    function mintArtwork(string memory metadataURI) public returns (uint256) {
        uint256 tokenId = currentTokenId;
        _safeMint(msg.sender, tokenId);
        _setTokenURI(tokenId, metadataURI);
        currentTokenId += 1;
        return tokenId;
    }
}

What’s the best way to prevent multiple minting and ensure only one NFT gets created? I want to make sure nobody can call the mint function more than once total.

Oh cool project! What’s your use case here - creating a one-of-a-kind digital artwork or just testing things out?

Your current code will mint multiple tokens since there’s no check stopping it. Easy fix is adding a boolean flag to track if you’ve already minted:

bool private _hasBeenMinted = false;

function mintArtwork(string memory metadataURI) public returns (uint256) {
    require(!_hasBeenMinted, "NFT already minted");
    _hasBeenMinted = true;
    
    uint256 tokenId = currentTokenId;
    _safeMint(msg.sender, tokenId);
    _setTokenURI(tokenId, metadataURI);
    return tokenId;
}

But who should be allowed to mint this single NFT? First person who calls it? Or do you want to control who can mint? Also, are you planning to have the metadata set already or should the minter decide?

There might be edge cases depending on what you’re building. What’s the bigger picture?

just check if currentTokenId is already 1 instead of using extra storage. something like require(currentTokenId == 0, "already minted"); at the start of your mint function. saves gas too since you dont need another boolean variable

The counter check works, but you’re missing the ERC721URIStorage import. Without it, _setTokenURI won’t work.

Here’s another way using a modifier:

modifier onlyOnce() {
    require(currentTokenId == 0, "Token already exists");
    _;
}

function mintArtwork(string memory metadataURI) public onlyOnce returns (uint256) {
    // your minting logic here
}

This makes it clearer what you’re doing, especially if you add more functions later that should only run before minting. You could also drop the currentTokenId increment since you’re only creating token 0. Just use totalSupply() from ERC721 to check if anything’s been minted.