jackofcrypto

Posted on Jan 23, 2022Read on Mirror.xyz

Decentralized Apps and NFTs

Learn about decentralized applications, or dApps, and non-fungible tokens, or NFTs, along with the ERC-721 token standard.

By the end of this post, readers will be able to:

  • Build non-fungible token (NFT) contracts using ERC-721 standards and the OpenZeppelin library.
  • Deploy the NFT to a local blockchain using Ganache, MetaMask, and Remix.
  • Build a decentralized application for an NFT using Streamlit and Web3.py.

We will create a smart contract for NFTs that uses the Ethereum Request for Comments (ERC)-721 standard. We will also build a dApp that can register new digital artwork on a blockchain by minting the NFTs.

Decentralized Applications

From the perspective of a user, a dApp is just like any other application that they’d use on the internet. What makes a dApp different is that it’s built on a decentralized network, such as a blockchain.

The structure of each application typically consists of a back end and a front end, as shown in the following image:

  • The back end consists of the core logic and manages the data.
  • The front end is the part of the application that a user interacts with (which typically happens through a web browser).

Back and front end of applications

dApps embrace decentralization by using new blockchain technologies, such as smart contracts, so they become robust against centralization issues, such as having a single point of failure, being susceptible to attack, or even encountering access problems.

Non-fungible Tokens

NFTs represent unique assets, such as land, art, or other items with no interchangeable counterpart. This stands in contrast to fungible tokens like cryptocurrencies where, for example, a single bitcoin is easily interchangeable with several ether.

With dApps specifically designed for NFTs, users can buy or sell artwork NFTs in a decentralized marketplace. They can also play games for which the avatars and game items are NFTs on a blockchain.

We’ll use the ERC-721 Non-Fungible Token Standard defined by the Ethereum Improvement Proposal (EIP)-721. ERC-721 is considered the default standard for implementing most NFTs.

Build a dApp Back-End Contract

As with the fungible tokens, first import the code from the OpenZeppelin ERC721Full contract, as the following code shows:

pragma solidity ^0.5.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.5.0/contracts/token/ERC721/ERC721Full.sol";

By creating the ArtToken contract and linking it to the ERC-721 GitHub page, the ArtToken contract will inherit all the functions that we need to satisfy the requirements of the ERC-721 standard.

Inside the ArtToken contract, a constructor is defined that calls the ERC721Full constructor. The full code for the art contract is thus as follows:

pragma solidity ^0.5.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.5.0/contracts/token/ERC721/ERC721Full.sol";

contract ArtToken is ERC721Full {
    constructor() public ERC721Full("ArtToken", "ART") { }

    function registerArtwork(address owner, string memory tokenURI)
        public
        returns (uint256)
    {
        uint256 tokenId = totalSupply();
        _mint(owner, tokenId);
        _setTokenURI(tokenId, tokenURI);

        return tokenId;
    }
}

The registerArtwork function accepts an address for the artwork owner and a string named tokenURI, which will consist of a link to where the digital artwork exists online.

URI, or Uniform Resource Identifier, originated from a World Wide Web initiative to standardize the way that files or objects get encoded for the web. The URI is an identifier of a specific resource, like a page, a document, or a book. This is in contrast to the more familiar URL, which is referred to as a locator, as it includes how to access a resource like HTTPs or FTP. The URL is a subset of the URI.

Uniform Resource Identifier

Compile and Deploy the Contract

As with the fungible tokens, the next step involves compiling and deploying the ArtToken contract by using the Remix IDE, MetaMask, and Ganache.

There is a new step we must undertake as part of the process of creating our dApp. The details of the contract will need to be saved for use by the front end of the application. This is accomplished by copying its application binary interface (ABI) file, found at the bottom of the Compiler page, into a local JSON file.

  • An ABI file contains contract details that the Web3.py library and Streamlit will use to execute the application in the EVM. Both Web3.py and Streamlit will be used for the front end of our dApp. They’ll use the details contained in the ABI file to connect to the contract and to use its functions and data.
  • This is how the Steamlit front-end interface for our dApp will eventually access the details of our contract.

Navigate to the Deploy & Run Transactions pane and select “Injected Web 3” as the environment. When the MetaMask window opens, select at least two Ganache accounts to make available in Remix. Select the “ArtToken - artwork.sol” contract and click Deploy. Click Confirm when the MetaMask window opens.

Build a dApp Front End

For this dApp's front end, we’ll use Streamlit components and the Web3.py library to provide the capability of interaction with the contract, which resides on the blockchain.

Specifically, users will be able to select their accounts and register new artwork tokens through a web interface. Users will also be able to display the tokens for their accounts so that they can display the artwork on the webpage.

Code the front-end application, starting with the required imports:

import os
import json
from web3 import Web3
from pathlib import Path
from dotenv import load_dotenv
import streamlit as st
load_dotenv()

The next step is to define and connect to a new Web3 provider, which, in our dApp, will be the Ganache local blockchain.

WEB3_PROVIDER_URI=http://127.0.0.1:7545
w3 = Web3(Web3.HTTPProvider(os.getenv("WEB3_PROVIDER_URI"))

Combine the above with the Streamlit dApp code below:


@st.cache(allow_output_mutation=True)
def load_contract():

    # Load the contract ABI
    with open(Path('./contracts/compiled/artwork_abi.json')) as f:
        artwork_abi = json.load(f)

    # Set the contract address (this is the address of the deployed contract)
    contract_address = os.getenv("SMART_CONTRACT_ADDRESS")

    # Get the contract
    contract = w3.eth.contract(
        address=contract_address,
        abi=artwork_abi
    )

    return contract


# Load the contract
contract = load_contract()


###
# Register New Artwork
###

st.title("Register New Artwork")
accounts = w3.eth.accounts
address = st.selectbox("Select Artwork Owner", options=accounts)
artwork_uri = st.text_input("The URI to the artwork")
if st.button("Register Artwork"):
    tx_hash = contract.functions.registerArtwork(address, artwork_uri).transact({'from': address, 'gas': 1000000})
    receipt = w3.eth.waitForTransactionReceipt(tx_hash)
    st.write("Transaction receipt mined:")
    st.write(dict(receipt))
st.markdown("---")

###
# Display a Token
###

st.markdown("## Display an Art Token")
selected_address = st.selectbox("Select Account", options=accounts)
tokens = contract.functions.balanceOf(selected_address).call()
st.write(f"This address owns {tokens} tokens")
token_id = st.selectbox("Artwork Tokens", list(range(tokens)))
if st.button("Display"):
    # Get the art token owner
    owner = contract.functions.ownerOf(token_id).call()
    st.write(f"The token is registered to {owner}")

    # Get the art token's URI
    token_uri = contract.functions.tokenURI(token_id).call()
    st.write(f"The tokenURI is {token_uri}")
    st.image(token_uri)

The dApp includes a drop-down list for selecting the artwork owner, a box for typing the URI to the artwork, and a Register Artwork button. We can now use our dApp to mint new NFTs to register digital artwork.

If you do not have a URI, use the following: https://www.artic.edu/iiif/2/25c31d8d-21a4-9ea1-1d73-6a2eca4dda7e/full/843,/0/default.jpg

Until next time, here’s a twitter thread summary of this post:

https://twitter.com/jackofcrypto/status/1485275412246659073?s=20