Skip to content

Commit

Permalink
Support NFT data in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
Wojtek committed Nov 9, 2023
1 parent 352ee2a commit a35418a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 24 deletions.
57 changes: 51 additions & 6 deletions x/asset/nft/client/cli/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ package cli_test

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/nft"
nftcli "github.com/cosmos/cosmos-sdk/x/nft/client/cli"
"github.com/google/uuid"
"github.com/samber/lo"
"github.com/stretchr/testify/require"
Expand All @@ -19,7 +24,7 @@ import (
"github.com/CoreumFoundation/coreum/v3/x/asset/nft/types"
)

func TestQueryClassAndClasses(t *testing.T) {
func TestQueryClassAndNFT(t *testing.T) {
requireT := require.New(t)

testNetwork := network.New(t)
Expand All @@ -32,7 +37,7 @@ func TestQueryClassAndClasses(t *testing.T) {
ctx := testNetwork.Validators[0].ClientCtx

classID := issueClass(
requireT, ctx,
t, ctx,
symbol, name, description, uri, uriHash,
testNetwork,
"0.1",
Expand All @@ -51,6 +56,10 @@ func TestQueryClassAndClasses(t *testing.T) {
Description: description,
URI: uri,
URIHash: uriHash,
Data: &codectypes.Any{
TypeUrl: "/coreum.asset.nft.v1.DataBytes",
Value: []byte{0xa, 0x2, 0x11, 0x12},
},
Features: []types.ClassFeature{
types.ClassFeature_burning,
types.ClassFeature_disable_sending,
Expand All @@ -66,6 +75,32 @@ func TestQueryClassAndClasses(t *testing.T) {
[]string{fmt.Sprintf("--%s", cli.IssuerFlag), testNetwork.Validators[0].Address.String(), "--output", "json"},
&classesRes))
requireT.Equal(expectedClass, classesRes.Classes[0])

mint(
t,
ctx,
classID,
nftID,
"https://my-nft-meta.invalid/1",
"9309e7e6e96150afbf181d308fe88343ab1cbec391b7717150a7fb217b4cf0a9",
testNetwork,
)

expectedNFT := nft.NFT{
ClassId: classID,
Id: nftID,
Uri: "https://my-nft-meta.invalid/1",
UriHash: "9309e7e6e96150afbf181d308fe88343ab1cbec391b7717150a7fb217b4cf0a9",
Data: &codectypes.Any{
TypeUrl: "/coreum.asset.nft.v1.DataBytes",
Value: []byte{0xa, 0x2, 0x21, 0x22},
},
}

var nftRes nft.QueryNFTResponse
requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, nftcli.GetCmdQueryNFT(), []string{classID, nftID}, &nftRes))

requireT.Equal(&expectedNFT, nftRes.Nft)
}

func TestCmdQueryParams(t *testing.T) {
Expand All @@ -83,29 +118,38 @@ func TestCmdQueryParams(t *testing.T) {

//nolint:unparam // using constant values here will make this function less flexible.
func mint(
requireT *require.Assertions,
t *testing.T,
ctx client.Context,
classID, nftID, uri, uriHash string,
testNetwork *network.Network,
) {
data := []byte{0x21, 0x22}
dataFile := filepath.Join(t.TempDir(), "data")
require.NoError(t, os.WriteFile(dataFile, data, 0o600))

args := []string{
classID, nftID,
fmt.Sprintf("--%s=%s", cli.URIFlag, uri),
fmt.Sprintf("--%s=%s", cli.URIHashFlag, uriHash),
fmt.Sprintf("--%s=%s", cli.DataFileFlag, dataFile),
}
args = append(args, txValidator1Args(testNetwork)...)
_, err := coreumclitestutil.ExecTxCmd(ctx, testNetwork, cli.CmdTxMint(), args)
requireT.NoError(err)
require.NoError(t, err)
}

func issueClass(
requireT *require.Assertions,
t *testing.T,
ctx client.Context,
symbol, name, description, uri, uriHash string, //nolint:unparam
testNetwork *network.Network,
royaltyRate string,
features ...types.ClassFeature,
) string {
data := []byte{0x11, 0x12}
dataFile := filepath.Join(t.TempDir(), "data")
require.NoError(t, os.WriteFile(dataFile, data, 0o600))

featuresStringList := lo.Map(features, func(s types.ClassFeature, _ int) string {
return s.String()
})
Expand All @@ -118,13 +162,14 @@ func issueClass(
fmt.Sprintf("--%s=%s", cli.FeaturesFlag, featuresString),
fmt.Sprintf("--%s=%s", cli.URIFlag, uri),
fmt.Sprintf("--%s=%s", cli.URIHashFlag, uriHash),
fmt.Sprintf("--%s=%s", cli.DataFileFlag, dataFile),
}
args = append(args, txValidator1Args(testNetwork)...)
if royaltyRate != "" {
args = append(args, fmt.Sprintf("--%s", cli.RoyaltyRateFlag), royaltyRate)
}
_, err := coreumclitestutil.ExecTxCmd(ctx, testNetwork, cli.CmdTxIssueClass(), args)
requireT.NoError(err)
require.NoError(t, err)

return types.BuildClassID(symbol, validator.Address)
}
47 changes: 43 additions & 4 deletions x/asset/nft/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/authz"
Expand All @@ -29,6 +30,7 @@ const (
RecipientFlag = "recipient"
URIFlag = "uri"
URIHashFlag = "uri_hash"
DataFileFlag = "data-file"
)

// GetTxCmd returns the transaction commands for this module.
Expand Down Expand Up @@ -68,14 +70,14 @@ func CmdTxIssueClass() *cobra.Command {
allowedFeaturesString := strings.Join(allowedFeatures, ",")

cmd := &cobra.Command{
Use: fmt.Sprintf("issue-class [symbol] [name] [description] --from [issuer] --%s=%s --uri https://my-token-meta.invalid/1 --uri_hash e000624", FeaturesFlag, allowedFeaturesString),
Use: fmt.Sprintf("issue-class [symbol] [name] [description] --from [issuer] --%s=%s --uri https://my-token-meta.invalid/1 --uri_hash e000624 --data-file [path]", FeaturesFlag, allowedFeaturesString),
Args: cobra.ExactArgs(3),
Short: "Issue new non-fungible token class",
Long: strings.TrimSpace(
fmt.Sprintf(`Issue new non-fungible token class.
Example:
$ %s tx %s issue-class abc "ABC Name" "ABC class description." --from [issuer] --%s=%s"
$ %s tx %s issue-class abc "ABC Name" "ABC class description." --from [issuer] --data-file [path] --%s=%s"
`,
version.AppName, types.ModuleName, FeaturesFlag, allowedFeaturesString,
),
Expand Down Expand Up @@ -123,13 +125,19 @@ $ %s tx %s issue-class abc "ABC Name" "ABC class description." --from [issuer] -
return errors.WithStack(err)
}

data, err := getDataFromFile(cmd)
if err != nil {
return err
}

msg := &types.MsgIssueClass{
Issuer: issuer.String(),
Symbol: symbol,
Name: name,
Description: description,
URI: uri,
URIHash: uriHash,
Data: data,
Features: features,
RoyaltyRate: royaltyRate,
}
Expand All @@ -142,6 +150,7 @@ $ %s tx %s issue-class abc "ABC Name" "ABC class description." --from [issuer] -
cmd.Flags().String(RoyaltyRateFlag, "0", fmt.Sprintf("%s is a number between 0 and 1, and will be used to determine royalties sent to issuer, when an nft in this class is traded.", RoyaltyRateFlag))
cmd.Flags().String(URIFlag, "", "Class URI.")
cmd.Flags().String(URIHashFlag, "", "Class URI hash.")
cmd.Flags().String(DataFileFlag, "", "path to the file containing data.")

flags.AddTxFlagsToCmd(cmd)

Expand All @@ -151,14 +160,14 @@ $ %s tx %s issue-class abc "ABC Name" "ABC class description." --from [issuer] -
// CmdTxMint returns Mint cobra command.
func CmdTxMint() *cobra.Command {
cmd := &cobra.Command{
Use: "mint [class-id] [id] --from [sender] --uri https://my-token-meta.invalid/1 --uri_hash e000624",
Use: "mint [class-id] [id] --from [sender] --uri https://my-token-meta.invalid/1 --uri_hash e000624 --data-file [path]",
Args: cobra.ExactArgs(2),
Short: "Mint new non-fungible token",
Long: strings.TrimSpace(
fmt.Sprintf(`Mint new non-fungible token.
Example:
$ %s tx %s mint abc-%s id1 --from [sender]
$ %s tx %s mint abc-%s id1 --from [sender] --data-file [path]
`,
version.AppName, types.ModuleName, constant.AddressSampleTest,
),
Expand Down Expand Up @@ -188,13 +197,19 @@ $ %s tx %s mint abc-%s id1 --from [sender]
return errors.WithStack(err)
}

data, err := getDataFromFile(cmd)
if err != nil {
return err
}

msg := &types.MsgMint{
Sender: sender.String(),
Recipient: recipient,
ClassID: classID,
ID: ID,
URI: uri,
URIHash: uriHash,
Data: data,
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
Expand All @@ -205,6 +220,7 @@ $ %s tx %s mint abc-%s id1 --from [sender]
cmd.Flags().String(RecipientFlag, "", "Address to send minted token to, if not specified minted token is sent to the class issuer")
cmd.Flags().String(URIFlag, "", "NFT URI.")
cmd.Flags().String(URIHashFlag, "", "NFT URI hash.")
cmd.Flags().String(DataFileFlag, "", "path to the file containing data.")

return cmd
}
Expand Down Expand Up @@ -656,3 +672,26 @@ func getExpireTime(cmd *cobra.Command) (*time.Time, error) {
e := time.Unix(exp, 0)
return &e, nil
}

func getDataFromFile(cmd *cobra.Command) (*codectypes.Any, error) {
if !cmd.Flags().Changed(DataFileFlag) {
return nil, nil //nolint:nilnil
}

dataFilePath, err := cmd.Flags().GetString(DataFileFlag)
if err != nil {
return nil, errors.WithStack(err)
}

data, err := os.ReadFile(dataFilePath)
if err != nil {
return nil, errors.WithStack(err)
}

dataAny, err := codectypes.NewAnyWithValue(&types.DataBytes{Data: data})
if err != nil {
return nil, errors.WithStack(err)
}

return dataAny, nil
}
27 changes: 13 additions & 14 deletions x/asset/nft/client/cli/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,14 @@ func TestCmdTxIssueClass(t *testing.T) {
}

func TestCmdTxMint(t *testing.T) {
requireT := require.New(t)
testNetwork := network.New(t)

symbol := "nft" + uuid.NewString()[:4]
validator := testNetwork.Validators[0]
ctx := validator.ClientCtx

classID := issueClass(
requireT, ctx,
t, ctx,
symbol,
"class name",
"class description",
Expand All @@ -63,7 +62,7 @@ func TestCmdTxMint(t *testing.T) {
)

mint(
requireT,
t,
ctx,
classID,
nftID,
Expand All @@ -82,7 +81,7 @@ func TestCmdTxBurn(t *testing.T) {
ctx := validator.ClientCtx

classID := issueClass(
requireT, ctx,
t, ctx,
symbol,
"class name",
"class description",
Expand All @@ -91,7 +90,7 @@ func TestCmdTxBurn(t *testing.T) {
)

mint(
requireT,
t,
ctx,
classID,
"nft-1",
Expand Down Expand Up @@ -127,7 +126,7 @@ func TestCmdMintToRecipient(t *testing.T) {

// create class
classID := issueClass(
requireT,
t,
ctx,
symbol,
"class name",
Expand Down Expand Up @@ -169,7 +168,7 @@ func TestCmdFreeze(t *testing.T) {

// create class
classID := issueClass(
requireT,
t,
ctx,
symbol,
"class name",
Expand All @@ -182,7 +181,7 @@ func TestCmdFreeze(t *testing.T) {
)
// mint nft
mint(
requireT,
t,
ctx,
classID,
nftID,
Expand Down Expand Up @@ -225,7 +224,7 @@ func TestCmdWhitelist(t *testing.T) {

// create class
classID := issueClass(
requireT,
t,
ctx,
symbol,
"class name",
Expand All @@ -238,7 +237,7 @@ func TestCmdWhitelist(t *testing.T) {
)
// mint nft
mint(
requireT,
t,
ctx,
classID,
nftID,
Expand Down Expand Up @@ -288,7 +287,7 @@ func TestCmdClassWhitelist(t *testing.T) {

// create class
classID := issueClass(
requireT,
t,
ctx,
symbol,
"class name",
Expand All @@ -302,7 +301,7 @@ func TestCmdClassWhitelist(t *testing.T) {
// mint nft
nftID := "nft"
mint(
requireT,
t,
ctx,
classID,
nftID,
Expand Down Expand Up @@ -352,7 +351,7 @@ func TestCmdClassFreeze(t *testing.T) {

// create class
classID := issueClass(
requireT,
t,
ctx,
symbol,
"class name",
Expand All @@ -365,7 +364,7 @@ func TestCmdClassFreeze(t *testing.T) {
)
// mint nft
mint(
requireT,
t,
ctx,
classID,
nftID,
Expand Down

0 comments on commit a35418a

Please sign in to comment.