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 }