github.com/KiraCore/sekai@v0.3.43/x/tokens/handler_test.go (about)

     1  package tokens_test
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	simapp "github.com/KiraCore/sekai/app"
    12  	appparams "github.com/KiraCore/sekai/app/params"
    13  	"github.com/KiraCore/sekai/x/gov"
    14  	"github.com/KiraCore/sekai/x/gov/types"
    15  	govtypes "github.com/KiraCore/sekai/x/gov/types"
    16  	tokens "github.com/KiraCore/sekai/x/tokens"
    17  	tokenstypes "github.com/KiraCore/sekai/x/tokens/types"
    18  	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
    19  	sdk "github.com/cosmos/cosmos-sdk/types"
    20  	"github.com/cosmos/cosmos-sdk/types/errors"
    21  	"github.com/gogo/protobuf/proto"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestMain(m *testing.M) {
    26  	appparams.SetConfig()
    27  	os.Exit(m.Run())
    28  }
    29  
    30  func NewAccountByIndex(accNum int) sdk.AccAddress {
    31  	var buffer bytes.Buffer
    32  	i := accNum + 100
    33  	numString := strconv.Itoa(i)
    34  	buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string
    35  
    36  	buffer.WriteString(numString) //adding on final two digits to make addresses unique
    37  	res, _ := sdk.AccAddressFromHexUnsafe(buffer.String())
    38  	bech := res.String()
    39  	addr, _ := simapp.TestAddr(buffer.String(), bech)
    40  	buffer.Reset()
    41  	return addr
    42  }
    43  
    44  func setPermissionToAddr(t *testing.T, app *simapp.SekaiApp, ctx sdk.Context, addr sdk.AccAddress, perm types.PermValue) error {
    45  	proposerActor := govtypes.NewDefaultActor(addr)
    46  	err := proposerActor.Permissions.AddToWhitelist(perm)
    47  	require.NoError(t, err)
    48  
    49  	app.CustomGovKeeper.SaveNetworkActor(ctx, proposerActor)
    50  
    51  	return nil
    52  }
    53  
    54  func TestNewHandler_MsgUpsertTokenAlias(t *testing.T) {
    55  	app := simapp.Setup(false)
    56  	ctx := app.NewContext(false, tmproto.Header{})
    57  	handler := tokens.NewHandler(app.TokensKeeper, app.CustomGovKeeper)
    58  
    59  	tests := []struct {
    60  		name        string
    61  		constructor func(sdk.AccAddress) (*tokenstypes.MsgUpsertTokenAlias, error)
    62  		handlerErr  string
    63  	}{
    64  		{
    65  			name: "good permission test",
    66  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenAlias, error) {
    67  				err := setPermissionToAddr(t, app, ctx, addr, types.PermUpsertTokenAlias)
    68  				require.NoError(t, err)
    69  				return tokenstypes.NewMsgUpsertTokenAlias(
    70  					addr,
    71  					"ETH",
    72  					"Ethereum",
    73  					"icon",
    74  					6,
    75  					[]string{"finney"},
    76  					false,
    77  				), nil
    78  			},
    79  		},
    80  		{
    81  			name: "lack permission test",
    82  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenAlias, error) {
    83  				return tokenstypes.NewMsgUpsertTokenAlias(
    84  					addr,
    85  					"ETH",
    86  					"Ethereum",
    87  					"icon",
    88  					6,
    89  					[]string{"finney"},
    90  					false,
    91  				), nil
    92  			},
    93  			handlerErr: "PERMISSION_UPSERT_TOKEN_ALIAS: not enough permissions",
    94  		},
    95  	}
    96  	for i, tt := range tests {
    97  		addr := NewAccountByIndex(i)
    98  		theMsg, err := tt.constructor(addr)
    99  		require.NoError(t, err)
   100  
   101  		_, err = handler(ctx, theMsg)
   102  		if len(tt.handlerErr) != 0 {
   103  			require.Error(t, err)
   104  			require.Contains(t, err.Error(), tt.handlerErr)
   105  		} else {
   106  			require.NoError(t, err)
   107  
   108  			// test various query commands
   109  			alias := app.TokensKeeper.GetTokenAlias(ctx, theMsg.Symbol)
   110  			require.True(t, alias != nil)
   111  			aliasesAll := app.TokensKeeper.ListTokenAlias(ctx)
   112  			require.True(t, len(aliasesAll) > 0)
   113  			aliasesByDenom := app.TokensKeeper.GetTokenAliasesByDenom(ctx, theMsg.Denoms)
   114  			require.True(t, aliasesByDenom[theMsg.Denoms[0]] != nil)
   115  
   116  			// try different alias for same denom
   117  			theMsg.Symbol += "V2"
   118  			_, err = handler(ctx, theMsg)
   119  			require.Error(t, err)
   120  			require.True(t, strings.Contains(err.Error(), "denom is already registered"))
   121  		}
   122  	}
   123  }
   124  
   125  func TestNewHandler_MsgUpsertTokenRate(t *testing.T) {
   126  
   127  	app := simapp.Setup(false)
   128  	ctx := app.NewContext(false, tmproto.Header{})
   129  	handler := tokens.NewHandler(app.TokensKeeper, app.CustomGovKeeper)
   130  
   131  	tests := []struct {
   132  		name        string
   133  		constructor func(sdk.AccAddress) (*tokenstypes.MsgUpsertTokenRate, error)
   134  		handlerErr  string
   135  	}{
   136  		{
   137  			name: "good permission test",
   138  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenRate, error) {
   139  				err := setPermissionToAddr(t, app, ctx, addr, types.PermUpsertTokenRate)
   140  				require.NoError(t, err)
   141  				return tokenstypes.NewMsgUpsertTokenRate(
   142  					addr,
   143  					"finney", sdk.NewDecWithPrec(1, 3), // 0.001
   144  					true,
   145  					sdk.ZeroDec(),
   146  					sdk.ZeroInt(),
   147  					false,
   148  					false,
   149  				), nil
   150  			},
   151  		},
   152  		{
   153  			name: "lack permission test",
   154  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenRate, error) {
   155  				return tokenstypes.NewMsgUpsertTokenRate(
   156  					addr,
   157  					"finney", sdk.NewDecWithPrec(1, 3), // 0.001
   158  					true,
   159  					sdk.ZeroDec(),
   160  					sdk.ZeroInt(),
   161  					false,
   162  					false,
   163  				), nil
   164  			},
   165  			handlerErr: "PERMISSION_UPSERT_TOKEN_RATE: not enough permissions",
   166  		},
   167  		{
   168  			name: "negative rate value test",
   169  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenRate, error) {
   170  				return tokenstypes.NewMsgUpsertTokenRate(
   171  					addr,
   172  					"finney", sdk.NewDec(-1), // -1
   173  					true,
   174  					sdk.ZeroDec(),
   175  					sdk.ZeroInt(),
   176  					false,
   177  					false,
   178  				), nil
   179  			},
   180  			handlerErr: "rate should be positive",
   181  		},
   182  		{
   183  			name: "bond denom rate change test",
   184  			constructor: func(addr sdk.AccAddress) (*tokenstypes.MsgUpsertTokenRate, error) {
   185  				err := setPermissionToAddr(t, app, ctx, addr, types.PermUpsertTokenRate)
   186  				require.NoError(t, err)
   187  				return tokenstypes.NewMsgUpsertTokenRate(
   188  					addr,
   189  					"ukex", sdk.NewDec(10),
   190  					true,
   191  					sdk.ZeroDec(),
   192  					sdk.ZeroInt(),
   193  					false,
   194  					false,
   195  				), nil
   196  			},
   197  			handlerErr: "bond denom rate is read-only",
   198  		},
   199  	}
   200  	for i, tt := range tests {
   201  		addr := NewAccountByIndex(i)
   202  		theMsg, err := tt.constructor(addr)
   203  		require.NoError(t, err)
   204  
   205  		_, err = handler(ctx, theMsg)
   206  		if len(tt.handlerErr) != 0 {
   207  			require.Error(t, err)
   208  			require.Contains(t, err.Error(), tt.handlerErr)
   209  		} else {
   210  			require.NoError(t, err)
   211  
   212  			// test various query commands
   213  			rate := app.TokensKeeper.GetTokenRate(ctx, theMsg.Denom)
   214  			require.True(t, rate != nil)
   215  			ratesAll := app.TokensKeeper.GetAllTokenRates(ctx)
   216  			require.True(t, len(ratesAll) > 0)
   217  			ratesByDenom := app.TokensKeeper.GetTokenRatesByDenom(ctx, []string{theMsg.Denom})
   218  			require.True(t, ratesByDenom[theMsg.Denom] != nil)
   219  		}
   220  	}
   221  }
   222  
   223  func TestHandler_CreateProposalUpsertTokenAliases_Errors(t *testing.T) {
   224  	proposerAddr, err := sdk.AccAddressFromBech32("kira1alzyfq40zjsveat87jlg8jxetwqmr0a29sgd0f")
   225  	require.NoError(t, err)
   226  
   227  	tests := []struct {
   228  		name         string
   229  		content      govtypes.Content
   230  		preparePerms func(t *testing.T, app *simapp.SekaiApp, ctx sdk.Context)
   231  		expectedErr  error
   232  	}{
   233  		{
   234  			"Proposer does not have Perm",
   235  			tokenstypes.NewUpsertTokenAliasProposal(
   236  				"BTC",
   237  				"Bitcoin",
   238  				"http://theicon.com",
   239  				18,
   240  				[]string{},
   241  				false,
   242  			),
   243  			func(t *testing.T, app *simapp.SekaiApp, ctx sdk.Context) {},
   244  			errors.Wrap(types.ErrNotEnoughPermissions, types.PermCreateUpsertTokenAliasProposal.String()),
   245  		},
   246  	}
   247  
   248  	for _, tt := range tests {
   249  		tt := tt
   250  		t.Run(tt.name, func(t *testing.T) {
   251  			app := simapp.Setup(false)
   252  			ctx := app.NewContext(false, tmproto.Header{})
   253  
   254  			tt.preparePerms(t, app, ctx)
   255  
   256  			handler := gov.NewHandler(app.CustomGovKeeper)
   257  			msg, err := govtypes.NewMsgSubmitProposal(proposerAddr, "title", "some desc", tt.content)
   258  			require.NoError(t, err)
   259  			_, err = handler(ctx, msg)
   260  			require.EqualError(t, err, tt.expectedErr.Error())
   261  		})
   262  	}
   263  }
   264  
   265  func TestHandler_CreateProposalUpsertTokenAliases(t *testing.T) {
   266  	proposerAddr, err := sdk.AccAddressFromBech32("kira1alzyfq40zjsveat87jlg8jxetwqmr0a29sgd0f")
   267  	require.NoError(t, err)
   268  
   269  	app := simapp.Setup(false)
   270  	ctx := app.NewContext(false, tmproto.Header{
   271  		Time: time.Now(),
   272  	})
   273  
   274  	// Set proposer Permissions
   275  	proposerActor := types.NewDefaultActor(proposerAddr)
   276  	err2 := app.CustomGovKeeper.AddWhitelistPermission(ctx, proposerActor, types.PermCreateUpsertTokenAliasProposal)
   277  	require.NoError(t, err2)
   278  
   279  	properties := app.CustomGovKeeper.GetNetworkProperties(ctx)
   280  	properties.MinimumProposalEndTime = 10
   281  	app.CustomGovKeeper.SetNetworkProperties(ctx, properties)
   282  
   283  	handler := gov.NewHandler(app.CustomGovKeeper)
   284  	proposal := tokenstypes.NewUpsertTokenAliasProposal(
   285  		"BTC",
   286  		"Bitcoin",
   287  		"http://sdlkfjalsdk.es",
   288  		18,
   289  		[]string{
   290  			"atom",
   291  		},
   292  		false,
   293  	)
   294  	msg, err := govtypes.NewMsgSubmitProposal(proposerAddr, "title", "some desc", proposal)
   295  	require.NoError(t, err)
   296  	res, err := handler(
   297  		ctx,
   298  		msg,
   299  	)
   300  	require.NoError(t, err)
   301  	expData, _ := proto.Marshal(&govtypes.MsgSubmitProposalResponse{ProposalID: 1})
   302  	require.Equal(t, expData, res.Data)
   303  
   304  	savedProposal, found := app.CustomGovKeeper.GetProposal(ctx, 1)
   305  	require.True(t, found)
   306  
   307  	expectedSavedProposal, err := types.NewProposal(
   308  		1,
   309  		"title",
   310  		"some desc",
   311  		tokenstypes.NewUpsertTokenAliasProposal(
   312  			"BTC",
   313  			"Bitcoin",
   314  			"http://sdlkfjalsdk.es",
   315  			18,
   316  			[]string{
   317  				"atom",
   318  			},
   319  			false,
   320  		),
   321  		ctx.BlockTime(),
   322  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)),
   323  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)+
   324  			time.Second*time.Duration(properties.ProposalEnactmentTime),
   325  		),
   326  		ctx.BlockHeight()+2,
   327  		ctx.BlockHeight()+3,
   328  	)
   329  	require.NoError(t, err)
   330  	require.Equal(t, expectedSavedProposal, savedProposal)
   331  
   332  	// Next proposal ID is increased.
   333  	id := app.CustomGovKeeper.GetNextProposalID(ctx)
   334  	require.Equal(t, uint64(2), id)
   335  
   336  	// Is not on finished active proposals.
   337  	iterator := app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   338  	require.False(t, iterator.Valid())
   339  
   340  	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Minute * 10))
   341  	iterator = app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   342  	require.True(t, iterator.Valid())
   343  }
   344  
   345  func TestHandler_CreateProposalUpsertTokenRates_Errors(t *testing.T) {
   346  	proposerAddr, err := sdk.AccAddressFromBech32("kira1alzyfq40zjsveat87jlg8jxetwqmr0a29sgd0f")
   347  	require.NoError(t, err)
   348  
   349  	tests := []struct {
   350  		name         string
   351  		content      govtypes.Content
   352  		preparePerms func(t *testing.T, app *simapp.SekaiApp, ctx sdk.Context)
   353  		expectedErr  error
   354  	}{
   355  		{
   356  			"Proposer does not have Perm",
   357  			tokenstypes.NewUpsertTokenRatesProposal(
   358  				"btc",
   359  				sdk.NewDec(1234),
   360  				false,
   361  				sdk.ZeroDec(),
   362  				sdk.ZeroInt(),
   363  				false,
   364  				false,
   365  			),
   366  			func(t *testing.T, app *simapp.SekaiApp, ctx sdk.Context) {},
   367  			errors.Wrap(types.ErrNotEnoughPermissions, types.PermCreateUpsertTokenRateProposal.String()),
   368  		},
   369  	}
   370  
   371  	for _, tt := range tests {
   372  		tt := tt
   373  		t.Run(tt.name, func(t *testing.T) {
   374  			app := simapp.Setup(false)
   375  			ctx := app.NewContext(false, tmproto.Header{})
   376  
   377  			tt.preparePerms(t, app, ctx)
   378  
   379  			handler := gov.NewHandler(app.CustomGovKeeper)
   380  			msg, err := govtypes.NewMsgSubmitProposal(proposerAddr, "title", "some desc", tt.content)
   381  			require.NoError(t, err)
   382  			_, err = handler(ctx, msg)
   383  			require.EqualError(t, err, tt.expectedErr.Error())
   384  		})
   385  	}
   386  }
   387  
   388  func TestHandler_CreateProposalUpsertTokenRates(t *testing.T) {
   389  	proposerAddr, err := sdk.AccAddressFromBech32("kira1alzyfq40zjsveat87jlg8jxetwqmr0a29sgd0f")
   390  	require.NoError(t, err)
   391  
   392  	app := simapp.Setup(false)
   393  	ctx := app.NewContext(false, tmproto.Header{
   394  		Time: time.Now(),
   395  	})
   396  
   397  	// Set proposer Permissions
   398  	proposerActor := types.NewDefaultActor(proposerAddr)
   399  	err2 := app.CustomGovKeeper.AddWhitelistPermission(ctx, proposerActor, types.PermCreateUpsertTokenRateProposal)
   400  	require.NoError(t, err2)
   401  
   402  	properties := app.CustomGovKeeper.GetNetworkProperties(ctx)
   403  	properties.MinimumProposalEndTime = 10
   404  	app.CustomGovKeeper.SetNetworkProperties(ctx, properties)
   405  
   406  	handler := gov.NewHandler(app.CustomGovKeeper)
   407  	proposal := tokenstypes.NewUpsertTokenRatesProposal(
   408  		"btc",
   409  		sdk.NewDec(1234),
   410  		false,
   411  		sdk.ZeroDec(),
   412  		sdk.ZeroInt(),
   413  		false,
   414  		false,
   415  	)
   416  	msg, err := govtypes.NewMsgSubmitProposal(proposerAddr, "title", "some desc", proposal)
   417  	require.NoError(t, err)
   418  	res, err := handler(
   419  		ctx,
   420  		msg,
   421  	)
   422  	require.NoError(t, err)
   423  	expData, _ := proto.Marshal(&govtypes.MsgSubmitProposalResponse{ProposalID: 1})
   424  	require.Equal(t, expData, res.Data)
   425  
   426  	savedProposal, found := app.CustomGovKeeper.GetProposal(ctx, 1)
   427  	require.True(t, found)
   428  
   429  	expectedSavedProposal, err := types.NewProposal(
   430  		1,
   431  		"title",
   432  		"some desc",
   433  		tokenstypes.NewUpsertTokenRatesProposal(
   434  			"btc",
   435  			sdk.NewDec(1234),
   436  			false,
   437  			sdk.ZeroDec(),
   438  			sdk.ZeroInt(),
   439  			false,
   440  			false,
   441  		),
   442  		ctx.BlockTime(),
   443  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)),
   444  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)+
   445  			time.Second*time.Duration(properties.ProposalEnactmentTime),
   446  		),
   447  		ctx.BlockHeight()+2,
   448  		ctx.BlockHeight()+3,
   449  	)
   450  	require.NoError(t, err)
   451  	require.Equal(t, expectedSavedProposal, savedProposal)
   452  
   453  	// Next proposal ID is increased.
   454  	id := app.CustomGovKeeper.GetNextProposalID(ctx)
   455  	require.Equal(t, uint64(2), id)
   456  
   457  	// Is not on finished active proposals.
   458  	iterator := app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   459  	require.False(t, iterator.Valid())
   460  
   461  	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Minute * 10))
   462  	iterator = app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   463  	require.True(t, iterator.Valid())
   464  }
   465  
   466  func TestHandler_CreateProposalTokensWhiteBlackChange(t *testing.T) {
   467  	proposerAddr, err := sdk.AccAddressFromBech32("kira1alzyfq40zjsveat87jlg8jxetwqmr0a29sgd0f")
   468  	require.NoError(t, err)
   469  
   470  	app := simapp.Setup(false)
   471  	ctx := app.NewContext(false, tmproto.Header{
   472  		Time: time.Now(),
   473  	})
   474  
   475  	// Set proposer Permissions
   476  	proposerActor := types.NewDefaultActor(proposerAddr)
   477  	err = app.CustomGovKeeper.AddWhitelistPermission(ctx, proposerActor, types.PermCreateTokensWhiteBlackChangeProposal)
   478  	require.NoError(t, err)
   479  
   480  	properties := app.CustomGovKeeper.GetNetworkProperties(ctx)
   481  	properties.MinimumProposalEndTime = 10
   482  	app.CustomGovKeeper.SetNetworkProperties(ctx, properties)
   483  
   484  	handler := gov.NewHandler(app.CustomGovKeeper)
   485  	proposal := tokenstypes.NewTokensWhiteBlackChangeProposal(
   486  		false,
   487  		true,
   488  		[]string{"atom"},
   489  	)
   490  	msg, err := govtypes.NewMsgSubmitProposal(proposerAddr, "title", "some desc", proposal)
   491  	require.NoError(t, err)
   492  	res, err := handler(
   493  		ctx,
   494  		msg,
   495  	)
   496  	require.NoError(t, err)
   497  	expData, _ := proto.Marshal(&govtypes.MsgSubmitProposalResponse{ProposalID: 1})
   498  	require.Equal(t, expData, res.Data)
   499  
   500  	savedProposal, found := app.CustomGovKeeper.GetProposal(ctx, 1)
   501  	require.True(t, found)
   502  
   503  	expectedSavedProposal, err := types.NewProposal(
   504  		1,
   505  		"title",
   506  		"some desc",
   507  		proposal,
   508  		ctx.BlockTime(),
   509  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)),
   510  		ctx.BlockTime().Add(time.Second*time.Duration(properties.MinimumProposalEndTime)+
   511  			time.Second*time.Duration(properties.ProposalEnactmentTime),
   512  		),
   513  		ctx.BlockHeight()+2,
   514  		ctx.BlockHeight()+3,
   515  	)
   516  	require.NoError(t, err)
   517  	require.Equal(t, expectedSavedProposal, savedProposal)
   518  
   519  	// Next proposal ID is increased.
   520  	id := app.CustomGovKeeper.GetNextProposalID(ctx)
   521  	require.Equal(t, uint64(2), id)
   522  
   523  	// Is not on finished active proposals.
   524  	iterator := app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   525  	require.False(t, iterator.Valid())
   526  
   527  	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Minute * 10))
   528  	iterator = app.CustomGovKeeper.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
   529  	require.True(t, iterator.Valid())
   530  }