What lines of code/.sol documents do I need in order to make the first token (NFT) free to mint, and all next ones payable?

Hi! Smart contract newbie here.
I'm having trouble starting the very beginning of my contract, as well as setting up the price for the first token to be 0, and later change to 0.01eth.
The .sol files I plan to import are: ERC721, IERC721, IERC721Receiver, IERC721Metadata, ERC165, IERC165, Ownable, Address, Context, Strings, ReentrancyGuard, and possibly MerkleProof (which I do no fully understand). I just plan to copy-paste these files and slap them onto a single .sol file. Does that make any sense at all?

I've gone through the majority of OpenZeppelin contract files, but I'm unable to find a .sol file where I can see a function that utilizes this feature.
Thanks for any replies!


oz team build the token contract interfaces based on EIPs (20, 721, 1155, etc).

we need to create a child/customize contract to include the price (msg.value) on a mint function and some other logics aren't based on EIPs while we still inherit from oz's contracts for most of the standards.

here's my simple approach to answer your question:
using a contract wizard, i select: erc721, baseURI (ipfs://CID/), mintable with auto increments id. and then i opened it on remix and make some adjustments:

  1. change state variable name _tokenIdCounter to totalSupply and change its visibility from private to public -- goal: to be used as a getter function to track the total supply

  2. adjust public & onlyOwner safeMint function from the contract wizard to _safeMintToken (function name) and change its visibility from public to internal function -- goal: to reduce code duplication on public payable mint function (#3)

  3. create a public payable mint function with the control flow based on your intention before call _safeMintToken (internal)

here's the snippet of the code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts@4.6.0/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.6.0/access/Ownable.sol";
import "@openzeppelin/contracts@4.6.0/utils/Counters.sol";

contract MyToken is ERC721, Ownable {
    using Counters for Counters.Counter;
    // init total supply tracker
    Counters.Counter public totalSupply;

    constructor() ERC721("MyToken", "MTK") {}

    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://CID/";

    // internal mint function to reduce code duplication
    function _safeMintToken(address to) internal {
        uint256 tokenId = totalSupply.current();
        _safeMint(to, tokenId);

    // public payable mint function
    function mint(address to) public payable {
        // memory variable
        uint256 currentSupply = totalSupply.current();
        // control flow before call _safeMintToken
        // if current supply is zero (there is no tokenId been minted)
        if (currentSupply == 0) {
        } else {
            // if current supply is not zero
            require (msg.value == 0.01 ether,"Revert message");

it's been tested on remix and it works.

i believe there are many better approaches to solve your problem. it is just an idea, so please don't use it on the production.

hope it helps!