Create an NFT as a Developer: Deep Dive into Smart Contracts
Published: 2024-01-08 17:24:38

<h4 data-pm-slice="1 1 []"><strong>Why NFTs?</strong></h4>
First, I do not believe NFTs are a big deal or critical to the future of web3. yes, I said so, but I am a programmer. I love coding and spending time on Patrick's course; I came across NFTs and learned a lot about them.

Does that mean I have changed my mind that NFTs are just a bunch of overhyped arts or whatever with no direct impact towards a sustainable future?

Absolutely Yes!.

NFTs (non-fungible tokens) have exploded in popularity recently as a way to represent ownership of unique digital assets. But why exactly are NFTs taking off?

NFTs provide a way to prove scarcity and ownership in the digital world. Unlike most digital content, which can be freely copied and distributed, each NFT is unique and not interchangeable.

This makes them similar to physical assets like artwork or collectibles. Just as you can prove you own an original Picasso, you can prove an NFT tied to a specific digital asset.

For creators, NFTs provide a new stream of revenue and a way to connect with fans. By selling limited edition NFTs, creators can monetize their work without relying solely on advertising or subscriptions. Fans also benefit from getting special access, content, or status by owning their favorite creators' NFTs.

NFTs introduce transparency and traceability into digital ownership. All NFT transactions are recorded on the blockchain ledger permanently. This prevents fakes or fraud while allowing owners to trace an NFT's full ownership history.

Finally, NFTs enable new types of digital art, content, and experiences. Without NFTs, most digital content lacks scarcity and authenticity. But NFTs allow the creation of provably rare digital goods, ushering in new trends like NFT art and virtual fashion.
<h4><strong>Smart Contracts for NFTs</strong></h4>
Most NFTs are issued and traded through smart contracts deployed on blockchains like Ethereum. A smart contract is a code that runs on a chain and controls logic like NFT issuance, ownership transfers, listings, bids, and more.

Writing smart contracts is a key skill for developing and understanding NFT projects. We can demystify how NFT mechanics work under the hood by diving into real NFT smart contract code.

We'll walk step-by-step through a basic NFT smart contract written in the Solidity language. We'll deploy it on the Ethereum testnet to mint some test NFTs for free. You'll have hands-on experience deploying an NFT smart contract from scratch by the end!
<h4><strong>OpenZeppelin Contracts</strong></h4>
We'll utilize some helpful building blocks from <a class="attrlink" href="https://www.openzeppelin.com/contracts" target="_blank" rel="noreferrer nofollow noopener">OpenZeppelin Contracts</a> for our contract. This is a library of secure, community-audited smart contracts for Ethereum and other blockchains.

As you can imagine, writing smart contracts from scratch introduces risks like security vulnerabilities and subtle bugs. By using community code that's heavily tested and audited, we can stand on the shoulders of giants.

Specifically, we'll inherit from OpenZeppelin's implementation of the ERC-721 standard. This standard defines a set of functions all NFT contracts should implement, like <code>mint</code>, <code>transfer</code>, and <code>ownerOf</code>.

By inheriting from a standard base, our NFTs will be compatible with external apps and marketplaces looking for contracts following ERC-721.

Here's how our contract import looks:

<code>import "@openzeppelin/contracts/token/ERC721/ERC721.sol";</code>

We also have to pass this contract as the parent contract in our inheritance:
```javascript
contract myNFT is ERC721 {
  // Our contract code
}
```
This contract inheritance allows us to focus on <strong>our custom NFT behavior rather than reimplementing NFT basics</strong>.
<h4><strong>NFT Contract Walkthrough</strong></h4>
Below, I'll walk through our contract section-by-section, explaining how each piece works:
<pre><code class="language-solidity">// Contract name
contract myNFT is ERC721 {
}</code></pre>
First we define our contract name as myNFT and inherit from ERC721.

Next, we define some contract state variables:
```javascript
uint private s_tokenCounter; // Internal counter for NFTs minted
mapping(uint256 =&gt; string) private s_tokenURIs; // Map NFT ids to associated URI
```
Here, we define a unit counter to track how many NFTs have been minted. This will be the unique identifier for each NFT.

We also define a mapping from the <code>uint ID</code> to a <code>string URI</code>. This will point out our NFT resources like images, metadata, etc.

In the contract constructor, we initialize the counters:
```javascript
 constructor() ERC721("UselessCoin", "ULC") {
      s_tokenCounter = 0;
  }
```
Note we call the ERC721 constructor with a name and symbol for our NFT collection.

Then, we set our counter to 0.

Next, we add our main NFT minting function:
```javascript
function mintNFT(string memory tokenURI) public {
      _safeMint(msg.sender, s_tokenCounter);
      s_tokenURIs[s_tokenCounter] = tokenURI;
      s_tokenCounter = s_tokenCounter + 1;
  }
```
This takes in a <code>tokenURI</code> string pointing to the NFT resource.

It uses OpenZeppelin's <code>_safeMint function</code> to mint an NFT to <code>msg.sender</code> (the function caller).

We increment <code>s_tokenCounter</code> to set the new NFT's unique ID.

Finally, we save the resource <code>tokenURI</code> mapping to this ID.

The <code>tokenURI</code> external apps use a method to fetch the resource for a particular NFT ID:
```javascript
function tokenURI(uint256 tokenId) public view override returns (string memory) {
    return s_tokenURIs[tokenId];
  }
```
We implement the required <code>tokenURI</code> function to return the URI simply mapped to the passed tokenId.

And that covers the basics of our NFT smart contract!

While simplified, this demonstrates:
<ul>
 	<li>Using OpenZeppelin contracts for quicker development</li>
 	<li>Inheriting from the ERC721 standard</li>
 	<li>Minting unique NFTs with IDs tracked by a counter</li>
 	<li>Mapping <code>tokenIds</code> to off-chain resource URIs</li>
 	<li>Overriding the <code>tokenURI</code> function to relay URI data</li>
</ul>
We could make improvements, like access controls, error handling, automating URI creation, etc. But this minimal contract already implements core NFT behavior and standards under the hood!
<h4>Deploying and Testing Our NFT Contract</h4>
We'll use Foundry - a modular toolkit for Ethereum application development, to test and deploy our contract.

Foundry provides excellent support for testing, scripting, and deploying Solidity smart contracts.

First, our deployment script <code>deploy.s.sol</code>:
```javascript
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Script} from "forge-std/script.sol";
import {moodNFT} from "../src/moodNft.sol";
contract DeployNFT is Script{
    function run() external returns(moodNFT){
        vm.startBroadcast();
        moodNFT moodnft = new moodNFT();
        vm.stopBroadcast();
        return moodnft;
    }
}
```
This handles deploying an instance of our myNFT contract and broadcasting it onto the blockchain.

Next, our simple test suite test.t.sol:
```javascript
//SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Test} from "forge-std/test.sol";

import {myNFT} from "../src/myNFT.sol";

import {DeployNFT} from "../script/DeployNFr.s.sol";

contract BasicNFTTest is Test{

    address public USER = makeAddr("user");

    string public constant PUG = 

        "ipfs://bafybeig37ioir76s7mg5oobetncojcm3c3hxasyd4rvid4jqhy4gkaheg4/?filename=0-PUG.json";

    DeployNFT public deployer;

    myNFT public mynft;

    function setUp() public {

        deployer = new DeployNFT();

        mynft = deployer.run();

    }

    function testnameiscorrect() public view {

        string memory expectedname = "DogieSco";

        string memory actualname = mynft.name();

        assert(keccak256(abi.encodePacked(expectedname)) == keccak256(abi.encodePacked(actualname)));

       

    }

    function testcanMintandBal() public {

        vm.prank(USER);

        mynft.mintNFT(PUG);

        assert(mynft.balanceOf(USER) == 1);

        assert(keccak256(abi.encodePacked(PUG)) == keccak256(abi.encodePacked(mynft.tokenURI(0))));

    }

}
```
Here, we validate the contract's name variable and test out minting an NFT.

We can run the full test flow with:

<code>forge test</code>

This handles deploying fresh contract instances for our test cases, checking expected behavior and revert conditions, gas usage, and more.

Ensure all your test functions start with <code>test</code> as forge test will not be able to pick it up.

Foundry provides excellent tooling for testing, scripting, and deploying smart contracts from development through production. As we scale up our NFT project, Foundry will help rapidly iterate and confidently ship across environments.
<h4><strong>Why Smart Contracts Are Critical for NFTs</strong></h4>
While NFT basics are simple in abstract (tokenized ownership records on a blockchain), smart contracts power NFTs under the hood.

Smart contracts formally encode business logic like ownership, transfers, limitations, royalties, etc. Everything from minting and burning NFTs to facilitating trades on a marketplace happens via executing contract code.

Upgrades, migrations, governance decisions, and more are enacted by shipping updated smart contracts. Things like transaction history and off-chain data availability are also managed in contract storage.

By learning smart contract development with real NFT code, as we've covered here, you gain quite deep insight into the inner workings of NFT projects:
<ul>
 	<li>Core mechanics like minting, ownership records</li>
 	<li>Upgradeability patterns and governance capabilities</li>
 	<li>Security protections and risk management</li>
 	<li>Standards compatibility and interoperability</li>
</ul>
Plus, low-level understanding improves security posture as both builder and trader.

As NFTs bridge more industries, from art to finance to Web3 identity, understanding smart contracts becomes more critical for builders and traders.

So, while buying and selling NFTs is accessible to anyone with a wallet, smart contract literacy is invaluable to understanding this new digital asset paradigm.
<h4><strong>Conclusion</strong></h4>
We've covered quite a lot of ground here! To recap:
<ul>
 	<li>We discussed the value proposition and emergence of NFTs</li>
 	<li>Provided background on smart contracts and inheritance</li>
 	<li>Walked through an NFT contract section-by-section</li>
 	<li>Deployed our contract with Foundry.</li>
 	<li>Discussed the critical role contracts play in NFT mechanics</li>
</ul>
While simplified, this demonstrates core NFT behavior like issuance, token tracking, metadata lookups, etc. We also used tooling like OpenZeppelin contracts for speedier and safer development.

There's always more to learn, but being able to read, write, and deploy real smart contract code unlocks a deeper understanding of the NFT space.

If you find this article thrilling, discover extra thrilling posts like this on <a class="attrlink" href="https://blog.learnhub.africa/" target="_blank" rel="noreferrer nofollow noopener">Learnhub Blog</a>; we write a lot of tech-related topics from <a class="attrlink" href="https://blog.learnhub.africa/category/cloud-computing/" target="_blank" rel="noreferrer nofollow noopener">Cloud computing</a> to <a class="attrlink" href="https://blog.learnhub.africa/category/frontend/" target="_blank" rel="noreferrer nofollow noopener">Frontend Dev</a>, <a class="attrlink" href="https://blog.learnhub.africa/category/security/" target="_blank" rel="noreferrer nofollow noopener">Cybersecurity</a>, <a class="attrlink" href="https://blog.learnhub.africa/category/data-science/" target="_blank" rel="noreferrer nofollow noopener">AI</a>, and <a class="attrlink" href="https://blog.learnhub.africa/category/blockchain/" target="_blank" rel="noreferrer nofollow noopener">Blockchain</a>. Take a look at <a class="attrlink" href="https://blog.learnhub.africa/2023/02/16/how-to-build-offline-web-applications/" target="_blank" rel="noreferrer nofollow noopener">How to Build Offline Web Applications. </a>
<h4>Resource</h4>
<ul>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/12/19/getting-started-with-foundry-for-beginners-in-2024/" target="_blank" rel="noreferrer nofollow noopener">Getting Started with Foundry for Beginners in 2024</a></li>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/09/25/what-are-smart-contracts/" target="_blank" rel="noreferrer nofollow noopener">What are Smart Contracts?</a></li>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/12/27/create-a-raffle-draw-smart-contract-using-foundry-2024-part-1/" target="_blank" rel="noreferrer nofollow noopener">Create a Raffle Draw Smart Contract Using Foundry 2024 (Part 1)</a></li>
</ul>
<a class="attrlink" href="https://www.buymeacoffee.com/Scofield" target="_blank" rel="noreferrer nofollow noopener">Gift us a cup of coffee if you are so kind. </a>