Create Non-Fungible Token with Golang

This document gives instructions and examples on how to use our pkg/client package to create and manage non-fungible token.

Complete code

Complete code with go.mod file you can find here

P.S. If you have issues with go mod tidy command, just copy go.mod file from the example above.

!!!include(/docs/tutorials/get-started/golang)!!!

Creating NFT class

First we create class which is a container for a set of NFTs having the same purpose:

senderAddress, err := senderInfo.GetAddress()
if err != nil {
	panic(err)
}
const classSymbol = "NFTClass"
msgIssueClass := &assetnfttypes.MsgIssueClass{
	Issuer:      senderAddress.String(),
	Symbol:      classSymbol,
	Name:        "NFT Class",
	Description: "NFT Class",
	Features:    []assetnfttypes.ClassFeature{assetnfttypes.ClassFeature_freezing},
}

ctx := context.Background()
_, err = client.BroadcastTx(
	ctx,
	clientCtx.WithFromAddress(senderAddress),
	txFactory,
	msgIssueClass,
)
if err != nil {
	panic(err)
}

Minting NFT

Then we mint new NFT for that class:

classID := assetnfttypes.BuildClassID(classSymbol, senderAddress)
const nftID = "myNFT"
msgMint := &assetnfttypes.MsgMint{
	Sender:  senderAddress.String(),
	ClassID: classID,
	ID:      nftID,
}

_, err = client.BroadcastTx(
	ctx,
	clientCtx.WithFromAddress(senderAddress),
	txFactory,
	msgMint,
)
if err != nil {
	panic(err)
}

Querying the owner

We query the owner of the NFT to verify that it is owned by the creator:

nftClient := nft.NewQueryClient(clientCtx)
resp, err := nftClient.Owner(ctx, &nft.QueryOwnerRequest{
	ClassId: classID,
	Id:      nftID,
})
if err != nil {
	panic(err)
}
fmt.Printf("Owner: %s\n", resp.Owner)

Sending NFT

Now we send NFT to someone else:

recipientInfo, _, err := clientCtx.Keyring().NewMnemonic(
	"recipient",
	keyring.English,
	sdk.GetConfig().GetFullBIP44Path(),
	"",
	hd.Secp256k1,
)
if err != nil {
	panic(err)
}

recipientAddress, err := recipientInfo.GetAddress()
if err != nil {
	panic(err)
}
msgSend := &nft.MsgSend{
	Sender:   senderAddress.String(),
	Receiver: recipientAddress.String(),
	Id:       nftID,
	ClassId:  classID,
}

_, err = client.BroadcastTx(
	ctx,
	clientCtx.WithFromAddress(senderAddress),
	txFactory,
	msgSend,
)
if err != nil {
	panic(err)
}

Let's verify that recipient is the owner now:

resp, err = nftClient.Owner(ctx, &nft.QueryOwnerRequest{
	ClassId: classID,
	Id:      nftID,
})
if err != nil {
	panic(err)
}
fmt.Printf("Owner: %s\n", resp.Owner)

Freezing

Because issuer enabled freezing feature during class issuance, he/she might freeze the NFT:

msgFreeze := &assetnfttypes.MsgFreeze{
	Sender:  senderAddress.String(),
	ClassID: classID,
	ID:      nftID,
}

_, err = client.BroadcastTx(
	ctx,
	clientCtx.WithFromAddress(senderAddress),
	txFactory,
	msgFreeze,
)
if err != nil {
	panic(err)
}

Update Data

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

dataDynamic := assetnfttypes.DataDynamic{
		Items: []assetnfttypes.DataDynamicItem{
			{
				Editors: []assetnfttypes.DataEditor{
					assetnfttypes.DataEditor_owner,
				},
				Data: jsonData,
			},
		},
	}
	data, err = codectypes.NewAnyWithValue(&dataDynamic)
	if err != nil {
		panic(err)
	}

	// Broadcast transaction minting new dynamic nft
	classID = assetnfttypes.BuildClassID(classSymbol, senderAddress)
	const dynamicNftID = "myDynamicNFT"
	msgMint = &assetnfttypes.MsgMint{
		Sender:  senderAddress.String(),
		ClassID: classID,
		ID:      dynamicNftID,
		Data:    data,
	}

	_, err = client.BroadcastTx(
		ctx,
		clientCtx.WithFromAddress(senderAddress),
		txFactory,
		msgMint,
	)
	if err != nil {
		panic(err)
	}

	// Query the nft
	storedNFT, err := nftClient.NFT(ctx, &nft.QueryNFTRequest{
		ClassId: classID,
		Id:      dynamicNftID,
	})
	if err != nil {
		panic(err)
	}

	var storedDataDynamic assetnfttypes.DataDynamic
	err = storedDataDynamic.Unmarshal(storedNFT.Nft.Data.Value)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Data: %s\n", string(storedDataDynamic.Items[0].Data))

	// update stored NFT
	msgUpdateData := &assetnfttypes.MsgUpdateData{
		Sender:  senderAddress.String(),
		ClassID: classID,
		ID:      dynamicNftID,
		Items: []assetnfttypes.DataDynamicIndexedItem{
			{
				Index: 0,
				Data:  []byte(`{"name": "Updated Name", "description": "Updated Description"}`),
			},
		},
	}

	_, err = client.BroadcastTx(
		ctx,
		clientCtx.WithFromAddress(senderAddress),
		txFactory,
		msgUpdateData,
	)
	if err != nil {
		panic(err)
	}

	storedNFT, err = nftClient.NFT(ctx, &nft.QueryNFTRequest{
		ClassId: classID,
		Id:      dynamicNftID,
	})
	if err != nil {
		panic(err)
	}

	var storedDataDynamic2 assetnfttypes.DataDynamic
	err = storedDataDynamic2.Unmarshal(storedNFT.Nft.Data.Value)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Data: %s\n", string(storedDataDynamic2.Items[0].Data))

After doing this, recipient is not allowed to transfer the NFT from its account.

All the other features may be used in a similar fashion. More info is available in the documentation