Create Non-Fungible Token with CLI

This tutorial will guide you through the process of using the assetnft module to create and manage Non-Fungible Tokens.

Please note that each subsequent section depends on the previous one.

Prerequisites

  • cored binary installed

  • We are going to interact with testnet, so we need to set --node and --chain-id flag in each request.

    export CHAIN_ID="coreum-testnet-1"
    export RPC_URL="https://full-node.testnet-1.coreum.dev:26657"
    

    If you want to use other network, find relevant values at network variables page.

  • Two accounts at your local keychain. If you don't have them, follow these steps - go to faucet page and generate two accounts there. Then, having the mnemonics, import them with the following commands

    cored keys add nft-issuer-wallet --recover --chain-id=$CHAIN_ID
    # put first mnemonic here
    cored keys add nft-receiver-wallet --recover --chain-id=$CHAIN_ID
    # put second mnemonic here
    
  • Also, since network operates with raw addresses, not your local names, we are going to bind raw addresses to env vars. We will export the following values:

    export NFT_ISSUER_ADDRESS=$(cored keys show nft-issuer-wallet --address --chain-id=$CHAIN_ID)
    export NFT_RECEIVER_ADDRESS=$(cored keys show nft-receiver-wallet --address --chain-id=$CHAIN_ID)
    

Create your first NFT

  • We will export one more environment variable to store NFT class id, which is a uniquely named group of NFT objects. It can be defined with different characteristics (features) that are common for all NFTs belonging to the class.
    export NFT_CLASS_ID=$(echo "puppysmartnft1-"$NFT_ISSUER_ADDRESS)
    

Create NFT class

  • Let's create new NFT class:

    # cored tx assetnft issue-class [symbol] [name] [description] [uri] [uri_hash] --from [issuer] --features=burning,freezing,whitelisting,disable_sending [flags]
    cored tx assetnft issue-class puppysmartnft1 "Puppy NFTs" "A collection of awesome puppy NFTs" "http://puppy-nfts.com" "somehash" --from $NFT_ISSUER_ADDRESS --features=burning,freezing,whitelisting --node=$RPC_URL --chain-id=$CHAIN_ID
    # confirm transaction before signing and broadcasting [y/N]: y
    

    As an output, you should receive tx hash, copy it and go to Block explorer to see the tx status. Features: burning, freezing and whitelisting are explained later in this guide.

  • Now, let's check what NFT classes reside on the Coreum blockchain:

    cored q nft classes --node=$RPC_URL --chain-id=$CHAIN_ID
    #classes:
    #- data: null
    #  description: A collection of awesome puppy NFTs
    #  id: puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7
    #  name: Puppy NFTs
    #  symbol: puppysmartnft1
    #  uri: http://puppy-nfts.com
    #  uri_hash: somehash
    

    As you see from the output, your new token has got unique class id which consists of the symbol provided and issuer account address.

Mint NFTs

  • We will now mint 2 NFTs within created class

    #cored tx assetnft mint [class-id] [id] [uri] [uri_hash] --from [sender] [flags]
    cored tx assetnft mint $NFT_CLASS_ID puppysmartnft-1 "http://puppy-nfts.com/puppynft-1" "somehash" --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID
    
    cored tx assetnft mint $NFT_CLASS_ID puppysmartnft-2 "http://puppy-nfts.com/puppynft-2" "somehash" --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID
    

    Again, we can check transaction status by providing transaction hashes on the Block explorer page

  • We can query all NFTs of a given class with the following command

cored q nft nfts --class-id=$NFT_CLASS_ID --node=$RPC_URL --chain-id=$CHAIN_ID
#nfts:
#- class_id: puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7
#  data: null
#  id: puppysmartnft-1
#  uri: http://puppy-nfts.com/puppynft-1
#  uri_hash: somehash
#- class_id: puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7
#  data: null
#  id: puppysmartnft-2
#  uri: http://puppy-nfts.com/puppynft-2
#  uri_hash: somehash

As you can see, 2 NFTs were properly minted.

Whitelisting

Whitelisting functionality allows transferring NFTs of a certain class only to specific addresses. Since we enabled that feature for our NFT class, in order to send one of our minted NFTs to a receiver, we need to first whitelist them.

cored tx assetnft whitelist $NFT_CLASS_ID puppysmartnft-1 $NFT_RECEIVER_ADDRESS --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID

Sending and querying balance

Our NFT is now ready to be transferred to the receiving account.

Send

cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_RECEIVER_ADDRESS --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID

Query the balance

We can query NFT balances in multiple ways. These could be as follows

cored q nft balance $NFT_RECEIVER_ADDRESS $NFT_CLASS_ID --node=$RPC_URL --chain-id=$CHAIN_ID
# amount: "1"

That way, we can query the number of NFTs of a given class owned by the owner. Our receiving account owns 1 NFT of this class.

We can also query the owner of the NFT based on its class and id.

cored q nft owner  $NFT_CLASS_ID puppysmartnft-1  --node=$RPC_URL --chain-id=$CHAIN_ID
# owner: testcore1k9575m9egrlmymnyd29p5g0p5e94d930tg67sv

Freezing

Freezing, if enabled for NFT class, allows issuer to freeze specific NFT of a given class. A frozen token cannot be transferred until it is unfrozen by the issuer.

Freeze

cored tx assetnft freeze $NFT_CLASS_ID puppysmartnft-1 --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID

Our NFT of this class is now frozen. Let's verify if this is actually the case

  • Query frozen

Using the following command, we can check frozen flag set for an NFT in a given class

cored q assetnft frozen $NFT_CLASS_ID puppysmartnft-1 --node=$RPC_URL --chain-id=$CHAIN_ID
#frozen: true
  • Let's now try to send the NFT from the receiver back to the issuer. Such transaction should not succeed.
cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_ISSUER_ADDRESS --from $NFT_RECEIVER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID -b=block
#at the end of the raw_log section, notice an error: "raw_log: 'failed to execute message; message index: 0: nft with classID:puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7 and ID:puppysmartnft-1 is frozen: unauthorized'

Notice we used -b=block argument which waits for the tx to pass/fail checks and be committed in a block.

Unfreeze

  • We can unfreeze the token and make it transferable again
cored tx assetnft unfreeze $NFT_CLASS_ID puppysmartnft-1 --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID
  • Sending the token back to the issuer succeeds this time
cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_ISSUER_ADDRESS --from $NFT_RECEIVER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID -b=block
  • Let's query the current owner of the token
cored q nft owner $NFT_CLASS_ID puppysmartnft-1  --node=$RPC_URL --chain-id=$CHAIN_ID
#owner: testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7

The address shown belongs to the issuer.

Class Freeze

  • Let's send the NFT back to receiver first.
cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_RECEIVER_ADDRESS --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID
  • Now we can freeze all the NFTs of a class held by an account
cored tx assetnft class-freeze $NFT_CLASS_ID $NFT_RECEIVER_ADDRESS --from $NFT_ISSUER_ADDRESS --chain-id=$CHAIN_ID -b=block
  • Let's now try to send the NFT from the issuer to the receiver. Such transaction should not succeed.
cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_ISSUER_ADDRESS --from $NFT_RECEIVER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID -b=block
#at the end of the raw_log section, notice an error: "raw_log: 'failed to execute message; message index: 0: nft with classID:puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7 and ID:puppysmartnft-1 is frozen: unauthorized'

Class Unfreeze

  • We can remove the class freeze of an account
cored tx assetnft class-unfreeze $NFT_CLASS_ID $NFT_RECEIVER_ADDRESS --from $NFT_ISSUER_ADDRESS --chain-id=$CHAIN_ID -b=block
  • Sending the token back to the issuer succeeds this time
cored tx nft send $NFT_CLASS_ID puppysmartnft-1 $NFT_ISSUER_ADDRESS --from $NFT_RECEIVER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID -b=block

Burning

If burning feature is enabled on NFT class level, it allows an owner of a token to burn it.

  • First, let's query an nft we want to burn
cored q nft nft $NFT_CLASS_ID puppysmartnft-2 --node=$RPC_URL --chain-id=$CHAIN_ID
#nft:
#  class_id: puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7
#  data: null
#  id: puppysmartnft-2
#  uri: http://puppy-nfts.com/puppynft-2
#  uri_hash: somehash
  • Now, then token can be burnt
cored tx assetnft burn $NFT_CLASS_ID puppysmartnft-2 --from $NFT_ISSUER_ADDRESS --node=$RPC_URL --chain-id=$CHAIN_ID
  • As a verification step, let's check if the token still exists
cored q nft nft $NFT_CLASS_ID puppysmartnft-2 --node=$RPC_URL --chain-id=$CHAIN_ID
#notice an error at the end of the output
#not found nft: class: puppysmartnft1-testcore105hmczwh0tkha2h5lu9rr07xtegzsm49d3hxq7, id: puppysmartnft-2: nft does not exist: invalid request

Update Data

update the data of the specified NFT with the information provided in the data file

cored tx assetnft update-data $NFT_CLASS_ID puppysmartnft-1 --from $NFT_ISSUER_ADDRESS --data-file /path/to/your/data-file.json --node=$RPC_URL --chain-id=$CHAIN_ID

FAQ

Can a token be always burnt by an owner?

No, when a token is frozen, it cannot be burnt. Also, burning feature needs to be enabled on NFT class id level.

In order to burn a token, does burning feature always need to be enabled?

In general yes. However, an issuer of a token, can always burn it (while they own it), regardless of the feature setting.

Can all tokens within an NFT class be frozen/unfrozen at once?

No. There's no single command allowing to achieve that. It can, though, be done programmatically (for example, by utilizing the output returned by cored q nft nfts --class-id=$NFT_CLASS_ID --node=$RPC_URL --chain-id=$CHAIN_ID command).

What is next?

You can read more about Non-Fungible Tokens at Smart Token Overview and assetnft spec pages