github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/client/cli/cli_test.go (about) 1 //go:build norace 2 // +build norace 3 4 package cli_test 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "io" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/gogo/protobuf/proto" 16 "github.com/spf13/viper" 17 "github.com/stretchr/testify/suite" 18 19 "github.com/cosmos/cosmos-sdk/client" 20 "github.com/cosmos/cosmos-sdk/client/flags" 21 "github.com/cosmos/cosmos-sdk/codec" 22 "github.com/cosmos/cosmos-sdk/codec/types" 23 "github.com/cosmos/cosmos-sdk/server" 24 servertypes "github.com/cosmos/cosmos-sdk/server/types" 25 "github.com/cosmos/cosmos-sdk/testutil" 26 clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" 27 "github.com/cosmos/cosmos-sdk/testutil/network" 28 sdk "github.com/cosmos/cosmos-sdk/types" 29 "github.com/cosmos/cosmos-sdk/types/module" 30 genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" 31 genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" 32 govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" 33 paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" 34 35 lapp "github.com/gravity-devs/liquidity/app" 36 "github.com/gravity-devs/liquidity/x/liquidity" 37 "github.com/gravity-devs/liquidity/x/liquidity/client/cli" 38 liquiditytestutil "github.com/gravity-devs/liquidity/x/liquidity/client/testutil" 39 liquiditytypes "github.com/gravity-devs/liquidity/x/liquidity/types" 40 41 tmcli "github.com/tendermint/tendermint/libs/cli" 42 tmjson "github.com/tendermint/tendermint/libs/json" 43 tmlog "github.com/tendermint/tendermint/libs/log" 44 tmtypes "github.com/tendermint/tendermint/types" 45 tmdb "github.com/tendermint/tm-db" 46 ) 47 48 type IntegrationTestSuite struct { 49 suite.Suite 50 51 cfg network.Config 52 network *network.Network 53 54 db *tmdb.MemDB // in-memory database is needed for exporting genesis cli integration test 55 } 56 57 // SetupTest creates a new network for _each_ integration test. We create a new 58 // network for each test because there are some state modifications that are 59 // needed to be made in order to make useful queries. However, we don't want 60 // these state changes to be present in other tests. 61 func (s *IntegrationTestSuite) SetupTest() { 62 s.T().Log("setting up integration test suite") 63 64 db := tmdb.NewMemDB() 65 66 cfg := liquiditytestutil.NewConfig(db) 67 cfg.NumValidators = 1 68 69 var liquidityGenState liquiditytypes.GenesisState 70 err := cfg.Codec.UnmarshalJSON(cfg.GenesisState[liquiditytypes.ModuleName], &liquidityGenState) 71 s.Require().NoError(err) 72 73 liquidityGenState.Params = liquiditytypes.DefaultParams() 74 75 cfg.GenesisState[liquiditytypes.ModuleName] = cfg.Codec.MustMarshalJSON(&liquidityGenState) 76 cfg.AccountTokens = sdk.NewInt(100_000_000_000) // node0token denom 77 cfg.StakingTokens = sdk.NewInt(100_000_000_000) // stake denom 78 79 genesisStateGov := govtypes.DefaultGenesisState() 80 genesisStateGov.DepositParams = govtypes.NewDepositParams(sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, govtypes.DefaultMinDepositTokens)), time.Duration(15)*time.Second) 81 genesisStateGov.VotingParams = govtypes.NewVotingParams(time.Duration(3) * time.Second) 82 genesisStateGov.TallyParams.Quorum = sdk.MustNewDecFromStr("0.01") 83 bz, err := cfg.Codec.MarshalJSON(genesisStateGov) 84 s.Require().NoError(err) 85 cfg.GenesisState["gov"] = bz 86 87 s.cfg = cfg 88 s.network = network.New(s.T(), s.cfg) 89 s.db = db 90 91 _, err = s.network.WaitForHeight(1) 92 s.Require().NoError(err) 93 } 94 95 // TearDownTest cleans up the curret test network after each test in the suite. 96 func (s *IntegrationTestSuite) TearDownTest() { 97 s.T().Log("tearing down integration test suite") 98 s.network.Cleanup() 99 } 100 101 // TestIntegrationTestSuite every integration test suite. 102 func TestIntegrationTestSuite(t *testing.T) { 103 suite.Run(t, new(IntegrationTestSuite)) 104 } 105 106 func (s *IntegrationTestSuite) TestNewCreatePoolCmd() { 107 val := s.network.Validators[0] 108 109 // use two different tokens that are minted to the test account 110 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 111 112 testCases := []struct { 113 name string 114 args []string 115 expectErr bool 116 respType proto.Message 117 expectedCode uint32 118 }{ 119 { 120 "invalid pool type id", 121 []string{ 122 "invalidpooltypeid", 123 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 124 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 125 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 126 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 127 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 128 }, 129 true, nil, 0, 130 }, 131 { 132 "pool type id is not supported", 133 []string{ 134 fmt.Sprintf("%d", uint32(2)), 135 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000)), sdk.NewCoin("denomZ", sdk.NewInt(100_000_000))).String(), 136 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 137 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 138 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 139 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 140 }, 141 true, nil, 0, 142 }, 143 { 144 "invalid number of denoms", 145 []string{ 146 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 147 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000)), sdk.NewCoin("denomZ", sdk.NewInt(100_000_000))).String(), 148 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 149 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 150 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 151 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 152 }, 153 true, nil, 0, 154 }, 155 { 156 "deposit coin less than minimum deposit amount", 157 []string{ 158 fmt.Sprintf("%d", uint32(1)), 159 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(1_000)), sdk.NewCoin(denomY, sdk.NewInt(10_000_000))).String(), 160 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 161 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 162 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 163 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 164 }, 165 false, &sdk.TxResponse{}, 9, 166 }, 167 { 168 "valid transaction", 169 []string{ 170 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 171 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 172 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 173 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 174 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 175 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 176 }, 177 false, &sdk.TxResponse{}, 0, 178 }, 179 } 180 181 for _, tc := range testCases { 182 tc := tc 183 184 s.Run(tc.name, func() { 185 cmd := cli.NewCreatePoolCmd() 186 clientCtx := val.ClientCtx 187 188 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 189 190 if tc.expectErr { 191 s.Require().Error(err) 192 } else { 193 s.Require().NoError(err, out.String()) 194 s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) 195 196 txResp := tc.respType.(*sdk.TxResponse) 197 s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) 198 } 199 }) 200 } 201 } 202 203 func (s *IntegrationTestSuite) TestNewDepositWithinBatchCmd() { 204 val := s.network.Validators[0] 205 206 // use two different tokens that are minted to the test account 207 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 208 209 // liquidity pool should be created prior to test this integration test 210 _, err := liquiditytestutil.MsgCreatePoolExec( 211 val.ClientCtx, 212 val.Address.String(), 213 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 214 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 215 ) 216 s.Require().NoError(err) 217 218 err = s.network.WaitForNextBlock() 219 s.Require().NoError(err) 220 221 testCases := []struct { 222 name string 223 args []string 224 expectErr bool 225 respType proto.Message 226 expectedCode uint32 227 }{ 228 { 229 "invalid pool id", 230 []string{ 231 "invalidpoolid", 232 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(1_000_000)), sdk.NewCoin(denomY, sdk.NewInt(1_000_000))).String(), 233 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 234 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 235 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 236 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 237 }, 238 true, nil, 0, 239 }, 240 { 241 "invalid number of denoms", 242 []string{ 243 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 244 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(1_000_000)), sdk.NewCoin(denomY, sdk.NewInt(1_000_000)), sdk.NewCoin("denomZ", sdk.NewInt(1_000_000))).String(), 245 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 246 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 247 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 248 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 249 }, 250 true, nil, 0, 251 }, 252 { 253 "valid transaction", 254 []string{ 255 fmt.Sprintf("%d", uint32(1)), 256 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000_000)), sdk.NewCoin(denomY, sdk.NewInt(10_000_000))).String(), 257 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 258 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 259 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 260 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 261 }, 262 false, &sdk.TxResponse{}, 0, 263 }, 264 } 265 266 for _, tc := range testCases { 267 tc := tc 268 269 s.Run(tc.name, func() { 270 cmd := cli.NewDepositWithinBatchCmd() 271 clientCtx := val.ClientCtx 272 273 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 274 275 if tc.expectErr { 276 s.Require().Error(err) 277 } else { 278 s.Require().NoError(err, out.String()) 279 s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) 280 281 txResp := tc.respType.(*sdk.TxResponse) 282 s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) 283 } 284 }) 285 } 286 } 287 288 func (s *IntegrationTestSuite) TestNewWithdrawWithinBatchCmd() { 289 val := s.network.Validators[0] 290 291 // use two different tokens that are minted to the test account 292 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 293 294 // liquidity pool should be created prior to test this integration test 295 _, err := liquiditytestutil.MsgCreatePoolExec( 296 val.ClientCtx, 297 val.Address.String(), 298 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 299 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 300 ) 301 s.Require().NoError(err) 302 303 err = s.network.WaitForNextBlock() 304 s.Require().NoError(err) 305 306 testCases := []struct { 307 name string 308 args []string 309 expectErr bool 310 respType proto.Message 311 expectedCode uint32 312 }{ 313 { 314 "invalid pool id", 315 []string{ 316 "invalidpoolid", 317 sdk.NewCoins(sdk.NewCoin("poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D", sdk.NewInt(10_000))).String(), 318 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 319 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 320 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 321 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 322 }, 323 true, nil, 0, 324 }, 325 { 326 "bad pool coin", 327 []string{ 328 fmt.Sprintf("%d", uint32(1)), 329 sdk.NewCoins(sdk.NewCoin("badpoolcoindenom", sdk.NewInt(10_000))).String(), 330 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 331 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 332 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 333 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 334 }, 335 false, &sdk.TxResponse{}, 29, 336 }, 337 { 338 "valid transaction", 339 []string{ 340 fmt.Sprintf("%d", uint32(1)), 341 sdk.NewCoins(sdk.NewCoin("poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D", sdk.NewInt(10_000))).String(), 342 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 343 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 344 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 345 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 346 }, 347 false, &sdk.TxResponse{}, 0, 348 }, 349 } 350 351 for _, tc := range testCases { 352 tc := tc 353 354 s.Run(tc.name, func() { 355 cmd := cli.NewWithdrawWithinBatchCmd() 356 clientCtx := val.ClientCtx 357 358 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 359 360 if tc.expectErr { 361 s.Require().Error(err) 362 } else { 363 s.Require().NoError(err, out.String()) 364 s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) 365 366 txResp := tc.respType.(*sdk.TxResponse) 367 s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) 368 } 369 }) 370 } 371 } 372 373 func (s *IntegrationTestSuite) TestNewSwapWithinBatchCmd() { 374 val := s.network.Validators[0] 375 376 // use two different tokens that are minted to the test account 377 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 378 379 // liquidity pool should be created prior to test this integration test 380 _, err := liquiditytestutil.MsgCreatePoolExec( 381 val.ClientCtx, 382 val.Address.String(), 383 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 384 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 385 ) 386 s.Require().NoError(err) 387 388 err = s.network.WaitForNextBlock() 389 s.Require().NoError(err) 390 391 testCases := []struct { 392 name string 393 args []string 394 expectErr bool 395 respType proto.Message 396 expectedCode uint32 397 }{ 398 { 399 "invalid pool id", 400 []string{ 401 "invalidpoolid", 402 fmt.Sprintf("%d", uint32(1)), 403 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000))).String(), 404 denomY, 405 fmt.Sprintf("%.2f", 0.02), 406 fmt.Sprintf("%.3f", 0.003), 407 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 408 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 409 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 410 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 411 }, 412 true, nil, 0, 413 }, 414 { 415 "swap type id not supported", 416 []string{ 417 fmt.Sprintf("%d", uint32(1)), 418 fmt.Sprintf("%d", uint32(2)), 419 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000))).String(), 420 denomY, 421 fmt.Sprintf("%.2f", 0.02), 422 fmt.Sprintf("%.2f", 0.03), 423 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 424 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 425 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 426 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 427 }, 428 true, nil, 0, 429 }, 430 { 431 "bad offer coin fee", 432 []string{ 433 fmt.Sprintf("%d", uint32(1)), 434 fmt.Sprintf("%d", uint32(1)), 435 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000))).String(), 436 denomY, 437 fmt.Sprintf("%.2f", 0.02), 438 fmt.Sprintf("%.2f", 0.01), 439 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 440 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 441 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 442 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 443 }, 444 false, &sdk.TxResponse{}, 35, 445 }, 446 { 447 "valid transaction", 448 []string{ 449 fmt.Sprintf("%d", uint32(1)), 450 fmt.Sprintf("%d", liquiditytypes.DefaultSwapTypeID), 451 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000))).String(), 452 denomY, 453 fmt.Sprintf("%.2f", 1.0), 454 fmt.Sprintf("%.3f", 0.003), 455 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), 456 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 457 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 458 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 459 }, 460 false, &sdk.TxResponse{}, 0, 461 }, 462 } 463 464 for _, tc := range testCases { 465 tc := tc 466 467 s.Run(tc.name, func() { 468 cmd := cli.NewSwapWithinBatchCmd() 469 clientCtx := val.ClientCtx 470 471 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 472 473 if tc.expectErr { 474 s.Require().Error(err) 475 } else { 476 s.Require().NoError(err, out.String()) 477 s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) 478 479 txResp := tc.respType.(*sdk.TxResponse) 480 s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) 481 } 482 }) 483 } 484 } 485 486 func (s *IntegrationTestSuite) TestGetCmdQueryParams() { 487 val := s.network.Validators[0] 488 489 testCases := []struct { 490 name string 491 args []string 492 expectedOutput string 493 }{ 494 { 495 "json output", 496 []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, 497 `{"pool_types":[{"id":1,"name":"StandardLiquidityPool","min_reserve_coin_num":2,"max_reserve_coin_num":2,"description":"Standard liquidity pool with pool price function X/Y, ESPM constraint, and two kinds of reserve coins"}],"min_init_deposit_amount":"1000000","init_pool_coin_mint_amount":"1000000","max_reserve_coin_amount":"0","pool_creation_fee":[{"denom":"stake","amount":"40000000"}],"swap_fee_rate":"0.003000000000000000","withdraw_fee_rate":"0.000000000000000000","max_order_amount_ratio":"0.100000000000000000","unit_batch_height":1,"circuit_breaker_enabled":false}`, 498 }, 499 { 500 "text output", 501 []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, 502 `circuit_breaker_enabled: false 503 init_pool_coin_mint_amount: "1000000" 504 max_order_amount_ratio: "0.100000000000000000" 505 max_reserve_coin_amount: "0" 506 min_init_deposit_amount: "1000000" 507 pool_creation_fee: 508 - amount: "40000000" 509 denom: stake 510 pool_types: 511 - description: Standard liquidity pool with pool price function X/Y, ESPM constraint, 512 and two kinds of reserve coins 513 id: 1 514 max_reserve_coin_num: 2 515 min_reserve_coin_num: 2 516 name: StandardLiquidityPool 517 swap_fee_rate: "0.003000000000000000" 518 unit_batch_height: 1 519 withdraw_fee_rate: "0.000000000000000000"`, 520 }, 521 } 522 523 for _, tc := range testCases { 524 tc := tc 525 526 s.Run(tc.name, func() { 527 cmd := cli.GetCmdQueryParams() 528 clientCtx := val.ClientCtx 529 530 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 531 s.Require().NoError(err) 532 s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) 533 }) 534 } 535 } 536 537 func (s *IntegrationTestSuite) TestGetCmdQueryLiquidityPool() { 538 val := s.network.Validators[0] 539 540 // use two different tokens that are minted to the test account 541 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 542 543 // liquidity pool should be created prior to test this integration test 544 _, err := liquiditytestutil.MsgCreatePoolExec( 545 val.ClientCtx, 546 val.Address.String(), 547 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 548 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 549 ) 550 s.Require().NoError(err) 551 552 err = s.network.WaitForNextBlock() 553 s.Require().NoError(err) 554 555 testCases := []struct { 556 name string 557 args []string 558 expectErr bool 559 }{ 560 { 561 "with invalid pool id", 562 []string{ 563 "invalidpoolid", 564 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 565 }, 566 true, 567 }, 568 { 569 "with not supported pool id", 570 []string{ 571 fmt.Sprintf("%d", uint32(2)), 572 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 573 }, 574 true, 575 }, 576 { 577 "valid case with pool id", 578 []string{ 579 fmt.Sprintf("%d", uint32(1)), 580 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 581 }, 582 false, 583 }, 584 { 585 "with invalid pool coin denom", 586 []string{ 587 fmt.Sprintf("--%s=%s", cli.FlagPoolCoinDenom, "invalid_value"), 588 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 589 }, 590 true, 591 }, 592 { 593 "with empty pool coin denom", 594 []string{ 595 fmt.Sprintf("--%s", cli.FlagPoolCoinDenom), 596 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 597 }, 598 true, 599 }, 600 { 601 "valid case with pool coin denom", 602 []string{ 603 fmt.Sprintf("--%s=%s", cli.FlagPoolCoinDenom, "poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D"), 604 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 605 }, 606 false, 607 }, 608 { 609 "with invalid reserve acc", 610 []string{ 611 fmt.Sprintf("--%s=%s", cli.FlagReserveAcc, "invalid_value"), 612 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 613 }, 614 true, 615 }, 616 { 617 "with empty reserve acc", 618 []string{ 619 fmt.Sprintf("--%s", cli.FlagReserveAcc), 620 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 621 }, 622 true, 623 }, 624 { 625 "valid case with reserve acc", 626 []string{ 627 fmt.Sprintf("--%s=%s", cli.FlagReserveAcc, "cosmos1cva80e6jcxpezd3k5dl7zwy2eg30u7ld3y0a67"), 628 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 629 }, 630 false, 631 }, 632 } 633 634 for _, tc := range testCases { 635 tc := tc 636 637 s.Run(tc.name, func() { 638 cmd := cli.GetCmdQueryLiquidityPool() 639 clientCtx := val.ClientCtx 640 641 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 642 643 if tc.expectErr { 644 s.Require().Error(err) 645 } else { 646 var resp liquiditytypes.QueryLiquidityPoolResponse 647 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) 648 s.Require().NoError(err) 649 s.Require().Equal(uint64(1), resp.GetPool().Id) 650 s.Require().Equal(uint32(1), resp.GetPool().TypeId) 651 s.Require().Len(resp.GetPool().ReserveCoinDenoms, 2) 652 } 653 }) 654 } 655 } 656 657 func (s *IntegrationTestSuite) TestGetCmdQueryLiquidityPools() { 658 val := s.network.Validators[0] 659 660 // use two different tokens that are minted to the test account 661 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 662 663 // liquidity pool should be created prior to test this integration test 664 _, err := liquiditytestutil.MsgCreatePoolExec( 665 val.ClientCtx, 666 val.Address.String(), 667 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 668 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 669 ) 670 s.Require().NoError(err) 671 672 err = s.network.WaitForNextBlock() 673 s.Require().NoError(err) 674 675 testCases := []struct { 676 name string 677 args []string 678 expectErr bool 679 }{ 680 { 681 "valid case", 682 []string{ 683 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 684 }, 685 false, 686 }, 687 } 688 689 for _, tc := range testCases { 690 tc := tc 691 692 s.Run(tc.name, func() { 693 cmd := cli.GetCmdQueryLiquidityPools() 694 clientCtx := val.ClientCtx 695 696 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 697 698 if tc.expectErr { 699 s.Require().Error(err) 700 } else { 701 var resps liquiditytypes.QueryLiquidityPoolsResponse 702 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resps) 703 s.Require().NoError(err) 704 705 for _, pool := range resps.GetPools() { 706 s.Require().Equal(uint64(1), pool.Id) 707 s.Require().Equal(uint32(1), pool.TypeId) 708 s.Require().Len(pool.ReserveCoinDenoms, 2) 709 } 710 } 711 }) 712 } 713 } 714 715 func (s *IntegrationTestSuite) TestGetCmdQueryLiquidityPoolBatch() { 716 val := s.network.Validators[0] 717 718 // use two different tokens that are minted to the test account 719 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 720 721 // liquidity pool should be created prior to test this integration test 722 _, err := liquiditytestutil.MsgCreatePoolExec( 723 val.ClientCtx, 724 val.Address.String(), 725 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 726 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 727 ) 728 s.Require().NoError(err) 729 730 testCases := []struct { 731 name string 732 args []string 733 expectErr bool 734 }{ 735 { 736 "with invalid pool id", 737 []string{ 738 "invalidpoolid", 739 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 740 }, 741 true, 742 }, 743 { 744 "with not supported pool id", 745 []string{ 746 fmt.Sprintf("%d", uint32(2)), 747 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 748 }, 749 true, 750 }, 751 { 752 "valid case", 753 []string{ 754 fmt.Sprintf("%d", uint32(1)), 755 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 756 }, 757 false, 758 }, 759 } 760 761 for _, tc := range testCases { 762 tc := tc 763 764 s.Run(tc.name, func() { 765 cmd := cli.GetCmdQueryLiquidityPoolBatch() 766 clientCtx := val.ClientCtx 767 768 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 769 770 if tc.expectErr { 771 s.Require().Error(err) 772 } else { 773 var resp liquiditytypes.QueryLiquidityPoolBatchResponse 774 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) 775 s.Require().NoError(err) 776 s.Require().Equal(uint64(1), resp.GetBatch().PoolId) 777 s.Require().Equal(false, resp.GetBatch().Executed) 778 } 779 }) 780 } 781 } 782 783 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchDepositMsg() { 784 val := s.network.Validators[0] 785 786 // use two different tokens that are minted to the test account 787 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 788 789 // liquidity pool should be created prior to test this integration test 790 _, err := liquiditytestutil.MsgCreatePoolExec( 791 val.ClientCtx, 792 val.Address.String(), 793 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 794 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 795 ) 796 s.Require().NoError(err) 797 798 err = s.network.WaitForNextBlock() 799 s.Require().NoError(err) 800 801 // create new deposit 802 _, err = liquiditytestutil.MsgDepositWithinBatchExec( 803 val.ClientCtx, 804 val.Address.String(), 805 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 806 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000_000)), sdk.NewCoin(denomY, sdk.NewInt(10_000_000))).String(), 807 ) 808 s.Require().NoError(err) 809 810 testCases := []struct { 811 name string 812 args []string 813 expectErr bool 814 }{ 815 { 816 "with invalid pool id", 817 []string{ 818 "invalidpoolid", 819 fmt.Sprintf("%d", uint32(1)), 820 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 821 }, 822 true, 823 }, 824 { 825 "with not supported pool id", 826 []string{ 827 fmt.Sprintf("%d", uint32(2)), 828 fmt.Sprintf("%d", uint32(1)), 829 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 830 }, 831 true, 832 }, 833 { 834 "valid case", 835 []string{ 836 fmt.Sprintf("%d", uint32(1)), 837 fmt.Sprintf("%d", uint32(1)), 838 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 839 }, 840 false, 841 }, 842 } 843 844 for _, tc := range testCases { 845 tc := tc 846 847 s.Run(tc.name, func() { 848 cmd := cli.GetCmdQueryPoolBatchDepositMsg() 849 clientCtx := val.ClientCtx 850 851 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 852 853 if tc.expectErr { 854 s.Require().Error(err) 855 } else { 856 var resp liquiditytypes.QueryPoolBatchDepositMsgResponse 857 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) 858 s.Require().NoError(err) 859 s.Require().Equal(val.Address.String(), resp.GetDeposit().Msg.DepositorAddress) 860 s.Require().Equal(true, resp.GetDeposit().Executed) 861 s.Require().Equal(true, resp.GetDeposit().Succeeded) 862 s.Require().Equal(true, resp.GetDeposit().ToBeDeleted) 863 } 864 }) 865 } 866 } 867 868 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchDepositMsgs() { 869 val := s.network.Validators[0] 870 871 // use two different tokens that are minted to the test account 872 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 873 874 // liquidity pool should be created prior to test this integration test 875 _, err := liquiditytestutil.MsgCreatePoolExec( 876 val.ClientCtx, 877 val.Address.String(), 878 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 879 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 880 ) 881 s.Require().NoError(err) 882 883 err = s.network.WaitForNextBlock() 884 s.Require().NoError(err) 885 886 // create new deposit 887 _, err = liquiditytestutil.MsgDepositWithinBatchExec( 888 val.ClientCtx, 889 val.Address.String(), 890 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 891 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000_000)), sdk.NewCoin(denomY, sdk.NewInt(10_000_000))).String(), 892 ) 893 s.Require().NoError(err) 894 895 testCases := []struct { 896 name string 897 args []string 898 expectErr bool 899 }{ 900 { 901 "with invalid pool id", 902 []string{ 903 "invalidpoolid", 904 fmt.Sprintf("%d", uint32(1)), 905 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 906 }, 907 true, 908 }, 909 { 910 "with not supported pool id", 911 []string{ 912 fmt.Sprintf("%d", uint32(2)), 913 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 914 }, 915 true, 916 }, 917 { 918 "valid case", 919 []string{ 920 fmt.Sprintf("%d", uint32(1)), 921 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 922 }, 923 false, 924 }, 925 } 926 927 for _, tc := range testCases { 928 tc := tc 929 930 s.Run(tc.name, func() { 931 cmd := cli.GetCmdQueryPoolBatchDepositMsgs() 932 clientCtx := val.ClientCtx 933 934 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 935 936 if tc.expectErr { 937 s.Require().Error(err) 938 } else { 939 var resps liquiditytypes.QueryPoolBatchDepositMsgsResponse 940 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resps) 941 s.Require().NoError(err) 942 943 for _, deposit := range resps.GetDeposits() { 944 s.Require().Equal(true, deposit.Executed) 945 s.Require().Equal(true, deposit.Succeeded) 946 s.Require().Equal(true, deposit.ToBeDeleted) 947 } 948 } 949 }) 950 } 951 } 952 953 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchWithdrawMsg() { 954 val := s.network.Validators[0] 955 956 // use two different tokens that are minted to the test account 957 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 958 959 // liquidity pool should be created prior to test this integration test 960 _, err := liquiditytestutil.MsgCreatePoolExec( 961 val.ClientCtx, 962 val.Address.String(), 963 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 964 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 965 ) 966 s.Require().NoError(err) 967 968 err = s.network.WaitForNextBlock() 969 s.Require().NoError(err) 970 971 // withdraw pool coin from the pool 972 poolCoinDenom := "poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D" 973 _, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 974 val.ClientCtx, 975 val.Address.String(), 976 fmt.Sprintf("%d", uint32(1)), 977 sdk.NewCoins(sdk.NewCoin(poolCoinDenom, sdk.NewInt(10_000))).String(), 978 ) 979 s.Require().NoError(err) 980 981 testCases := []struct { 982 name string 983 args []string 984 expectErr bool 985 }{ 986 { 987 "with invalid pool id", 988 []string{ 989 "invalidpoolid", 990 fmt.Sprintf("%d", uint32(1)), 991 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 992 }, 993 true, 994 }, 995 { 996 "with not suppoorted pool id", 997 []string{ 998 fmt.Sprintf("%d", uint32(2)), 999 fmt.Sprintf("%d", uint32(1)), 1000 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1001 }, 1002 true, 1003 }, 1004 { 1005 "valid case", 1006 []string{ 1007 fmt.Sprintf("%d", uint32(1)), 1008 fmt.Sprintf("%d", uint32(1)), 1009 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1010 }, 1011 false, 1012 }, 1013 } 1014 1015 for _, tc := range testCases { 1016 tc := tc 1017 1018 s.Run(tc.name, func() { 1019 cmd := cli.GetCmdQueryPoolBatchWithdrawMsg() 1020 clientCtx := val.ClientCtx 1021 1022 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 1023 1024 if tc.expectErr { 1025 s.Require().Error(err) 1026 } else { 1027 var resp liquiditytypes.QueryPoolBatchWithdrawMsgResponse 1028 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) 1029 s.Require().NoError(err) 1030 s.Require().Equal(val.Address.String(), resp.GetWithdraw().Msg.WithdrawerAddress) 1031 s.Require().Equal(poolCoinDenom, resp.GetWithdraw().Msg.PoolCoin.Denom) 1032 s.Require().Equal(true, resp.GetWithdraw().Executed) 1033 s.Require().Equal(true, resp.GetWithdraw().Succeeded) 1034 s.Require().Equal(true, resp.GetWithdraw().ToBeDeleted) 1035 } 1036 }) 1037 } 1038 } 1039 1040 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchWithdrawMsgs() { 1041 val := s.network.Validators[0] 1042 1043 // use two different tokens that are minted to the test account 1044 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 1045 1046 // liquidity pool should be created prior to test this integration test 1047 _, err := liquiditytestutil.MsgCreatePoolExec( 1048 val.ClientCtx, 1049 val.Address.String(), 1050 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1051 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(100_000_000)), sdk.NewCoin(denomY, sdk.NewInt(100_000_000))).String(), 1052 ) 1053 s.Require().NoError(err) 1054 1055 err = s.network.WaitForNextBlock() 1056 s.Require().NoError(err) 1057 1058 // withdraw pool coin from the pool 1059 _, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 1060 val.ClientCtx, 1061 val.Address.String(), 1062 fmt.Sprintf("%d", uint32(1)), 1063 sdk.NewCoins(sdk.NewCoin("poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D", sdk.NewInt(10_000))).String(), 1064 ) 1065 s.Require().NoError(err) 1066 1067 testCases := []struct { 1068 name string 1069 args []string 1070 expectErr bool 1071 }{ 1072 { 1073 "with invalid pool id", 1074 []string{ 1075 "invalidpoolid", 1076 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1077 }, 1078 true, 1079 }, 1080 { 1081 "with not supported pool id", 1082 []string{ 1083 fmt.Sprintf("%d", uint32(2)), 1084 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1085 }, 1086 true, 1087 }, 1088 { 1089 "valid case", 1090 []string{ 1091 fmt.Sprintf("%d", uint32(1)), 1092 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1093 }, 1094 false, 1095 }, 1096 } 1097 1098 for _, tc := range testCases { 1099 tc := tc 1100 1101 s.Run(tc.name, func() { 1102 cmd := cli.GetCmdQueryPoolBatchWithdrawMsgs() 1103 clientCtx := val.ClientCtx 1104 1105 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 1106 1107 if tc.expectErr { 1108 s.Require().Error(err) 1109 } else { 1110 var resps liquiditytypes.QueryPoolBatchWithdrawMsgsResponse 1111 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resps) 1112 s.Require().NoError(err) 1113 1114 for _, withdraw := range resps.GetWithdraws() { 1115 s.Require().Equal(val.Address.String(), withdraw.Msg.WithdrawerAddress) 1116 s.Require().Equal(true, withdraw.Executed) 1117 s.Require().Equal(true, withdraw.Succeeded) 1118 s.Require().Equal(true, withdraw.ToBeDeleted) 1119 } 1120 } 1121 }) 1122 } 1123 } 1124 1125 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchSwapMsg() { 1126 val := s.network.Validators[0] 1127 1128 // use two different tokens that are minted to the test account 1129 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 1130 X := sdk.NewCoin(denomX, sdk.NewInt(1_000_000_000)) 1131 Y := sdk.NewCoin(denomY, sdk.NewInt(5_000_000_000)) 1132 1133 // liquidity pool should be created prior to test this integration test 1134 _, err := liquiditytestutil.MsgCreatePoolExec( 1135 val.ClientCtx, 1136 val.Address.String(), 1137 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1138 sdk.NewCoins(X, Y).String(), 1139 ) 1140 s.Require().NoError(err) 1141 1142 err = s.network.WaitForNextBlock() 1143 s.Require().NoError(err) 1144 1145 // swap coins from the pool 1146 offerCoin := sdk.NewCoin(denomY, sdk.NewInt(50_000_000)) 1147 _, err = liquiditytestutil.MsgSwapWithinBatchExec( 1148 val.ClientCtx, 1149 val.Address.String(), 1150 fmt.Sprintf("%d", uint32(1)), 1151 fmt.Sprintf("%d", liquiditytypes.DefaultSwapTypeID), 1152 offerCoin.String(), 1153 denomX, 1154 fmt.Sprintf("%.3f", 0.019), 1155 fmt.Sprintf("%.3f", 0.003), 1156 ) 1157 s.Require().NoError(err) 1158 1159 testCases := []struct { 1160 name string 1161 args []string 1162 expectErr bool 1163 }{ 1164 { 1165 "with invalid pool id", 1166 []string{ 1167 "invalidpoolid", 1168 fmt.Sprintf("%d", uint32(1)), 1169 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1170 }, 1171 true, 1172 }, 1173 { 1174 "with not supported pool id", 1175 []string{ 1176 fmt.Sprintf("%d", uint32(2)), 1177 fmt.Sprintf("%d", uint32(1)), 1178 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1179 }, 1180 true, 1181 }, 1182 { 1183 "valid case", 1184 []string{ 1185 fmt.Sprintf("%d", uint32(1)), 1186 fmt.Sprintf("%d", uint32(1)), 1187 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1188 }, 1189 false, 1190 }, 1191 } 1192 1193 for _, tc := range testCases { 1194 tc := tc 1195 1196 s.Run(tc.name, func() { 1197 cmd := cli.GetCmdQueryPoolBatchSwapMsg() 1198 clientCtx := val.ClientCtx 1199 1200 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 1201 1202 if tc.expectErr { 1203 s.Require().Error(err) 1204 } else { 1205 var resp liquiditytypes.QueryPoolBatchSwapMsgResponse 1206 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) 1207 s.Require().NoError(err) 1208 s.Require().Equal(val.Address.String(), resp.GetSwap().Msg.SwapRequesterAddress) 1209 s.Require().Equal(true, resp.GetSwap().Executed) 1210 s.Require().Equal(true, resp.GetSwap().Succeeded) 1211 s.Require().Equal(true, resp.GetSwap().ToBeDeleted) 1212 } 1213 }) 1214 } 1215 } 1216 1217 func (s *IntegrationTestSuite) TestGetCircuitBreaker() { 1218 val := s.network.Validators[0] 1219 1220 // use two different tokens that are minted to the test account 1221 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 1222 X := sdk.NewCoin(denomX, sdk.NewInt(1_000_000_000)) 1223 Y := sdk.NewCoin(denomY, sdk.NewInt(5_000_000_000)) 1224 1225 // liquidity pool should be created prior to test this integration test 1226 _, err := liquiditytestutil.MsgCreatePoolExec( 1227 val.ClientCtx, 1228 val.Address.String(), 1229 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1230 sdk.NewCoins(X, Y).String(), 1231 ) 1232 s.Require().NoError(err) 1233 1234 err = s.network.WaitForNextBlock() 1235 s.Require().NoError(err) 1236 1237 // swap coins from the pool 1238 offerCoin := sdk.NewCoin(denomY, sdk.NewInt(50_000_000)) 1239 output, err := liquiditytestutil.MsgSwapWithinBatchExec( 1240 val.ClientCtx, 1241 val.Address.String(), 1242 fmt.Sprintf("%d", uint32(1)), 1243 fmt.Sprintf("%d", liquiditytypes.DefaultSwapTypeID), 1244 offerCoin.String(), 1245 denomX, 1246 fmt.Sprintf("%.3f", 0.019), 1247 fmt.Sprintf("%.3f", 0.003), 1248 ) 1249 var txRes sdk.TxResponse 1250 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1251 s.Require().Equal(txRes.Code, uint32(0)) 1252 s.Require().NoError(err) 1253 1254 circuitBreakerEnabled := true 1255 circuitBreakerEnabledStr, err := json.Marshal(&circuitBreakerEnabled) 1256 if err != nil { 1257 panic(err) 1258 } 1259 1260 paramChange := paramscutils.ParamChangeProposalJSON{ 1261 Title: "enable-circuit-breaker", 1262 Description: "enable circuit breaker", 1263 Changes: []paramscutils.ParamChangeJSON{{ 1264 Subspace: liquiditytypes.ModuleName, 1265 Key: "CircuitBreakerEnabled", 1266 Value: circuitBreakerEnabledStr, 1267 }, 1268 }, 1269 Deposit: sdk.NewCoin(s.cfg.BondDenom, govtypes.DefaultMinDepositTokens).String(), 1270 } 1271 paramChangeProp, err := json.Marshal(¶mChange) 1272 if err != nil { 1273 panic(err) 1274 } 1275 1276 //create a param change proposal with deposit 1277 _, err = liquiditytestutil.MsgParamChangeProposalExec( 1278 val.ClientCtx, 1279 val.Address.String(), 1280 testutil.WriteToNewTempFile(s.T(), string(paramChangeProp)).Name(), 1281 ) 1282 err = s.network.WaitForNextBlock() 1283 s.Require().NoError(err) 1284 1285 _, err = liquiditytestutil.MsgVote(val.ClientCtx, val.Address.String(), "1", "yes") 1286 s.Require().NoError(err) 1287 err = s.network.WaitForNextBlock() 1288 s.Require().NoError(err) 1289 1290 // check if circuit breaker is enabled 1291 expectedOutput := `{"pool_types":[{"id":1,"name":"StandardLiquidityPool","min_reserve_coin_num":2,"max_reserve_coin_num":2,"description":"Standard liquidity pool with pool price function X/Y, ESPM constraint, and two kinds of reserve coins"}],"min_init_deposit_amount":"1000000","init_pool_coin_mint_amount":"1000000","max_reserve_coin_amount":"0","pool_creation_fee":[{"denom":"stake","amount":"40000000"}],"swap_fee_rate":"0.003000000000000000","withdraw_fee_rate":"0.000000000000000000","max_order_amount_ratio":"0.100000000000000000","unit_batch_height":1,"circuit_breaker_enabled":true}` 1292 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.GetCmdQueryParams(), []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) 1293 s.Require().NoError(err) 1294 s.Require().Equal(expectedOutput, strings.TrimSpace(out.String())) 1295 1296 // fail swap coins because of circuit breaker 1297 output, err = liquiditytestutil.MsgSwapWithinBatchExec( 1298 val.ClientCtx, 1299 val.Address.String(), 1300 fmt.Sprintf("%d", uint32(1)), 1301 fmt.Sprintf("%d", liquiditytypes.DefaultSwapTypeID), 1302 offerCoin.String(), 1303 denomX, 1304 fmt.Sprintf("%.3f", 0.019), 1305 fmt.Sprintf("%.3f", 0.003), 1306 ) 1307 s.Require().NoError(err) 1308 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1309 s.Require().Equal(txRes.Code, uint32(40)) 1310 s.Require().Equal(txRes.RawLog, "failed to execute message; message index: 0: circuit breaker is triggered") 1311 1312 // fail create new pool because of circuit breaker 1313 output, err = liquiditytestutil.MsgCreatePoolExec( 1314 val.ClientCtx, 1315 val.Address.String(), 1316 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1317 sdk.NewCoins(X, Y).String(), 1318 ) 1319 s.Require().NoError(err) 1320 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1321 s.Require().Equal(txRes.Code, uint32(40)) 1322 s.Require().Equal(txRes.RawLog, "failed to execute message; message index: 0: circuit breaker is triggered") 1323 1324 // fail create new deposit because of circuit breaker 1325 _, err = liquiditytestutil.MsgDepositWithinBatchExec( 1326 val.ClientCtx, 1327 val.Address.String(), 1328 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1329 sdk.NewCoins(sdk.NewCoin(denomX, sdk.NewInt(10_000_000)), sdk.NewCoin(denomY, sdk.NewInt(10_000_000))).String(), 1330 ) 1331 s.Require().NoError(err) 1332 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1333 s.Require().Equal(txRes.Code, uint32(40)) 1334 s.Require().Equal(txRes.RawLog, "failed to execute message; message index: 0: circuit breaker is triggered") 1335 1336 // success withdraw pool coin from the pool even though circuit breaker is true 1337 poolCoinDenom := "poolC33A77E752C183913636A37FE1388ACA22FE7BED792BEB2E72EF2DA857703D8D" 1338 output, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 1339 val.ClientCtx, 1340 val.Address.String(), 1341 fmt.Sprintf("%d", uint32(1)), 1342 sdk.NewCoins(sdk.NewCoin(poolCoinDenom, sdk.NewInt(500000))).String(), 1343 ) 1344 s.Require().NoError(err) 1345 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1346 s.Require().Equal(txRes.Code, uint32(0)) 1347 1348 output, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 1349 val.ClientCtx, 1350 val.Address.String(), 1351 fmt.Sprintf("%d", uint32(1)), 1352 sdk.NewCoins(sdk.NewCoin(poolCoinDenom, sdk.NewInt(499999))).String(), 1353 ) 1354 s.Require().NoError(err) 1355 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1356 s.Require().Equal(txRes.Code, uint32(0)) 1357 1358 // withdraw last pool coin 1359 output, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 1360 val.ClientCtx, 1361 val.Address.String(), 1362 fmt.Sprintf("%d", uint32(1)), 1363 sdk.NewCoins(sdk.NewCoin(poolCoinDenom, sdk.NewInt(1))).String(), 1364 ) 1365 s.Require().NoError(err) 1366 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1367 s.Require().Equal(txRes.Code, uint32(0)) 1368 1369 // fail withdraw because of the pool is depleted 1370 output, err = liquiditytestutil.MsgWithdrawWithinBatchExec( 1371 val.ClientCtx, 1372 val.Address.String(), 1373 fmt.Sprintf("%d", uint32(1)), 1374 sdk.NewCoins(sdk.NewCoin(poolCoinDenom, sdk.NewInt(1))).String(), 1375 ) 1376 s.Require().NoError(err) 1377 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(output.Bytes(), &txRes)) 1378 s.Require().Equal(txRes.Code, uint32(39)) 1379 s.Require().Equal(txRes.RawLog, "failed to execute message; message index: 0: the pool is depleted of reserve coin, reinitializing is required by deposit") 1380 } 1381 1382 func (s *IntegrationTestSuite) TestGetCmdQueryPoolBatchSwapMsgs() { 1383 val := s.network.Validators[0] 1384 1385 // use two different tokens that are minted to the test account 1386 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 1387 X := sdk.NewCoin(denomX, sdk.NewInt(1_000_000_000)) 1388 Y := sdk.NewCoin(denomY, sdk.NewInt(5_000_000_000)) 1389 1390 // liquidity pool should be created prior to test this integration test 1391 _, err := liquiditytestutil.MsgCreatePoolExec( 1392 val.ClientCtx, 1393 val.Address.String(), 1394 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1395 sdk.NewCoins(X, Y).String(), 1396 ) 1397 s.Require().NoError(err) 1398 1399 err = s.network.WaitForNextBlock() 1400 s.Require().NoError(err) 1401 1402 // swap coins from the pool 1403 offerCoin := sdk.NewCoin(denomY, sdk.NewInt(50_000_000)) 1404 _, err = liquiditytestutil.MsgSwapWithinBatchExec( 1405 val.ClientCtx, 1406 val.Address.String(), 1407 fmt.Sprintf("%d", uint32(1)), 1408 fmt.Sprintf("%d", liquiditytypes.DefaultSwapTypeID), 1409 offerCoin.String(), 1410 denomX, 1411 fmt.Sprintf("%.3f", 0.019), 1412 fmt.Sprintf("%.3f", 0.003), 1413 ) 1414 s.Require().NoError(err) 1415 1416 testCases := []struct { 1417 name string 1418 args []string 1419 expectErr bool 1420 }{ 1421 { 1422 "with invalid pool id", 1423 []string{ 1424 "invalidpoolid", 1425 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1426 }, 1427 true, 1428 }, 1429 { 1430 "with not supported pool id", 1431 []string{ 1432 fmt.Sprintf("%d", uint32(2)), 1433 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1434 }, 1435 true, 1436 }, 1437 { 1438 "valid case", 1439 []string{ 1440 fmt.Sprintf("%d", uint32(1)), 1441 fmt.Sprintf("--%s=json", tmcli.OutputFlag), 1442 }, 1443 false, 1444 }, 1445 } 1446 1447 for _, tc := range testCases { 1448 tc := tc 1449 1450 s.Run(tc.name, func() { 1451 cmd := cli.GetCmdQueryPoolBatchSwapMsgs() 1452 clientCtx := val.ClientCtx 1453 1454 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 1455 1456 if tc.expectErr { 1457 s.Require().Error(err) 1458 } else { 1459 var resps liquiditytypes.QueryPoolBatchSwapMsgsResponse 1460 err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resps) 1461 s.Require().NoError(err) 1462 1463 for _, swap := range resps.GetSwaps() { 1464 s.Require().Equal(val.Address.String(), swap.Msg.SwapRequesterAddress) 1465 s.Require().Equal(true, swap.Executed) 1466 s.Require().Equal(true, swap.Succeeded) 1467 s.Require().Equal(true, swap.ToBeDeleted) 1468 } 1469 } 1470 }) 1471 } 1472 } 1473 1474 func (s *IntegrationTestSuite) TestInitGenesis() { 1475 testCases := []struct { 1476 name string 1477 flags func(dir string) []string 1478 expectErr bool 1479 err error 1480 }{ 1481 { 1482 name: "default genesis state", 1483 flags: func(dir string) []string { 1484 return []string{ 1485 "liquidity-test", 1486 } 1487 }, 1488 expectErr: false, 1489 err: nil, 1490 }, 1491 } 1492 for _, tc := range testCases { 1493 tc := tc 1494 s.Run(tc.name, func() { 1495 testMbm := module.NewBasicManager(liquidity.AppModuleBasic{}) 1496 1497 home := s.T().TempDir() 1498 logger := tmlog.NewNopLogger() 1499 cfg, err := genutiltest.CreateDefaultTendermintConfig(home) 1500 s.Require().NoError(err) 1501 1502 serverCtx := server.NewContext(viper.New(), cfg, logger) 1503 interfaceRegistry := types.NewInterfaceRegistry() 1504 marshaler := codec.NewProtoCodec(interfaceRegistry) 1505 clientCtx := client.Context{}. 1506 WithCodec(marshaler). 1507 WithHomeDir(home) 1508 1509 ctx := context.Background() 1510 ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) 1511 ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) 1512 1513 cmd := genutilcli.InitCmd(testMbm, home) 1514 cmd.SetArgs( 1515 tc.flags(home), 1516 ) 1517 1518 if tc.expectErr { 1519 err := cmd.ExecuteContext(ctx) 1520 s.Require().EqualError(err, tc.err.Error()) 1521 } else { 1522 s.Require().NoError(cmd.ExecuteContext(ctx)) 1523 } 1524 }) 1525 } 1526 } 1527 1528 func (s *IntegrationTestSuite) TestExportGenesis() { 1529 clientCtx := s.network.Validators[0].ClientCtx 1530 serverCtx := s.network.Validators[0].Ctx 1531 1532 home := clientCtx.HomeDir 1533 1534 // verify genesis file saved in temp directory 1535 genDocFile := clientCtx.HomeDir + "/config/genesis.json" 1536 genDoc, err := tmtypes.GenesisDocFromFile(genDocFile) 1537 s.Require().NoError(err) 1538 s.Require().NotNil(genDoc) 1539 1540 val := s.network.Validators[0] 1541 1542 // use two different tokens that are minted to the test account 1543 denomX, denomY := liquiditytypes.AlphabeticalDenomPair("node0token", s.network.Config.BondDenom) 1544 X := sdk.NewCoin(denomX, sdk.NewInt(1_000_000_000)) 1545 Y := sdk.NewCoin(denomY, sdk.NewInt(5_000_000_000)) 1546 1547 // create liquidity pool 1548 _, err = liquiditytestutil.MsgCreatePoolExec( 1549 val.ClientCtx, 1550 val.Address.String(), 1551 fmt.Sprintf("%d", liquiditytypes.DefaultPoolTypeID), 1552 sdk.NewCoins(X, Y).String(), 1553 ) 1554 s.Require().NoError(err) 1555 1556 err = s.network.WaitForNextBlock() 1557 s.Require().NoError(err) 1558 1559 cmd := server.ExportCmd( 1560 func(_ tmlog.Logger, _ tmdb.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, 1561 appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) { 1562 1563 encCfg := lapp.MakeEncodingConfig() 1564 encCfg.Marshaler = codec.NewProtoCodec(encCfg.InterfaceRegistry) 1565 1566 // get logger and in-memory database 1567 logger := serverCtx.Logger 1568 db := s.db 1569 1570 var app *lapp.LiquidityApp 1571 if height != -1 { 1572 app = lapp.NewLiquidityApp(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), encCfg, appOpts) 1573 1574 if err := app.LoadHeight(height); err != nil { 1575 return servertypes.ExportedApp{}, err 1576 } 1577 } else { 1578 app = lapp.NewLiquidityApp(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), encCfg, appOpts) 1579 } 1580 1581 return app.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) 1582 }, 1583 home, 1584 ) 1585 1586 args := []string{fmt.Sprintf("--%s=%s", flags.FlagHome, home)} 1587 1588 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) 1589 s.Require().NoError(err) 1590 1591 var exportedGenDoc tmtypes.GenesisDoc 1592 err = tmjson.Unmarshal(out.Bytes(), &exportedGenDoc) 1593 s.Require().NoError(err) 1594 1595 s.Require().Equal(clientCtx.ChainID, exportedGenDoc.ChainID) 1596 }