github.com/Finschia/finschia-sdk@v0.48.1/x/collection/msgs_test.go (about)

     1  package collection_test
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/Finschia/finschia-sdk/crypto/keys/secp256k1"
    11  	sdk "github.com/Finschia/finschia-sdk/types"
    12  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    13  	"github.com/Finschia/finschia-sdk/x/auth/legacy/legacytx"
    14  	"github.com/Finschia/finschia-sdk/x/collection"
    15  	"github.com/Finschia/finschia-sdk/x/token/class"
    16  )
    17  
    18  func TestMsgSendFT(t *testing.T) {
    19  	addrs := make([]sdk.AccAddress, 2)
    20  	for i := range addrs {
    21  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
    22  	}
    23  
    24  	amount := collection.NewCoins(
    25  		collection.NewFTCoin("00bab10c", sdk.OneInt()),
    26  	)
    27  
    28  	testCases := map[string]struct {
    29  		contractID string
    30  		from       sdk.AccAddress
    31  		to         sdk.AccAddress
    32  		amount     []collection.Coin
    33  		err        error
    34  		panic      bool
    35  	}{
    36  		"valid msg": {
    37  			contractID: "deadbeef",
    38  			from:       addrs[0],
    39  			to:         addrs[1],
    40  			amount:     amount,
    41  		},
    42  		"invalid from": {
    43  			contractID: "deadbeef",
    44  			to:         addrs[1],
    45  			amount:     amount,
    46  			err:        sdkerrors.ErrInvalidAddress,
    47  		},
    48  		"invalid contract id": {
    49  			from:   addrs[0],
    50  			to:     addrs[1],
    51  			amount: amount,
    52  			err:    class.ErrInvalidContractID,
    53  		},
    54  		"invalid to": {
    55  			contractID: "deadbeef",
    56  			from:       addrs[0],
    57  			amount:     amount,
    58  			err:        sdkerrors.ErrInvalidAddress,
    59  		},
    60  		"nil amount": {
    61  			contractID: "deadbeef",
    62  			from:       addrs[0],
    63  			to:         addrs[1],
    64  			amount: []collection.Coin{{
    65  				TokenId: collection.NewFTID("00bab10c"),
    66  			}},
    67  			panic: true,
    68  		},
    69  		"zero amount": {
    70  			contractID: "deadbeef",
    71  			from:       addrs[0],
    72  			to:         addrs[1],
    73  			amount: []collection.Coin{{
    74  				TokenId: collection.NewFTID("00bab10c"),
    75  				Amount:  sdk.ZeroInt(),
    76  			}},
    77  			err: collection.ErrInvalidAmount,
    78  		},
    79  		"invalid token id": {
    80  			contractID: "deadbeef",
    81  			from:       addrs[0],
    82  			to:         addrs[1],
    83  			amount: []collection.Coin{{
    84  				Amount: sdk.OneInt(),
    85  			}},
    86  			err: collection.ErrInvalidTokenID,
    87  		},
    88  	}
    89  
    90  	for name, tc := range testCases {
    91  		t.Run(name, func(t *testing.T) {
    92  			msg := collection.MsgSendFT{
    93  				ContractId: tc.contractID,
    94  				From:       tc.from.String(),
    95  				To:         tc.to.String(),
    96  				Amount:     tc.amount,
    97  			}
    98  
    99  			if tc.panic {
   100  				require.Panics(t, func() { msg.ValidateBasic() })
   101  				return
   102  			}
   103  
   104  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   105  			if tc.err != nil {
   106  				return
   107  			}
   108  
   109  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
   110  		})
   111  	}
   112  }
   113  
   114  func TestMsgOperatorSendFT(t *testing.T) {
   115  	addrs := make([]sdk.AccAddress, 3)
   116  	for i := range addrs {
   117  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   118  	}
   119  
   120  	amount := collection.NewCoins(
   121  		collection.NewFTCoin("00bab10c", sdk.OneInt()),
   122  	)
   123  
   124  	testCases := map[string]struct {
   125  		contractID string
   126  		operator   sdk.AccAddress
   127  		from       sdk.AccAddress
   128  		to         sdk.AccAddress
   129  		amount     []collection.Coin
   130  		err        error
   131  	}{
   132  		"valid msg": {
   133  			contractID: "deadbeef",
   134  			operator:   addrs[0],
   135  			from:       addrs[1],
   136  			to:         addrs[2],
   137  			amount:     amount,
   138  		},
   139  		"invalid operator": {
   140  			contractID: "deadbeef",
   141  			from:       addrs[1],
   142  			to:         addrs[2],
   143  			amount:     amount,
   144  			err:        sdkerrors.ErrInvalidAddress,
   145  		},
   146  		"invalid contract id": {
   147  			operator: addrs[0],
   148  			from:     addrs[1],
   149  			to:       addrs[2],
   150  			amount:   amount,
   151  			err:      class.ErrInvalidContractID,
   152  		},
   153  		"invalid from": {
   154  			contractID: "deadbeef",
   155  			operator:   addrs[0],
   156  			to:         addrs[1],
   157  			amount:     amount,
   158  			err:        sdkerrors.ErrInvalidAddress,
   159  		},
   160  		"invalid to": {
   161  			contractID: "deadbeef",
   162  			operator:   addrs[0],
   163  			from:       addrs[1],
   164  			amount:     amount,
   165  			err:        sdkerrors.ErrInvalidAddress,
   166  		},
   167  		"invalid amount": {
   168  			contractID: "deadbeef",
   169  			operator:   addrs[0],
   170  			from:       addrs[1],
   171  			to:         addrs[2],
   172  			amount: []collection.Coin{{
   173  				TokenId: collection.NewFTID("00bab10c"),
   174  				Amount:  sdk.ZeroInt(),
   175  			}},
   176  			err: collection.ErrInvalidAmount,
   177  		},
   178  		"invalid denom": {
   179  			contractID: "deadbeef",
   180  			operator:   addrs[0],
   181  			from:       addrs[1],
   182  			to:         addrs[2],
   183  			amount: []collection.Coin{{
   184  				Amount: sdk.OneInt(),
   185  			}},
   186  			err: collection.ErrInvalidTokenID,
   187  		},
   188  	}
   189  
   190  	for name, tc := range testCases {
   191  		t.Run(name, func(t *testing.T) {
   192  			msg := collection.MsgOperatorSendFT{
   193  				ContractId: tc.contractID,
   194  				Operator:   tc.operator.String(),
   195  				From:       tc.from.String(),
   196  				To:         tc.to.String(),
   197  				Amount:     tc.amount,
   198  			}
   199  
   200  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   201  			if tc.err != nil {
   202  				return
   203  			}
   204  
   205  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   206  		})
   207  	}
   208  }
   209  
   210  func TestMsgSendNFT(t *testing.T) {
   211  	addrs := make([]sdk.AccAddress, 2)
   212  	for i := range addrs {
   213  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   214  	}
   215  
   216  	ids := []string{collection.NewNFTID("deadbeef", 1)}
   217  
   218  	testCases := map[string]struct {
   219  		contractID string
   220  		from       sdk.AccAddress
   221  		to         sdk.AccAddress
   222  		ids        []string
   223  		err        error
   224  	}{
   225  		"valid msg": {
   226  			contractID: "deadbeef",
   227  			from:       addrs[0],
   228  			to:         addrs[1],
   229  			ids:        ids,
   230  		},
   231  		"invalid from": {
   232  			contractID: "deadbeef",
   233  			to:         addrs[1],
   234  			ids:        ids,
   235  			err:        sdkerrors.ErrInvalidAddress,
   236  		},
   237  		"invalid contract id": {
   238  			from: addrs[0],
   239  			to:   addrs[1],
   240  			ids:  ids,
   241  			err:  class.ErrInvalidContractID,
   242  		},
   243  		"invalid to": {
   244  			contractID: "deadbeef",
   245  			from:       addrs[0],
   246  			ids:        ids,
   247  			err:        sdkerrors.ErrInvalidAddress,
   248  		},
   249  		"empty token ids": {
   250  			contractID: "deadbeef",
   251  			from:       addrs[0],
   252  			to:         addrs[1],
   253  			err:        collection.ErrEmptyField,
   254  		},
   255  		"invalid token ids": {
   256  			contractID: "deadbeef",
   257  			from:       addrs[0],
   258  			to:         addrs[1],
   259  			ids:        []string{""},
   260  			err:        collection.ErrInvalidTokenID,
   261  		},
   262  	}
   263  
   264  	for name, tc := range testCases {
   265  		t.Run(name, func(t *testing.T) {
   266  			msg := collection.MsgSendNFT{
   267  				ContractId: tc.contractID,
   268  				From:       tc.from.String(),
   269  				To:         tc.to.String(),
   270  				TokenIds:   tc.ids,
   271  			}
   272  
   273  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   274  			if tc.err != nil {
   275  				return
   276  			}
   277  
   278  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
   279  		})
   280  	}
   281  }
   282  
   283  func TestMsgOperatorSendNFT(t *testing.T) {
   284  	addrs := make([]sdk.AccAddress, 3)
   285  	for i := range addrs {
   286  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   287  	}
   288  
   289  	ids := []string{collection.NewNFTID("deadbeef", 1)}
   290  
   291  	testCases := map[string]struct {
   292  		contractID string
   293  		operator   sdk.AccAddress
   294  		from       sdk.AccAddress
   295  		to         sdk.AccAddress
   296  		ids        []string
   297  		err        error
   298  	}{
   299  		"valid msg": {
   300  			contractID: "deadbeef",
   301  			operator:   addrs[0],
   302  			from:       addrs[1],
   303  			to:         addrs[2],
   304  			ids:        ids,
   305  		},
   306  		"invalid operator": {
   307  			contractID: "deadbeef",
   308  			from:       addrs[1],
   309  			to:         addrs[2],
   310  			ids:        ids,
   311  			err:        sdkerrors.ErrInvalidAddress,
   312  		},
   313  		"invalid contract id": {
   314  			operator: addrs[0],
   315  			from:     addrs[1],
   316  			to:       addrs[2],
   317  			ids:      ids,
   318  			err:      class.ErrInvalidContractID,
   319  		},
   320  		"invalid from": {
   321  			contractID: "deadbeef",
   322  			operator:   addrs[0],
   323  			to:         addrs[1],
   324  			ids:        ids,
   325  			err:        sdkerrors.ErrInvalidAddress,
   326  		},
   327  		"invalid to": {
   328  			contractID: "deadbeef",
   329  			operator:   addrs[0],
   330  			from:       addrs[1],
   331  			ids:        ids,
   332  			err:        sdkerrors.ErrInvalidAddress,
   333  		},
   334  		"empty ids": {
   335  			contractID: "deadbeef",
   336  			operator:   addrs[0],
   337  			from:       addrs[1],
   338  			to:         addrs[2],
   339  			err:        collection.ErrEmptyField,
   340  		},
   341  		"invalid id": {
   342  			contractID: "deadbeef",
   343  			operator:   addrs[0],
   344  			from:       addrs[1],
   345  			to:         addrs[2],
   346  			ids:        []string{""},
   347  			err:        collection.ErrInvalidTokenID,
   348  		},
   349  	}
   350  
   351  	for name, tc := range testCases {
   352  		t.Run(name, func(t *testing.T) {
   353  			msg := collection.MsgOperatorSendNFT{
   354  				ContractId: tc.contractID,
   355  				Operator:   tc.operator.String(),
   356  				From:       tc.from.String(),
   357  				To:         tc.to.String(),
   358  				TokenIds:   tc.ids,
   359  			}
   360  
   361  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   362  			if tc.err != nil {
   363  				return
   364  			}
   365  
   366  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   367  		})
   368  	}
   369  }
   370  
   371  func TestMsgAuthorizeOperator(t *testing.T) {
   372  	addrs := make([]sdk.AccAddress, 2)
   373  	for i := range addrs {
   374  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   375  	}
   376  
   377  	testCases := map[string]struct {
   378  		contractID string
   379  		holder     sdk.AccAddress
   380  		operator   sdk.AccAddress
   381  		err        error
   382  	}{
   383  		"valid msg": {
   384  			contractID: "deadbeef",
   385  			holder:     addrs[0],
   386  			operator:   addrs[1],
   387  		},
   388  		"invalid contract id": {
   389  			holder:   addrs[0],
   390  			operator: addrs[1],
   391  			err:      class.ErrInvalidContractID,
   392  		},
   393  		"invalid holder": {
   394  			contractID: "deadbeef",
   395  			operator:   addrs[1],
   396  			err:        sdkerrors.ErrInvalidAddress,
   397  		},
   398  		"empty operator": {
   399  			contractID: "deadbeef",
   400  			holder:     addrs[0],
   401  			err:        sdkerrors.ErrInvalidAddress,
   402  		},
   403  	}
   404  
   405  	for name, tc := range testCases {
   406  		t.Run(name, func(t *testing.T) {
   407  			msg := collection.MsgAuthorizeOperator{
   408  				ContractId: tc.contractID,
   409  				Holder:     tc.holder.String(),
   410  				Operator:   tc.operator.String(),
   411  			}
   412  
   413  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   414  			if tc.err != nil {
   415  				return
   416  			}
   417  
   418  			require.Equal(t, []sdk.AccAddress{tc.holder}, msg.GetSigners())
   419  		})
   420  	}
   421  }
   422  
   423  func TestMsgRevokeOperator(t *testing.T) {
   424  	addrs := make([]sdk.AccAddress, 2)
   425  	for i := range addrs {
   426  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   427  	}
   428  
   429  	testCases := map[string]struct {
   430  		contractID string
   431  		holder     sdk.AccAddress
   432  		operator   sdk.AccAddress
   433  		err        error
   434  	}{
   435  		"valid msg": {
   436  			contractID: "deadbeef",
   437  			holder:     addrs[0],
   438  			operator:   addrs[1],
   439  		},
   440  		"invalid contract id": {
   441  			holder:   addrs[0],
   442  			operator: addrs[1],
   443  			err:      class.ErrInvalidContractID,
   444  		},
   445  		"invalid holder": {
   446  			contractID: "deadbeef",
   447  			operator:   addrs[1],
   448  			err:        sdkerrors.ErrInvalidAddress,
   449  		},
   450  		"empty operator": {
   451  			contractID: "deadbeef",
   452  			holder:     addrs[0],
   453  			err:        sdkerrors.ErrInvalidAddress,
   454  		},
   455  	}
   456  
   457  	for name, tc := range testCases {
   458  		t.Run(name, func(t *testing.T) {
   459  			msg := collection.MsgRevokeOperator{
   460  				ContractId: tc.contractID,
   461  				Holder:     tc.holder.String(),
   462  				Operator:   tc.operator.String(),
   463  			}
   464  
   465  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   466  			if tc.err != nil {
   467  				return
   468  			}
   469  
   470  			require.Equal(t, []sdk.AccAddress{tc.holder}, msg.GetSigners())
   471  		})
   472  	}
   473  }
   474  
   475  func TestMsgCreateContract(t *testing.T) {
   476  	addrs := make([]sdk.AccAddress, 1)
   477  	for i := range addrs {
   478  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   479  	}
   480  
   481  	name := "tibetian fox"
   482  	uri := "file:///tibetian_fox.png"
   483  	meta := "Tibetian fox"
   484  	testCases := map[string]struct {
   485  		owner      sdk.AccAddress
   486  		name       string
   487  		baseImgURI string
   488  		meta       string
   489  		err        error
   490  	}{
   491  		"valid msg": {
   492  			owner:      addrs[0],
   493  			name:       name,
   494  			baseImgURI: uri,
   495  			meta:       meta,
   496  		},
   497  		"invalid owner": {
   498  			name:       name,
   499  			baseImgURI: uri,
   500  			meta:       meta,
   501  			err:        sdkerrors.ErrInvalidAddress,
   502  		},
   503  		"long name": {
   504  			owner:      addrs[0],
   505  			name:       string(make([]rune, 21)),
   506  			baseImgURI: uri,
   507  			meta:       meta,
   508  			err:        collection.ErrInvalidNameLength,
   509  		},
   510  		"invalid base image uri": {
   511  			owner:      addrs[0],
   512  			name:       name,
   513  			baseImgURI: string(make([]rune, 1001)),
   514  			meta:       meta,
   515  			err:        collection.ErrInvalidBaseImgURILength,
   516  		},
   517  		"invalid meta": {
   518  			owner:      addrs[0],
   519  			name:       name,
   520  			baseImgURI: uri,
   521  			meta:       string(make([]rune, 1001)),
   522  			err:        collection.ErrInvalidMetaLength,
   523  		},
   524  	}
   525  
   526  	for name, tc := range testCases {
   527  		t.Run(name, func(t *testing.T) {
   528  			msg := collection.MsgCreateContract{
   529  				Owner: tc.owner.String(),
   530  				Name:  tc.name,
   531  				Uri:   tc.baseImgURI,
   532  				Meta:  tc.meta,
   533  			}
   534  
   535  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   536  			if tc.err != nil {
   537  				return
   538  			}
   539  
   540  			require.Equal(t, []sdk.AccAddress{tc.owner}, msg.GetSigners())
   541  		})
   542  	}
   543  }
   544  
   545  func TestMsgIssueFT(t *testing.T) {
   546  	addrs := make([]sdk.AccAddress, 2)
   547  	for i := range addrs {
   548  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   549  	}
   550  
   551  	contractID := "deadbeef"
   552  	name := "tibetian fox"
   553  	meta := "Tibetian Fox"
   554  	decimals := int32(8)
   555  	testCases := map[string]struct {
   556  		contractID string
   557  		owner      sdk.AccAddress
   558  		to         sdk.AccAddress
   559  		name       string
   560  		meta       string
   561  		decimals   int32
   562  		mintable   bool
   563  		amount     sdk.Int
   564  		err        error
   565  	}{
   566  		"valid msg": {
   567  			contractID: contractID,
   568  			owner:      addrs[0],
   569  			to:         addrs[1],
   570  			name:       name,
   571  			meta:       meta,
   572  			decimals:   decimals,
   573  			amount:     sdk.OneInt(),
   574  		},
   575  		"invalid contract id": {
   576  			owner:    addrs[0],
   577  			to:       addrs[1],
   578  			name:     name,
   579  			meta:     meta,
   580  			decimals: decimals,
   581  			amount:   sdk.OneInt(),
   582  			err:      class.ErrInvalidContractID,
   583  		},
   584  		"invalid owner": {
   585  			contractID: contractID,
   586  			to:         addrs[1],
   587  			name:       name,
   588  			meta:       meta,
   589  			decimals:   decimals,
   590  			amount:     sdk.OneInt(),
   591  			err:        sdkerrors.ErrInvalidAddress,
   592  		},
   593  		"invalid to": {
   594  			contractID: contractID,
   595  			owner:      addrs[0],
   596  			name:       name,
   597  			meta:       meta,
   598  			decimals:   decimals,
   599  			amount:     sdk.OneInt(),
   600  			err:        sdkerrors.ErrInvalidAddress,
   601  		},
   602  		"empty name": {
   603  			contractID: contractID,
   604  			owner:      addrs[0],
   605  			to:         addrs[1],
   606  			meta:       meta,
   607  			decimals:   decimals,
   608  			amount:     sdk.OneInt(),
   609  			err:        collection.ErrInvalidTokenName,
   610  		},
   611  		"long name": {
   612  			contractID: contractID,
   613  			owner:      addrs[0],
   614  			to:         addrs[1],
   615  			name:       string(make([]rune, 21)),
   616  			meta:       meta,
   617  			decimals:   decimals,
   618  			amount:     sdk.OneInt(),
   619  			err:        collection.ErrInvalidNameLength,
   620  		},
   621  		"invalid meta": {
   622  			contractID: contractID,
   623  			owner:      addrs[0],
   624  			to:         addrs[1],
   625  			name:       name,
   626  			meta:       string(make([]rune, 1001)),
   627  			decimals:   decimals,
   628  			amount:     sdk.OneInt(),
   629  			err:        collection.ErrInvalidMetaLength,
   630  		},
   631  		"invalid decimals": {
   632  			contractID: contractID,
   633  			owner:      addrs[0],
   634  			to:         addrs[1],
   635  			name:       name,
   636  			meta:       meta,
   637  			decimals:   19,
   638  			amount:     sdk.OneInt(),
   639  			err:        collection.ErrInvalidTokenDecimals,
   640  		},
   641  		"invalid decimals - negative": {
   642  			contractID: contractID,
   643  			owner:      addrs[0],
   644  			to:         addrs[1],
   645  			name:       name,
   646  			meta:       meta,
   647  			decimals:   -1,
   648  			amount:     sdk.OneInt(),
   649  			err:        collection.ErrInvalidTokenDecimals,
   650  		},
   651  		"daphne compat": {
   652  			contractID: contractID,
   653  			owner:      addrs[0],
   654  			to:         addrs[1],
   655  			name:       name,
   656  			meta:       meta,
   657  			amount:     sdk.OneInt(),
   658  			err:        collection.ErrInvalidIssueFT,
   659  		},
   660  	}
   661  
   662  	for name, tc := range testCases {
   663  		t.Run(name, func(t *testing.T) {
   664  			msg := collection.MsgIssueFT{
   665  				ContractId: tc.contractID,
   666  				Owner:      tc.owner.String(),
   667  				To:         tc.to.String(),
   668  				Name:       tc.name,
   669  				Meta:       tc.meta,
   670  				Decimals:   tc.decimals,
   671  				Amount:     tc.amount,
   672  			}
   673  
   674  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   675  			if tc.err != nil {
   676  				return
   677  			}
   678  
   679  			require.Equal(t, []sdk.AccAddress{tc.owner}, msg.GetSigners())
   680  		})
   681  	}
   682  }
   683  
   684  func TestMsgIssueNFT(t *testing.T) {
   685  	addrs := make([]sdk.AccAddress, 1)
   686  	for i := range addrs {
   687  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   688  	}
   689  
   690  	contractID := "deadbeef"
   691  	name := "tibetian fox"
   692  	meta := "Tibetian Fox"
   693  	testCases := map[string]struct {
   694  		contractID string
   695  		operator   sdk.AccAddress
   696  		name       string
   697  		meta       string
   698  		err        error
   699  	}{
   700  		"valid msg": {
   701  			contractID: contractID,
   702  			operator:   addrs[0],
   703  			name:       name,
   704  			meta:       meta,
   705  		},
   706  		"invalid contract id": {
   707  			operator: addrs[0],
   708  			name:     name,
   709  			meta:     meta,
   710  			err:      class.ErrInvalidContractID,
   711  		},
   712  		"invalid operator": {
   713  			contractID: contractID,
   714  			name:       name,
   715  			meta:       meta,
   716  			err:        sdkerrors.ErrInvalidAddress,
   717  		},
   718  		"long name": {
   719  			contractID: contractID,
   720  			operator:   addrs[0],
   721  			name:       string(make([]rune, 21)),
   722  			meta:       meta,
   723  			err:        collection.ErrInvalidNameLength,
   724  		},
   725  		"invalid meta": {
   726  			contractID: contractID,
   727  			operator:   addrs[0],
   728  			name:       name,
   729  			meta:       string(make([]rune, 1001)),
   730  			err:        collection.ErrInvalidMetaLength,
   731  		},
   732  	}
   733  
   734  	for name, tc := range testCases {
   735  		t.Run(name, func(t *testing.T) {
   736  			msg := collection.MsgIssueNFT{
   737  				ContractId: tc.contractID,
   738  				Owner:      tc.operator.String(),
   739  				Name:       tc.name,
   740  				Meta:       tc.meta,
   741  			}
   742  
   743  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   744  			if tc.err != nil {
   745  				return
   746  			}
   747  
   748  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   749  		})
   750  	}
   751  }
   752  
   753  func TestMsgMintFT(t *testing.T) {
   754  	addrs := make([]sdk.AccAddress, 2)
   755  	for i := range addrs {
   756  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   757  	}
   758  
   759  	amount := collection.NewCoins(
   760  		collection.NewFTCoin("00bab10c", sdk.OneInt()),
   761  	)
   762  
   763  	contractID := "deadbeef"
   764  	testCases := map[string]struct {
   765  		contractID string
   766  		operator   sdk.AccAddress
   767  		to         sdk.AccAddress
   768  		amount     []collection.Coin
   769  		err        error
   770  	}{
   771  		"valid msg": {
   772  			contractID: contractID,
   773  			operator:   addrs[0],
   774  			to:         addrs[1],
   775  			amount:     amount,
   776  		},
   777  		// for daphne compatible
   778  		"valid msg - zero amount": {
   779  			contractID: contractID,
   780  			operator:   addrs[0],
   781  			to:         addrs[1],
   782  		},
   783  		"invalid contract id": {
   784  			operator: addrs[0],
   785  			to:       addrs[1],
   786  			amount:   amount,
   787  			err:      class.ErrInvalidContractID,
   788  		},
   789  		"invalid operator": {
   790  			contractID: contractID,
   791  			to:         addrs[1],
   792  			amount:     amount,
   793  			err:        sdkerrors.ErrInvalidAddress,
   794  		},
   795  		"invalid to": {
   796  			contractID: contractID,
   797  			operator:   addrs[0],
   798  			amount:     amount,
   799  			err:        sdkerrors.ErrInvalidAddress,
   800  		},
   801  		"invalid amount": {
   802  			contractID: contractID,
   803  			operator:   addrs[0],
   804  			to:         addrs[1],
   805  			amount: []collection.Coin{{
   806  				TokenId: collection.NewFTID("00bab10c"),
   807  				Amount:  sdk.ZeroInt(),
   808  			}},
   809  			err: collection.ErrInvalidAmount,
   810  		},
   811  		"invalid token id": {
   812  			contractID: contractID,
   813  			operator:   addrs[0],
   814  			to:         addrs[1],
   815  			amount: []collection.Coin{{
   816  				Amount: sdk.OneInt(),
   817  			}},
   818  			err: collection.ErrInvalidTokenID,
   819  		},
   820  	}
   821  
   822  	for name, tc := range testCases {
   823  		t.Run(name, func(t *testing.T) {
   824  			msg := collection.MsgMintFT{
   825  				ContractId: tc.contractID,
   826  				From:       tc.operator.String(),
   827  				To:         tc.to.String(),
   828  				Amount:     tc.amount,
   829  			}
   830  
   831  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   832  			if tc.err != nil {
   833  				return
   834  			}
   835  
   836  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   837  		})
   838  	}
   839  }
   840  
   841  func TestMsgMintNFT(t *testing.T) {
   842  	addrs := make([]sdk.AccAddress, 2)
   843  	for i := range addrs {
   844  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   845  	}
   846  
   847  	params := []collection.MintNFTParam{{
   848  		TokenType: "deadbeef",
   849  		Name:      "tibetian fox",
   850  		Meta:      "Tibetian Fox",
   851  	}}
   852  	testCases := map[string]struct {
   853  		contractID string
   854  		operator   sdk.AccAddress
   855  		to         sdk.AccAddress
   856  		params     []collection.MintNFTParam
   857  		err        error
   858  	}{
   859  		"valid msg": {
   860  			contractID: "deadbeef",
   861  			operator:   addrs[0],
   862  			to:         addrs[1],
   863  			params:     params,
   864  		},
   865  		"invalid contract id": {
   866  			operator: addrs[0],
   867  			to:       addrs[1],
   868  			params:   params,
   869  			err:      class.ErrInvalidContractID,
   870  		},
   871  		"invalid operator": {
   872  			contractID: "deadbeef",
   873  			to:         addrs[1],
   874  			params:     params,
   875  			err:        sdkerrors.ErrInvalidAddress,
   876  		},
   877  		"invalid to": {
   878  			contractID: "deadbeef",
   879  			operator:   addrs[0],
   880  			params:     params,
   881  			err:        sdkerrors.ErrInvalidAddress,
   882  		},
   883  		"empty params": {
   884  			contractID: "deadbeef",
   885  			operator:   addrs[0],
   886  			to:         addrs[1],
   887  			err:        collection.ErrEmptyField,
   888  		},
   889  		"param of invalid token type": {
   890  			contractID: "deadbeef",
   891  			operator:   addrs[0],
   892  			to:         addrs[1],
   893  			params: []collection.MintNFTParam{{
   894  				Name: "tibetian fox",
   895  			}},
   896  			err: collection.ErrInvalidTokenType,
   897  		},
   898  		"param of empty name": {
   899  			contractID: "deadbeef",
   900  			operator:   addrs[0],
   901  			to:         addrs[1],
   902  			params: []collection.MintNFTParam{{
   903  				TokenType: "deadbeef",
   904  			}},
   905  			err: collection.ErrInvalidTokenName,
   906  		},
   907  		"param of too long name": {
   908  			contractID: "deadbeef",
   909  			operator:   addrs[0],
   910  			to:         addrs[1],
   911  			params: []collection.MintNFTParam{{
   912  				TokenType: "deadbeef",
   913  				Name:      string(make([]rune, 21)),
   914  			}},
   915  			err: collection.ErrInvalidNameLength,
   916  		},
   917  		"param of invalid meta": {
   918  			contractID: "deadbeef",
   919  			operator:   addrs[0],
   920  			to:         addrs[1],
   921  			params: []collection.MintNFTParam{{
   922  				TokenType: "deadbeef",
   923  				Name:      "tibetian fox",
   924  				Meta:      string(make([]rune, 1001)),
   925  			}},
   926  			err: collection.ErrInvalidMetaLength,
   927  		},
   928  	}
   929  
   930  	for name, tc := range testCases {
   931  		t.Run(name, func(t *testing.T) {
   932  			msg := collection.MsgMintNFT{
   933  				ContractId: tc.contractID,
   934  				From:       tc.operator.String(),
   935  				To:         tc.to.String(),
   936  				Params:     tc.params,
   937  			}
   938  
   939  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
   940  			if tc.err != nil {
   941  				return
   942  			}
   943  
   944  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   945  		})
   946  	}
   947  }
   948  
   949  func TestMsgBurnFT(t *testing.T) {
   950  	addrs := make([]sdk.AccAddress, 1)
   951  	for i := range addrs {
   952  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   953  	}
   954  
   955  	amount := collection.NewCoins(
   956  		collection.NewFTCoin("00bab10c", sdk.OneInt()),
   957  	)
   958  
   959  	testCases := map[string]struct {
   960  		contractID string
   961  		from       sdk.AccAddress
   962  		amount     []collection.Coin
   963  		err        error
   964  	}{
   965  		"valid msg": {
   966  			contractID: "deadbeef",
   967  			from:       addrs[0],
   968  			amount:     amount,
   969  		},
   970  		// for daphne compatible
   971  		"valid msg - zero amount": {
   972  			contractID: "deadbeef",
   973  			from:       addrs[0],
   974  		},
   975  		"invalid contract id": {
   976  			from:   addrs[0],
   977  			amount: amount,
   978  			err:    class.ErrInvalidContractID,
   979  		},
   980  		"invalid from": {
   981  			contractID: "deadbeef",
   982  			amount:     amount,
   983  			err:        sdkerrors.ErrInvalidAddress,
   984  		},
   985  		"invalid token id": {
   986  			contractID: "deadbeef",
   987  			from:       addrs[0],
   988  			amount: []collection.Coin{{
   989  				Amount: sdk.OneInt(),
   990  			}},
   991  			err: collection.ErrInvalidTokenID,
   992  		},
   993  		"invalid amount": {
   994  			contractID: "deadbeef",
   995  			from:       addrs[0],
   996  			amount: []collection.Coin{{
   997  				TokenId: collection.NewFTID("00bab10c"),
   998  				Amount:  sdk.ZeroInt(),
   999  			}},
  1000  			err: collection.ErrInvalidAmount,
  1001  		},
  1002  	}
  1003  
  1004  	for name, tc := range testCases {
  1005  		t.Run(name, func(t *testing.T) {
  1006  			msg := collection.MsgBurnFT{
  1007  				ContractId: tc.contractID,
  1008  				From:       tc.from.String(),
  1009  				Amount:     tc.amount,
  1010  			}
  1011  
  1012  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1013  			if tc.err != nil {
  1014  				return
  1015  			}
  1016  
  1017  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1018  		})
  1019  	}
  1020  }
  1021  
  1022  func TestMsgOperatorBurnFT(t *testing.T) {
  1023  	addrs := make([]sdk.AccAddress, 2)
  1024  	for i := range addrs {
  1025  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1026  	}
  1027  
  1028  	amount := collection.NewCoins(
  1029  		collection.NewFTCoin("00bab10c", sdk.OneInt()),
  1030  	)
  1031  
  1032  	testCases := map[string]struct {
  1033  		contractID string
  1034  		grantee    sdk.AccAddress
  1035  		from       sdk.AccAddress
  1036  		amount     []collection.Coin
  1037  		err        error
  1038  	}{
  1039  		"valid msg": {
  1040  			contractID: "deadbeef",
  1041  			grantee:    addrs[0],
  1042  			from:       addrs[1],
  1043  			amount:     amount,
  1044  		},
  1045  		"invalid contract id": {
  1046  			grantee: addrs[0],
  1047  			from:    addrs[1],
  1048  			amount:  amount,
  1049  			err:     class.ErrInvalidContractID,
  1050  		},
  1051  		"invalid grantee": {
  1052  			contractID: "deadbeef",
  1053  			from:       addrs[1],
  1054  			amount:     amount,
  1055  			err:        sdkerrors.ErrInvalidAddress,
  1056  		},
  1057  		"invalid from": {
  1058  			contractID: "deadbeef",
  1059  			grantee:    addrs[0],
  1060  			amount:     amount,
  1061  			err:        sdkerrors.ErrInvalidAddress,
  1062  		},
  1063  		"invalid token id": {
  1064  			contractID: "deadbeef",
  1065  			grantee:    addrs[0],
  1066  			from:       addrs[1],
  1067  			amount: []collection.Coin{{
  1068  				Amount: sdk.OneInt(),
  1069  			}},
  1070  			err: collection.ErrInvalidTokenID,
  1071  		},
  1072  		"invalid amount": {
  1073  			contractID: "deadbeef",
  1074  			grantee:    addrs[0],
  1075  			from:       addrs[1],
  1076  			amount: []collection.Coin{{
  1077  				TokenId: collection.NewFTID("00bab10c"),
  1078  				Amount:  sdk.ZeroInt(),
  1079  			}},
  1080  			err: collection.ErrInvalidAmount,
  1081  		},
  1082  	}
  1083  
  1084  	for name, tc := range testCases {
  1085  		t.Run(name, func(t *testing.T) {
  1086  			msg := collection.MsgOperatorBurnFT{
  1087  				ContractId: tc.contractID,
  1088  				Operator:   tc.grantee.String(),
  1089  				From:       tc.from.String(),
  1090  				Amount:     tc.amount,
  1091  			}
  1092  
  1093  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1094  			if tc.err != nil {
  1095  				return
  1096  			}
  1097  
  1098  			require.Equal(t, []sdk.AccAddress{tc.grantee}, msg.GetSigners())
  1099  		})
  1100  	}
  1101  }
  1102  
  1103  func TestMsgBurnNFT(t *testing.T) {
  1104  	addrs := make([]sdk.AccAddress, 1)
  1105  	for i := range addrs {
  1106  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1107  	}
  1108  
  1109  	ids := []string{collection.NewNFTID("deadbeef", 1)}
  1110  
  1111  	testCases := map[string]struct {
  1112  		contractID string
  1113  		from       sdk.AccAddress
  1114  		ids        []string
  1115  		err        error
  1116  	}{
  1117  		"valid msg": {
  1118  			contractID: "deadbeef",
  1119  			from:       addrs[0],
  1120  			ids:        ids,
  1121  		},
  1122  		"invalid contract id": {
  1123  			from: addrs[0],
  1124  			ids:  ids,
  1125  			err:  class.ErrInvalidContractID,
  1126  		},
  1127  		"invalid from": {
  1128  			contractID: "deadbeef",
  1129  			ids:        ids,
  1130  			err:        sdkerrors.ErrInvalidAddress,
  1131  		},
  1132  		"empty ids": {
  1133  			contractID: "deadbeef",
  1134  			from:       addrs[0],
  1135  			err:        collection.ErrEmptyField,
  1136  		},
  1137  		"invalid id": {
  1138  			contractID: "deadbeef",
  1139  			from:       addrs[0],
  1140  			ids:        []string{""},
  1141  			err:        collection.ErrInvalidTokenID,
  1142  		},
  1143  	}
  1144  
  1145  	for name, tc := range testCases {
  1146  		t.Run(name, func(t *testing.T) {
  1147  			msg := collection.MsgBurnNFT{
  1148  				ContractId: tc.contractID,
  1149  				From:       tc.from.String(),
  1150  				TokenIds:   tc.ids,
  1151  			}
  1152  
  1153  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1154  			if tc.err != nil {
  1155  				return
  1156  			}
  1157  
  1158  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1159  		})
  1160  	}
  1161  }
  1162  
  1163  func TestMsgOperatorBurnNFT(t *testing.T) {
  1164  	addrs := make([]sdk.AccAddress, 2)
  1165  	for i := range addrs {
  1166  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1167  	}
  1168  
  1169  	ids := []string{collection.NewNFTID("deadbeef", 1)}
  1170  
  1171  	testCases := map[string]struct {
  1172  		contractID string
  1173  		grantee    sdk.AccAddress
  1174  		from       sdk.AccAddress
  1175  		ids        []string
  1176  		err        error
  1177  	}{
  1178  		"valid msg": {
  1179  			contractID: "deadbeef",
  1180  			grantee:    addrs[0],
  1181  			from:       addrs[1],
  1182  			ids:        ids,
  1183  		},
  1184  		"invalid contract id": {
  1185  			grantee: addrs[0],
  1186  			from:    addrs[1],
  1187  			ids:     ids,
  1188  			err:     class.ErrInvalidContractID,
  1189  		},
  1190  		"invalid grantee": {
  1191  			contractID: "deadbeef",
  1192  			from:       addrs[1],
  1193  			ids:        ids,
  1194  			err:        sdkerrors.ErrInvalidAddress,
  1195  		},
  1196  		"invalid from": {
  1197  			contractID: "deadbeef",
  1198  			grantee:    addrs[0],
  1199  			ids:        ids,
  1200  			err:        sdkerrors.ErrInvalidAddress,
  1201  		},
  1202  		"empty ids": {
  1203  			contractID: "deadbeef",
  1204  			grantee:    addrs[0],
  1205  			from:       addrs[1],
  1206  			err:        collection.ErrEmptyField,
  1207  		},
  1208  		"invalid id": {
  1209  			contractID: "deadbeef",
  1210  			grantee:    addrs[0],
  1211  			from:       addrs[0],
  1212  			ids:        []string{""},
  1213  			err:        collection.ErrInvalidTokenID,
  1214  		},
  1215  	}
  1216  
  1217  	for name, tc := range testCases {
  1218  		t.Run(name, func(t *testing.T) {
  1219  			msg := collection.MsgOperatorBurnNFT{
  1220  				ContractId: tc.contractID,
  1221  				Operator:   tc.grantee.String(),
  1222  				From:       tc.from.String(),
  1223  				TokenIds:   tc.ids,
  1224  			}
  1225  
  1226  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1227  			if tc.err != nil {
  1228  				return
  1229  			}
  1230  
  1231  			require.Equal(t, []sdk.AccAddress{tc.grantee}, msg.GetSigners())
  1232  		})
  1233  	}
  1234  }
  1235  
  1236  func TestMsgModify(t *testing.T) {
  1237  	addrs := make([]sdk.AccAddress, 1)
  1238  	for i := range addrs {
  1239  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1240  	}
  1241  
  1242  	changes := []collection.Attribute{{Key: collection.AttributeKeyName.String(), Value: "New test"}}
  1243  	testCases := map[string]struct {
  1244  		contractID string
  1245  		owner      sdk.AccAddress
  1246  		tokenType  string
  1247  		tokenIndex string
  1248  		changes    []collection.Attribute
  1249  		err        error
  1250  	}{
  1251  		"valid contract modification": {
  1252  			contractID: "deadbeef",
  1253  			owner:      addrs[0],
  1254  			changes:    changes,
  1255  		},
  1256  		"valid token class modification": {
  1257  			contractID: "deadbeef",
  1258  			tokenType:  "deadbeef",
  1259  			owner:      addrs[0],
  1260  			changes:    changes,
  1261  		},
  1262  		"invalid nft class modification": {
  1263  			contractID: "deadbeef",
  1264  			tokenType:  "deadbeef",
  1265  			tokenIndex: "00000000",
  1266  			owner:      addrs[0],
  1267  			changes:    changes,
  1268  			err:        collection.ErrInvalidTokenIndex,
  1269  		},
  1270  		"valid nft modification": {
  1271  			contractID: "deadbeef",
  1272  			tokenType:  "deadbeef",
  1273  			tokenIndex: "deadbeef",
  1274  			owner:      addrs[0],
  1275  			changes:    changes,
  1276  		},
  1277  		"invalid contract id": {
  1278  			owner:   addrs[0],
  1279  			changes: changes,
  1280  			err:     class.ErrInvalidContractID,
  1281  		},
  1282  		"invalid owner": {
  1283  			contractID: "deadbeef",
  1284  			changes:    changes,
  1285  			err:        sdkerrors.ErrInvalidAddress,
  1286  		},
  1287  		"invalid key of change": {
  1288  			contractID: "deadbeef",
  1289  			owner:      addrs[0],
  1290  			changes:    []collection.Attribute{{Key: strings.ToUpper(collection.AttributeKeyName.String()), Value: "tt"}},
  1291  			err:        collection.ErrInvalidChangesField,
  1292  		},
  1293  		"invalid value of change": {
  1294  			contractID: "deadbeef",
  1295  			owner:      addrs[0],
  1296  			changes:    []collection.Attribute{{Key: collection.AttributeKeyName.String(), Value: string(make([]rune, 21))}},
  1297  			err:        collection.ErrInvalidNameLength,
  1298  		},
  1299  		"empty changes": {
  1300  			contractID: "deadbeef",
  1301  			owner:      addrs[0],
  1302  			err:        collection.ErrEmptyChanges,
  1303  		},
  1304  		"too many changes": {
  1305  			contractID: "deadbeef",
  1306  			owner:      addrs[0],
  1307  			changes:    make([]collection.Attribute, 101),
  1308  			err:        collection.ErrInvalidChangesFieldCount,
  1309  		},
  1310  		"duplicated changes": {
  1311  			contractID: "deadbeef",
  1312  			owner:      addrs[0],
  1313  			changes: []collection.Attribute{
  1314  				{Key: collection.AttributeKeyBaseImgURI.String(), Value: "hello"},
  1315  				{Key: collection.AttributeKeyURI.String(), Value: "world"},
  1316  			},
  1317  			err: collection.ErrDuplicateChangesField,
  1318  		},
  1319  	}
  1320  
  1321  	for name, tc := range testCases {
  1322  		t.Run(name, func(t *testing.T) {
  1323  			msg := collection.MsgModify{
  1324  				ContractId: tc.contractID,
  1325  				TokenType:  tc.tokenType,
  1326  				TokenIndex: tc.tokenIndex,
  1327  				Owner:      tc.owner.String(),
  1328  				Changes:    tc.changes,
  1329  			}
  1330  
  1331  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1332  			if tc.err != nil {
  1333  				return
  1334  			}
  1335  
  1336  			require.Equal(t, []sdk.AccAddress{tc.owner}, msg.GetSigners())
  1337  		})
  1338  	}
  1339  }
  1340  
  1341  func TestMsgGrantPermission(t *testing.T) {
  1342  	addrs := make([]sdk.AccAddress, 2)
  1343  	for i := range addrs {
  1344  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1345  	}
  1346  
  1347  	testCases := map[string]struct {
  1348  		contractID string
  1349  		from       sdk.AccAddress
  1350  		to         sdk.AccAddress
  1351  		permission string
  1352  		err        error
  1353  	}{
  1354  		"valid msg": {
  1355  			contractID: "deadbeef",
  1356  			from:       addrs[0],
  1357  			to:         addrs[1],
  1358  			permission: collection.LegacyPermissionMint.String(),
  1359  		},
  1360  		"invalid contract id": {
  1361  			from:       addrs[0],
  1362  			to:         addrs[1],
  1363  			permission: collection.LegacyPermissionMint.String(),
  1364  			err:        class.ErrInvalidContractID,
  1365  		},
  1366  		"invalid from": {
  1367  			contractID: "deadbeef",
  1368  			to:         addrs[1],
  1369  			permission: collection.LegacyPermissionMint.String(),
  1370  			err:        sdkerrors.ErrInvalidAddress,
  1371  		},
  1372  		"invalid to": {
  1373  			contractID: "deadbeef",
  1374  			from:       addrs[0],
  1375  			permission: collection.LegacyPermissionMint.String(),
  1376  			err:        sdkerrors.ErrInvalidAddress,
  1377  		},
  1378  		"invalid permission": {
  1379  			contractID: "deadbeef",
  1380  			from:       addrs[0],
  1381  			to:         addrs[1],
  1382  			err:        sdkerrors.ErrInvalidPermission,
  1383  		},
  1384  	}
  1385  
  1386  	for name, tc := range testCases {
  1387  		t.Run(name, func(t *testing.T) {
  1388  			msg := collection.MsgGrantPermission{
  1389  				ContractId: tc.contractID,
  1390  				From:       tc.from.String(),
  1391  				To:         tc.to.String(),
  1392  				Permission: tc.permission,
  1393  			}
  1394  
  1395  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1396  			if tc.err != nil {
  1397  				return
  1398  			}
  1399  
  1400  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1401  		})
  1402  	}
  1403  }
  1404  
  1405  func TestMsgRevokePermission(t *testing.T) {
  1406  	addrs := make([]sdk.AccAddress, 1)
  1407  	for i := range addrs {
  1408  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1409  	}
  1410  
  1411  	testCases := map[string]struct {
  1412  		contractID string
  1413  		from       sdk.AccAddress
  1414  		permission string
  1415  		err        error
  1416  	}{
  1417  		"valid msg": {
  1418  			contractID: "deadbeef",
  1419  			from:       addrs[0],
  1420  			permission: collection.LegacyPermissionMint.String(),
  1421  		},
  1422  		"invalid contract id": {
  1423  			from:       addrs[0],
  1424  			permission: collection.LegacyPermissionMint.String(),
  1425  			err:        class.ErrInvalidContractID,
  1426  		},
  1427  		"invalid from": {
  1428  			contractID: "deadbeef",
  1429  			permission: collection.LegacyPermissionMint.String(),
  1430  			err:        sdkerrors.ErrInvalidAddress,
  1431  		},
  1432  		"invalid permission": {
  1433  			contractID: "deadbeef",
  1434  			from:       addrs[0],
  1435  			err:        sdkerrors.ErrInvalidPermission,
  1436  		},
  1437  	}
  1438  
  1439  	for name, tc := range testCases {
  1440  		t.Run(name, func(t *testing.T) {
  1441  			msg := collection.MsgRevokePermission{
  1442  				ContractId: tc.contractID,
  1443  				From:       tc.from.String(),
  1444  				Permission: tc.permission,
  1445  			}
  1446  
  1447  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1448  			if tc.err != nil {
  1449  				return
  1450  			}
  1451  
  1452  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1453  		})
  1454  	}
  1455  }
  1456  
  1457  func TestMsgAttach(t *testing.T) {
  1458  	addrs := make([]sdk.AccAddress, 1)
  1459  	for i := range addrs {
  1460  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1461  	}
  1462  
  1463  	contractID := "deadbeef"
  1464  	tokenIDs := []string{
  1465  		collection.NewNFTID("deadbeef", 1),
  1466  		collection.NewNFTID("fee1dead", 1),
  1467  	}
  1468  
  1469  	testCases := map[string]struct {
  1470  		contractID string
  1471  		from       sdk.AccAddress
  1472  		tokenID    string
  1473  		toTokenID  string
  1474  		err        error
  1475  	}{
  1476  		"valid msg": {
  1477  			contractID: contractID,
  1478  			from:       addrs[0],
  1479  			tokenID:    tokenIDs[0],
  1480  			toTokenID:  tokenIDs[1],
  1481  		},
  1482  		"invalid from": {
  1483  			contractID: contractID,
  1484  			tokenID:    tokenIDs[0],
  1485  			toTokenID:  tokenIDs[1],
  1486  			err:        sdkerrors.ErrInvalidAddress,
  1487  		},
  1488  		"invalid contract id": {
  1489  			from:      addrs[0],
  1490  			tokenID:   tokenIDs[0],
  1491  			toTokenID: tokenIDs[1],
  1492  			err:       class.ErrInvalidContractID,
  1493  		},
  1494  		"invalid token id": {
  1495  			contractID: contractID,
  1496  			from:       addrs[0],
  1497  			toTokenID:  tokenIDs[1],
  1498  			err:        collection.ErrInvalidTokenID,
  1499  		},
  1500  		"invalid to id": {
  1501  			contractID: contractID,
  1502  			from:       addrs[0],
  1503  			tokenID:    tokenIDs[0],
  1504  			err:        collection.ErrInvalidTokenID,
  1505  		},
  1506  		"to itself": {
  1507  			contractID: contractID,
  1508  			from:       addrs[0],
  1509  			tokenID:    tokenIDs[0],
  1510  			toTokenID:  tokenIDs[0],
  1511  			err:        collection.ErrCannotAttachToItself,
  1512  		},
  1513  	}
  1514  
  1515  	for name, tc := range testCases {
  1516  		t.Run(name, func(t *testing.T) {
  1517  			msg := collection.MsgAttach{
  1518  				ContractId: tc.contractID,
  1519  				From:       tc.from.String(),
  1520  				TokenId:    tc.tokenID,
  1521  				ToTokenId:  tc.toTokenID,
  1522  			}
  1523  
  1524  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1525  			if tc.err != nil {
  1526  				return
  1527  			}
  1528  
  1529  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1530  		})
  1531  	}
  1532  }
  1533  
  1534  func TestMsgDetach(t *testing.T) {
  1535  	addrs := make([]sdk.AccAddress, 1)
  1536  	for i := range addrs {
  1537  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1538  	}
  1539  
  1540  	contractID := "deadbeef"
  1541  	tokenID := collection.NewNFTID("deadbeef", 1)
  1542  
  1543  	testCases := map[string]struct {
  1544  		contractID string
  1545  		from       sdk.AccAddress
  1546  		tokenID    string
  1547  		err        error
  1548  	}{
  1549  		"valid msg": {
  1550  			contractID: contractID,
  1551  			from:       addrs[0],
  1552  			tokenID:    tokenID,
  1553  		},
  1554  		"invalid from": {
  1555  			contractID: contractID,
  1556  			tokenID:    tokenID,
  1557  			err:        sdkerrors.ErrInvalidAddress,
  1558  		},
  1559  		"invalid contract id": {
  1560  			from:    addrs[0],
  1561  			tokenID: tokenID,
  1562  			err:     class.ErrInvalidContractID,
  1563  		},
  1564  		"invalid token id": {
  1565  			contractID: contractID,
  1566  			from:       addrs[0],
  1567  			err:        collection.ErrInvalidTokenID,
  1568  		},
  1569  	}
  1570  
  1571  	for name, tc := range testCases {
  1572  		t.Run(name, func(t *testing.T) {
  1573  			msg := collection.MsgDetach{
  1574  				ContractId: tc.contractID,
  1575  				From:       tc.from.String(),
  1576  				TokenId:    tc.tokenID,
  1577  			}
  1578  
  1579  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1580  			if tc.err != nil {
  1581  				return
  1582  			}
  1583  
  1584  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
  1585  		})
  1586  	}
  1587  }
  1588  
  1589  func TestMsgOperatorAttach(t *testing.T) {
  1590  	addrs := make([]sdk.AccAddress, 2)
  1591  	for i := range addrs {
  1592  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1593  	}
  1594  
  1595  	tokenIDs := []string{
  1596  		collection.NewNFTID("deadbeef", 1),
  1597  		collection.NewNFTID("fee1dead", 1),
  1598  	}
  1599  
  1600  	testCases := map[string]struct {
  1601  		contractID string
  1602  		operator   sdk.AccAddress
  1603  		from       sdk.AccAddress
  1604  		tokenID    string
  1605  		toTokenID  string
  1606  		err        error
  1607  	}{
  1608  		"valid msg": {
  1609  			contractID: "deadbeef",
  1610  			operator:   addrs[0],
  1611  			from:       addrs[1],
  1612  			tokenID:    tokenIDs[0],
  1613  			toTokenID:  tokenIDs[1],
  1614  		},
  1615  		"empty operator": {
  1616  			contractID: "deadbeef",
  1617  			from:       addrs[1],
  1618  			tokenID:    tokenIDs[0],
  1619  			toTokenID:  tokenIDs[1],
  1620  			err:        sdkerrors.ErrInvalidAddress,
  1621  		},
  1622  		"invalid from": {
  1623  			contractID: "deadbeef",
  1624  			operator:   addrs[0],
  1625  			tokenID:    tokenIDs[0],
  1626  			toTokenID:  tokenIDs[1],
  1627  			err:        sdkerrors.ErrInvalidAddress,
  1628  		},
  1629  		"invalid contract id": {
  1630  			operator:  addrs[0],
  1631  			from:      addrs[1],
  1632  			tokenID:   tokenIDs[0],
  1633  			toTokenID: tokenIDs[1],
  1634  			err:       class.ErrInvalidContractID,
  1635  		},
  1636  		"invalid token id": {
  1637  			contractID: "deadbeef",
  1638  			operator:   addrs[0],
  1639  			from:       addrs[1],
  1640  			toTokenID:  tokenIDs[1],
  1641  			err:        collection.ErrInvalidTokenID,
  1642  		},
  1643  		"invalid to id": {
  1644  			contractID: "deadbeef",
  1645  			operator:   addrs[0],
  1646  			from:       addrs[1],
  1647  			tokenID:    tokenIDs[0],
  1648  			err:        collection.ErrInvalidTokenID,
  1649  		},
  1650  		"to itself": {
  1651  			contractID: "deadbeef",
  1652  			operator:   addrs[0],
  1653  			from:       addrs[1],
  1654  			tokenID:    tokenIDs[0],
  1655  			toTokenID:  tokenIDs[0],
  1656  			err:        collection.ErrCannotAttachToItself,
  1657  		},
  1658  	}
  1659  
  1660  	for name, tc := range testCases {
  1661  		t.Run(name, func(t *testing.T) {
  1662  			msg := collection.MsgOperatorAttach{
  1663  				ContractId: tc.contractID,
  1664  				Operator:   tc.operator.String(),
  1665  				From:       tc.from.String(),
  1666  				TokenId:    tc.tokenID,
  1667  				ToTokenId:  tc.toTokenID,
  1668  			}
  1669  
  1670  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1671  			if tc.err != nil {
  1672  				return
  1673  			}
  1674  
  1675  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
  1676  		})
  1677  	}
  1678  }
  1679  
  1680  func TestMsgOperatorDetach(t *testing.T) {
  1681  	addrs := make([]sdk.AccAddress, 2)
  1682  	for i := range addrs {
  1683  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1684  	}
  1685  
  1686  	tokenID := collection.NewNFTID("deadbeef", 1)
  1687  
  1688  	testCases := map[string]struct {
  1689  		contractID string
  1690  		operator   sdk.AccAddress
  1691  		from       sdk.AccAddress
  1692  		tokenID    string
  1693  		err        error
  1694  	}{
  1695  		"valid msg": {
  1696  			contractID: "deadbeef",
  1697  			operator:   addrs[0],
  1698  			from:       addrs[1],
  1699  			tokenID:    tokenID,
  1700  		},
  1701  		"empty operator": {
  1702  			contractID: "deadbeef",
  1703  			from:       addrs[1],
  1704  			tokenID:    tokenID,
  1705  			err:        sdkerrors.ErrInvalidAddress,
  1706  		},
  1707  		"invalid from": {
  1708  			contractID: "deadbeef",
  1709  			operator:   addrs[0],
  1710  			tokenID:    tokenID,
  1711  			err:        sdkerrors.ErrInvalidAddress,
  1712  		},
  1713  		"invalid contract id": {
  1714  			operator: addrs[0],
  1715  			from:     addrs[1],
  1716  			tokenID:  tokenID,
  1717  			err:      class.ErrInvalidContractID,
  1718  		},
  1719  		"invalid token id": {
  1720  			contractID: "deadbeef",
  1721  			operator:   addrs[0],
  1722  			from:       addrs[1],
  1723  			err:        collection.ErrInvalidTokenID,
  1724  		},
  1725  	}
  1726  
  1727  	for name, tc := range testCases {
  1728  		t.Run(name, func(t *testing.T) {
  1729  			msg := collection.MsgOperatorDetach{
  1730  				ContractId: tc.contractID,
  1731  				Operator:   tc.operator.String(),
  1732  				From:       tc.from.String(),
  1733  				TokenId:    tc.tokenID,
  1734  			}
  1735  
  1736  			require.ErrorIs(t, msg.ValidateBasic(), tc.err)
  1737  			if tc.err != nil {
  1738  				return
  1739  			}
  1740  
  1741  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
  1742  		})
  1743  	}
  1744  }
  1745  
  1746  func TestAminoJSON(t *testing.T) {
  1747  	tx := legacytx.StdTx{}
  1748  	var contractId = "deadbeef"
  1749  	var ftClassId = "00bab10c"
  1750  
  1751  	addrs := make([]sdk.AccAddress, 3)
  1752  	for i := range addrs {
  1753  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
  1754  	}
  1755  
  1756  	ftAmount := collection.NewCoins(collection.NewFTCoin(ftClassId, sdk.NewInt(1000000)))
  1757  	tokenIds := []string{collection.NewNFTID(contractId, 1)}
  1758  	nftParams := []collection.MintNFTParam{{
  1759  		TokenType: "deadbeef",
  1760  		Name:      "tibetian fox",
  1761  		Meta:      "Tibetian Fox",
  1762  	}}
  1763  
  1764  	testCase := map[string]struct {
  1765  		msg          legacytx.LegacyMsg
  1766  		expectedType string
  1767  		expected     string
  1768  	}{
  1769  		"MsgSendFT": {
  1770  			&collection.MsgSendFT{
  1771  				ContractId: contractId,
  1772  				From:       addrs[0].String(),
  1773  				To:         addrs[1].String(),
  1774  				Amount:     ftAmount,
  1775  			},
  1776  			"/lbm.collection.v1.MsgSendFT",
  1777  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgSendFT\",\"value\":{\"amount\":[{\"amount\":\"1000000\",\"token_id\":\"00bab10c00000000\"}],\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1778  		},
  1779  		"MsgOperatorSendFT": {
  1780  			&collection.MsgOperatorSendFT{
  1781  				ContractId: contractId,
  1782  				Operator:   addrs[0].String(),
  1783  				From:       addrs[1].String(),
  1784  				To:         addrs[2].String(),
  1785  				Amount:     ftAmount,
  1786  			},
  1787  			"/lbm.collection.v1.MsgOperatorSendFT",
  1788  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorSendFT\",\"value\":{\"amount\":[{\"amount\":\"1000000\",\"token_id\":\"00bab10c00000000\"}],\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String(), addrs[2].String()),
  1789  		},
  1790  		"MsgSendNFT": {
  1791  			&collection.MsgSendNFT{
  1792  				ContractId: contractId,
  1793  				From:       addrs[0].String(),
  1794  				To:         addrs[1].String(),
  1795  				TokenIds:   tokenIds,
  1796  			},
  1797  			"/lbm.collection.v1.MsgSendNFT",
  1798  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgSendNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to\":\"%s\",\"token_ids\":[\"deadbeef00000001\"]}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1799  		},
  1800  		"MsgOperatorSendNFT": {
  1801  			&collection.MsgOperatorSendNFT{
  1802  				ContractId: contractId,
  1803  				Operator:   addrs[0].String(),
  1804  				From:       addrs[1].String(),
  1805  				To:         addrs[2].String(),
  1806  				TokenIds:   tokenIds,
  1807  			},
  1808  			"/lbm.collection.v1.MsgOperatorSendNFT",
  1809  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorSendNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"to\":\"%s\",\"token_ids\":[\"deadbeef00000001\"]}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String(), addrs[2].String()),
  1810  		},
  1811  		"MsgAuthorizeOperator": {
  1812  			&collection.MsgAuthorizeOperator{
  1813  				ContractId: contractId,
  1814  				Holder:     addrs[0].String(),
  1815  				Operator:   addrs[1].String(),
  1816  			},
  1817  			"/lbm.collection.v1.MsgAuthorizeOperator",
  1818  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/collection/MsgAuthorizeOperator\",\"value\":{\"contract_id\":\"deadbeef\",\"holder\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1819  		},
  1820  		"MsgRevokeOperator": {
  1821  			&collection.MsgRevokeOperator{
  1822  				ContractId: contractId,
  1823  				Holder:     addrs[0].String(),
  1824  				Operator:   addrs[1].String(),
  1825  			},
  1826  			"/lbm.collection.v1.MsgRevokeOperator",
  1827  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/collection/MsgRevokeOperator\",\"value\":{\"contract_id\":\"deadbeef\",\"holder\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1828  		},
  1829  		"MsgCreateContract": {
  1830  			&collection.MsgCreateContract{
  1831  				Owner: addrs[0].String(),
  1832  				Name:  "Test Contract",
  1833  				Uri:   "http://image.url",
  1834  				Meta:  "This is test",
  1835  			},
  1836  			"/lbm.collection.v1.MsgCreateContract",
  1837  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgCreateContract\",\"value\":{\"meta\":\"This is test\",\"name\":\"Test Contract\",\"owner\":\"%s\",\"uri\":\"http://image.url\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1838  		},
  1839  		"MsgIssueFT": {
  1840  			&collection.MsgIssueFT{
  1841  				ContractId: contractId,
  1842  				Name:       "Test FT",
  1843  				Meta:       "This is FT Meta",
  1844  				Decimals:   6,
  1845  				Mintable:   false,
  1846  				Owner:      addrs[0].String(),
  1847  				To:         addrs[1].String(),
  1848  				Amount:     sdk.NewInt(1000000),
  1849  			},
  1850  			"/lbm.collection.v1.MsgIssueFT",
  1851  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgIssueFT\",\"value\":{\"amount\":\"1000000\",\"contract_id\":\"deadbeef\",\"decimals\":6,\"meta\":\"This is FT Meta\",\"name\":\"Test FT\",\"owner\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1852  		},
  1853  		"MsgIssueNFT": {
  1854  			&collection.MsgIssueNFT{
  1855  				ContractId: contractId,
  1856  				Name:       "Test NFT",
  1857  				Meta:       "This is NFT Meta",
  1858  				Owner:      addrs[0].String(),
  1859  			},
  1860  			"/lbm.collection.v1.MsgIssueNFT",
  1861  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgIssueNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"meta\":\"This is NFT Meta\",\"name\":\"Test NFT\",\"owner\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1862  		},
  1863  		"MsgMintFT": {
  1864  			&collection.MsgMintFT{
  1865  				ContractId: contractId,
  1866  				From:       addrs[0].String(),
  1867  				To:         addrs[1].String(),
  1868  				Amount:     ftAmount,
  1869  			},
  1870  			"/lbm.collection.v1.MsgMintFT",
  1871  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgMintFT\",\"value\":{\"amount\":[{\"amount\":\"1000000\",\"token_id\":\"00bab10c00000000\"}],\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1872  		},
  1873  		"MsgMintNFT": {
  1874  			&collection.MsgMintNFT{
  1875  				ContractId: contractId,
  1876  				From:       addrs[0].String(),
  1877  				To:         addrs[1].String(),
  1878  				Params:     nftParams,
  1879  			},
  1880  			"/lbm.collection.v1.MsgMintNFT",
  1881  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgMintNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"params\":[{\"meta\":\"Tibetian Fox\",\"name\":\"tibetian fox\",\"token_type\":\"deadbeef\"}],\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1882  		},
  1883  		"MsgBurnFT": {
  1884  			&collection.MsgBurnFT{
  1885  				ContractId: contractId,
  1886  				From:       addrs[0].String(),
  1887  				Amount:     ftAmount,
  1888  			},
  1889  			"/lbm.collection.v1.MsgBurnFT",
  1890  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgBurnFT\",\"value\":{\"amount\":[{\"amount\":\"1000000\",\"token_id\":\"00bab10c00000000\"}],\"contract_id\":\"deadbeef\",\"from\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1891  		},
  1892  		"MsgOperatorBurnFT": {
  1893  			&collection.MsgOperatorBurnFT{
  1894  				ContractId: contractId,
  1895  				Operator:   addrs[0].String(),
  1896  				From:       addrs[1].String(),
  1897  				Amount:     ftAmount,
  1898  			},
  1899  			"/lbm.collection.v1.MsgOperatorBurnFT",
  1900  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorBurnFT\",\"value\":{\"amount\":[{\"amount\":\"1000000\",\"token_id\":\"00bab10c00000000\"}],\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String()),
  1901  		},
  1902  		"MsgBurnNFT": {
  1903  			&collection.MsgBurnNFT{
  1904  				ContractId: contractId,
  1905  				From:       addrs[0].String(),
  1906  				TokenIds:   tokenIds,
  1907  			},
  1908  			"/lbm.collection.v1.MsgBurnNFT",
  1909  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgBurnNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"token_ids\":[\"deadbeef00000001\"]}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1910  		},
  1911  		"MsgOperatorBurnNFT": {
  1912  			&collection.MsgOperatorBurnNFT{
  1913  				ContractId: contractId,
  1914  				Operator:   addrs[0].String(),
  1915  				From:       addrs[1].String(),
  1916  				TokenIds:   tokenIds,
  1917  			},
  1918  			"/lbm.collection.v1.MsgOperatorBurnNFT",
  1919  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorBurnNFT\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"token_ids\":[\"deadbeef00000001\"]}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String()),
  1920  		},
  1921  		"MsgModify": {
  1922  			&collection.MsgModify{
  1923  				ContractId: contractId,
  1924  				Owner:      addrs[0].String(),
  1925  				TokenType:  "NewType",
  1926  				TokenIndex: "deadbeef",
  1927  				Changes:    []collection.Attribute{{Key: "name", Value: "New test"}},
  1928  			},
  1929  			"/lbm.collection.v1.MsgModify",
  1930  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/collection/MsgModify\",\"value\":{\"changes\":[{\"key\":\"name\",\"value\":\"New test\"}],\"contract_id\":\"deadbeef\",\"owner\":\"%s\",\"token_index\":\"deadbeef\",\"token_type\":\"NewType\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1931  		},
  1932  		"MsgGrantPermission": {
  1933  			&collection.MsgGrantPermission{
  1934  				ContractId: contractId,
  1935  				From:       addrs[0].String(),
  1936  				To:         addrs[1].String(),
  1937  				Permission: collection.LegacyPermissionMint.String(),
  1938  			},
  1939  			"/lbm.collection.v1.MsgGrantPermission",
  1940  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/collection/MsgGrantPermission\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"permission\":\"mint\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
  1941  		},
  1942  		"MsgRevokePermission": {
  1943  			&collection.MsgRevokePermission{
  1944  				ContractId: contractId,
  1945  				From:       addrs[0].String(),
  1946  				Permission: collection.LegacyPermissionMint.String(),
  1947  			},
  1948  			"/lbm.collection.v1.MsgRevokePermission",
  1949  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/collection/MsgRevokePermission\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"permission\":\"mint\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1950  		},
  1951  		"MsgAttach": {
  1952  			&collection.MsgAttach{
  1953  				ContractId: contractId,
  1954  				From:       addrs[0].String(),
  1955  				TokenId:    collection.NewNFTID("deadbeef", 1),
  1956  				ToTokenId:  collection.NewNFTID("fee1dead", 1),
  1957  			},
  1958  			"/lbm.collection.v1.MsgAttach",
  1959  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgAttach\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to_token_id\":\"fee1dead00000001\",\"token_id\":\"deadbeef00000001\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1960  		},
  1961  		"MsgDetach": {
  1962  			&collection.MsgDetach{
  1963  				ContractId: contractId,
  1964  				From:       addrs[0].String(),
  1965  				TokenId:    collection.NewNFTID("fee1dead", 1),
  1966  			},
  1967  			"/lbm.collection.v1.MsgDetach",
  1968  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgDetach\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"token_id\":\"fee1dead00000001\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1969  		},
  1970  		"MsgOperatorAttach": {
  1971  			&collection.MsgOperatorAttach{
  1972  				ContractId: contractId,
  1973  				Operator:   addrs[0].String(),
  1974  				From:       addrs[1].String(),
  1975  				TokenId:    collection.NewNFTID("deadbeef", 1),
  1976  				ToTokenId:  collection.NewNFTID("fee1dead", 1),
  1977  			},
  1978  			"/lbm.collection.v1.MsgOperatorAttach",
  1979  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorAttach\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"to_token_id\":\"fee1dead00000001\",\"token_id\":\"deadbeef00000001\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String()),
  1980  		},
  1981  		"MsgOperatorDetach": {
  1982  			&collection.MsgOperatorDetach{
  1983  				ContractId: contractId,
  1984  				Operator:   addrs[0].String(),
  1985  				From:       addrs[1].String(),
  1986  				TokenId:    collection.NewNFTID("fee1dead", 1),
  1987  			},
  1988  			"/lbm.collection.v1.MsgOperatorDetach",
  1989  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorDetach\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"token_id\":\"fee1dead00000001\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String()),
  1990  		},
  1991  	}
  1992  
  1993  	for name, tc := range testCase {
  1994  		tc := tc
  1995  		t.Run(name, func(t *testing.T) {
  1996  			tx.Msgs = []sdk.Msg{tc.msg}
  1997  			require.Equal(t, collection.RouterKey, tc.msg.Route())
  1998  			require.Equal(t, tc.expectedType, tc.msg.Type())
  1999  			require.Equal(t, tc.expected, string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{tc.msg}, "memo")))
  2000  		})
  2001  	}
  2002  }