ERC721 Tokens and How It Works.

What is an ERC721 Standard

An ERC721 (Ethereum Request for Comment) standard defines a set of rules for creating non-fungible tokens (NFTs), which are unique and invisible tokens. An ERC721 contract is basically a type of non-fungible token (NFT) standard on the Ethereum blockchain.

In ERC721 contracts, each token have a distinct identifier and represents ownership of a unique item, piece of art, or any other physical and digital asset.

ERC721 consists of some key features:

Metadata: ERC-721 tokens have a metadata feature which provides additional information about the token, such as the token's name, description, and external references like a link or an image.

Invisibility: ERC721 tokens are referred to be invisible tokens, on like ERC20 (fungible) tokens, ERC721 are non-fungible and each token is a distinct unit and cannot be divided into smaller units like cents.

Standard Interface: The ERC721 standard have defined sets that a compliant smart contracts will implement, and also have functions for minting new token, accessing ownership, transferring tokens, and to query a token's metadata.

Ownership AndTransferability: ERC721 tokens are owned and controlled by Ethereum addresses, In ERC721 tokens owners can transfer their tokens to other addresses, And it provids a way to buy, sell, or trade unique digital assets, and also track the ownership on the Ethereum blockchain.

Uniqueness: When you mint a token using ERC721 contract, by default it is unique and is identified by a specific token ID, this makes ERC-721 tokens suitable for representing distinct and distinguishable digital or real-world assets.

It's important to note that while ERC-721 is a widely adopted standard, other non-fungible token standards, such as ERC-1155, have also been introduced to provide additional functionality and flexibility for managing both fungible and non-fungible assets within a single contract.

How to Develop an NFT Smart Contract (ERC721)

When developing a Smart Contract with Solidity and deploying it on the blockchain it might sound daunting at first, but here are some of the things you need to go through to able to develop and host your code on the blockchain **.e.g.(gas fees, gas optimization, solidiity, developer enviroment and security).

There are some released tools for developers to make their Smart contracts development jobs much easier, tools like OppenZeppelin Wizard offers developers with functionalities to create composable and secured smart contracts within a short period of time

In this article we will be using theOpenZeppelin Wizardto create the smart contract because it offers standard smart contracts.

OpenZeppelin Wizard is one of the biggest maintainers of smart contract standards both ERC20, ERC721, and many others allowing developers to use thoroughly audited code to develop reliable contracts.

You can use the OpenZeppelin to access and select the features you want, and OpenZeppelin will populate the code of the Smart Contract.

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

import "@openzeppelin/contracts@5.0.1/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@5.0.1/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts@5.0.1/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts@5.0.1/access/Ownable.sol";

contract LeviToken is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
    uint256 private _nextTokenId;

    constructor(address initialOwner)
        ERC721("LeviToken", "LITO")
        Ownable(initialOwner)
    {}

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _update(address to, uint256 tokenId, address auth)
        internal
        override(ERC721, ERC721Enumerable)
        returns (address)
    {
        return super._update(to, tokenId, auth);
    }

    function _increaseBalance(address account, uint128 value)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._increaseBalance(account, value);
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable, ERC721URIStorage)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

The example above is an OpenZeppelin ERC721 populated code showing the OpenZeppelin default libraries.

The safeMint function is passing two parameters to and uri with the Type of address and string, and a modifier of onlyOwner and this will allow only the owner of the smart contract (the wallet address that deployed the smart contract) to mint NFTs

uint256 tokenId = _nextTokenId++; -This line of code declares a local variable tokenId of type uint256 and assigns it the current value of _nextTokenId.
And it have a storage variable representing the next available token ID to be minted.

_safeMint(to, tokenId); - this calls a function named _safeMint to mint a new token and the parameter specifies the address to which the newly minted token will be assigned.

_setTokenURI(tokenId, uri); - this calls a function named _setTokenURI to associate a URI (Uniform Resource Identifier) with the minted token.
And the tokenId parameter identifies the token to which the URI is being associated.
The uri parameter is a string representing the metadata URI for the token.

Update Function

The _update function is set to external within a contract that inherits from both ERC721 and ERC721Enumerable, and It overrides the _update function from those parent contracts, ensuring that it provides the required functionality.
The function takes three parameters, it calls the parent function with those parameters, and returns the result.

The _increaseBalance function is also an internal function within a contract that inherits from both ERC721 and ERC721Enumerable, it also overrides the _increaseBalance function from those parent contracts, ensuring that it provides the required functionality

The main purpose of this function is to increase the balance associated with a specific address (account) by a certain value (value).

The tokenURI function have a public visibility and view keyword that it declare functions that are read-only and do not modify the state of the contract within a contract that inherits from both ERC721 and ERC721URIStorage.
This function overrides the tokenURI function from those parent contracts, and it ensures to provides the required functionality.

The main purpose of the tokenURI function is to retrieve the Uniform Resource Identifier (URI) associated with a specific token ID.

The supportsInterface function also has a public visibility and view function within a contract that inherits from ERC721, ERC721Enumerable, and ERC721URIStorage***,*** and this also overrides the supportsInterface function from those parent contracts, ensuring that it provides the required functionality.
The function returns true if the contract supports the specified interface, and false if otherwise, and the main purpose of this function is to check if the contract supports a particular interface based on its identifier (interfaceId).

Now everyone will be able to mint our NFTs, and we will need to avoid people from minting more NFTs than the max number of NFTs in our collection. To do so let's specify the max number of mintable NFTs.

Counters.Counter private _tokenIdCounter;
    uint256 MAX_SUPPLY = 100000;

Now let's assume the users want to be able to mint up to a total of 10,000 NFTs. To do so, you can use uint256 variable, call it MAX_SUPPLY, and assign it 10,000.

After limiting the max supply of our NFTs, now you can compile the smart contract and deploy it on the Goerli Testnet. To do so, you'll need to create a free account on Alchemy.com, add it as a node provider on Metamask.

After getting the test Ether, now you NFT is ready to be compiled and deployed using Remix, and to do that you need to go to your remix and click on compile.

After doing that, then you can deploy and run you smart contract by clicking the deploy button.

After deploying your smart contract, you will see a green checkmark at the bottom of the of you Remix IDE, and also at the left bottom left of your Remix IDE you will see the deployed contract.

Conclusion

In conclusion, Non-Fungible Tokens (NFTs)/ERC721 tokens has ushered in a new era of digital ownership and decentralized creativity. Through the Ethereum blockchain and the ERC721 standard, individuals can truly own and trade unique digital assets, opening up endless possibilities for artists, developers, and collectors alike.
As you navigate through this dynamic landscape, remember the power and potential that ERC721 tokens bring to the table and to web3.0 generally.