Skip to main content

NFT Introduction

This document will guide you through the process of creating DIDs that conform to Chia's DID standard, as well as minting NFTs that adhere to Chia's NFT standard. It will demonstrate this functionality using both the command-line and RPCs on Windows, MacOS and Linux. It will also show you how to verify that your NFTs are working as designed in Chia's Electron wallet. We'll use the testnet for most of this guide, but we'll also give you some tips on deploying NFTs on Chia's mainnet.

Detailed instructions for each of the commands from this tutorial are available from the following references on Chia Docs:

info

As detailed in our CLVM reference, the CLVM cost for an XCH transaction with two inputs and two outputs is around 17 million.

The cost for minting and modifying NFTs is significantly higher. The following chart shows the approximate CLVM cost (rounded up to the nearest million), as well as the recommended minimum fee (at 5 mojos per cost). Note that the minimum fees listed here will only apply when the mempool is full.

OperationCost (approx)Min fee (mojos)
Minting NFT without DID53 million265 million
Minting NFT with DID123 million615 million
Adding a URI to NFT without DID41 million205 million
Transfer NFT with DID67 million335 million
Assign DID to NFT107 million535 million
Adding URI to NFT with DID71 million355 million

The maximum CLVM cost in a transaction block is 11 billion. If you plan to mint several NFTs at once, be careful not to exceed this cost or your CLVM program will fail. Even if your spend bundle's total cost is less than the maximum, it may take a very long time to be included in a block if an insufficient fee is included. We therefore recommended that you create multiple spend bundles with only a few mintings that adhere to the minimum fees laid out above, which will increase the likelihood of your blocks making it onto the chain in a timely manner.

Note about Python RuntimeError on Windows

If you are running on Windows, you might occasionally see a Python Runtime Error. This is a known issue in Python and can be safely ignored. For example:

chia stop -d all

Exception ignored in: function _ProactorBasePipeTransport.__del__ at 0x000001A719716160
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 746, in call_soon
self._check_closed()
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 510, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
daemon: {'ack': True, 'command': 'exit', 'data': {'success': True}, 'destination': 'client', 'origin': 'daemon', 'request_id': '0de5449121b6873ce18661b2adc4213d7dc795c2943ff7f4be9502058e8eaba0'}

Install and configure Chia (testnet)

This section will show you how to download and install Chia from the release/1.4.0 branch, configure your installation to run on the testnet, sync your node, and obtain some TXCH. If you have already done all of these things, you can skip to the next section, Create an NFT wallet (CLI).

tip

Your firewall might give warnings when installing Chia. This is normal. Allow the installations to continue.

We'll be running on Chia's testnet. You can either run on a full node, or use the light wallet. A full node takes longer to sync (see detailed instructions below), but once it is synced, it is generally faster than the light wallet. You also have the option of starting the light wallet and switching to the full node after it is synced.

If you wish to run a full testnet node, you can safely download a copy of the database. Do not attempt this on mainnet. Click here to begin the download. Save the file to your Downloads folder.

note

The file you will download is around 20 GB, compressed. Uncompressed, it will be around 40 GB. Make sure you have at least this much free space, and ideally 60 GB.

You may continue with the next steps while the download is in progress.

If Chia is already installed on your system, make sure it is stopped:

chia stop -d all

Ensure there are no chia related processes running. If you are unsure, you may want to uninstall Chia before continuing.

The code to be released in version 1.4 (including NFTs and DIDs) is in the release/1.4.0 branch of the chia-blockchain repository. You'll need to download this branch.

note

If you don't already have the git CLI tool installed, follow these instructions to install it

Create a new directory and download the release/1.4.0 branch of the chia-blockchain repository into it:

mkdir release_1_4_0
cd release_1_4_0
git clone -b release/1.4.0 --recurse-submodules https://github.com/Chia-Network/chia-blockchain.git

Install Chia:

info

If, rather than following the steps from this tutorial, you want to run a binary Chia installer, here's how to set an alias and switch to using the testnet:

Set-Alias -Name chia "C:\Users\<username>\AppData\Local\chia-blockchain\app-<version>\resources\app.asar.unpacked\daemon\chia.exe"
chia configure -t true
cd chia-blockchain
.\Install.ps1
.\venv\Scripts\Activate.ps1
chia init
tip

If you receive the message WARNING: UNPROTECTED SSL FILE! then run:

chia init --fix-ssl-permissions

Check the version of the Chia software:

chia version

For example, as of writing the current version is:

1.3.6.dev682

IMPORTANT: Run the following command to set up Chia to use the testnet:

chia configure --testnet true

We recommend that you use INFO level logging instead of the default WARNING level. To do this, run:

chia configure --set-log-level INFO

We recommend that you use a public/private key pair for testnet that is separate from your mainnet keys. If you don't have a separate set of testnet keys, generate them:

chia keys generate

This will give an output such as the following:

Generating private key
Added private key with public key fingerprint 3049838316
(...)
tip

It is good security practice to use this set of keys for testnet development only. In case of key compromise, your TXCH and NFTs will be sandboxed from your XCH.

If you generated new testnet keys in the last step, we recommend that you write down your seed phrase for later recovery. Run the following:

chia keys show --show-mnemonic-seed

You'll be shown your public and private keys. The last line of the output will be a list of 24 secret words. This is your seed phrase. Carefully copy the words on paper and store them in a secure location. Order is important.

Showing all public and private keys

Fingerprint: 3049838316
(...)
Mnemonic seed (24 secret words):
youth stomach social aware clay pottery benefit asthma mail cry rubber panda wife around provide atom cute sand staff exotic pink east gloom minute
important

Your seed phrase is all that is required to recover your wallet. If you lose your seed phrase, recovery will not be possible. If a bad actor gains access to your seed phrase, they'll be able to steal your Chia assets, including XCH, CATs and NFTs.

info

If you ever need to display your address, run chia keys show. This command will only output your public keys and address; your private keys and seed phrase will not be shown.

Note: At this point we'll start the Chia light wallet, but not the full node. As noted above, the full node is not required for minting NFTs, but it will generally be faster than the light wallet. The main reason not to start the node now is because the database file might still be downloading. If your database download has completed and you want to run the full node now, run steps 13, 14, and 15, then return here.

Start your wallet by running:

chia start wallet

Wait for your wallet to sync. You can view the status of your wallet with the following command:

chia wallet show

Be sure to select the correct key/fingerprint, which you obtained from the chia keys show command:

Wallet keys:
1) 285637561
2) * 3049838316 (Not Synced)
Choose a wallet key [1-2] ('q' to quit, or Enter to use 3049838316):
Wallet height: 938814
Sync status: Syncing...

Syncing should only require a few minutes, unless your wallet has a large number of previous transactions. Eventually, the chia wallet show command will show that your wallet has been synced. For example:

Wallet height: 938990
Sync status: Synced
Balances, fingerprint: 3049838316

Chia Wallet:
-Total Balance: 14.5 txch (14500000000000 mojo)
-Pending Total Balance: 14.5 txch (14500000000000 mojo)
-Spendable: 14.5 txch (14500000000000 mojo)
-Type: STANDARD_WALLET
-Wallet ID: 1

In order to continue, you'll need to have some TXCH in your wallet. If your total balance is 0, you can obtain 1 TXCH from our faucet. Copy the value of "First wallet address:" from the output of the chia keys show command. It will be a long string beginning with "txch".

Open our testnet faucet page. Paste your address and click "Submit".

You'll receive this message: Accepted. Your request is in the queue and will be processed in the order it was received. At some point you'll receive 1 TXCH. Depending on how busy the faucet and the testnet are, this could take several minutes. However, you don't need to wait for your coins to arrive before continuing.

Create the folder where your database will reside:

mkdir ~/.chia/mainnet/db

You must wait for your database file to finish downloading before continuing. After the download has completed, use an archive manager such as 7-Zip to extract the file. You should now have a file in your Downloads folder called blockchain_v2_testnet10.sqlite.

Move the database to the folder you just created:

mv ~/Downloads/blockchain_v2_testnet10.sqlite ~/.chia/mainnet/db

Start the full node, which will begin syncing to the database file:

chia start node
chia_full_node: started

Check the sync status:

chia show -s

Eventually, it will say Full Node Synced:

Network: testnet10    Port: 58444   RPC Port: 8555
Node ID: 82a73b06b3a5f9493a3ac4e3d903026b39c85b748158ba41c623d531947f2a2a
Genesis Challenge: ae83525ba8d1dd3f09b277de18ca3e43fc0af20d20c4b3e92ef2a48bd291ccb2
Current Blockchain Status: Full Node Synced

Optionally install and run Chia's GUI:

(You should be in the <chia-blockchain> folder)

.\Install-gui.ps1
cd .\chia-blockchain-gui
npm run electron

Once you have a synced full node and some TXCH, you may proceed to the next section. If your requested TXCH has not yet arrived, post your address on the #dev channel on Keybase. Someone might be able to send some to you.

Note about Chia wallets

In Chia, each public/private key pair can hold multiple wallets of various types.

A "fingerprint" is a 4-byte hash of a public key. The computer running the examples we'll use in the next sections had two public/private key pairs, and thus two fingerprints, labeled 1) and 2).

Note that simultaneous syncing of multiple fingerprints is not currently possible. Whether you're using the CLI or the GUI (in general, not just for NFTs) you have to select one of the fingerprints to sync, while the other will be temporarily ignored and not synced.

Within each of the fingerprints, you can store multiple wallets of various types. The default wallet is a Chia Wallet, aka a STANDARD_WALLET. This label is part of an enum with a value of 0. The other wallet types are as follows:

STANDARD_WALLET = 0
RATE_LIMITED = 1
ATOMIC_SWAP = 2
AUTHORIZED_PAYEE = 3
MULTI_SIG = 4
CUSTODY = 5
CAT = 6
RECOVERABLE = 7
DECENTRALIZED_ID = 8
POOLING_WALLET = 9
NFT = 10

A few notes about this enum:

  • The only types that are supported as of June 2022 are STANDARD_WALLET, DECENTRALIZED_ID, CAT, and NFT
  • Each fingerprint/Wallet Key can support each of the following:
    • One STANDARD_WALLET
    • An unbounded number of CATs
    • An unbounded number of DECENTRALIZED_IDs
    • One NFT wallet per DECENTRALIZED_ID (where each NFT wallet is associated with exactly one DECENTRALIZED_ID)
    • One additional NFT wallet that is not associated a DECENTRALIZED_ID
  • If an existing NFT wallet is already associated with a DECENTRALIZED_ID, then an attempt to create a new NFT wallet associated with the same DECENTRALIZED_ID will pass, but it will not actually create the NFT wallet because it already exists
  • If an existing NFT wallet is not associated with a DECENTRALIZED_ID, then an attempt to create a new NFT wallet that is not associated with a DECENTRALIZED_ID will pass, but it will not actually create the NFT wallet because it already exists
  • In addition to a type, each wallet also has a Wallet ID. This is simply an integer that increments for each new wallet.

For example, the following setup would be possible to run on a single computer:

  • Wallet Key 1 (not synced)
    • Chia Wallet (Type = STANDARD_WALLET, Wallet ID = 1)
    • Marmot (Type = CAT, Wallet ID = 2)
  • Wallet Key 2 (synced)
    • Chia Wallet (Type = STANDARD_WALLET, Wallet ID = 1)
    • NFT Wallet (Type = NFT, Wallet ID = 2)
    • Marmot (Type = CAT, Wallet ID = 3)
    • Spacebucks (Type = CAT, Wallet ID = 4)
  • Wallet Key 3 (synced)
    • Chia Wallet (Type = STANDARD_WALLET, Wallet ID = 1)
    • NFT Wallet (Type = NFT, Wallet ID = 2, not associated with a DID)
    • DID Wallet (Type = DECENTRALIZED_ID, Wallet ID = 3)
    • DID Wallet (Type = DECENTRALIZED_ID, Wallet ID = 4)
    • NFT Wallet (Type = NFT, Wallet ID = 5, associated with DID from Wallet ID 3)

Under Wallet Key 3 of this setup, it would not be possible to create another NFT wallet that is not associated with a DID, or another DID wallet that is associated with the DID from Wallet ID 3. However, it would be possible to create an NFT wallet that is associated with the DID from WALLET ID 4. It would also be possible to create a new DID within Wallet Key 3.

Obtain images with corresponding hashes

For this guide, we'll obtain four images and hashes to be used for creating NFTs with the following use cases:

  • CLI, no DID
  • CLI, with DID
  • RPC, no DID
  • RPC, with DID

Here's the general technique to obtain images and hashes:

First, find an image to mint as an NFT. For this example, we'll use an image that is licensed in the public domain: https://images.pexels.com/photos/11053072/pexels-photo-11053072.jpeg

Then, calculate the image's hash. Here are three (of many) options:

  • cURL with sha256sum. If you're on Windows, you'll need to run this command from Git Bash.

    curl -s https://images.pexels.com/photos/11053072/pexels-photo-11053072.jpeg | sha256sum
    14836b86a48e1b2b5e857213af97534704475b4c155d34b2cb83ed4b7cba2bb0 *-
  • cURL with shasum. If you're on Windows, you'll need to run this command from Git Bash.

    curl -s https://images.pexels.com/photos/11053072/pexels-photo-11053072.jpeg | shasum -a 256
    14836b86a48e1b2b5e857213af97534704475b4c155d34b2cb83ed4b7cba2bb0 *-
  • Save the image locally. Visit https://emn178.github.io/online-tools/sha256_checksum.html. Upload the image and click the Hash button.

In all three cases, the hash for this sample image is 14836b86a48e1b2b5e857213af97534704475b4c155d34b2cb83ed4b7cba2bb0.

The following is a list of the sample images and hashes this guide will use. For the NFTs with DIDs, we'll specify the metadata and license information. Additionally, we'll use multiple copies of the Metadata URI with these NFTs. For the NFTs without DIDs, we won't specify license or metadata information (this is only for simplicity -- normally you would want to specify this data).

CLI, no DID

CLI, with DID

RPC, no DID

RPC, with DID

Where to store images for NFTs

In theory you can use any hosting site to host your NFT images. However, do make sure that the service doesn't modify the image at all. Doing so will also change the hash and make it more likely to be lost long-term. The best way to verify this is to use each of the above techniques to generate the image hash, and make sure they all match.

Certain decentralized services such as IPFS can be slow. The first time a user attempts to view an NFT in their wallet (before caching), it might take a long time to load.

Chia NFTs use a list to store image URIs, so it is possible to add multiple locations to increase permanance. However, do make sure each image's hash is the same as the data hash.