How To Mint NFTs in Solidity
Tags: web3, webdev, blockchain, tutorial
Published: 2022-04-21 08:34:49

## Introduction
Among this "web3" and blockchain hype, I decided to venture into the dev side of the hype. Initialized hardhat project, opened my editor & struck the keyboard keys until I got to a working thingy & in this post I'll be walking you through how to make one & share what I learned along the way.
**_Let's get started!_**

## Pre-Requisites
*A short note to all my beginner friends, this post is totally suited for you & I'll be walking you through, **_LIKE A BABY_** on how to write these smart contracts*

Before jumping on to the code, let us just zoom out for a second & ask:
**"What are smart contracts"**
**"What are NFTs"** and
**"What are Tokens anyways"**

**A Smart Contract is simply some code, written in the solidity programming language (Solidity is for Ethereum Blockchain. Other blockchains may require something different if they have smart contracts), deployed on the Ethereum Blockchain to perform certain operations defined in the code itself**. You see, the Ethereum Blockchain consists of thousands of decentralized compute nodes sitting & crunching numbers to keep the blockchain active. **When you deploy code on the blockchain, you're required to provide incentives or fees to encourage nodes to process your deployment transaction**.

Also, anyone (a user with some wallet address) calling your smart contract function **which causes some state change in your code (like modifying the value of a variable in your code), will have to give a fee.**

This fee is known as **_GAS FEE_**, *similar to you providing 💰 to AWS* for their lambda functions.

A Token on the other hand is SOMETHING on the blockchain, with which these smart contracts interact, creating, manipulating, or destroying them. That SOMETHING can itself be anything saved in a smart contract's state, some text, number, etc. while REPRESENTING numerous things like an image, currency, or even a virtual girlfriend! the possibilities are endless!

Let's see an example of a smart contract written in solidity, which maintains a record of "who called the smart contract how many times?"

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1650105243448/be6RDVRQJ.png)

In this solidity code or Ethereum smart contract, we have a map (a data structure that is used to maintain key-value pairs in solidity, here the key is user address & value is a positive 256-bit integer) `contractCalls` which can be viewed publicly & a public function`incrementState` which grabs the address of the user who called the function by using `msg.sender` and increments its value in that map.

*[Note: when grabbing a non-existing `msg.sender` address with `contractCalls[msg.sender]`, solidity defaults to 0 (zero) for an unsigned int (uint)]*

If we call `contractCalls[msg.sender]` again to see the number accumulated by the user, THAT number, THAT UINT256, IS THE TOKEN!
That's it!

{% youtube BLQsAVaDJCE %}

Also, looking at the solidity code, if you've ever worked with any object-oriented language, the `contract Test { ... }` syntax along with that `public` keyword may look similar to you, because it is. Solidity, like any other OOP language, supports visibility modifiers (`public`, `private`, `internal` & `external`) and concepts like inheriting from other contracts.

![solidity inheritance diagram](https://cdn.hashnode.com/res/hashnode/image/upload/v1650138065460/gS77JKaor.png)

Yeah, I know, it was an absurd way of telling someone "what is a token?" by teaching them how to write an almost hello world version of a new language, but hey! what if, I would've shown you this instead
>TOKENS are digital assets defined by a project or smart contract and built on a specific blockchain. Token can be UTILITY TOKENS or SECURITY TOKENS. UTILITY TOKENS are also called consumer or incentive tokens

Now, let us discuss, ***"What is an NFT?"***<br>
NFT, short for Non-Fungible Tokens (which I think, even a 3rd grader knows), is a class of tokens that have a standard protocol of creating, transferring, and burning/destroying them. A smart contract should follow the rules laid down by ERC721 (standard for Non-fungible tokens) in order to even count as an NFT.

## Code
When talking about developing & testing smart contracts, the first thing which comes to me is [Remix IDE](https://remix.ethereum.org), an online smart contract compiler. Remix makes writing & testing smart contracts extremely convenient. But Remix is not THE GO-TO choice when developing big projects with solidity.

To develop complex & big projects in solidity, you need the **Hardhat Toolchain** which helps you efficiently write smart contracts, compile them, deploy them on testnet or even mainnet and perform automated tests.

I'll be talking in detail about the whole hardhat dev workflow in another post, as it sometimes makes things LOOK complex while they aren't and in this post, I target to cover our beginner friends too 😉

For our cute little NFT smart contract, let us, for now, open up the [Remix IDE](https://remix.ethereum.org), create a new file in the `contracts` folder, and write the following stuff in there:

```solidity
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MyToken is ERC721 {
    uint256 public totalMints = 0;

    uint256 public mintPrice = 1 ether;
    uint256 public maxSupply = 50;
    uint256 public maxPerWallet = 5;
    string public URI = "https://bafybeifqmgyfy4by3gpms5sdv3ft3knccmjsqxfqquuxemohtwfm7y7nwa.ipfs.dweb.link/metadata.json";
    mapping(address => uint256) public walletMints;

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

    function safeMint(address to) internal {
        uint256 tokenId = totalMints;
        totalMints++;

        _safeMint(to, tokenId);
    }

    function mintToken(uint256 quantity_) public payable {
        require(quantity_ * mintPrice == msg.value, "wrong amount sent");
        require(walletMints[msg.sender] + quantity_ <= maxPerWallet, "mints per wallet exceeded");
        
        walletMints[msg.sender] += quantity_;
        safeMint(msg.sender);
    }

    function getMyWalletMints() public view returns (uint256) {
        return walletMints[msg.sender];
    }
}
```

The first line is used to tell the solidity compiler *what version of solidity we're using*, this is important as **this language is relatively new & thus has ongoing changes** (and, don't worry, even I don't know what the hell "pragma" means 🤡). And, due to the immutable nature of these smart contracts running on the blockchain, **THERE IS NO UPDATING YOUR CODE**, thus we need to specify, what version was used when this code was written.

Next, comes the import statement to import the ERC721 contract from open zeppelin into our contract to add the basic functionality of an NFT on top of our sh*tty code. There are some really important reasons, why we need to import this rather making our own buggy implementation 🐞:
1. Too lazy to write all of these functions (see implementation docs [here](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721))
2. The implementations provided by open zeppelin are tested hundreds of thousands of times & thus can be trusted more than our own code, also, their code has been optimized to require as low gas fees as possible while providing maximum functionality when run on actual Ethereum nodes

**Our contract `MyToken` inherits all internal and public variables & functions of the `ERC721` contract provided by open zeppelin**, this means that now we have functions like `_safeMint()` that could handle all the functionality of assigning someone a token with some ID, **we just have to control who takes how much & what's the total supply for the NFT.**

Now, in the contract let's define some state variables:<br>

* `totalMints` is a publicly visible positive integer that we'll be using to count how many tokens have been minted so far. that we'll also use to increment & assign a new & unique number to each token or NFT.
```solidity
uint256 public totalMints = 0;
```

* `mintPrice` is a public uint which has a value of `1 ether`, solidity has a special suffix `ether` for integers which are to be represented as some ETH value.
```solidity
uint256 public mintPrice = 1 ether;
```

* `maxSupply` is the total number of NFTs we decide to mint (public uint) so as to limit mints.
```solidity
uint256 public maxSupply = 50;
```

* `maxPerWallet` is the maximum NFTs a single wallet/user/address can have (public unit).
```solidity
uint256 public maxPerWallet = 5;
```

* `URI` is the URL which points to JSON data containing `name`, `description` & `image` for this NFT. This JSON data is also known as "metadata" & it should at least have at least these 3 properties to correctly show the image in your wallet (Metamask or any other wallet).<br><br>
Also, I uploaded the JSON metadata file for this NFT on https://web3.storage which is a decentralized file system to store & host your uploaded files (see the video below for your "**how to's**"). You can choose a centralized service or even host it yourself but what's the point of all this if you want to use centralized servers in the end to host the metadata.<br><br>
Notice when looking at the JSON pasted, I used the image which seems to be coming from a web server & you're correct, which I can justify by saying, "Yeah... 👀" but you would want to host the image on it or any other [IPFS](https://docs.ipfs.io/concepts/what-is-ipfs/) 😬.
```solidity
string public URI = "https://bafybeifqmgyfy4by3gpms5sdv3ft3knccmjsqxfqquuxemohtwfm7y7nwa.ipfs.dweb.link/metadata.json";
```

![json data](https://cdn.hashnode.com/res/hashnode/image/upload/v1650174838667/XeakxPYVB.png)

{%youtube LlQU9fGfbr8%}

* `walletMints` which is a map datatype to contain key-value pairs of addresses mapped to uint (token ids). We'll be using it to limit mints per wallet.
```solidity
mapping(address => string) public walletMints;
```

Now coming to the contract's constructor, a special function that runs only when the contract is deployed. When declaring the constructor, we call the ERC721 contract's constructor to initialize the inherited state from ERC721 with `ERC721("TokenName", "TKNSYMBL")`.
```solidity
constructor() ERC721("MyToken", "MTK") {}
```

Notice, that after calling ERC721 contract's constructor, we left the body of our contract's constructor empty and that is because, FOR NOW, we don't need to perform any operations when our contract is deployed.

Next, comes this function
```solidity
function safeMint(address to) internal {
    uint256 tokenId = totalMints;
    totalMints++;

    _safeMint(to, tokenId);
}
```

It uses the `_safeMint` function inherited from the `ERC721` contract which mints or assigns an address a token ID. And before doing so, makes sure that the token id is not owned by anyone. There is also a function `_mint` that does NOT check for ownership and just assigns the address to the given token id irrespective of whether the token id is already assigned to some address or not (which is almost never recommended... unless you want to shoot yourself in the foot).

[see a full list of all functions provided by the ERC721 contract [here](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721)]

Also, while these functions like `_safeMint` & `_mint` are callable from our inherited contract, users cannot directly call them as `internal` functions of a contract are usable by their inherited contract but can't be called from external wallet addresses. If they were callable, anyone could have minted unlimited tokens for free by calling it on their address and different token ids!

OUR `safeMint` function is marked as `internal` which means users can't directly call this function externally. This is what we want because the "pay to mint" functionality is still missing from our contract & we will be creating a new function to ensure users follow the rules laid down by us when buying this NFT(e.g following the limits for max supply & mints per wallet).

To ensure that users are paying for this NFT, we will be creating *a function `mintToken` which will be calling our `internal` function `safeMint` along with performing some additional checks*. It'll take a positive integer as its parameter `quantity_`, **requiring users to pay the amount of `mintPrice * quantity_`** in ethers (e.g exactly 2 ether for 2 NFT purchases) and check if this purchase does not exceed the `maxPerWallet` limit for the wallet calling the function.

```solidity
function mintToken(uint256 quantity_) public payable {
    require(quantity_ * mintPrice == msg.value, "wrong amount sent");
    require(walletMints[msg.sender] + quantity_ <= maxPerWallet, "mints per wallet exceeded");
        
    walletMints[msg.sender] += quantity_;
    safeMint(msg.sender);
}
```

Notice the `payable` keyword in front of the visibility modifier `public` which indicates to the solidity compiler that, specifically this function `mintToken` **CAN accept any ether value sent to it while calling it**.

"What!? these contracts can store my ether?!" Yup! when someone purchases your NFT, they send this ether to the contract & are stored on the contract itself until you decide to add code to send that balance to yourself, or else the ether is just stuck there FOREVER!

For checking that the limits are being followed, we use the inbuilt `require` function to check a condition, if it turns out to be `false`, the function will throw an error with the specified message (in the 2nd argument in the `require(condition, "error message")` function), **reverting the whole function call transaction** along with the sent ether.

If everything goes fine, it will accept the sent ether, increment the `walletMints` for the user's wallet address and call the `safeMint` function on the user's address (referenced by `msg.sender`) to actually mint the NFT to them.

The same thing could be done in this way too, where you pass one more parameter in the `mintToken` function named `recipient` whom to mint the NFT. Both ways are totally right & its your call what to implement.
```solidity
function mintToken(uint256 quantity_, address recipient) public payable {
    require(quantity_ * mintPrice == msg.value, "wrong amount sent");
    require(walletMints[recipient] + quantity_ <= maxPerWallet, "mints per wallet exceeded");
        
    walletMints[recipient] += quantity_;
    safeMint(recipient);
}
```

Now our NFT contract is almost finished with the functionality to purchase & transfer the tokens, let's test it on remix ide

{%youtube zQdd5VvrV7c%}

Great! now let's write one more function to actually withdraw your "hard-earned" ether from the contract's balance.

```solidity
address owner;

constructor() ERC721("MyToken", "MTK") {
    owner = msg.sender;
}
```

```solidity
function withdraw() public {
    payable(owner).transfer(address(this).balance);
}
```

Here, we declare a variable `owner` of a special datatype `address` which will store a public wallet address. In the constructor, we assign it to the address of the person who called the constructor, or the person who deployed the contract (by referencing `msg.sender`).

The `withdraw` function is publicly callable & will transfer all the ether balance present in the contract (use `address(this).balance` to get the balance of the contract) to the owner's address.<br>
Syntax for sending ETH from contracts to wallet addresses: ```solidity
payable(sender_address).transfer(X ether)
```

---

Aaaand... that's a wrap!
Like this post, if you liked it 🙃<br>
But if you loved it? man you gotta follow me on [Twitter](https://twitter.com/bhardwajkuvam) 😉


Want to show some more love? here's my public wallet address
```
0x9d1092f5cd0459eDaff0bcA6943dC6A6F8F85F38
```
Feel free to donate 1,2,3,4,5 ETH 😘

---