diff --git a/x/amm/keeper/pool.go b/x/amm/keeper/pool.go index 380f03f1..d686af13 100644 --- a/x/amm/keeper/pool.go +++ b/x/amm/keeper/pool.go @@ -91,12 +91,14 @@ func (k Keeper) IteratePoolOrders(ctx sdk.Context, pool types.Pool, isBuy bool, qty = types.Amount0DeltaRoundingDec(currentSqrtPrice, orderSqrtPrice, orderLiquidity, false) openQty = sdk.MinDec(reserveBalance, qty) } - if openQty.IsPositive() && (openQty.GTE(pool.MinOrderQuantity) || orderTick == tick) { + if openQty.IsPositive() && (orderTick == tick || (openQty.GTE(pool.MinOrderQuantity))) { if cb(orderPrice, qty, openQty) { return true } reserveBalance = reserveBalance.Sub(exchangetypes.DepositAmount(isBuy, orderPrice, qty)) currentPrice = orderPrice + } else { // No more possible order price + break } if orderTick == tick { break @@ -129,30 +131,36 @@ func NextOrderTick( intermediate := liquidityDec.Power(2).Add( minOrderQty.Mul(liquidityDec).MulTruncate(currentSqrtPrice).MulInt64(4)) orderSqrtPrice := utils.DecApproxSqrt(intermediate).Sub(liquidityDec).QuoTruncate(minOrderQty.MulInt64(2)) - if !orderSqrtPrice.IsPositive() || orderSqrtPrice.GTE(currentSqrtPrice) { + if !orderSqrtPrice.IsPositive() { return 0, false } // 2. Check min order quote orderSqrtPrice2 := currentSqrtPrice.Mul(liquidityDec).Sub(minOrderQuote).QuoTruncate(liquidityDec) - if !orderSqrtPrice2.IsPositive() || orderSqrtPrice2.GTE(currentSqrtPrice) { + if !orderSqrtPrice2.IsPositive() { return 0, false } orderPrice := sdk.MinDec(orderSqrtPrice, orderSqrtPrice2).Power(2) + if orderPrice.GTE(currentPrice) { + return 0, false + } tick = types.AdjustPriceToTickSpacing(orderPrice, tickSpacing, false) return tick, true } // 1. Check min order qty orderSqrtPrice := currentSqrtPrice.Mul(liquidityDec). QuoRoundUp(liquidityDec.Sub(minOrderQty.Mul(currentSqrtPrice))) - if !orderSqrtPrice.IsPositive() || orderSqrtPrice.LTE(currentSqrtPrice) { + if !orderSqrtPrice.IsPositive() { return 0, false } // 2. Check min order quote orderSqrtPrice2 := minOrderQuote.Mul(currentSqrtPrice).QuoRoundUp(liquidityDec).Add(currentSqrtPrice) - if !orderSqrtPrice2.IsPositive() || orderSqrtPrice2.LTE(currentSqrtPrice) { + if !orderSqrtPrice2.IsPositive() { return 0, false } orderPrice := sdk.MaxDec(orderSqrtPrice, orderSqrtPrice2).Power(2) + if orderPrice.LTE(currentPrice) { + return 0, false + } tick = types.AdjustPriceToTickSpacing(orderPrice, tickSpacing, true) return tick, true } diff --git a/x/amm/keeper/pool_test.go b/x/amm/keeper/pool_test.go index caee4ef9..1ab084db 100644 --- a/x/amm/keeper/pool_test.go +++ b/x/amm/keeper/pool_test.go @@ -199,3 +199,27 @@ func (s *KeeperTestSuite) TestSwapEdgecase1() { s.SwapExactAmountIn(ordererAddr, []uint64{market.Id}, utils.ParseDecCoin("35987097uusd"), utils.ParseDecCoin("0ucre"), false) } + +func (s *KeeperTestSuite) TestPoolOrdersEdgecase() { + // Check if there's no infinite loop in IteratePoolOrders. + market, pool := s.CreateMarketAndPool("ucre", "uusd", utils.ParseDec("0.000000089916180444")) + marketState := s.App.ExchangeKeeper.MustGetMarketState(s.Ctx, market.Id) + marketState.LastPrice = utils.ParseDecP("0.000000089795000000") + s.App.ExchangeKeeper.SetMarketState(s.Ctx, market.Id, marketState) + + lpAddr := s.FundedAccount(1, enoughCoins) + s.AddLiquidity( + lpAddr, pool.Id, types.MinPrice, types.MaxPrice, + utils.ParseCoins("13010176813853779ucre,1169825406uusd")) + + obs := s.App.ExchangeKeeper.ConstructMemOrderBookSide(s.Ctx, market, exchangetypes.MemOrderBookSideOptions{ + IsBuy: false, + MaxNumPriceLevels: 1, + }, nil) + s.Require().Len(obs.Levels(), 1) + obs = s.App.ExchangeKeeper.ConstructMemOrderBookSide(s.Ctx, market, exchangetypes.MemOrderBookSideOptions{ + IsBuy: true, + MaxNumPriceLevels: 1, + }, nil) + s.Require().Len(obs.Levels(), 1) +}