From c83205fc21a82ce50fb70f417ff681d6e83eef87 Mon Sep 17 00:00:00 2001 From: Hanjun Kim Date: Mon, 18 Jul 2022 17:24:46 +0900 Subject: [PATCH 1/2] fix: limit the number of active pools in a pair --- x/liquidity/keeper/pool.go | 18 ++++++++++++ x/liquidity/keeper/pool_test.go | 52 +++++++++++++++++++++++++++++++++ x/liquidity/types/errors.go | 1 + x/liquidity/types/params.go | 4 +++ 4 files changed, 75 insertions(+) diff --git a/x/liquidity/keeper/pool.go b/x/liquidity/keeper/pool.go index 2d0178d94..bc745b11d 100644 --- a/x/liquidity/keeper/pool.go +++ b/x/liquidity/keeper/pool.go @@ -89,16 +89,23 @@ func (k Keeper) ValidateMsgCreatePool(ctx sdk.Context, msg *types.MsgCreatePool) // Check if there is a basic pool in the pair. // Creating multiple basic pools within the same pair is disallowed. duplicate := false + numActivePools := 0 _ = k.IteratePoolsByPair(ctx, pair.Id, func(pool types.Pool) (stop bool, err error) { if pool.Type == types.PoolTypeBasic && !pool.Disabled { duplicate = true return true, nil } + if !pool.Disabled { + numActivePools++ + } return false, nil }) if duplicate { return types.ErrPoolAlreadyExists } + if numActivePools >= types.MaxNumActivePoolsPerPair { + return types.ErrTooManyPools + } return nil } @@ -191,6 +198,17 @@ func (k Keeper) ValidateMsgCreateRangedPool(ctx sdk.Context, msg *types.MsgCreat } } + numActivePools := 0 + _ = k.IteratePoolsByPair(ctx, pair.Id, func(pool types.Pool) (stop bool, err error) { + if !pool.Disabled { + numActivePools++ + } + return false, nil + }) + if numActivePools >= types.MaxNumActivePoolsPerPair { + return types.ErrTooManyPools + } + return nil } diff --git a/x/liquidity/keeper/pool_test.go b/x/liquidity/keeper/pool_test.go index 920a1c1fe..572c72357 100644 --- a/x/liquidity/keeper/pool_test.go +++ b/x/liquidity/keeper/pool_test.go @@ -591,3 +591,55 @@ func (s *KeeperTestSuite) TestRangedPoolDepositWithdraw_single_side2() { s.Require().True(intEq(sdk.ZeroInt(), s.getBalance(s.addr(3), "denom2").Amount)) s.Require().True(intEq(sdk.NewInt(1000000), s.getBalance(s.addr(3), "denom1").Amount)) } + +func (s *KeeperTestSuite) TestMaxNumActivePoolsPerPair() { + s.Run("basic pool and ranged pools", func() { + s.SetupTest() + pair := s.createPair(s.addr(0), "denom1", "denom2", true) + + pool1 := s.createPool(s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), true) + for i := 0; i < types.MaxNumActivePoolsPerPair-1; i++ { + s.createRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.0"), true) + } + + s.fundAddr(s.addr(0), utils.ParseCoins("1000000denom1,1000000denom2")) + _, err := s.keeper.CreateRangedPool(s.ctx, types.NewMsgCreateRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.5"))) + s.Require().ErrorIs(err, types.ErrTooManyPools) + + s.withdraw(s.addr(0), pool1.Id, sdk.NewCoin(pool1.PoolCoinDenom, s.keeper.GetPoolCoinSupply(s.ctx, pool1))) + s.nextBlock() + + s.createRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.0"), true) + }) + + s.Run("ranged pools only", func() { + s.SetupTest() + pair := s.createPair(s.addr(0), "denom1", "denom2", true) + + for i := 0; i < types.MaxNumActivePoolsPerPair; i++ { + s.createRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.0"), true) + } + pool1, _ := s.keeper.GetPool(s.ctx, 1) + + s.fundAddr(s.addr(0), utils.ParseCoins("1000000denom1,1000000denom2")) + _, err := s.keeper.CreateRangedPool(s.ctx, types.NewMsgCreateRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.5"))) + s.Require().ErrorIs(err, types.ErrTooManyPools) + + s.withdraw(s.addr(0), pool1.Id, sdk.NewCoin(pool1.PoolCoinDenom, s.keeper.GetPoolCoinSupply(s.ctx, pool1))) + s.nextBlock() + + s.createRangedPool( + s.addr(0), pair.Id, utils.ParseCoins("1000000denom1,1000000denom2"), + utils.ParseDec("0.5"), utils.ParseDec("2.0"), utils.ParseDec("1.0"), true) + }) +} diff --git a/x/liquidity/types/errors.go b/x/liquidity/types/errors.go index 1f64b9c1c..b77f759a6 100644 --- a/x/liquidity/types/errors.go +++ b/x/liquidity/types/errors.go @@ -24,4 +24,5 @@ var ( ErrDuplicatePairId = sdkerrors.Register(ModuleName, 16, "duplicate pair id presents in the pair id list") ErrTooSmallOrder = sdkerrors.Register(ModuleName, 17, "too small order") ErrTooLargePool = sdkerrors.Register(ModuleName, 18, "too large pool") + ErrTooManyPools = sdkerrors.Register(ModuleName, 19, "too many pools in the pair") ) diff --git a/x/liquidity/types/params.go b/x/liquidity/types/params.go index a636a0afd..7b82bb393 100644 --- a/x/liquidity/types/params.go +++ b/x/liquidity/types/params.go @@ -39,6 +39,10 @@ const ( PairEscrowAddressPrefix = "PairEscrowAddress" ModuleAddressNameSplitter = "|" AddressType = farmingtypes.AddressType32Bytes + + // MaxNumActivePoolsPerPair is the maximum number of active(not disabled) + // pools per pair. + MaxNumActivePoolsPerPair = 50 ) var ( From a406c7080df12f2edef47771fa87da810ff15052 Mon Sep 17 00:00:00 2001 From: Hanjun Kim Date: Mon, 18 Jul 2022 17:54:51 +0900 Subject: [PATCH 2/2] chore: update CHANGELOG --- CHANGELOG.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf81c459..05335a34c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,23 +38,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +## v2.1.0 + ### Client Breaking Changes * (x/liquidity) [\#338](https://github.com/cosmosquad-labs/squad/pull/338) Refactor `OrderBooks` query: * `tick_precisions` field has been removed from `QueryOrderBooksRequest` * `tick_precision` field has been removed from `OrderBookResponse` and `price_unit` has been added instead * The order between `sells` and `buys` has been changed - -### CLI Breaking Changes - -* (x/liquidity) [\#338](https://github.com/cosmosquad-labs/squad/pull/338) Refactor `order-books` query cmd: - * `[tick-precisions]` argument has been removed: `order-books [pair-ids]` - * Response structure has been changed - -## v2.0.0 - -### Client Breaking Changes - * (x/liquidity) [\#335](https://github.com/cosmosquad-labs/squad/pull/335) Modify `PoolResponse`: * `balances` field has been modified to contain `base_coin` and `quote_coin` fields * `pool_coin_supply` field has been added @@ -70,6 +61,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### CLI Breaking Changes +* (x/liquidity) [\#338](https://github.com/cosmosquad-labs/squad/pull/338) Refactor `order-books` query cmd: + * `[tick-precisions]` argument has been removed: `order-books [pair-ids]` + * Response structure has been changed * (x/farming) [\#334](https://github.com/cosmosquad-labs/squad/pull/334) Add `historical-rewards` query cmd: * `historical-rewards [staking-coin-denom]` * (x/liquidity) [\#318](https://github.com/cosmosquad-labs/squad/pull/318) Add `create-ranged-pool` tx cmd and `order-books` query cmd: @@ -82,6 +76,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking +* (x/liquidity) [\#340](https://github.com/cosmosquad-labs/squad/pull/340) Add `MaxNumActivePoolsPerPair` global constant * (x/liquidity) [\#318](https://github.com/cosmosquad-labs/squad/pull/318) Change `Pool` struct for ranged pools and refactor matching logic * Add `type`, `creator`, `min_price` and `max_price` fields to `Pool` struct * Refactor matching logic both for fairness of matching and efficiency of pool order placement