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

     1  package token_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/token"
    15  	"github.com/Finschia/finschia-sdk/x/token/class"
    16  )
    17  
    18  func TestMsgSend(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  	testCases := map[string]struct {
    25  		contractID string
    26  		from       sdk.AccAddress
    27  		to         sdk.AccAddress
    28  		amount     sdk.Int
    29  		err        error
    30  	}{
    31  		"valid msg": {
    32  			contractID: "deadbeef",
    33  			from:       addrs[0],
    34  			to:         addrs[1],
    35  			amount:     sdk.OneInt(),
    36  		},
    37  		"invalid from": {
    38  			contractID: "deadbeef",
    39  			to:         addrs[1],
    40  			amount:     sdk.OneInt(),
    41  			err:        sdkerrors.ErrInvalidAddress,
    42  		},
    43  		"invalid contract id": {
    44  			from:   addrs[0],
    45  			to:     addrs[1],
    46  			amount: sdk.OneInt(),
    47  			err:    class.ErrInvalidContractID,
    48  		},
    49  		"invalid to": {
    50  			contractID: "deadbeef",
    51  			from:       addrs[0],
    52  			amount:     sdk.OneInt(),
    53  			err:        sdkerrors.ErrInvalidAddress,
    54  		},
    55  		"invalid amount": {
    56  			contractID: "deadbeef",
    57  			from:       addrs[0],
    58  			to:         addrs[1],
    59  			amount:     sdk.ZeroInt(),
    60  			err:        token.ErrInvalidAmount,
    61  		},
    62  	}
    63  
    64  	for name, tc := range testCases {
    65  		t.Run(name, func(t *testing.T) {
    66  			msg := token.MsgSend{
    67  				ContractId: tc.contractID,
    68  				From:       tc.from.String(),
    69  				To:         tc.to.String(),
    70  				Amount:     tc.amount,
    71  			}
    72  
    73  			err := msg.ValidateBasic()
    74  			require.ErrorIs(t, err, tc.err)
    75  			if tc.err != nil {
    76  				return
    77  			}
    78  
    79  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
    80  		})
    81  	}
    82  }
    83  
    84  func TestMsgOperatorSend(t *testing.T) {
    85  	addrs := make([]sdk.AccAddress, 3)
    86  	for i := range addrs {
    87  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
    88  	}
    89  
    90  	testCases := map[string]struct {
    91  		contractID string
    92  		operator   sdk.AccAddress
    93  		from       sdk.AccAddress
    94  		to         sdk.AccAddress
    95  		amount     sdk.Int
    96  		err        error
    97  	}{
    98  		"valid msg": {
    99  			contractID: "deadbeef",
   100  			operator:   addrs[0],
   101  			from:       addrs[1],
   102  			to:         addrs[2],
   103  			amount:     sdk.OneInt(),
   104  		},
   105  		"invalid operator": {
   106  			contractID: "deadbeef",
   107  			from:       addrs[1],
   108  			to:         addrs[2],
   109  			amount:     sdk.OneInt(),
   110  			err:        sdkerrors.ErrInvalidAddress,
   111  		},
   112  		"invalid contract id": {
   113  			operator: addrs[0],
   114  			from:     addrs[1],
   115  			to:       addrs[2],
   116  			amount:   sdk.OneInt(),
   117  			err:      class.ErrInvalidContractID,
   118  		},
   119  		"invalid from": {
   120  			contractID: "deadbeef",
   121  			operator:   addrs[0],
   122  			to:         addrs[1],
   123  			amount:     sdk.OneInt(),
   124  			err:        sdkerrors.ErrInvalidAddress,
   125  		},
   126  		"invalid to": {
   127  			contractID: "deadbeef",
   128  			operator:   addrs[0],
   129  			from:       addrs[1],
   130  			amount:     sdk.OneInt(),
   131  			err:        sdkerrors.ErrInvalidAddress,
   132  		},
   133  		"invalid amount": {
   134  			contractID: "deadbeef",
   135  			operator:   addrs[0],
   136  			from:       addrs[1],
   137  			to:         addrs[2],
   138  			amount:     sdk.ZeroInt(),
   139  			err:        token.ErrInvalidAmount,
   140  		},
   141  	}
   142  
   143  	for name, tc := range testCases {
   144  		t.Run(name, func(t *testing.T) {
   145  			msg := token.MsgOperatorSend{
   146  				ContractId: tc.contractID,
   147  				Operator:   tc.operator.String(),
   148  				From:       tc.from.String(),
   149  				To:         tc.to.String(),
   150  				Amount:     tc.amount,
   151  			}
   152  
   153  			err := msg.ValidateBasic()
   154  			require.ErrorIs(t, err, tc.err)
   155  			if tc.err != nil {
   156  				return
   157  			}
   158  
   159  			require.Equal(t, []sdk.AccAddress{tc.operator}, msg.GetSigners())
   160  		})
   161  	}
   162  }
   163  
   164  func TestMsgRevokeOperator(t *testing.T) {
   165  	addrs := make([]sdk.AccAddress, 2)
   166  	for i := range addrs {
   167  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   168  	}
   169  
   170  	testCases := map[string]struct {
   171  		contractID string
   172  		holder     sdk.AccAddress
   173  		operator   sdk.AccAddress
   174  		err        error
   175  	}{
   176  		"valid msg": {
   177  			contractID: "deadbeef",
   178  			holder:     addrs[0],
   179  			operator:   addrs[1],
   180  		},
   181  		"invalid contract id": {
   182  			holder:   addrs[0],
   183  			operator: addrs[1],
   184  			err:      class.ErrInvalidContractID,
   185  		},
   186  		"invalid holder": {
   187  			contractID: "deadbeef",
   188  			operator:   addrs[1],
   189  			err:        sdkerrors.ErrInvalidAddress,
   190  		},
   191  		"invalid operator": {
   192  			contractID: "deadbeef",
   193  			holder:     addrs[0],
   194  			err:        sdkerrors.ErrInvalidAddress,
   195  		},
   196  		"operator and holder should be different": {
   197  			contractID: "deadbeef",
   198  			holder:     addrs[0],
   199  			operator:   addrs[0],
   200  			err:        token.ErrApproverProxySame,
   201  		},
   202  	}
   203  
   204  	for name, tc := range testCases {
   205  		t.Run(name, func(t *testing.T) {
   206  			msg := token.MsgRevokeOperator{
   207  				ContractId: tc.contractID,
   208  				Holder:     tc.holder.String(),
   209  				Operator:   tc.operator.String(),
   210  			}
   211  
   212  			err := msg.ValidateBasic()
   213  			require.ErrorIs(t, err, tc.err)
   214  			if tc.err != nil {
   215  				return
   216  			}
   217  
   218  			require.Equal(t, []sdk.AccAddress{tc.holder}, msg.GetSigners())
   219  		})
   220  	}
   221  }
   222  
   223  func TestMsgAuthorizeOperator(t *testing.T) {
   224  	addrs := make([]sdk.AccAddress, 2)
   225  	for i := range addrs {
   226  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   227  	}
   228  
   229  	testCases := map[string]struct {
   230  		contractID string
   231  		holder     sdk.AccAddress
   232  		operator   sdk.AccAddress
   233  		err        error
   234  	}{
   235  		"valid msg": {
   236  			contractID: "deadbeef",
   237  			holder:     addrs[0],
   238  			operator:   addrs[1],
   239  		},
   240  		"invalid contract id": {
   241  			holder:   addrs[0],
   242  			operator: addrs[1],
   243  			err:      class.ErrInvalidContractID,
   244  		},
   245  		"invalid holder": {
   246  			contractID: "deadbeef",
   247  			operator:   addrs[1],
   248  			err:        sdkerrors.ErrInvalidAddress,
   249  		},
   250  		"empty operator": {
   251  			contractID: "deadbeef",
   252  			holder:     addrs[0],
   253  			err:        sdkerrors.ErrInvalidAddress,
   254  		},
   255  		"proxy and approver should be different": {
   256  			contractID: "deadbeef",
   257  			holder:     addrs[0],
   258  			operator:   addrs[0],
   259  			err:        token.ErrApproverProxySame,
   260  		},
   261  	}
   262  
   263  	for name, tc := range testCases {
   264  		t.Run(name, func(t *testing.T) {
   265  			msg := token.MsgAuthorizeOperator{
   266  				ContractId: tc.contractID,
   267  				Holder:     tc.holder.String(),
   268  				Operator:   tc.operator.String(),
   269  			}
   270  
   271  			err := msg.ValidateBasic()
   272  			require.ErrorIs(t, err, tc.err)
   273  			if tc.err != nil {
   274  				return
   275  			}
   276  
   277  			require.Equal(t, []sdk.AccAddress{tc.holder}, msg.GetSigners())
   278  		})
   279  	}
   280  }
   281  
   282  func TestMsgIssue(t *testing.T) {
   283  	addrs := make([]sdk.AccAddress, 2)
   284  	for i := range addrs {
   285  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   286  	}
   287  
   288  	testCases := map[string]struct {
   289  		owner    sdk.AccAddress
   290  		to       sdk.AccAddress
   291  		name     string
   292  		symbol   string
   293  		imageUri string
   294  		meta     string
   295  		decimals int32
   296  		amount   sdk.Int
   297  		err      error
   298  	}{
   299  		"valid msg": {
   300  			owner:    addrs[0],
   301  			to:       addrs[1],
   302  			name:     "test",
   303  			symbol:   "TT",
   304  			imageUri: "some URI",
   305  			meta:     "some meta",
   306  			decimals: 8,
   307  			amount:   sdk.OneInt(),
   308  		},
   309  		"invalid owner": {
   310  			to:       addrs[1],
   311  			name:     "test",
   312  			symbol:   "TT",
   313  			imageUri: "some URI",
   314  			meta:     "some meta",
   315  			decimals: 8,
   316  			amount:   sdk.OneInt(),
   317  			err:      sdkerrors.ErrInvalidAddress,
   318  		},
   319  		"invalid to": {
   320  			owner:    addrs[0],
   321  			name:     "test",
   322  			symbol:   "TT",
   323  			imageUri: "some URI",
   324  			meta:     "some meta",
   325  			decimals: 8,
   326  			amount:   sdk.OneInt(),
   327  			err:      sdkerrors.ErrInvalidAddress,
   328  		},
   329  		"empty name": {
   330  			owner:    addrs[0],
   331  			to:       addrs[1],
   332  			symbol:   "TT",
   333  			imageUri: "some URI",
   334  			meta:     "some meta",
   335  			decimals: 8,
   336  			amount:   sdk.OneInt(),
   337  			err:      token.ErrInvalidTokenName,
   338  		},
   339  		"long name": {
   340  			owner:    addrs[0],
   341  			to:       addrs[1],
   342  			name:     "TOO Looooooooooooooog",
   343  			symbol:   "TT",
   344  			imageUri: "some URI",
   345  			meta:     "some meta",
   346  			decimals: 8,
   347  			amount:   sdk.OneInt(),
   348  			err:      token.ErrInvalidNameLength,
   349  		},
   350  		"invalid symbol": {
   351  			owner:    addrs[0],
   352  			to:       addrs[1],
   353  			name:     "test",
   354  			imageUri: "some URI",
   355  			meta:     "some meta",
   356  			decimals: 8,
   357  			amount:   sdk.OneInt(),
   358  			err:      token.ErrInvalidTokenSymbol,
   359  		},
   360  		"invalid symbol - start number": {
   361  			owner:    addrs[0],
   362  			to:       addrs[1],
   363  			name:     "test",
   364  			symbol:   "1TT",
   365  			imageUri: "some URI",
   366  			meta:     "some meta",
   367  			decimals: 8,
   368  			amount:   sdk.OneInt(),
   369  			err:      token.ErrInvalidTokenSymbol,
   370  		},
   371  		"invalid symbol - start lowercase letter": {
   372  			owner:    addrs[0],
   373  			to:       addrs[1],
   374  			name:     "test",
   375  			symbol:   "tTT",
   376  			imageUri: "some URI",
   377  			meta:     "some meta",
   378  			decimals: 8,
   379  			amount:   sdk.OneInt(),
   380  			err:      token.ErrInvalidTokenSymbol,
   381  		},
   382  		"invalid symbol - include lowercase letter": {
   383  			owner:    addrs[0],
   384  			to:       addrs[1],
   385  			name:     "test",
   386  			symbol:   "TtT",
   387  			imageUri: "some URI",
   388  			meta:     "some meta",
   389  			decimals: 8,
   390  			amount:   sdk.OneInt(),
   391  			err:      token.ErrInvalidTokenSymbol,
   392  		},
   393  		"invalid symbol - long length": {
   394  			owner:    addrs[0],
   395  			to:       addrs[1],
   396  			name:     "test",
   397  			symbol:   "TTTTT6",
   398  			imageUri: "some URI",
   399  			meta:     "some meta",
   400  			decimals: 8,
   401  			amount:   sdk.OneInt(),
   402  			err:      token.ErrInvalidTokenSymbol,
   403  		},
   404  		"invalid image uri": {
   405  			owner:    addrs[0],
   406  			to:       addrs[1],
   407  			name:     "test",
   408  			symbol:   "TT",
   409  			imageUri: string(make([]rune, 1001)),
   410  			meta:     "some meta",
   411  			decimals: 8,
   412  			amount:   sdk.OneInt(),
   413  			err:      token.ErrInvalidImageURILength,
   414  		},
   415  		"invalid meta": {
   416  			owner:    addrs[0],
   417  			to:       addrs[1],
   418  			name:     "test",
   419  			symbol:   "TT",
   420  			imageUri: "some URI",
   421  			meta:     string(make([]rune, 1001)),
   422  			decimals: 8,
   423  			amount:   sdk.OneInt(),
   424  			err:      token.ErrInvalidMetaLength,
   425  		},
   426  		"invalid decimals": {
   427  			owner:    addrs[0],
   428  			to:       addrs[1],
   429  			name:     "test",
   430  			symbol:   "TT",
   431  			imageUri: "some URI",
   432  			meta:     "some meta",
   433  			decimals: 19,
   434  			amount:   sdk.OneInt(),
   435  			err:      token.ErrInvalidTokenDecimals,
   436  		},
   437  		"invalid decimals - negative": {
   438  			owner:    addrs[0],
   439  			to:       addrs[1],
   440  			name:     "test",
   441  			symbol:   "TT",
   442  			imageUri: "some URI",
   443  			meta:     "some meta",
   444  			decimals: -1,
   445  			amount:   sdk.OneInt(),
   446  			err:      token.ErrInvalidTokenDecimals,
   447  		},
   448  		"invalid amount": {
   449  			owner:    addrs[0],
   450  			to:       addrs[1],
   451  			name:     "test",
   452  			symbol:   "TT",
   453  			imageUri: "some URI",
   454  			meta:     "some meta",
   455  			decimals: 8,
   456  			amount:   sdk.ZeroInt(),
   457  			err:      token.ErrInvalidAmount,
   458  		},
   459  	}
   460  
   461  	for name, tc := range testCases {
   462  		t.Run(name, func(t *testing.T) {
   463  			msg := token.MsgIssue{
   464  				Owner:    tc.owner.String(),
   465  				To:       tc.to.String(),
   466  				Name:     tc.name,
   467  				Symbol:   tc.symbol,
   468  				Uri:      tc.imageUri,
   469  				Meta:     tc.meta,
   470  				Decimals: tc.decimals,
   471  				Amount:   tc.amount,
   472  			}
   473  
   474  			err := msg.ValidateBasic()
   475  			require.ErrorIs(t, err, tc.err)
   476  			if tc.err != nil {
   477  				return
   478  			}
   479  
   480  			require.Equal(t, []sdk.AccAddress{tc.owner}, msg.GetSigners())
   481  		})
   482  	}
   483  }
   484  
   485  func TestMsgMint(t *testing.T) {
   486  	addrs := make([]sdk.AccAddress, 2)
   487  	for i := range addrs {
   488  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   489  	}
   490  
   491  	testCases := map[string]struct {
   492  		contractID string
   493  		grantee    sdk.AccAddress
   494  		to         sdk.AccAddress
   495  		amount     sdk.Int
   496  		err        error
   497  	}{
   498  		"valid msg": {
   499  			contractID: "deadbeef",
   500  			grantee:    addrs[0],
   501  			to:         addrs[1],
   502  			amount:     sdk.OneInt(),
   503  		},
   504  		"invalid contract id": {
   505  			grantee: addrs[0],
   506  			to:      addrs[1],
   507  			amount:  sdk.OneInt(),
   508  			err:     class.ErrInvalidContractID,
   509  		},
   510  		"invalid grantee": {
   511  			contractID: "deadbeef",
   512  			to:         addrs[1],
   513  			amount:     sdk.OneInt(),
   514  			err:        sdkerrors.ErrInvalidAddress,
   515  		},
   516  		"invalid to": {
   517  			contractID: "deadbeef",
   518  			grantee:    addrs[0],
   519  			amount:     sdk.OneInt(),
   520  			err:        sdkerrors.ErrInvalidAddress,
   521  		},
   522  		"zero amount": {
   523  			contractID: "deadbeef",
   524  			grantee:    addrs[0],
   525  			to:         addrs[1],
   526  			amount:     sdk.ZeroInt(),
   527  			err:        token.ErrInvalidAmount,
   528  		},
   529  		"negative amount": {
   530  			contractID: "deadbeef",
   531  			grantee:    addrs[0],
   532  			to:         addrs[1],
   533  			amount:     sdk.NewInt(-10),
   534  			err:        token.ErrInvalidAmount,
   535  		},
   536  	}
   537  
   538  	for name, tc := range testCases {
   539  		t.Run(name, func(t *testing.T) {
   540  			msg := token.MsgMint{
   541  				ContractId: tc.contractID,
   542  				From:       tc.grantee.String(),
   543  				To:         tc.to.String(),
   544  				Amount:     tc.amount,
   545  			}
   546  
   547  			err := msg.ValidateBasic()
   548  			require.ErrorIs(t, err, tc.err)
   549  			if tc.err != nil {
   550  				return
   551  			}
   552  
   553  			require.Equal(t, []sdk.AccAddress{tc.grantee}, msg.GetSigners())
   554  		})
   555  	}
   556  }
   557  
   558  func TestMsgBurn(t *testing.T) {
   559  	addrs := make([]sdk.AccAddress, 1)
   560  	for i := range addrs {
   561  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   562  	}
   563  
   564  	testCases := map[string]struct {
   565  		contractID string
   566  		from       sdk.AccAddress
   567  		amount     sdk.Int
   568  		err        error
   569  	}{
   570  		"valid msg": {
   571  			contractID: "deadbeef",
   572  			from:       addrs[0],
   573  			amount:     sdk.OneInt(),
   574  		},
   575  		"invalid contract id": {
   576  			from:   addrs[0],
   577  			amount: sdk.OneInt(),
   578  			err:    class.ErrInvalidContractID,
   579  		},
   580  		"invalid from": {
   581  			contractID: "deadbeef",
   582  			amount:     sdk.OneInt(),
   583  			err:        sdkerrors.ErrInvalidAddress,
   584  		},
   585  		"invalid amount": {
   586  			contractID: "deadbeef",
   587  			from:       addrs[0],
   588  			amount:     sdk.ZeroInt(),
   589  			err:        token.ErrInvalidAmount,
   590  		},
   591  		"negative amount": {
   592  			contractID: "deadbeef",
   593  			from:       addrs[0],
   594  			amount:     sdk.NewInt(-10),
   595  			err:        token.ErrInvalidAmount,
   596  		},
   597  	}
   598  
   599  	for name, tc := range testCases {
   600  		t.Run(name, func(t *testing.T) {
   601  			msg := token.MsgBurn{
   602  				ContractId: tc.contractID,
   603  				From:       tc.from.String(),
   604  				Amount:     tc.amount,
   605  			}
   606  
   607  			err := msg.ValidateBasic()
   608  			require.ErrorIs(t, err, tc.err)
   609  			if tc.err != nil {
   610  				return
   611  			}
   612  
   613  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
   614  		})
   615  	}
   616  }
   617  
   618  func TestMsgOperatorBurn(t *testing.T) {
   619  	addrs := make([]sdk.AccAddress, 2)
   620  	for i := range addrs {
   621  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   622  	}
   623  
   624  	testCases := map[string]struct {
   625  		contractID string
   626  		grantee    sdk.AccAddress
   627  		from       sdk.AccAddress
   628  		amount     sdk.Int
   629  		err        error
   630  	}{
   631  		"valid msg": {
   632  			contractID: "deadbeef",
   633  			grantee:    addrs[0],
   634  			from:       addrs[1],
   635  			amount:     sdk.OneInt(),
   636  		},
   637  		"invalid contract id": {
   638  			grantee: addrs[0],
   639  			from:    addrs[1],
   640  			amount:  sdk.OneInt(),
   641  			err:     class.ErrInvalidContractID,
   642  		},
   643  		"invalid grantee": {
   644  			contractID: "deadbeef",
   645  			from:       addrs[1],
   646  			amount:     sdk.OneInt(),
   647  			err:        sdkerrors.ErrInvalidAddress,
   648  		},
   649  		"invalid from": {
   650  			contractID: "deadbeef",
   651  			grantee:    addrs[0],
   652  			amount:     sdk.OneInt(),
   653  			err:        sdkerrors.ErrInvalidAddress,
   654  		},
   655  		"invalid amount": {
   656  			contractID: "deadbeef",
   657  			grantee:    addrs[0],
   658  			from:       addrs[1],
   659  			amount:     sdk.ZeroInt(),
   660  			err:        token.ErrInvalidAmount,
   661  		},
   662  	}
   663  
   664  	for name, tc := range testCases {
   665  		t.Run(name, func(t *testing.T) {
   666  			msg := token.MsgOperatorBurn{
   667  				ContractId: tc.contractID,
   668  				Operator:   tc.grantee.String(),
   669  				From:       tc.from.String(),
   670  				Amount:     tc.amount,
   671  			}
   672  
   673  			err := msg.ValidateBasic()
   674  			require.ErrorIs(t, err, tc.err)
   675  			if tc.err != nil {
   676  				return
   677  			}
   678  
   679  			require.Equal(t, []sdk.AccAddress{tc.grantee}, msg.GetSigners())
   680  		})
   681  	}
   682  }
   683  
   684  func TestMsgModify(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  	validChange := token.Attribute{Key: token.AttributeKeyName.String(), Value: "New test"}
   691  	testCases := map[string]struct {
   692  		contractID string
   693  		grantee    sdk.AccAddress
   694  		changes    []token.Attribute
   695  		err        error
   696  	}{
   697  		"valid msg": {
   698  			contractID: "deadbeef",
   699  			grantee:    addrs[0],
   700  			changes:    []token.Attribute{validChange},
   701  		},
   702  		"invalid contract id": {
   703  			grantee: addrs[0],
   704  			changes: []token.Attribute{validChange},
   705  			err:     class.ErrInvalidContractID,
   706  		},
   707  		"invalid grantee": {
   708  			contractID: "deadbeef",
   709  			changes:    []token.Attribute{validChange},
   710  			err:        sdkerrors.ErrInvalidAddress,
   711  		},
   712  		"invalid key of change": {
   713  			contractID: "deadbeef",
   714  			grantee:    addrs[0],
   715  			changes:    []token.Attribute{{Key: strings.ToUpper(token.AttributeKeyName.String()), Value: "tt"}},
   716  			err:        token.ErrInvalidChangesField,
   717  		},
   718  		"invalid value of change": {
   719  			contractID: "deadbeef",
   720  			grantee:    addrs[0],
   721  			changes:    []token.Attribute{{Key: token.AttributeKeyName.String(), Value: string(make([]rune, 21))}},
   722  			err:        token.ErrInvalidNameLength,
   723  		},
   724  		"empty changes": {
   725  			contractID: "deadbeef",
   726  			grantee:    addrs[0],
   727  			err:        token.ErrEmptyChanges,
   728  		},
   729  		"duplicated changes": {
   730  			contractID: "deadbeef",
   731  			grantee:    addrs[0],
   732  			changes: []token.Attribute{
   733  				{Key: token.AttributeKeyImageURI.String(), Value: "hello"},
   734  				{Key: token.AttributeKeyURI.String(), Value: "world"},
   735  			},
   736  			err: token.ErrDuplicateChangesField,
   737  		},
   738  		"over max length changes(uri)": {
   739  			contractID: "deadbeef",
   740  			grantee:    addrs[0],
   741  			changes:    []token.Attribute{{Key: token.AttributeKeyURI.String(), Value: string(make([]rune, 1001))}},
   742  			err:        token.ErrInvalidImageURILength,
   743  		},
   744  		"over max length changes(meta)": {
   745  			contractID: "deadbeef",
   746  			grantee:    addrs[0],
   747  			changes:    []token.Attribute{{Key: token.AttributeKeyMeta.String(), Value: string(make([]rune, 1001))}},
   748  			err:        token.ErrInvalidMetaLength,
   749  		},
   750  	}
   751  
   752  	for name, tc := range testCases {
   753  		t.Run(name, func(t *testing.T) {
   754  			msg := token.MsgModify{
   755  				ContractId: tc.contractID,
   756  				Owner:      tc.grantee.String(),
   757  				Changes:    tc.changes,
   758  			}
   759  
   760  			err := msg.ValidateBasic()
   761  			require.ErrorIs(t, err, tc.err)
   762  			if tc.err != nil {
   763  				return
   764  			}
   765  
   766  			require.Equal(t, []sdk.AccAddress{tc.grantee}, msg.GetSigners())
   767  		})
   768  	}
   769  }
   770  
   771  func TestMsgGrantPermission(t *testing.T) {
   772  	addrs := make([]sdk.AccAddress, 2)
   773  	for i := range addrs {
   774  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   775  	}
   776  
   777  	testCases := map[string]struct {
   778  		contractID string
   779  		from       sdk.AccAddress
   780  		to         sdk.AccAddress
   781  		permission string
   782  		err        error
   783  	}{
   784  		"valid msg": {
   785  			contractID: "deadbeef",
   786  			from:       addrs[0],
   787  			to:         addrs[1],
   788  			permission: token.LegacyPermissionMint.String(),
   789  		},
   790  		"invalid contract id": {
   791  			from:       addrs[0],
   792  			to:         addrs[1],
   793  			permission: token.LegacyPermissionMint.String(),
   794  			err:        class.ErrInvalidContractID,
   795  		},
   796  		"invalid from": {
   797  			contractID: "deadbeef",
   798  			to:         addrs[1],
   799  			permission: token.LegacyPermissionMint.String(),
   800  			err:        sdkerrors.ErrInvalidAddress,
   801  		},
   802  		"invalid to": {
   803  			contractID: "deadbeef",
   804  			from:       addrs[0],
   805  			permission: token.LegacyPermissionMint.String(),
   806  			err:        sdkerrors.ErrInvalidAddress,
   807  		},
   808  		"invalid permission": {
   809  			contractID: "deadbeef",
   810  			from:       addrs[0],
   811  			to:         addrs[1],
   812  			err:        sdkerrors.ErrInvalidPermission,
   813  		},
   814  	}
   815  
   816  	for name, tc := range testCases {
   817  		t.Run(name, func(t *testing.T) {
   818  			msg := token.MsgGrantPermission{
   819  				ContractId: tc.contractID,
   820  				From:       tc.from.String(),
   821  				To:         tc.to.String(),
   822  				Permission: tc.permission,
   823  			}
   824  
   825  			err := msg.ValidateBasic()
   826  			require.ErrorIs(t, err, tc.err)
   827  			if tc.err != nil {
   828  				return
   829  			}
   830  
   831  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
   832  		})
   833  	}
   834  }
   835  
   836  func TestMsgRevokePermission(t *testing.T) {
   837  	addrs := make([]sdk.AccAddress, 1)
   838  	for i := range addrs {
   839  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   840  	}
   841  
   842  	testCases := map[string]struct {
   843  		contractID string
   844  		from       sdk.AccAddress
   845  		permission string
   846  		err        error
   847  	}{
   848  		"valid msg": {
   849  			contractID: "deadbeef",
   850  			from:       addrs[0],
   851  			permission: token.LegacyPermissionMint.String(),
   852  		},
   853  		"invalid contract id": {
   854  			from:       addrs[0],
   855  			permission: token.LegacyPermissionMint.String(),
   856  			err:        class.ErrInvalidContractID,
   857  		},
   858  		"invalid from": {
   859  			contractID: "deadbeef",
   860  			permission: token.LegacyPermissionMint.String(),
   861  			err:        sdkerrors.ErrInvalidAddress,
   862  		},
   863  		"invalid permission": {
   864  			contractID: "deadbeef",
   865  			from:       addrs[0],
   866  			err:        sdkerrors.ErrInvalidPermission,
   867  		},
   868  	}
   869  
   870  	for name, tc := range testCases {
   871  		t.Run(name, func(t *testing.T) {
   872  			msg := token.MsgRevokePermission{
   873  				ContractId: tc.contractID,
   874  				From:       tc.from.String(),
   875  				Permission: tc.permission,
   876  			}
   877  
   878  			err := msg.ValidateBasic()
   879  			require.ErrorIs(t, err, tc.err)
   880  			if tc.err != nil {
   881  				return
   882  			}
   883  
   884  			require.Equal(t, []sdk.AccAddress{tc.from}, msg.GetSigners())
   885  		})
   886  	}
   887  }
   888  
   889  func TestAminoJSON(t *testing.T) {
   890  	tx := legacytx.StdTx{}
   891  	contractId := "deadbeef"
   892  
   893  	addrs := make([]sdk.AccAddress, 3)
   894  	for i := range addrs {
   895  		addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   896  	}
   897  
   898  	testCases := map[string]struct {
   899  		msg          legacytx.LegacyMsg
   900  		expectedType string
   901  		expected     string
   902  	}{
   903  		"MsgSend": {
   904  			&token.MsgSend{
   905  				ContractId: contractId,
   906  				From:       addrs[0].String(),
   907  				To:         addrs[1].String(),
   908  				Amount:     sdk.OneInt(),
   909  			},
   910  			"/lbm.token.v1.MsgSend",
   911  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgSend\",\"value\":{\"amount\":\"1\",\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   912  		},
   913  		"MsgOperatorSend": {
   914  			&token.MsgOperatorSend{
   915  				ContractId: contractId,
   916  				Operator:   addrs[0].String(),
   917  				From:       addrs[1].String(),
   918  				To:         addrs[2].String(),
   919  				Amount:     sdk.OneInt(),
   920  			},
   921  			"/lbm.token.v1.MsgOperatorSend",
   922  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorSend\",\"value\":{\"amount\":\"1\",\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String(), addrs[2].String()),
   923  		},
   924  		"MsgRevokeOperator": {
   925  			&token.MsgRevokeOperator{
   926  				ContractId: contractId,
   927  				Holder:     addrs[0].String(),
   928  				Operator:   addrs[1].String(),
   929  			},
   930  			"/lbm.token.v1.MsgRevokeOperator",
   931  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/token/MsgRevokeOperator\",\"value\":{\"contract_id\":\"deadbeef\",\"holder\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   932  		},
   933  		"MsgAuthorizeOperator": {
   934  			&token.MsgAuthorizeOperator{
   935  				ContractId: contractId,
   936  				Holder:     addrs[0].String(),
   937  				Operator:   addrs[1].String(),
   938  			},
   939  			"/lbm.token.v1.MsgAuthorizeOperator",
   940  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/token/MsgAuthorizeOperator\",\"value\":{\"contract_id\":\"deadbeef\",\"holder\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   941  		},
   942  		"MsgIssue": {
   943  			&token.MsgIssue{
   944  				Name:     "Test Name",
   945  				Symbol:   "LN",
   946  				Uri:      "http://image.url",
   947  				Meta:     "This is test",
   948  				Decimals: 6,
   949  				Mintable: false,
   950  				Owner:    addrs[0].String(),
   951  				To:       addrs[1].String(),
   952  				Amount:   sdk.NewInt(1000000),
   953  			},
   954  			"/lbm.token.v1.MsgIssue",
   955  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgIssue\",\"value\":{\"amount\":\"1000000\",\"decimals\":6,\"meta\":\"This is test\",\"name\":\"Test Name\",\"owner\":\"%s\",\"symbol\":\"LN\",\"to\":\"%s\",\"uri\":\"http://image.url\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   956  		},
   957  		"MsgGrantPermission": {
   958  			&token.MsgGrantPermission{
   959  				ContractId: contractId,
   960  				From:       addrs[0].String(),
   961  				To:         addrs[1].String(),
   962  				Permission: token.LegacyPermissionMint.String(),
   963  			},
   964  			"/lbm.token.v1.MsgGrantPermission",
   965  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/token/MsgGrantPermission\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"permission\":\"mint\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   966  		},
   967  		"MsgRevokePermission": {
   968  			&token.MsgRevokePermission{
   969  				ContractId: contractId,
   970  				From:       addrs[0].String(),
   971  				Permission: token.LegacyPermissionMint.String(),
   972  			},
   973  			"/lbm.token.v1.MsgRevokePermission",
   974  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/token/MsgRevokePermission\",\"value\":{\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"permission\":\"mint\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
   975  		},
   976  		"MsgMint": {
   977  			&token.MsgMint{
   978  				ContractId: contractId,
   979  				From:       addrs[0].String(),
   980  				To:         addrs[1].String(),
   981  				Amount:     sdk.NewInt(1000000),
   982  			},
   983  			"/lbm.token.v1.MsgMint",
   984  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgMint\",\"value\":{\"amount\":\"1000000\",\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"to\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String(), addrs[1].String()),
   985  		},
   986  		"MsgBurn": {
   987  			&token.MsgBurn{
   988  				ContractId: contractId,
   989  				From:       addrs[0].String(),
   990  				Amount:     sdk.Int{},
   991  			},
   992  			"/lbm.token.v1.MsgBurn",
   993  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgBurn\",\"value\":{\"amount\":\"0\",\"contract_id\":\"deadbeef\",\"from\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
   994  		},
   995  		"MsgOperatorBurn": {
   996  			&token.MsgOperatorBurn{
   997  				ContractId: contractId,
   998  				Operator:   addrs[0].String(),
   999  				From:       addrs[1].String(),
  1000  				Amount:     sdk.NewInt(1000000),
  1001  			},
  1002  			"/lbm.token.v1.MsgOperatorBurn",
  1003  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/MsgOperatorBurn\",\"value\":{\"amount\":\"1000000\",\"contract_id\":\"deadbeef\",\"from\":\"%s\",\"operator\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[1].String(), addrs[0].String()),
  1004  		},
  1005  		"MsgModify": {
  1006  			&token.MsgModify{
  1007  				ContractId: contractId,
  1008  				Owner:      addrs[0].String(),
  1009  				Changes:    []token.Attribute{{Key: token.AttributeKeyName.String(), Value: "New test"}},
  1010  			},
  1011  			"/lbm.token.v1.MsgModify",
  1012  			fmt.Sprintf("{\"account_number\":\"1\",\"chain_id\":\"foo\",\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"memo\",\"msgs\":[{\"type\":\"lbm-sdk/token/MsgModify\",\"value\":{\"changes\":[{\"key\":\"name\",\"value\":\"New test\"}],\"contract_id\":\"deadbeef\",\"owner\":\"%s\"}}],\"sequence\":\"1\",\"timeout_height\":\"1\"}", addrs[0].String()),
  1013  		},
  1014  	}
  1015  
  1016  	for name, tc := range testCases {
  1017  		t.Run(name, func(t *testing.T) {
  1018  			tx.Msgs = []sdk.Msg{tc.msg}
  1019  			require.Equal(t, token.RouterKey, tc.msg.Route())
  1020  			require.Equal(t, tc.expectedType, tc.msg.Type())
  1021  			require.Equal(t, tc.expected, string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{tc.msg}, "memo")))
  1022  		})
  1023  	}
  1024  }