How to create a motherf*cking NFT using Solidity
Tags: solidity, web3, nft, ethereum
Published: 2021-12-11 14:13:15

This is a simple no-bullshit guide to creating an NFT using Solidity.

> _Before you go any further, you need to know a little about NFTs, Crypto wallets, Solidity, and the ERC721 token standard to understand this article._

I am going to start by showing you the entire [code](https://gist.github.com/abdulrauf11/179411d79dfee19b050cddb0e1fa07a4).

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

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


contract BasicNFT is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("BasicNFT", "BNFT") {}

    function mint(string memory tokenURI) public {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        
        _safeMint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);
    }
}
```

Wait…that’s it?

Yes, what else did you expect you beautiful b*stard.

Now let's try to understand the not-so-obvious lines of code.

## OpenZeppelin to the rescue
The OpenZeppelin library is there to save you from writing any shitty code. It does all the hard work for you. You import the necessary contracts from the library, and they just work. I am talking about the following imports made in the code.
```solidity
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
```
We'll discuss what exactly I am using from these contracts in the coming sections.

## Inheriting from 'ERC721URIStorage'
Next, we inherit our contract from the `ERC721URIStorage` contract - which was given to us by the OpenZeppelin library. In short, contracts that inherit from other contracts can access their (non-private) functions and variables.
```solidity
contract BasicNFT is ERC721URIStorage {}
```

## Initialising the constructor
Since we're inheriting from `ERC721URIStorage`, we'll initialise its constructor in the `BasicNFT` constructor. It requires two parameters i.e. a name and a symbol.
```solidity
// name: 'BasicNFT', symbol: 'BNFT'
constructor() ERC721("BasicNFT", "BNFT") {}
```
The name and symbol are not f*cking important, so let's just move on.

## Assign token ids with 'Counters'
Let's see another utility provided to us by the brilliant OpenZeppelin library i.e. `Counters`. This gets you a simple counter that can be incremented or decremented. It will allow us to issue ids to our ERC721 tokens.
```solidity
// include counters
using Counters for Counters.Counter;
// declare token ids as counters
Counters.Counter private _tokenIds;
```

## Coding the mint function
Now comes the most important function in this contract i.e the motherf*cking `mint` function. I've declared it as a public method so that everyone is allowed to call it.

Let's go through all the steps we take inside this function. Here's the code with some comments:
```solidity
 function mint(string memory tokenURI) public {
     // 1. Increment the the token Ids by one
     _tokenIds.increment();
     // 2. Get the current Id (the first id will be 1)
     uint256 newItemId = _tokenIds.current();
     // 3. Call the `_safeMint` function provided by OpenZeppelin
     _safeMint(msg.sender, newItemId);
     // 4. Call the `_setTokenURI` function provided by OpenZeppelin
     _setTokenURI(newItemId, tokenURI);
 }
```
Yeah, I should probably explain the last two lines of the code.

### _safeMint explained
This function literally mints an ERC721 token for you. It takes in an address and a token id as arguments. I am passing in `msg.sender` and `newItemId` as the address and token id respectively. 

- `msg.sender` is the wallet calling the mint function
- `newItemId` is the current token id i.e. `_tokenIds.current()`

Once called, this function will safely mint a token and transfer it to the wallet calling the mint function.

### _setTokenURI explained
This is one of my favourite methods provided by OpenZeppelin. You can set the metadata for your token through this function. You know the kind of metadata you see on OpenSea - a name, description, and even an image. It takes in a token id and uri as arguments.

We'll discuss how to get a `tokenURI` soon. Basically, it's just a url that sends back a **JSON** object.

This is the [tokenURI](https://ipfs.io/ipfs/bafkreibsk54mbtejlffbvbl2fepyfgnee3ccdiy7ywkv5nu5ja6erxwazy) I'll be using as an example. It returns the following object:
```json
{
  "name": "Basic NFT",
  "description": "Basic NFT tutorial by @lilcoderman",
  "image": "https://ipfs.io/ipfs/bafkreid5vd3sw2wwj2uagm22nomnktbhgl2qqcgksgxvu7xfwwbxtxecly"
}
``` 
After the contract is deployed, you will be able to call the mint function with a `tokenURI` and get a unique ERC721 token in return.

WHEW! It looks like most of the work was done by the OpenZeppelin library. You ask why? Well, because you deserve to be taken care of you beautiful son of a b*tch.

## Deploying to testnet
I am going to use [Remix](https://remix.ethereum.org/) to compile, deploy and interact with the contract. The contract will be deployed to the **Rinkeby** testnet. If you need to learn how to do that, please go through this [link](https://forum.openzeppelin.com/t/create-an-nft-and-deploy-to-a-public-testnet-using-remix/6358).

My contract got deployed to this address: **0x20A65d15fFf5315A4c9E79dED87273b1BfC3Fd65**

After deploying, let's call the mint function with the `tokenURI` I mentioned above.

![Contract interface provided by Remix](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g12pdsd9i1ptxq3l8si0.png)

We'll wait a few minutes after it finishes minting. Now, let's take a look at our newly minted NFT on OpenSea/Rarible:
- **OpenSea:** https://testnets.opensea.io/assets/0x20a65d15fff5315a4c9e79ded87273b1bfc3fd65/1
- **Rarible:** https://rinkeby.rarible.com/token/0x20a65d15fff5315a4c9e79ded87273b1bfc3fd65:1

So freaking cool, right?

## Image and Metadata
Throughout this article, we've assumed that we already have the `tokenURI` i.e. the url pointing towards our metadata. But, how did I get that? Where did I upload my NFT's image and metadata?

I uploaded them to an **IPFS**. IPFS is a decentralised file storage system which isn't controlled by one entity, and is instead maintained by a large number of peers in the network. 

### nft.storage
I used a free service called [nft.storage](https://nft.storage/) to upload my assets to the IPFS network. You need to upload the image first and then use its link in your metadata. After doing that, you need to upload your metadata file as well.

![Image and metadata files as shown on nft.storage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96fwgf02crybeoxa0rrf.png)

Copy the [link](https://ipfs.io/ipfs/bafkreibsk54mbtejlffbvbl2fepyfgnee3ccdiy7ywkv5nu5ja6erxwazy) to your metadata and pass it as an argument to the mint function.

## Off-chain vs. On-chain
In this article, we've only discussed how we can create an off-chain NFT. You might have heard about an on-chain token as well. The difference between them is simple:
- off-chain: Only the token ids are stored on the blockchain. Image and metadata are stored somewhere else e.g. IPFS.
- on-chain: Both the metadata and image are stored directly on the blockchain.

We stored our image and metadata on IPFS, which is a pretty good second option. Many top projects have used this same approach.

<hr />
## Don't leave me, take me with you

Like what you read? Follow me on social media to know more about NFTs, Web development, and shit-posting.

Twitter: [@lilcoderman](https://twitter.com/lilcoderman)

Instagram: [@lilcoderman](https://www.instagram.com/lilcoderman/)