github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/types/msgs_test.go (about)

     1  package types_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	sdk "github.com/cosmos/cosmos-sdk/types"
     7  	"github.com/stretchr/testify/require"
     8  	"github.com/tendermint/tendermint/crypto"
     9  
    10  	"github.com/gravity-devs/liquidity/x/liquidity/types"
    11  )
    12  
    13  const (
    14  	DefaultPoolTypeId = uint32(1)
    15  	DefaultPoolId     = uint64(1)
    16  	DefaultSwapTypeId = uint32(1)
    17  	DenomX            = "denomX"
    18  	DenomY            = "denomY"
    19  )
    20  
    21  func TestMsgCreatePool(t *testing.T) {
    22  	poolCreator := sdk.AccAddress(crypto.AddressHash([]byte("testAccount")))
    23  
    24  	cases := []struct {
    25  		expectedErr string // empty means no error expected
    26  		msg         *types.MsgCreatePool
    27  	}{
    28  		{
    29  			"",
    30  			types.NewMsgCreatePool(poolCreator, DefaultPoolTypeId, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    31  		},
    32  		{
    33  			"invalid index of the pool type",
    34  			types.NewMsgCreatePool(poolCreator, 0, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    35  		},
    36  		{
    37  			"invalid pool creator address",
    38  			types.NewMsgCreatePool(sdk.AccAddress{}, DefaultPoolTypeId, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    39  		},
    40  		{
    41  			"invalid number of reserve coin",
    42  			types.NewMsgCreatePool(poolCreator, DefaultPoolTypeId, sdk.NewCoins(sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    43  		},
    44  		{
    45  			"invalid number of reserve coin",
    46  			types.NewMsgCreatePool(poolCreator, DefaultPoolTypeId, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)), sdk.NewCoin("denomZ", sdk.NewInt(1000)))),
    47  		},
    48  	}
    49  
    50  	for _, tc := range cases {
    51  		require.IsType(t, &types.MsgCreatePool{}, tc.msg)
    52  		require.Equal(t, types.TypeMsgCreatePool, tc.msg.Type())
    53  		require.Equal(t, types.RouterKey, tc.msg.Route())
    54  		require.Equal(t, sdk.MustSortJSON(types.ModuleCdc.MustMarshalJSON(tc.msg)), tc.msg.GetSignBytes())
    55  
    56  		err := tc.msg.ValidateBasic()
    57  		if tc.expectedErr == "" {
    58  			require.Nil(t, err)
    59  			signers := tc.msg.GetSigners()
    60  			require.Len(t, signers, 1)
    61  			require.Equal(t, tc.msg.GetPoolCreator(), signers[0])
    62  		} else {
    63  			require.EqualError(t, err, tc.expectedErr)
    64  		}
    65  	}
    66  }
    67  
    68  func TestMsgDepositWithinBatch(t *testing.T) {
    69  	depositor := sdk.AccAddress(crypto.AddressHash([]byte("testAccount")))
    70  
    71  	cases := []struct {
    72  		expectedErr string // empty means no error expected
    73  		msg         *types.MsgDepositWithinBatch
    74  	}{
    75  		{
    76  			"",
    77  			types.NewMsgDepositWithinBatch(depositor, DefaultPoolId, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    78  		},
    79  		{
    80  			"",
    81  			types.NewMsgDepositWithinBatch(depositor, 0, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    82  		},
    83  		{
    84  			"invalid pool depositor address",
    85  			types.NewMsgDepositWithinBatch(sdk.AccAddress{}, DefaultPoolId, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1000)), sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    86  		},
    87  		{
    88  			"invalid number of reserve coin",
    89  			types.NewMsgDepositWithinBatch(depositor, DefaultPoolId, sdk.NewCoins(sdk.NewCoin(DenomY, sdk.NewInt(1000)))),
    90  		},
    91  	}
    92  
    93  	for _, tc := range cases {
    94  		require.IsType(t, &types.MsgDepositWithinBatch{}, tc.msg)
    95  		require.Equal(t, types.TypeMsgDepositWithinBatch, tc.msg.Type())
    96  		require.Equal(t, types.RouterKey, tc.msg.Route())
    97  		require.Equal(t, sdk.MustSortJSON(types.ModuleCdc.MustMarshalJSON(tc.msg)), tc.msg.GetSignBytes())
    98  
    99  		err := tc.msg.ValidateBasic()
   100  		if tc.expectedErr == "" {
   101  			require.Nil(t, err)
   102  			signers := tc.msg.GetSigners()
   103  			require.Len(t, signers, 1)
   104  			require.Equal(t, tc.msg.GetDepositor(), signers[0])
   105  		} else {
   106  			require.EqualError(t, err, tc.expectedErr)
   107  		}
   108  	}
   109  }
   110  
   111  func TestMsgWithdrawWithinBatch(t *testing.T) {
   112  	withdrawer := sdk.AccAddress(crypto.AddressHash([]byte("testAccount")))
   113  	poolCoinDenom := "poolCoinDenom"
   114  
   115  	cases := []struct {
   116  		expectedErr string // empty means no error expected
   117  		msg         *types.MsgWithdrawWithinBatch
   118  	}{
   119  		{
   120  			"",
   121  			types.NewMsgWithdrawWithinBatch(withdrawer, DefaultPoolId, sdk.NewCoin(poolCoinDenom, sdk.NewInt(1000))),
   122  		},
   123  		{
   124  			"invalid pool withdrawer address",
   125  			types.NewMsgWithdrawWithinBatch(sdk.AccAddress{}, DefaultPoolId, sdk.NewCoin(poolCoinDenom, sdk.NewInt(1000))),
   126  		},
   127  		{
   128  			"invalid pool coin amount",
   129  			types.NewMsgWithdrawWithinBatch(withdrawer, DefaultPoolId, sdk.NewCoin(poolCoinDenom, sdk.NewInt(0))),
   130  		},
   131  	}
   132  
   133  	for _, tc := range cases {
   134  		require.IsType(t, &types.MsgWithdrawWithinBatch{}, tc.msg)
   135  		require.Equal(t, types.TypeMsgWithdrawWithinBatch, tc.msg.Type())
   136  		require.Equal(t, types.RouterKey, tc.msg.Route())
   137  		require.Equal(t, sdk.MustSortJSON(types.ModuleCdc.MustMarshalJSON(tc.msg)), tc.msg.GetSignBytes())
   138  
   139  		err := tc.msg.ValidateBasic()
   140  		if tc.expectedErr == "" {
   141  			require.Nil(t, err)
   142  			signers := tc.msg.GetSigners()
   143  			require.Len(t, signers, 1)
   144  			require.Equal(t, tc.msg.GetWithdrawer(), signers[0])
   145  		} else {
   146  			require.EqualError(t, err, tc.expectedErr)
   147  		}
   148  	}
   149  }
   150  
   151  func TestMsgSwapWithinBatch(t *testing.T) {
   152  	swapRequester := sdk.AccAddress(crypto.AddressHash([]byte("testAccount")))
   153  	offerCoin := sdk.NewCoin(DenomX, sdk.NewInt(1000))
   154  	orderPrice, err := sdk.NewDecFromStr("0.1")
   155  	require.NoError(t, err)
   156  
   157  	cases := []struct {
   158  		expectedErr string // empty means no error expected
   159  		msg         *types.MsgSwapWithinBatch
   160  	}{
   161  		{
   162  			"",
   163  			types.NewMsgSwapWithinBatch(swapRequester, DefaultPoolId, DefaultSwapTypeId, offerCoin, DenomY, orderPrice, types.DefaultSwapFeeRate),
   164  		},
   165  		{
   166  			"invalid pool swap requester address",
   167  			types.NewMsgSwapWithinBatch(sdk.AccAddress{}, DefaultPoolId, DefaultSwapTypeId, offerCoin, DenomY, orderPrice, types.DefaultSwapFeeRate),
   168  		},
   169  		{
   170  			"invalid offer coin amount",
   171  			types.NewMsgSwapWithinBatch(swapRequester, DefaultPoolId, DefaultSwapTypeId, sdk.NewCoin(DenomX, sdk.NewInt(0)), DenomY, orderPrice, types.DefaultSwapFeeRate),
   172  		},
   173  		{
   174  			"invalid order price",
   175  			types.NewMsgSwapWithinBatch(swapRequester, DefaultPoolId, DefaultSwapTypeId, offerCoin, DenomY, sdk.ZeroDec(), types.DefaultSwapFeeRate),
   176  		},
   177  		{
   178  			"offer amount should be over 100 micro",
   179  			types.NewMsgSwapWithinBatch(swapRequester, DefaultPoolId, DefaultSwapTypeId, sdk.NewCoin(DenomX, sdk.NewInt(1)), DenomY, orderPrice, types.DefaultSwapFeeRate),
   180  		},
   181  	}
   182  
   183  	for _, tc := range cases {
   184  		require.IsType(t, &types.MsgSwapWithinBatch{}, tc.msg)
   185  		require.Equal(t, types.TypeMsgSwapWithinBatch, tc.msg.Type())
   186  		require.Equal(t, types.RouterKey, tc.msg.Route())
   187  		require.Equal(t, sdk.MustSortJSON(types.ModuleCdc.MustMarshalJSON(tc.msg)), tc.msg.GetSignBytes())
   188  
   189  		err := tc.msg.ValidateBasic()
   190  		if tc.expectedErr == "" {
   191  			require.Nil(t, err)
   192  			signers := tc.msg.GetSigners()
   193  			require.Len(t, signers, 1)
   194  			require.Equal(t, tc.msg.GetSwapRequester(), signers[0])
   195  		} else {
   196  			require.EqualError(t, err, tc.expectedErr)
   197  		}
   198  	}
   199  }
   200  
   201  func TestMsgPanics(t *testing.T) {
   202  	emptyMsgCreatePool := types.MsgCreatePool{}
   203  	emptyMsgDeposit := types.MsgDepositWithinBatch{}
   204  	emptyMsgWithdraw := types.MsgWithdrawWithinBatch{}
   205  	emptyMsgSwap := types.MsgSwapWithinBatch{}
   206  	for _, msg := range []sdk.Msg{&emptyMsgCreatePool, &emptyMsgDeposit, &emptyMsgWithdraw, &emptyMsgSwap} {
   207  		require.PanicsWithError(t, "empty address string is not allowed", func() { msg.GetSigners() })
   208  	}
   209  	for _, tc := range []func() sdk.AccAddress{
   210  		emptyMsgCreatePool.GetPoolCreator,
   211  		emptyMsgDeposit.GetDepositor,
   212  		emptyMsgWithdraw.GetWithdrawer,
   213  		emptyMsgSwap.GetSwapRequester,
   214  	} {
   215  		require.PanicsWithError(t, "empty address string is not allowed", func() { tc() })
   216  	}
   217  }
   218  
   219  func TestMsgValidateBasic(t *testing.T) {
   220  	validPoolTypeId := DefaultPoolTypeId
   221  	validAddr := sdk.AccAddress(crypto.AddressHash([]byte("testAccount"))).String()
   222  	validCoin := sdk.NewCoin(DenomY, sdk.NewInt(10000))
   223  
   224  	invalidDenomCoin := sdk.Coin{Denom: "-", Amount: sdk.NewInt(10000)}
   225  	negativeCoin := sdk.Coin{Denom: DenomX, Amount: sdk.NewInt(-1)}
   226  	zeroCoin := sdk.Coin{Denom: DenomX, Amount: sdk.ZeroInt()}
   227  
   228  	coinsWithInvalidDenom := sdk.Coins{invalidDenomCoin, validCoin}
   229  	coinsWithNegative := sdk.Coins{negativeCoin, validCoin}
   230  	coinsWithZero := sdk.Coins{zeroCoin, validCoin}
   231  
   232  	invalidDenomErrMsg := "invalid denom: -"
   233  	negativeCoinErrMsg := "coin -1denomX amount is not positive"
   234  	negativeAmountErrMsg := "negative coin amount: -1"
   235  	zeroCoinErrMsg := "coin 0denomX amount is not positive"
   236  
   237  	t.Run("MsgCreatePool", func(t *testing.T) {
   238  		for _, tc := range []struct {
   239  			msg    types.MsgCreatePool
   240  			errMsg string
   241  		}{
   242  			{
   243  				types.MsgCreatePool{},
   244  				types.ErrBadPoolTypeID.Error(),
   245  			},
   246  			{
   247  				types.MsgCreatePool{PoolTypeId: validPoolTypeId},
   248  				types.ErrInvalidPoolCreatorAddr.Error(),
   249  			},
   250  			{
   251  				types.MsgCreatePool{PoolCreatorAddress: validAddr, PoolTypeId: validPoolTypeId},
   252  				types.ErrNumOfReserveCoin.Error(),
   253  			},
   254  			{
   255  				types.MsgCreatePool{
   256  					PoolCreatorAddress: validAddr,
   257  					PoolTypeId:         validPoolTypeId,
   258  					DepositCoins:       coinsWithInvalidDenom,
   259  				},
   260  				invalidDenomErrMsg,
   261  			},
   262  			{
   263  				types.MsgCreatePool{
   264  					PoolCreatorAddress: validAddr,
   265  					PoolTypeId:         validPoolTypeId,
   266  					DepositCoins:       coinsWithNegative,
   267  				},
   268  				negativeCoinErrMsg,
   269  			},
   270  			{
   271  				types.MsgCreatePool{
   272  					PoolCreatorAddress: validAddr,
   273  					PoolTypeId:         validPoolTypeId,
   274  					DepositCoins:       coinsWithZero,
   275  				},
   276  				zeroCoinErrMsg,
   277  			},
   278  			{
   279  				types.MsgCreatePool{
   280  					PoolCreatorAddress: validAddr,
   281  					PoolTypeId:         validPoolTypeId,
   282  					DepositCoins:       sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(int64(types.MinReserveCoinNum)-1))),
   283  				},
   284  				types.ErrNumOfReserveCoin.Error(),
   285  			},
   286  			{
   287  				types.MsgCreatePool{
   288  					PoolCreatorAddress: validAddr,
   289  					PoolTypeId:         validPoolTypeId,
   290  					DepositCoins:       sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(int64(types.MaxReserveCoinNum)+1))),
   291  				},
   292  				types.ErrNumOfReserveCoin.Error(),
   293  			},
   294  		} {
   295  			err := tc.msg.ValidateBasic()
   296  			require.EqualError(t, err, tc.errMsg)
   297  		}
   298  	})
   299  	t.Run("MsgDepositWithinBatch", func(t *testing.T) {
   300  		for _, tc := range []struct {
   301  			msg    types.MsgDepositWithinBatch
   302  			errMsg string
   303  		}{
   304  			{
   305  				types.MsgDepositWithinBatch{},
   306  				types.ErrInvalidDepositorAddr.Error(),
   307  			},
   308  			{
   309  				types.MsgDepositWithinBatch{DepositorAddress: validAddr},
   310  				types.ErrBadDepositCoinsAmount.Error(),
   311  			},
   312  			{
   313  				types.MsgDepositWithinBatch{DepositorAddress: validAddr, DepositCoins: coinsWithInvalidDenom},
   314  				invalidDenomErrMsg,
   315  			},
   316  			{
   317  				types.MsgDepositWithinBatch{DepositorAddress: validAddr, DepositCoins: coinsWithNegative},
   318  				negativeCoinErrMsg,
   319  			},
   320  			{
   321  				types.MsgDepositWithinBatch{DepositorAddress: validAddr, DepositCoins: coinsWithZero},
   322  				zeroCoinErrMsg,
   323  			},
   324  			{
   325  				types.MsgDepositWithinBatch{
   326  					DepositorAddress: validAddr,
   327  					DepositCoins:     sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(int64(types.MinReserveCoinNum)-1))),
   328  				},
   329  				types.ErrNumOfReserveCoin.Error(),
   330  			},
   331  			{
   332  				types.MsgDepositWithinBatch{
   333  					DepositorAddress: validAddr,
   334  					DepositCoins:     sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(int64(types.MaxReserveCoinNum)+1))),
   335  				},
   336  				types.ErrNumOfReserveCoin.Error(),
   337  			},
   338  		} {
   339  			err := tc.msg.ValidateBasic()
   340  			require.EqualError(t, err, tc.errMsg)
   341  		}
   342  	})
   343  	t.Run("MsgWithdrawWithinBatch", func(t *testing.T) {
   344  		for _, tc := range []struct {
   345  			msg    types.MsgWithdrawWithinBatch
   346  			errMsg string
   347  		}{
   348  			{
   349  				types.MsgWithdrawWithinBatch{},
   350  				types.ErrInvalidWithdrawerAddr.Error(),
   351  			},
   352  			{
   353  				types.MsgWithdrawWithinBatch{WithdrawerAddress: validAddr, PoolCoin: invalidDenomCoin},
   354  				invalidDenomErrMsg,
   355  			},
   356  			{
   357  				types.MsgWithdrawWithinBatch{WithdrawerAddress: validAddr, PoolCoin: negativeCoin},
   358  				negativeAmountErrMsg,
   359  			},
   360  			{
   361  				types.MsgWithdrawWithinBatch{WithdrawerAddress: validAddr, PoolCoin: zeroCoin},
   362  				types.ErrBadPoolCoinAmount.Error(),
   363  			},
   364  		} {
   365  			err := tc.msg.ValidateBasic()
   366  			require.EqualError(t, err, tc.errMsg)
   367  		}
   368  	})
   369  	t.Run("MsgSwap", func(t *testing.T) {
   370  		offerCoin := sdk.NewCoin(DenomX, sdk.NewInt(10000))
   371  		orderPrice := sdk.MustNewDecFromStr("1.0")
   372  
   373  		for _, tc := range []struct {
   374  			msg    types.MsgSwapWithinBatch
   375  			errMsg string
   376  		}{
   377  			{
   378  				types.MsgSwapWithinBatch{},
   379  				types.ErrInvalidSwapRequesterAddr.Error(),
   380  			},
   381  			{
   382  				types.MsgSwapWithinBatch{SwapRequesterAddress: validAddr, OfferCoin: invalidDenomCoin, OrderPrice: orderPrice},
   383  				invalidDenomErrMsg,
   384  			},
   385  			{
   386  				types.MsgSwapWithinBatch{SwapRequesterAddress: validAddr, OfferCoin: zeroCoin},
   387  				types.ErrBadOfferCoinAmount.Error(),
   388  			},
   389  			{
   390  				types.MsgSwapWithinBatch{SwapRequesterAddress: validAddr, OfferCoin: negativeCoin},
   391  				negativeAmountErrMsg,
   392  			},
   393  			{
   394  				types.MsgSwapWithinBatch{SwapRequesterAddress: validAddr, OfferCoin: offerCoin, OrderPrice: sdk.ZeroDec()},
   395  				types.ErrBadOrderPrice.Error(),
   396  			},
   397  			{
   398  				types.MsgSwapWithinBatch{SwapRequesterAddress: validAddr, OfferCoin: sdk.NewCoin(DenomX, sdk.OneInt()), OrderPrice: orderPrice},
   399  				types.ErrLessThanMinOfferAmount.Error(),
   400  			},
   401  		} {
   402  			err := tc.msg.ValidateBasic()
   403  			require.EqualError(t, err, tc.errMsg)
   404  		}
   405  	})
   406  }