How to mint NFTs programmatically (Zero Solidity knowledge needed)

Did you know that the NFT industry generated around $24.7 billion in sales last year(Source)? If you’re a programmer trying to get your hands dirty in this space with little or no Blockchain development experience, this article can be a great start for you. In this article, you’ll learn about how you can mint NFTs programmatically using Alchemy and Pinata Cloud with 0 Solidity knowledge. There are 2 parts to this article:

  1. Deploying an ERC721 smart contract
  2. Minting NFTs programmatically

If you know how to deploy an ERC721 smart contract or have one deployed already, you may directly jump to the 2nd part.

What you’ll need: Metamask wallet with Polygon Mumbai added, NodeJS, npm and the desire to learn something new✨.

Part 1: Deploying an ERC721 smart contract

Step 1: Create an Alchemy account

The first step is to sign up for a free Alchemy account in case you don’t have one already.

Alchemy provides a powerful set of Web3 development tools to build and scale your dApp with ease and allows you to make requests to the Ethereum Network without running your own nodes. — Alchemy

Step 2: Create an App

On your dashboard, click on the + Create App button or from the top nav, select apps > create new.


Enter a name and a description for your app

Make sure you select the Polygon Mumbai Testnet and click on the Create app button. The newly created app appears in a table below.

Step 3: Get some MATIC for free :moneybag:

Go to https://faucet.polygon.technology/. Select Mumbai under network and MATIC Token. Paste your Metamask wallet address and click Submit.

Notice we have 0 MATIC, copy the wallet address

Click Confirm and wait for 1–2 minutes for the tokens to get transferred to your wallet.

Free MATICs credited💰

Here are your free MATIC but don’t celebrate yet, you won’t be able to use these for transactions on the mainnet, since these are for testing/ development purposes only.

Time to get some coding!!

Step 4: Create a new Project

Create a new folder called MintNFTs and open the terminal in this directory/folder. Run the following command to create an npm project:

npm init

Accept all default values or edit them if you like, here’s what I chose:

{
“name”: “mintnfts”,
“version”: “1.0.0”,
“description”: “Exploring how to mint NFTs programmatically”,
“main”: “index.js”,
“scripts”: {
“test”: “echo "Error: no test specified" && exit 1”
},
“author”: “”,
“license”: “ISC”
}

Step 5: Hardhat setup

Hardhat is a development environment for Ethereum software. It consists of different components for editing, compiling, debugging and deploying your smart contracts and dApps, all of which work together to create a complete development environment. — Hardhat

To install Hardhat in our project, run the following command inside the same project directory:

npm install --save-dev hardhat

then to create the hardhat config file run:

npx hardhat

and select the “create an empty hardhat.config.js” option. When that’s done, create two folders inside the project, i.e contracts and scripts.

Time to write the ERC721 smart contract, which is the Non-Fungible Token standard. Find out more about ERC721 here. You can write the smart contract yourself, but we’ll use the openzeppelin contracts wizard for simplicity.

Go to https://docs.openzeppelin.com/contracts/4.x/wizard, and under the ERC721 option, select the following:

Openzeppelin contracts wizard
This generates an ERC721 smart contract code written in Solidity. How cool is that? You can play around with the options and modify them as you like, but I’ll leave it at this for the scope of this tutorial. Finally, copy the generated code and go to our project. Inside the contracts folder, create a new file called MintNFTs.sol and paste the copied code.

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

import “@openzeppelin/contracts/token/ERC721/ERC721.sol”;
import “@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol”;
import “@openzeppelin/contracts/access/Ownable.sol”;
import “@openzeppelin/contracts/utils/Counters.sol”;

contract MintNFTS is ERC721, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;

Counters.Counter private _tokenIdCounter;

constructor() ERC721("MintNFTS", "MNFT") {}

function safeMint(address to, string memory uri) public onlyOwner {
    uint256 tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    _safeMint(to, tokenId);
    _setTokenURI(tokenId, uri);
}

// The following functions are overrides required by Solidity.

function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
    super._burn(tokenId);
}

function tokenURI(uint256 tokenId)
    public
    view
    override(ERC721, ERC721URIStorage)
    returns (string memory)
{
    return super.tokenURI(tokenId);
}

}
In order to use openzeppelin contracts in our code, add it as a dependency. Run the following command in the terminal.

npm install @openzeppelin/contracts
Step 6: Connect Metamask and Alchemy to our Local Project
Install the dotenv package. We’ll need this to store some confidential information.

npm i dotenv --save
Now create a .env file at the project level. Go to the Alchemy dashboard, find your MintNFTs project and click on the ‘View key’ button. Copy the API KEY and HTTPS API URL and paste them into the .env file.

The next step is to get your Metamask private key. Follow these steps to obtain it:

[

How to export an account’s private key

Warning! Exporting your account could be risky as it displays your private key in clear text. To avoid possible loss of…

](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)

Once done paste it into the .env file again. At the end of this step, your .env file should look something like this:

API_URL=‘https://polygon-mumbai.g.alchemy.com/v2/api-key
PRIVATE_KEY=‘metamask-private-key’
API_KEY = ‘api-key’
If you have a git repo initialised, make sure to add the .env to .gitignore to make sure you never commit the .env file.

Step 7: Towards deployment
Next, we’ll need Ethers.js to deploy our smart contract. Run the following command from the root of your project:

npm install --save-dev @nomiclabs/hardhat-ethers “ethers@^5.0.0”
Update the hardhat.config.js file like so:

require(‘dotenv’).config();

require(“@nomiclabs/hardhat-ethers”);
const { API_URL, PRIVATE_KEY } = process.env;

/**

  • @type import(‘hardhat/config’).HardhatUserConfig
    */
    module.exports = {
    solidity: “0.8.18”,
    defaultNetwork: “mumbai”,
    networks: {
    hardhat: {},
    mumbai: {
    url: API_URL,
    accounts: [0x${PRIVATE_KEY}]
    }
    },
    }
    Compile the smart contract, to make sure everything is done correctly.

npx hardhat compile
The deployment script:

Inside the scripts folder create a new file called deploy.js

const hre = require(“hardhat”);

async function main() {
const MintNFTs = await hre.ethers.getContractFactory(“MintNFTS”);
const mintNFts = await MintNFTs.deploy();

await mintNFts.deployed();

console.log(
MintNFTs deployed to ${mintNFts.address}
);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Step 8: Deploy
To finally deploy the smart contract, run the above script by running:

npx run scripts/deploy.js --network mumbai
if you see a console log as shown below, the smart contract is deployed successfully!:tada:

MintNFTs deployed to 0x485eF7672BC79d7c97f7386fFCF6599265c…
You can also check the newly deployed smart contract on Polygon PoS Chain Account (Invalid Address)

Updated balance

Notice that some amount of MATIC was spent to pay for the gas fees.

Part 2: Minting NFTs programmatically

We’re going to use pinata to store our NFT images. Pinata handles the complexities of media storage and distribution on IPFS while also opening up revenue opportunities. Learn more about IPFS here.

To install pinata in our project, run

npm install --save @pinata/sdk

Sign up on Pinata, create an account and go to the API Keys tab and create a new key.

Permissions on Pinata
Give permissions as shown above, click create and copy. Paste the key and secret in the .env file. Make sure your .env looks like this

API_URL=‘https://polygon-mumbai.g.alchemy.com/v2/api-key
PRIVATE_KEY=‘metamask-private-key’
API_KEY=‘api-key’
CONTRACT_ADDRESS=‘deployed-contract-address’
RECEIVER_ADDRESS = ‘metamask-wallet-address’
PINATA_API_KEY= ‘pinata-api-key’
PINATA_API_SECRET= ‘pinata-api-secret’
Create a new folder inside the project called mint, inside this folder create two files pinata.jsand index.js

Now inside the pinata.js file, we will define 3 functions:

pinFileToIPFS: This function will be used to upload the image to IPFS.
pinJSONToIPFS: We will use this function to add metadata for the NFT image(for eg: name and description of the NFT)
getPinataTokenUri: This function will call the above 2 functions and return us the IPFS URL of the uploaded image.
const pinataSDK = require(‘@pinata/sdk’);
const fs = require(‘fs’);
require(‘dotenv’).config()

const pinata = new pinataSDK(process.env.PINATA_API_KEY, process.env.PINATA_API_SECRET);

const options = {
pinataMetadata: {
name: “A wonderful NFT”,
},
pinataOptions: {
cidVersion: 0
}
};

//To upload the image to IPFS
const pinFileToIPFS = () => {
const readableStreamForFile = fs.createReadStream(‘./image.png’);
return pinata.pinFileToIPFS(readableStreamForFile, options).then((result) => {
return https://gateway.pinata.cloud/ipfs/${result.IpfsHash}
}).catch((err) => {
console.log(err);
});
}

//To add metadata to the image
const pinJSONToIPFS = (body) => {
return pinata.pinJSONToIPFS(body, options).then((result) => {
return https://gateway.pinata.cloud/ipfs/${result.IpfsHash}
}).catch((err) => {
console.log(err);
});
}

//To get the token URI from IPFS
module.exports = async function getPinataTokenUri() {
const imageUrl = await pinFileToIPFS()
const body = {
name: ‘A wonderful NFT’,
description: “Minted Programmatically”,
image: imageUrl
};
const metadata = await pinJSONToIPFS(body)
console.log(metadata)
return metadata;
}
Be sure to add an image at the root level of the project. This image will be used by our project to mint an NFT.

image

This is how the folder structure should look like, at this stage

Next, we will call the safeMint()function defined in our smart contract. Inside the index.js file, add the following code:

require(‘dotenv’).config();
var getPinataTokenUri = require(‘./pinata’)
const ethers = require(‘ethers’);

// Get Alchemy API Key
const API_KEY = process.env.API_KEY;

// Define an Alchemy Provider
const provider = new ethers.providers.AlchemyProvider(‘maticmum’, API_KEY)

const contract = require(“…/artifacts/contracts/MintNFTs.sol/MintNFTs.json”);
const privateKey = process.env.PRIVATE_KEY
const signer = new ethers.Wallet(privateKey, provider)

// Get contract ABI and address
const abi = contract.abi
const contractAddress = process.env.CONTRACT_ADDRESS

// Create a contract instance
const mintNftContract = new ethers.Contract(contractAddress, abi, signer)
const to = process.env.RECEIVER_ADDRESS

// Call safeMint function
async function mint(){
let tokenUri = await getPinataTokenUri()
let nftTxn = await mintNftContract.safeMint(to, tokenUri)
await nftTxn.wait()
console.log(NFT Minted! Check it out)
}

mint()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

The mint() function first calls the getPinataTokenUri() which uploads the image, and its metadata and returns the IPFS token URI. It then calls the smart contracts safeMint function which mints the NFT in the RECEIVER_ADDRESS wallet address, which in my case is my wallet address. From your terminal run

node ./mint/index.js

When the transaction is complete and you see NFT Minted! Check it outprinted, you can go to testnets.opensea.io, connect the specified wallet and under your profile you’ll find the NFT you just minted from your smart contract successfully! Mission Accomplished! :sunglasses:

Thanks for reading this far! I hope you found this article helpful. If you did, feel free to leave some claps! It motivates me to keep writing.:sparkles:

Resources:

2 Likes

Warrerrr :flushed:… Work great :+1:

I will try this… thank you

alot of info you got there, thanks!