Deploy Your First NFT with Python

Introduction

NFT (Non-Fungible Token) has exploded since last year and keeps on roaring regardless of how the cryptocurrency is performing. You must have heard of CryptoPunk, BAYC or the most recent Phantom Bear if you are following any crypto news. In the nutshell, NFT is a smart contract with some meta data such as image, video or some other attribute data to make it unique from one another. And since it’s on blockchain, it’s easy for anybody to verify the ownership. In this article, I will walk you through the process on how to deploy your own NFT with Python and we will be using a Python based smart contract development framework called Brownie.

Prerequisite

You will need to install the Brownie package. It is recommended to use pipx to install it in an isolated environment, but since I have my own virtual environment created, I would continue to use pip instead. Below is the pip command to install Brownie:

#run in your venv
pip install eth-brownie

In order to test your code locally, Brownie will automatically install ganache cli which launches a local Ethereum blockchain for you to execute your smart contracts. You can follow its documentation to download and install it if you wish to install it manually.

Get Started with Brownie

Let’s create an empty folder and then run the below command to initiate a new Brownie project:

brownie init

With the above command, brownie will create a project structure for you under your current folder. The majority of your work shall be done in the following folders:

  • contracts – for solidity smart contract source code
  • interfaces – for the interface files that referenced by the smart contract
  • scripts – for deployment scripts or interacting with deployed contracts
  • tests – for test scripts

 

Brownie supports both Solidity and Vyper language for creating smart contracts. In this article, I will be using the smart contract in Solidity style as the example.

Create NFT Smart Contract

NFTs use ERC721 token standard, so we will need to create a smart contract that implements this standard. Since the objective of this article is to walk through the process rather than a deep dive into the smart contract coding, I have just shared a sample smart contract here.  You can download this LegendNFT.sol smart contract file and the libraries folder, then put them under the contracts folder.

Here is a quick explanation of what this smart contract does:

  • an ERC721 contract with name Codeforests Legend, token symbol CFL and total supply of 1,000,000
  • 3 lists of words to generate a “random” word phrase
  • a SVG xml template to create a picture of the above word phrase
  • a list of color codes to provide “random” background color to the SVG
  • a mint function for minting the NFT tokens
  • finally a function to return how many tokens have minted

 

As you can see from the source code that I used two external contracts from OpenZeppelin, so I will need to specify this dependency so that they can be recognized when compiling the contract. Brownie provides a config file brownie-config.yaml which allows you specify these configurations. This file is not created automatically when initiating the project as all the settings are optional, you will only need it when necessary.

To specify the dependencies, you can manually create this yaml file under your project root folder. And add the below configurations:

dependencies:
  - OpenZeppelin/[email protected]
compiler:
  solc:
    remappings:
      - '@openzeppelin=OpenZeppelin/[email protected]'

It basically tells Brownie package manager to automatically download this package for you and let compiler find the correct path of the package when using the short form ‘import @openzeppelin/…’.

Compile Smart Contract

Once you have saved your contract and the config file, you can run below compile command on your terminal:

brownie compile

You shall see brownie auto downloaded the dependencies and compiled the code:

brownie compile smart contract, NFT smart contract

The output Json files will be placed into the build/contracts folder. The next step we shall try to deploy this contract locally.

Deploy Smart Contract Locally

To deploy our contract locally, we will need to use the ganache as I mentioned in the beginning. By default, when you want to deploy a contract, brownie will always start the ganache local network and deploy it to the local network. Brownie provides a console window where you can use the commands to deploy contract, but since we may need to do the deployment multiples for testing or in different networks, so let’s create a re-usable Python script called deploy.py under the scripts folder for the deployment purpose.

When your smart contract is compiled, the contract class object will be automatically added to brownie runtime environment, so we can import it from brownie directly.

Below is the Python code for deploying my LegendNFT contract:

from brownie import LegendNFT, network, config, accounts


def deploy_contract():
    account = accounts[0]
    legend_contract = LegendNFT.deploy({"from" : account})
    print("contract has been deployed successfully to :", legend_contract.address)

    return legend_contract

def main():
    deploy_contract()

The accounts is some dummy accounts provided by ganache only for your local network, so we just pick the first dummy account as the contract creator for the from parameter.

Note that your script needs to have a main function to let brownie know which function to run.

Next, let’s run the below command in the terminal:

brownie run .\scripts\deploy.py

You shall see something similar to the below output showing that both contract address and transaction hash have been generated:

brownie deploy smart contract python, NFT smart contract

Congratulations that you’ve just deployed your contract to your local network, although you may have no idea if the contract works correctly. Don’t worry, let’s continue to explore how to deploy to a public network and come back to the unit test later.

Deploy Smart Contract to Public Network

The public network usually refers to the public accessible and persistent blockchain network such as the Ethereum mainnet, rinkeby, kovan, popsten etc. To access the public network, you will need to connect to a node, but it would be too much to run your own node just for deploying a contract.

Luckily there are some service providers like Infura, Alchemy or Pocket Network etc., who allows you to use their API to connect to the public network nodes. For Brownie, it uses Infura as the default provider for connecting to public network. If you run below command in your terminal:

brownie networks list true

You can see it is using Infura’s API:

brownie deploy smart contract python, NFT smart contract, rinkeby network

In this case, you will need to sign up Infura for a free account and get your own project API token. Once you have your API token, you can create a environment variable called WEB3_INFURA_PROJECT_ID and assign your token ID to it. My personal preference would be putting this information into a .env file and load it into the Brownie config file.

And another thing you need is a real wallet account with some fund, as deployment will always incur some cost for confirming the transaction. You can create a wallet with Metamask, and for testnet like rinkeby, you can get some testing ether token from chainlink rinkeby faucet. You can also find other faucets for the rest of the testnets.

So let’s put below info in the .env file:

PRIVATE_KEY = 0x{your wallet private key}
WEB3_INFURA_PROJECT_ID = {your infrua project ID}
ETHERSCAN_TOKEN = {etherscan API token; to submit your contract source code to etherscan (optional)}

Note: to add 0x before your wallet private key so that it’s recognized as hex string. If you wish to get your contract source code verified by etherscan, you will need to sign up with etherscan and get an API token as well.

And add below lines in the brownie-config.yaml file:

dotenv: .env

wallets:
  from_key: ${PRIVATE_KEY}

# optional for verifying the contract source code
networks:
  development:
    verify : false
  rinkeby:
    verify : true

I strongly recommend you to add .env in .gitignore, so that you won’t accidently push your sensitive information into github. Your wallet private key for testnet and mainnet is the same, so bear in mind to not expose to anyone.

Now let’s also do some minor change to our deployment script, so that it uses the new account we created for deploying the contract to public network.

def deploy_contract():
    if(network.show_active() == "development"):
        account = accounts[0]
    else:
        account = accounts.add(config["wallets"]["from_key"])

    legend_contract = LegendNFT.deploy(
        {"from" : account}, 
        publish_source=config["networks"][network.show_active()].get("verify")
    )
    print("contract has been deployed successfully to :", legend_contract.address)

    return legend_contract

We have added a condition to check whether we are working on local/development network, when it’s not local network, we will load our account by the private key and use it to sign the transaction for deployment.

Now we have everything ready, let’s run the deploy.py again and specify the target network with the –network option, e.g. :

brownie run .\scripts\deploy.py --network rinkeby

Below is the output from my terminal:

brownie deploy smart contract python, NFT smart contract, Python deploy smart contract to public network

It would take slightly longer time since it requires the transaction to be minted and confirmed. You can copy the contract address or transaction hash then search it from etherscan rinkeby network.

You can see my deployed contract address here and the verified source code here.

Awesome! You just had your contract deployed in the public network where everybody can see it!!! Since you’ve not yet minted any NFT from your contract, you won’t see anything visually at the moment. To give you a idea of how your NFTs will look like, I have created this website for minting the LegendNFT, you can try and experience the minting process and then view the NFT from Opeasea. It currently supports Rinkeby, Matic and Matic Mumbai network. Below is the minting page after you have connected your wallet:

brownie deploy smart contract python, NFT smart contract, mint NFT, free NFT

Conclusion

Creating your own NFT can be a very fun journey although you may not be able to eventually sell it on the market. And there are just too much details to be covered and which is impossible to write in one post, so I will try to write a few more articles on how to perform unit test, interact with your deployed contract, as well as deploy the contract to other EVM compatible networks. Do follow me on my twitter and let me know if you encounter any issue with your contract deployment.