"Mint NFT" button on your website

Ahmed Castro - Aug 23 '22 - - Dev Community

Collectibles NFTs proyects that attract more buyers have a website for mint. In this video we launch a NFT collection smart contract on Goerli and then the required UI containing the mint button.

https://www.youtube.com/watch?v=6m17YQWHq-M

Dependencies

For this tutorial you will need NodeJs, I recommend downloading it from Linux via NVM, a RPC URL, I recommend using INFURA or Alchemy, Metamask with Goerli Testnet funds that you can get from the Faucet.

1. Initial configuration

mkdir MyNFT
cd MyNFT
npm install --save-dev truffle dotenv @truffle/hdwallet-provider @openzeppelin/contracts
npx truffle init
Enter fullscreen mode Exit fullscreen mode

truffle-config.js

require('dotenv').config()
const HDWalletProvider = require('@truffle/hdwallet-provider');

const fs = require('fs');

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*",
    },
    goerli: {
      provider: function () {
        return new HDWalletProvider(process.env.PRIVATE_KEY, process.env.GOERLI_RPC_URL);
      },
      network_id: 5,
      gas: 4000000,
      networkCheckTimeout: 10000
    }
  },
  mocha: {
  },
  compilers: {
    solc: {
      version: "0.8.16",
    }
  },
  db: {
    enabled: false
  }
};
Enter fullscreen mode Exit fullscreen mode

2. Contract launch

Create and edit the contract at your convenience.

contracts/MyNFT.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

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

contract MyNFT is ERC721Enumerable {
    uint256 public MAX_ELEMENTS = 5;
    uint256 public PRICE = 0.01 ether;
    address public CREATOR = 0x0000000000000000000000000000000000000000;
    uint256 public token_count;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdTracker;

    constructor() ERC721("My NFT", "MNFT") {}

    function _baseURI() internal view virtual override returns (string memory) {
        return "MIURL";
    }

    function _totalSupply() internal view returns (uint) {
        return _tokenIdTracker.current();
    }

    function totalMint() public view returns (uint256) {
        return _totalSupply();
    }

    function mint(address _to, uint256 _count) public payable {
        uint256 total = _totalSupply();
        require(total + _count <= MAX_ELEMENTS, "Max limit");
        require(total <= MAX_ELEMENTS, "Sale end");
        require(msg.value >= PRICE*_count, "Value below price");

        for (uint256 i = 0; i < _count; i++) {
            _mintAnElement(_to);
        }
    }

    function _mintAnElement(address _to) private {
        uint id = _totalSupply();
        _tokenIdTracker.increment();
        _safeMint(_to, id);
    }

    function withdrawAll() public {
        (bool success, ) = CREATOR.call{value:address(this).balance}("");
        require(success, "Transfer failed.");
    }
}
Enter fullscreen mode Exit fullscreen mode

migrations/1_my_deploy.js

const MyNFT = artifacts.require("MyNFT")

module.exports = async function (deployer) {
  await deployer.deploy(MyNFT)
}
Enter fullscreen mode Exit fullscreen mode

.env

GOERLI_RPC_URL=YOURRPCKEY
PRIVATE_KEY=YOURPRIVATEKEY
Enter fullscreen mode Exit fullscreen mode
npx truffle migrate --network goerli
Enter fullscreen mode Exit fullscreen mode

3. Frontend

Create the client/contracts directory and copypaste build/MyNFT.json on it. Also, add the necessary html and js and edit them at your convenience.

client/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>
  <input id="connect_button" type="button" value="Connect" onclick="connectWallet()" style="display: none"></input>
  <p id="account_address" style="display: none"></p>
  <p id="web3_message"></p>
  <p id="contract_state"></p>
  <select id="mint_amount">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
  </select>
  <input type="button" value="mint!" onclick="_mint()">
  <p id="nft_price"></p>
  <p id="nft_balance"></p>
  <br>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.3.5/web3.min.js"></script>
  <script type="text/javascript" src="blockchain_stuff.js"></script>
</body>
</html>

<script>
  function _mint()
  {
    mint_amount = document.getElementById("mint_amount").value
    mint(mint_amount)
  }
</script>
Enter fullscreen mode Exit fullscreen mode

client/src/contract_interaction.js

const NETWORK_ID = 5

const MY_NFT_CONTRACT_ADDRESS = "0xc7c463b90B393b4A0d3650Aa70a08211a6D5fB79"
const MY_NFT_CONTRACT_ABI_PATH = "./MyNFTABI.json"
var my_nft_contract
var price

var accounts
var web3

function metamaskReloadCallback() {
  window.ethereum.on('accountsChanged', (accounts) => {
    document.getElementById("web3_message").textContent="Network changed, refreshing...";
    window.location.reload()
  })
  window.ethereum.on('networkChanged', (accounts) => {
    document.getElementById("web3_message").textContent="Network chainged, refreshing...";
    window.location.reload()
  })
}

const getWeb3 = async () => {
  return new Promise((resolve, reject) => {
    if(document.readyState=="complete")
    {
      if (window.ethereum) {
        const web3 = new Web3(window.ethereum)
        window.location.reload()
        resolve(web3)
      } else {
        reject("must install MetaMask")
        document.getElementById("web3_message").textContent="Error: Connect to Metamask";
      }
    }else
    {
      window.addEventListener("load", async () => {
        if (window.ethereum) {
          const web3 = new Web3(window.ethereum)
          resolve(web3)
        } else {
          reject("must install MetaMask")
          document.getElementById("web3_message").textContent="Error: Please install Metamask";
        }
      });
    }
  });
};

const getContract = async (web3, address, abi_path) => {
  const response = await fetch(abi_path);
  const data = await response.json();

  const netId = await web3.eth.net.getId();
  contract = new web3.eth.Contract(
    data,
    address
    );
  return contract
}

async function loadDapp() {
  metamaskReloadCallback()
  document.getElementById("web3_message").textContent="Please connect to Metamask"
  var awaitWeb3 = async function () {
    web3 = await getWeb3()
    web3.eth.net.getId((err, netId) => {
      if (netId == NETWORK_ID) {
        var awaitContract = async function () {
          my_nft_contract = await getContract(web3, MY_NFT_CONTRACT_ADDRESS, MY_NFT_CONTRACT_ABI_PATH)
          document.getElementById("web3_message").textContent="You are connected to Metamask"
          onContractInitCallback()
          web3.eth.getAccounts(function(err, _accounts){
            accounts = _accounts
            if (err != null)
            {
              console.error("An error occurred: "+err)
            } else if (accounts.length > 0)
            {
              onWalletConnectedCallback()
              document.getElementById("account_address").style.display = "block"
            } else
            {
              document.getElementById("connect_button").style.display = "block"
            }
          });
        };
        awaitContract();
      } else {
        document.getElementById("web3_message").textContent="Please connect to Goerli";
      }
    });
  };
  awaitWeb3();
}

async function connectWallet() {
  await window.ethereum.request({ method: "eth_requestAccounts" })
  accounts = await web3.eth.getAccounts()
  onWalletConnectedCallback()
}

loadDapp()

const onContractInitCallback = async () => {
  price = await my_nft_contract.methods.PRICE().call()
  document.getElementById("nft_price").textContent = "NFT Price: " + web3.utils.fromWei(price) + " ETH";
}

const onWalletConnectedCallback = async () => {
  balance = await my_nft_contract.methods.balanceOf(accounts[0]).call()
  document.getElementById("nft_balance").textContent= "Your balance: " + balance;
}


//// Functions ////

const mint = async (amount) => {
  const result = await my_nft_contract.methods.mint(accounts[0], amount)
  .send({ from: accounts[0], gas: 0, value: price * amount })
  .on('transactionHash', function(hash){
    document.getElementById("web3_message").textContent="Executing...";
  })
  .on('receipt', function(receipt){
    document.getElementById("web3_message").textContent="Success.";    })
  .catch((revertReason) => {
    console.log("ERROR! Transaction reverted: " + revertReason.receipt.transactionHash)
  });
}
Enter fullscreen mode Exit fullscreen mode

4. Interact via web

npm install -g lite-server
cd client
lite-server
Enter fullscreen mode Exit fullscreen mode

The result is in http://localhost:3000.

Now you can do changes to the contract and then adapt the web UI. I recommend Netlify as a deploying plataform, is free and simple.
Feel free to use the [Github public template]https://github.com/FilosofiaCodigo/NFTCollectionTemplate) as a reference.

Thanks for watching this tutorial!

. . . . . . . . . . . . . . . . . . . . . . . . .