diff --git a/proto/neutron/dex/limit_order_tranche.proto b/proto/neutron/dex/limit_order_tranche.proto index 2f54beec0..8923ff8dc 100644 --- a/proto/neutron/dex/limit_order_tranche.proto +++ b/proto/neutron/dex/limit_order_tranche.proto @@ -49,10 +49,19 @@ message LimitOrderTranche { (gogoproto.stdtime) = true, (gogoproto.nullable) = true ]; + // DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. string price_taker_to_maker = 7 [ (gogoproto.moretags) = "yaml:\"price_taker_to_maker\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v5/utils/math.PrecDec", (gogoproto.nullable) = false, - (gogoproto.jsontag) = "price_taker_to_maker" + (gogoproto.jsontag) = "price_taker_to_maker", + deprecated = true + ]; + // This is the price of the LimitOrder denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) + string maker_price = 8 [ + (gogoproto.moretags) = "yaml:\"maker_price\"", + (gogoproto.customtype) = "github.com/neutron-org/neutron/v5/utils/math.PrecDec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "maker_price" ]; } diff --git a/proto/neutron/dex/pool_reserves.proto b/proto/neutron/dex/pool_reserves.proto index 998b2d0cb..9b143dac6 100644 --- a/proto/neutron/dex/pool_reserves.proto +++ b/proto/neutron/dex/pool_reserves.proto @@ -20,16 +20,28 @@ message PoolReserves { (gogoproto.jsontag) = "reserves_maker_denom", (gogoproto.nullable) = false ]; + // DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. string price_taker_to_maker = 3 [ (gogoproto.moretags) = "yaml:\"price_taker_to_maker\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v5/utils/math.PrecDec", (gogoproto.nullable) = false, - (gogoproto.jsontag) = "price_taker_to_maker" + (gogoproto.jsontag) = "price_taker_to_maker", + deprecated = true ]; + // DEPRECATED: price_opposite_taker_maker was an internal implementation detail and will be removed in a future release. + // It is being kept strictly for backwards compatibility. The actual field value is unused. string price_opposite_taker_to_maker = 4 [ (gogoproto.moretags) = "yaml:\"price_opposite_taker_to_maker\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v5/utils/math.PrecDec", (gogoproto.nullable) = false, - (gogoproto.jsontag) = "price_opposite_taker_to_maker" + (gogoproto.jsontag) = "price_opposite_taker_to_maker", + deprecated = true + ]; + // This is the price of the PoolReserves denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) + string maker_price = 5 [ + (gogoproto.moretags) = "yaml:\"maker_price\"", + (gogoproto.customtype) = "github.com/neutron-org/neutron/v5/utils/math.PrecDec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "maker_price" ]; } diff --git a/x/dex/keeper/grpc_query_simulate_place_limit_order.go b/x/dex/keeper/grpc_query_simulate_place_limit_order.go index 66ad120eb..6a3d3cd55 100644 --- a/x/dex/keeper/grpc_query_simulate_place_limit_order.go +++ b/x/dex/keeper/grpc_query_simulate_place_limit_order.go @@ -6,6 +6,7 @@ import ( "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + math_utils "github.com/neutron-org/neutron/v5/utils/math" "github.com/neutron-org/neutron/v5/x/dex/types" ) @@ -36,7 +37,8 @@ func (k Keeper) SimulatePlaceLimitOrder( } tickIndex := msg.TickIndexInToOut if msg.LimitSellPrice != nil { - tickIndex, err = types.CalcTickIndexFromPrice(*msg.LimitSellPrice) + limitBuyPrice := math_utils.OnePrecDec().Quo(*msg.LimitSellPrice) + tickIndex, err = types.CalcTickIndexFromPrice(limitBuyPrice) if err != nil { return nil, errors.Wrapf(err, "invalid LimitSellPrice %s", msg.LimitSellPrice.String()) } diff --git a/x/dex/keeper/integration_cancellimitorder_test.go b/x/dex/keeper/integration_cancellimitorder_test.go index 75b2fac56..9b5ef34ab 100644 --- a/x/dex/keeper/integration_cancellimitorder_test.go +++ b/x/dex/keeper/integration_cancellimitorder_test.go @@ -246,8 +246,8 @@ func (s *DexTestSuite) TestCancelPartiallyFilledWithdrawFails() { s.aliceCancelsLimitSell(trancheKey) // Then alice gets back remaining ~37 BIGTokenA LO reserves & 10 BIGTokenB taker tokens - s.assertAliceBalancesInt(sdkmath.NewInt(37786094), sdkmath.NewInt(10000000)) - s.assertDexBalancesInt(sdkmath.OneInt(), sdkmath.ZeroInt()) + s.assertAliceBalancesInt(sdkmath.NewInt(37786094), sdkmath.NewInt(9999999)) + s.assertDexBalancesInt(sdkmath.OneInt(), sdkmath.OneInt()) // Assert that the LimitOrderTrancheUser has been deleted _, found := s.App.DexKeeper.GetLimitOrderTrancheUser(s.Ctx, s.alice.String(), trancheKey) @@ -304,8 +304,8 @@ func (s *DexTestSuite) TestCancelPartiallyFilledMultiUser2() { s.aliceCancelsLimitSell(trancheKey) // THEN alice gets back remaining ~38 BIGTokenA LO reserves & 10 BIGTokenB taker tokens - s.assertAliceBalancesInt(sdkmath.NewInt(37786094), sdkmath.NewInt(10000000)) - s.assertDexBalancesInt(sdkmath.NewInt(37786096), sdkmath.NewInt(10000000)) + s.assertAliceBalancesInt(sdkmath.NewInt(37786094), sdkmath.NewInt(9999999)) + s.assertDexBalancesInt(sdkmath.NewInt(37786096), sdkmath.NewInt(10000001)) // THEN carol swap through more of the limitorder s.carolLimitSells("TokenB", -2001, 20, types.LimitOrderType_FILL_OR_KILL) diff --git a/x/dex/keeper/limit_order_tranche.go b/x/dex/keeper/limit_order_tranche.go index 68078abfa..2c0dcae57 100644 --- a/x/dex/keeper/limit_order_tranche.go +++ b/x/dex/keeper/limit_order_tranche.go @@ -10,6 +10,7 @@ import ( storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + math_utils "github.com/neutron-org/neutron/v5/utils/math" "github.com/neutron-org/neutron/v5/x/dex/types" "github.com/neutron-org/neutron/v5/x/dex/utils" ) @@ -18,7 +19,7 @@ func NewLimitOrderTranche( limitOrderTrancheKey *types.LimitOrderTrancheKey, goodTil *time.Time, ) (*types.LimitOrderTranche, error) { - priceTakerToMaker, err := limitOrderTrancheKey.PriceTakerToMaker() + price, err := limitOrderTrancheKey.Price() if err != nil { return nil, err } @@ -29,7 +30,8 @@ func NewLimitOrderTranche( TotalMakerDenom: math.ZeroInt(), TotalTakerDenom: math.ZeroInt(), ExpirationTime: goodTil, - PriceTakerToMaker: priceTakerToMaker, + MakerPrice: price, + PriceTakerToMaker: math_utils.OnePrecDec().Quo(price), }, nil } diff --git a/x/dex/keeper/liquidity.go b/x/dex/keeper/liquidity.go index 89c17c66f..87de37680 100644 --- a/x/dex/keeper/liquidity.go +++ b/x/dex/keeper/liquidity.go @@ -37,7 +37,7 @@ func (k Keeper) Swap( } // break as soon as we iterated past limitPrice - if limitPrice != nil && liq.Price().LT(*limitPrice) { + if limitPrice != nil && liq.Price().GT(*limitPrice) { break } @@ -52,11 +52,11 @@ func (k Keeper) Swap( // this avoids unnecessary iteration since outAmount will always be 0 going forward // this also catches the normal exit case where remainingTakerDenom == 0 - // NOTE: In theory this check should be: price * remainingTakerDenom < 1 + // NOTE: In theory this check should be: remainingTakerDenom / price < 1 // but due to rounding and inaccuracy of fixed decimal math, it is possible // for liq.swap to use the full the amount of taker liquidity and have a leftover // amount of the taker Denom > than 1 token worth of maker denom - if liq.Price().MulInt(remainingTakerDenom).LT(math_utils.NewPrecDec(2)) { + if math_utils.NewPrecDecFromInt(remainingTakerDenom).Quo(liq.Price()).LT(math_utils.NewPrecDec(2)) { orderFilled = true break } @@ -181,7 +181,7 @@ func (k Keeper) MakerLimitOrderSwap( if totalInCoin.Amount.IsPositive() { remainingIn := amountIn.Sub(totalInCoin.Amount) - expectedOutMakerPortion := limitPrice.MulInt(remainingIn) + expectedOutMakerPortion := math_utils.NewPrecDecFromInt(remainingIn).Quo(limitPrice) totalExpectedOut := expectedOutMakerPortion.Add(math_utils.NewPrecDecFromInt(totalOutCoin.Amount)) truePrice := totalExpectedOut.QuoInt(amountIn) diff --git a/x/dex/keeper/liquidity_test.go b/x/dex/keeper/liquidity_test.go index f5d4458f3..ce08905ba 100644 --- a/x/dex/keeper/liquidity_test.go +++ b/x/dex/keeper/liquidity_test.go @@ -483,8 +483,8 @@ func (s *DexTestSuite) TestSwap1To0LOMaxAmountNotUsed() { tokenIn, tokenOut := s.swapWithMaxOut("TokenB", "TokenA", 8, 15) // THEN swap should return 8 BIGTokenB in and ~8 BIGTokenA out - s.assertSwapOutputInt(tokenIn, sdkmath.NewInt(8_000_000), tokenOut, sdkmath.NewInt(8_000_800)) - s.assertTickBalancesInt(sdkmath.NewInt(1_999_200), sdkmath.NewInt(8_000_000)) + s.assertSwapOutputInt(tokenIn, sdkmath.NewInt(8_000_000), tokenOut, sdkmath.NewInt(8_000_799)) + s.assertTickBalancesInt(sdkmath.NewInt(1_999_201), sdkmath.NewInt(8_000_000)) } func (s *DexTestSuite) TestSwap0To1LOMaxAmountUsedMultiTick() { diff --git a/x/dex/keeper/migrations.go b/x/dex/keeper/migrations.go index ff154a91a..86912a78a 100644 --- a/x/dex/keeper/migrations.go +++ b/x/dex/keeper/migrations.go @@ -5,6 +5,7 @@ import ( v3 "github.com/neutron-org/neutron/v5/x/dex/migrations/v3" v4 "github.com/neutron-org/neutron/v5/x/dex/migrations/v4" + v5 "github.com/neutron-org/neutron/v5/x/dex/migrations/v5" ) // Migrator is a struct for handling in-place store migrations. @@ -26,3 +27,8 @@ func (m Migrator) Migrate2to3(ctx sdk.Context) error { func (m Migrator) Migrate3to4(ctx sdk.Context) error { return v4.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) } + +// Migrate4to5 migrates from version 4 to 5. +func (m Migrator) Migrate4to5(ctx sdk.Context) error { + return v5.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) +} diff --git a/x/dex/keeper/msg_server.go b/x/dex/keeper/msg_server.go index 70fe6bd65..03c5b80e2 100644 --- a/x/dex/keeper/msg_server.go +++ b/x/dex/keeper/msg_server.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + math_utils "github.com/neutron-org/neutron/v5/utils/math" "github.com/neutron-org/neutron/v5/x/dex/types" ) @@ -136,7 +137,8 @@ func (k MsgServer) PlaceLimitOrder( } tickIndex := msg.TickIndexInToOut if msg.LimitSellPrice != nil { - tickIndex, err = types.CalcTickIndexFromPrice(*msg.LimitSellPrice) + limitBuyPrice := math_utils.OnePrecDec().Quo(*msg.LimitSellPrice) + tickIndex, err = types.CalcTickIndexFromPrice(limitBuyPrice) if err != nil { return &types.MsgPlaceLimitOrderResponse{}, errors.Wrapf(err, "invalid LimitSellPrice %s", msg.LimitSellPrice.String()) } diff --git a/x/dex/keeper/multihop_swap.go b/x/dex/keeper/multihop_swap.go index 81e912306..c0f7a7485 100644 --- a/x/dex/keeper/multihop_swap.go +++ b/x/dex/keeper/multihop_swap.go @@ -149,7 +149,7 @@ func (k Keeper) HopsToRouteData( if !found { return routeArr, types.ErrLimitPriceNotSatisfied } - priceAcc = priceAcc.Mul(price) + priceAcc = priceAcc.Quo(price) routeArr[index] = MultihopStep{ tradePairID: tradePairID, RemainingBestPrice: priceAcc, diff --git a/x/dex/keeper/place_limit_order.go b/x/dex/keeper/place_limit_order.go index deb613f75..c827a0a4e 100644 --- a/x/dex/keeper/place_limit_order.go +++ b/x/dex/keeper/place_limit_order.go @@ -109,30 +109,29 @@ func (k Keeper) ExecutePlaceLimitOrder( ) (trancheKey string, totalIn math.Int, swapInCoin, swapOutCoin sdk.Coin, sharesIssued math.Int, err error) { amountLeft := amountIn - var limitPrice math_utils.PrecDec - limitPrice, err = types.CalcPrice(tickIndexInToOut) - + limitBuyPrice, err := types.CalcPrice(tickIndexInToOut) if err != nil { return trancheKey, totalIn, swapInCoin, swapOutCoin, math.ZeroInt(), err } - // Use limitPrice for minAvgSellPrice if it has not be specified - minAvgSellPrice := limitPrice + // Use limitPrice for minAvgSellPrice if it has not been specified + minAvgSellPrice := math_utils.OnePrecDec().Quo(limitBuyPrice) + if minAvgSellPriceP != nil { minAvgSellPrice = *minAvgSellPriceP } // Ensure that after rounding user will get at least 1 token out. - err = types.ValidateFairOutput(amountIn, limitPrice) + err = types.ValidateFairOutput(amountIn, limitBuyPrice) if err != nil { return trancheKey, totalIn, swapInCoin, swapOutCoin, math.ZeroInt(), err } var orderFilled bool if orderType.IsTakerOnly() { - swapInCoin, swapOutCoin, err = k.TakerLimitOrderSwap(ctx, *takerTradePairID, amountIn, maxAmountOut, limitPrice, minAvgSellPrice, orderType) + swapInCoin, swapOutCoin, err = k.TakerLimitOrderSwap(ctx, *takerTradePairID, amountIn, maxAmountOut, limitBuyPrice, minAvgSellPrice, orderType) } else { - swapInCoin, swapOutCoin, orderFilled, err = k.MakerLimitOrderSwap(ctx, *takerTradePairID, amountIn, limitPrice, minAvgSellPrice) + swapInCoin, swapOutCoin, orderFilled, err = k.MakerLimitOrderSwap(ctx, *takerTradePairID, amountIn, limitBuyPrice, minAvgSellPrice) } if err != nil { return trancheKey, totalIn, swapInCoin, swapOutCoin, math.ZeroInt(), err @@ -142,12 +141,12 @@ func (k Keeper) ExecutePlaceLimitOrder( amountLeft = amountLeft.Sub(swapInCoin.Amount) makerTradePairID := takerTradePairID.Reversed() - makerTickIndexTakerToMaker := tickIndexInToOut * -1 + tickIndexTakerToMaker := tickIndexInToOut * -1 var placeTranche *types.LimitOrderTranche placeTranche, err = k.GetOrInitPlaceTranche( ctx, makerTradePairID, - makerTickIndexTakerToMaker, + tickIndexTakerToMaker, goodTil, orderType, ) @@ -159,7 +158,7 @@ func (k Keeper) ExecutePlaceLimitOrder( trancheUser := k.GetOrInitLimitOrderTrancheUser( ctx, makerTradePairID, - makerTickIndexTakerToMaker, + tickIndexTakerToMaker, trancheKey, orderType, receiverAddr.String(), @@ -173,7 +172,7 @@ func (k Keeper) ExecutePlaceLimitOrder( // NOTE: This does mean that a successful taker leg of the trade will be thrown away since the entire tx will fail. // In most circumstances this seems preferable to executing the taker leg and exiting early before placing a maker // order with the remaining liquidity. - err = types.ValidateFairOutput(amountLeft, limitPrice) + err = types.ValidateFairOutput(amountLeft, limitBuyPrice) if err != nil { return trancheKey, totalIn, swapInCoin, swapOutCoin, math.ZeroInt(), err } diff --git a/x/dex/migrations/v4/store.go b/x/dex/migrations/v4/store.go index b9546a179..470439e7a 100644 --- a/x/dex/migrations/v4/store.go +++ b/x/dex/migrations/v4/store.go @@ -45,12 +45,11 @@ func migrateTickLiquidityPrices(ctx sdk.Context, cdc codec.BinaryCodec, storeKey // Recalculate all prices switch liquidity := tickLiq.Liquidity.(type) { case *types.TickLiquidity_LimitOrderTranche: - liquidity.LimitOrderTranche.PriceTakerToMaker = types.MustCalcPrice(liquidity.LimitOrderTranche.Key.TickIndexTakerToMaker) + liquidity.LimitOrderTranche.PriceTakerToMaker = types.MustCalcPrice(-liquidity.LimitOrderTranche.Key.TickIndexTakerToMaker) updatedTickLiq = types.TickLiquidity{Liquidity: liquidity} case *types.TickLiquidity_PoolReserves: poolReservesKey := liquidity.PoolReserves.Key - liquidity.PoolReserves.PriceTakerToMaker = types.MustCalcPrice(poolReservesKey.TickIndexTakerToMaker) - liquidity.PoolReserves.PriceOppositeTakerToMaker = poolReservesKey.Counterpart().MustPriceTakerToMaker() + liquidity.PoolReserves.PriceTakerToMaker = types.MustCalcPrice(-poolReservesKey.TickIndexTakerToMaker) updatedTickLiq = types.TickLiquidity{Liquidity: liquidity} default: @@ -90,7 +89,7 @@ func migrateInactiveTranchePrices(ctx sdk.Context, cdc codec.BinaryCodec, storeK var tranche types.LimitOrderTranche cdc.MustUnmarshal(iterator.Value(), &tranche) // Recalculate price - tranche.PriceTakerToMaker = types.MustCalcPrice(tranche.Key.TickIndexTakerToMaker) + tranche.PriceTakerToMaker = types.MustCalcPrice(-tranche.Key.TickIndexTakerToMaker) bz := cdc.MustMarshal(&tranche) ticksToUpdate = append(ticksToUpdate, migrationUpdate{key: iterator.Key(), val: bz}) diff --git a/x/dex/migrations/v4/store_test.go b/x/dex/migrations/v4/store_test.go index 9957b90f9..80840c91b 100644 --- a/x/dex/migrations/v4/store_test.go +++ b/x/dex/migrations/v4/store_test.go @@ -49,9 +49,8 @@ func (suite *V4DexMigrationTestSuite) TestPriceUpdates() { Fee: 1, } poolReserves := &types.PoolReserves{ - Key: poolKey, - PriceTakerToMaker: math.ZeroPrecDec(), - PriceOppositeTakerToMaker: math.ZeroPrecDec(), + Key: poolKey, + PriceTakerToMaker: math.ZeroPrecDec(), } app.DexKeeper.SetPoolReserves(ctx, poolReserves) @@ -60,6 +59,7 @@ func (suite *V4DexMigrationTestSuite) TestPriceUpdates() { // Check LimitOrderTranche has correct price newTranche := app.DexKeeper.GetLimitOrderTranche(ctx, trancheKey) + suite.True(newTranche.PriceTakerToMaker.Equal(math.MustNewPrecDecFromStr("1.005012269623051203500693815"))) // check InactiveLimitOrderTranche has correct price @@ -68,6 +68,5 @@ func (suite *V4DexMigrationTestSuite) TestPriceUpdates() { // Check PoolReserves has the correct prices newPool, _ := app.DexKeeper.GetPoolReserves(ctx, poolKey) - suite.True(newPool.PriceTakerToMaker.Equal(math.MustNewPrecDecFromStr("0.002479495864288162666675923"))) - suite.True(newPool.PriceOppositeTakerToMaker.Equal(math.MustNewPrecDecFromStr("403.227141612124702272520931931"))) + suite.True(newPool.PriceTakerToMaker.Equal(math.MustNewPrecDecFromStr("0.002479495864288162666675934"))) } diff --git a/x/dex/migrations/v5/store.go b/x/dex/migrations/v5/store.go new file mode 100644 index 000000000..575f2b61e --- /dev/null +++ b/x/dex/migrations/v5/store.go @@ -0,0 +1,109 @@ +package v5 + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/neutron-org/neutron/v5/x/dex/types" +) + +// MigrateStore performs in-place store migrations. +// v5 adds a new field `MakerPrice` to all tickLiquidity. It must be calculated and added +func MigrateStore(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { + if err := migrateTickLiquidityPrices(ctx, cdc, storeKey); err != nil { + return err + } + + if err := migrateInactiveTranchePrices(ctx, cdc, storeKey); err != nil { + return err + } + + return nil +} + +type migrationUpdate struct { + key []byte + val []byte +} + +func migrateTickLiquidityPrices(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { + ctx.Logger().Info("Migrating TickLiquidity Prices...") + + // Iterate through all tickLiquidity + store := prefix.NewStore(ctx.KVStore(storeKey), types.KeyPrefix(types.TickLiquidityKeyPrefix)) + iterator := storetypes.KVStorePrefixIterator(store, []byte{}) + ticksToUpdate := make([]migrationUpdate, 0) + + for ; iterator.Valid(); iterator.Next() { + var tickLiq types.TickLiquidity + var updatedTickLiq types.TickLiquidity + cdc.MustUnmarshal(iterator.Value(), &tickLiq) + // Add MakerPrice + switch liquidity := tickLiq.Liquidity.(type) { + case *types.TickLiquidity_LimitOrderTranche: + liquidity.LimitOrderTranche.MakerPrice = types.MustCalcPrice(liquidity.LimitOrderTranche.Key.TickIndexTakerToMaker) + updatedTickLiq = types.TickLiquidity{Liquidity: liquidity} + case *types.TickLiquidity_PoolReserves: + poolReservesKey := liquidity.PoolReserves.Key + liquidity.PoolReserves.MakerPrice = types.MustCalcPrice(poolReservesKey.TickIndexTakerToMaker) + updatedTickLiq = types.TickLiquidity{Liquidity: liquidity} + + default: + panic("Tick does not contain valid liqudityType") + } + + bz := cdc.MustMarshal(&updatedTickLiq) + ticksToUpdate = append(ticksToUpdate, migrationUpdate{key: iterator.Key(), val: bz}) + + } + + err := iterator.Close() + if err != nil { + return errorsmod.Wrap(err, "iterator failed to close during migration") + } + + // Store the updated TickLiquidity + for _, v := range ticksToUpdate { + store.Set(v.key, v.val) + } + + ctx.Logger().Info("Finished migrating TickLiquidity Prices...") + + return nil +} + +func migrateInactiveTranchePrices(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { + ctx.Logger().Info("Migrating InactiveLimitOrderTranche Prices...") + + // Iterate through all InactiveTranches + store := prefix.NewStore(ctx.KVStore(storeKey), types.KeyPrefix(types.InactiveLimitOrderTrancheKeyPrefix)) + iterator := storetypes.KVStorePrefixIterator(store, []byte{}) + ticksToUpdate := make([]migrationUpdate, 0) + + for ; iterator.Valid(); iterator.Next() { + var tranche types.LimitOrderTranche + cdc.MustUnmarshal(iterator.Value(), &tranche) + // Add MakerPrice + tranche.MakerPrice = types.MustCalcPrice(tranche.Key.TickIndexTakerToMaker) + + bz := cdc.MustMarshal(&tranche) + ticksToUpdate = append(ticksToUpdate, migrationUpdate{key: iterator.Key(), val: bz}) + } + + err := iterator.Close() + if err != nil { + return errorsmod.Wrap(err, "iterator failed to close during migration") + } + + // Store the updated InactiveTranches + for _, v := range ticksToUpdate { + store.Set(v.key, v.val) + } + + ctx.Logger().Info("Finished migrating InactiveLimitOrderTranche Prices...") + + return nil +} diff --git a/x/dex/migrations/v5/store_test.go b/x/dex/migrations/v5/store_test.go new file mode 100644 index 000000000..6923ca7d9 --- /dev/null +++ b/x/dex/migrations/v5/store_test.go @@ -0,0 +1,70 @@ +package v5_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/neutron-org/neutron/v5/testutil" + "github.com/neutron-org/neutron/v5/utils/math" + v5 "github.com/neutron-org/neutron/v5/x/dex/migrations/v5" + "github.com/neutron-org/neutron/v5/x/dex/types" +) + +type V4DexMigrationTestSuite struct { + testutil.IBCConnectionTestSuite +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(V4DexMigrationTestSuite)) +} + +func (suite *V4DexMigrationTestSuite) TestPriceUpdates() { + var ( + app = suite.GetNeutronZoneApp(suite.ChainA) + storeKey = app.GetKey(types.StoreKey) + ctx = suite.ChainA.GetContext() + cdc = app.AppCodec() + ) + + // Write tranches + trancheKey := &types.LimitOrderTrancheKey{ + TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), + TickIndexTakerToMaker: -1150, + TrancheKey: "123", + } + tranche := &types.LimitOrderTranche{ + Key: trancheKey, + PriceTakerToMaker: math.ZeroPrecDec(), + } + app.DexKeeper.SetLimitOrderTranche(ctx, tranche) + + // also create inactive tranche + app.DexKeeper.SetInactiveLimitOrderTranche(ctx, tranche) + + // Write poolReserves + poolKey := &types.PoolReservesKey{ + TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), + TickIndexTakerToMaker: 256000, + Fee: 1, + } + poolReserves := &types.PoolReserves{ + Key: poolKey, + } + app.DexKeeper.SetPoolReserves(ctx, poolReserves) + + // Run migration + suite.NoError(v5.MigrateStore(ctx, cdc, storeKey)) + + // Check LimitOrderTranche has correct price + newTranche := app.DexKeeper.GetLimitOrderTranche(ctx, trancheKey) + suite.True(newTranche.MakerPrice.Equal(math.MustNewPrecDecFromStr("0.891371268935227562508365227"))) + + // check InactiveLimitOrderTranche has correct price + inactiveTranche, _ := app.DexKeeper.GetInactiveLimitOrderTranche(ctx, trancheKey) + suite.True(inactiveTranche.MakerPrice.Equal(math.MustNewPrecDecFromStr("0.891371268935227562508365227"))) + + // Check PoolReserves has the correct prices + newPool, _ := app.DexKeeper.GetPoolReserves(ctx, poolKey) + suite.True(newPool.MakerPrice.Equal(math.MustNewPrecDecFromStr("131033661522.558812694915985539856164620"))) +} diff --git a/x/dex/module.go b/x/dex/module.go index 5e986448c..6caf10a58 100644 --- a/x/dex/module.go +++ b/x/dex/module.go @@ -162,6 +162,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 3, m.Migrate3to4); err != nil { panic(fmt.Sprintf("failed to migrate x/dex from version 3 to 4: %v", err)) } + if err := cfg.RegisterMigration(types.ModuleName, 4, m.Migrate4to5); err != nil { + panic(fmt.Sprintf("failed to migrate x/dex from version 4 to 5: %v", err)) + } } // RegisterInvariants registers the capability module's invariants. diff --git a/x/dex/types/constants.go b/x/dex/types/constants.go index 119d6c47e..a5bb2eb73 100644 --- a/x/dex/types/constants.go +++ b/x/dex/types/constants.go @@ -1,3 +1,3 @@ package types -const ConsensusVersion = 4 +const ConsensusVersion = 5 diff --git a/x/dex/types/limit_order_tranche.go b/x/dex/types/limit_order_tranche.go index 5ea40d2ce..c9560100f 100644 --- a/x/dex/types/limit_order_tranche.go +++ b/x/dex/types/limit_order_tranche.go @@ -24,7 +24,7 @@ func NewLimitOrderTranche( if err != nil { return nil, err } - priceTakerToMaker, err := tradePairID.PriceTakerToMaker(tickIndex) + makerPrice, err := tradePairID.MakerPrice(tickIndex) if err != nil { return nil, err } @@ -38,7 +38,8 @@ func NewLimitOrderTranche( ReservesTakerDenom: reservesTakerDenom, TotalMakerDenom: totalMakerDenom, TotalTakerDenom: totalTakerDenom, - PriceTakerToMaker: priceTakerToMaker, + MakerPrice: makerPrice, + PriceTakerToMaker: math_utils.OnePrecDec().Quo(makerPrice), }, nil } @@ -107,11 +108,11 @@ func (t LimitOrderTranche) HasTokenOut() bool { } func (t LimitOrderTranche) Price() math_utils.PrecDec { - return t.PriceTakerToMaker + return t.MakerPrice } func (t LimitOrderTranche) RatioFilled() math_utils.PrecDec { - amountFilled := t.PriceTakerToMaker.MulInt(t.TotalTakerDenom) + amountFilled := math_utils.NewPrecDecFromInt(t.TotalTakerDenom).Quo(t.MakerPrice) ratioFilled := amountFilled.QuoInt(t.TotalMakerDenom) // Cap ratio filled at 100% so that makers cannot over withdraw @@ -119,7 +120,7 @@ func (t LimitOrderTranche) RatioFilled() math_utils.PrecDec { } func (t LimitOrderTranche) AmountUnfilled() math_utils.PrecDec { - amountFilled := t.PriceTakerToMaker.MulInt(t.TotalTakerDenom) + amountFilled := math_utils.NewPrecDecFromInt(t.TotalTakerDenom).Quo(t.MakerPrice) trueAmountUnfilled := math_utils.NewPrecDecFromInt(t.TotalMakerDenom).Sub(amountFilled) // It is possible for a tranche to be overfilled due to rounding. Thus we cap the unfilled amount at 0 @@ -152,7 +153,7 @@ func (t *LimitOrderTranche) CalcWithdrawAmount(trancheUser *LimitOrderTrancheUse if !sharesToWithdrawDec.IsPositive() { return math.ZeroInt(), math.ZeroInt() } - amountOutTokenOutDec := sharesToWithdrawDec.Quo(t.PriceTakerToMaker) + amountOutTokenOutDec := sharesToWithdrawDec.Mul(t.MakerPrice) // Round shares withdrawn up and amountOut down to ensure math favors dex return sharesToWithdrawDec.Ceil().TruncateInt(), amountOutTokenOutDec.TruncateInt() @@ -172,14 +173,14 @@ func (t *LimitOrderTranche) Swap(maxAmountTakerIn math.Int, maxAmountMakerOut *m reservesTokenOut := &t.ReservesMakerDenom fillTokenIn := &t.ReservesTakerDenom totalTokenIn := &t.TotalTakerDenom - maxOutGivenIn := t.PriceTakerToMaker.MulInt(maxAmountTakerIn).TruncateInt() + maxOutGivenIn := math_utils.NewPrecDecFromInt(maxAmountTakerIn).Quo(t.MakerPrice).TruncateInt() possibleOutAmounts := []math.Int{*reservesTokenOut, maxOutGivenIn} if maxAmountMakerOut != nil { possibleOutAmounts = append(possibleOutAmounts, *maxAmountMakerOut) } outAmount = utils.MinIntArr(possibleOutAmounts) - inAmount = math_utils.NewPrecDecFromInt(outAmount).Quo(t.PriceTakerToMaker).Ceil().TruncateInt() + inAmount = t.MakerPrice.MulInt(outAmount).Ceil().TruncateInt() *fillTokenIn = fillTokenIn.Add(inAmount) *totalTokenIn = totalTokenIn.Add(inAmount) diff --git a/x/dex/types/limit_order_tranche.pb.go b/x/dex/types/limit_order_tranche.pb.go index e8a8104b1..4f9386367 100644 --- a/x/dex/types/limit_order_tranche.pb.go +++ b/x/dex/types/limit_order_tranche.pb.go @@ -98,8 +98,11 @@ type LimitOrderTranche struct { // JIT orders also use expiration_time to handle deletion but represent a special case // All JIT orders have a expiration_time of 0 and an exception is made to still treat these orders as live // Order deletion still functions the same and the orders will be deleted at the end of the block - ExpirationTime *time.Time `protobuf:"bytes,6,opt,name=expiration_time,json=expirationTime,proto3,stdtime" json:"expiration_time,omitempty"` - PriceTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,7,opt,name=price_taker_to_maker,json=priceTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_taker_to_maker" yaml:"price_taker_to_maker"` + ExpirationTime *time.Time `protobuf:"bytes,6,opt,name=expiration_time,json=expirationTime,proto3,stdtime" json:"expiration_time,omitempty"` + // DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. + PriceTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,7,opt,name=price_taker_to_maker,json=priceTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_taker_to_maker" yaml:"price_taker_to_maker"` // Deprecated: Do not use. + // This is the price of the LimitOrder denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) + MakerPrice github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,8,opt,name=maker_price,json=makerPrice,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"maker_price" yaml:"maker_price"` } func (m *LimitOrderTranche) Reset() { *m = LimitOrderTranche{} } @@ -159,43 +162,45 @@ func init() { } var fileDescriptor_8c2ded67c80756d1 = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x41, 0x8b, 0xd3, 0x40, - 0x14, 0xc7, 0x3b, 0x56, 0xab, 0x3b, 0x45, 0x97, 0x86, 0x2e, 0x64, 0x2b, 0x24, 0x35, 0x20, 0xf4, - 0xb2, 0x09, 0xb8, 0x0a, 0x22, 0x9e, 0x4a, 0x41, 0x8a, 0x56, 0x97, 0x90, 0x93, 0x97, 0x90, 0x26, - 0x63, 0x3a, 0xb4, 0xc9, 0x84, 0xc9, 0xeb, 0xd2, 0xfa, 0x01, 0x3c, 0xef, 0x87, 0xf0, 0xee, 0xd7, - 0xe8, 0x71, 0x8f, 0xe2, 0x21, 0x4a, 0x7b, 0xf3, 0xb8, 0x9f, 0x40, 0x66, 0xd2, 0x6e, 0x53, 0x1b, - 0x5d, 0xf6, 0xd4, 0xc9, 0x7b, 0xff, 0xd7, 0xf7, 0xfb, 0xbf, 0xcc, 0x0b, 0x7e, 0x1a, 0x93, 0x29, - 0x70, 0x16, 0x5b, 0x01, 0x99, 0x59, 0x13, 0x1a, 0x51, 0x70, 0x19, 0x0f, 0x08, 0x77, 0x81, 0x7b, - 0xb1, 0x3f, 0x22, 0x66, 0xc2, 0x19, 0x30, 0xa5, 0xbe, 0x96, 0x99, 0x01, 0x99, 0xb5, 0x9a, 0x21, - 0x0b, 0x99, 0x8c, 0x5b, 0xe2, 0x94, 0x4b, 0x5a, 0x7a, 0xc8, 0x58, 0x38, 0x21, 0x96, 0x7c, 0x1a, - 0x4e, 0x3f, 0x59, 0x40, 0x23, 0x92, 0x82, 0x17, 0x25, 0x6b, 0xc1, 0x71, 0xb1, 0x55, 0xe2, 0x51, - 0xee, 0xd2, 0x60, 0x53, 0x5b, 0x4c, 0x01, 0xf7, 0x02, 0xe2, 0xee, 0x08, 0x8c, 0x6f, 0x08, 0x37, - 0xdf, 0x09, 0xba, 0x0f, 0x02, 0xce, 0xc9, 0xd9, 0xde, 0x92, 0xb9, 0xf2, 0x1a, 0x3f, 0xdc, 0xd1, - 0xab, 0xa8, 0x8d, 0x3a, 0xf5, 0x67, 0xaa, 0x59, 0x00, 0x36, 0x1d, 0xa1, 0x38, 0xf3, 0x28, 0xef, - 0xf7, 0xec, 0x3a, 0x5c, 0x3f, 0x04, 0xca, 0x4b, 0x7c, 0x0c, 0xd4, 0x1f, 0xbb, 0x34, 0x0e, 0xc8, - 0xcc, 0x05, 0x6f, 0x2c, 0x8c, 0x33, 0x37, 0x12, 0x07, 0xf5, 0x4e, 0x1b, 0x75, 0xaa, 0xf6, 0x91, - 0x10, 0xf4, 0x45, 0xde, 0x11, 0x51, 0x87, 0x0d, 0xc4, 0x8f, 0xa2, 0xe3, 0xfa, 0x7a, 0x42, 0xee, - 0x98, 0xcc, 0xd5, 0x6a, 0x1b, 0x75, 0x0e, 0x6c, 0x0c, 0xd7, 0x60, 0xc6, 0xa2, 0x86, 0x1b, 0x7b, - 0xc4, 0xca, 0x29, 0xae, 0x0a, 0x79, 0x0e, 0xf9, 0x64, 0x07, 0xb2, 0xcc, 0x9e, 0x2d, 0xd4, 0xca, - 0x17, 0x84, 0x9b, 0x9c, 0xa4, 0x84, 0x9f, 0x93, 0x34, 0x67, 0x73, 0x03, 0x12, 0xb3, 0x48, 0x12, - 0x1e, 0x74, 0x9d, 0x45, 0xa6, 0x57, 0x7e, 0x64, 0xfa, 0x91, 0xcf, 0xd2, 0x88, 0xa5, 0x69, 0x30, - 0x36, 0x29, 0xb3, 0x22, 0x0f, 0x46, 0x66, 0x3f, 0x86, 0xdf, 0x99, 0x5e, 0x5a, 0x7c, 0x95, 0xe9, - 0x8f, 0xe7, 0x5e, 0x34, 0x79, 0x65, 0x94, 0x65, 0x0d, 0x5b, 0xd9, 0x84, 0xa5, 0xdf, 0x9e, 0x08, - 0xee, 0x82, 0x40, 0x01, 0xa4, 0x7a, 0x5b, 0x10, 0xf8, 0x2f, 0x08, 0x94, 0x82, 0x38, 0x5b, 0x90, - 0xcf, 0xb8, 0x01, 0x0c, 0xbc, 0xc9, 0xce, 0x34, 0xee, 0x4a, 0x88, 0xf7, 0x37, 0x41, 0xec, 0x57, - 0x5e, 0x65, 0xba, 0x9a, 0x13, 0xec, 0xa5, 0x0c, 0xfb, 0x50, 0xc6, 0x06, 0x25, 0xbd, 0x8b, 0x03, - 0xb8, 0x77, 0xab, 0xde, 0xf0, 0xef, 0xde, 0xb0, 0xdf, 0xbb, 0xe0, 0x7b, 0x80, 0x0f, 0xc9, 0x2c, - 0xa1, 0xdc, 0x03, 0xca, 0x62, 0x57, 0x2c, 0x98, 0x5a, 0x93, 0x57, 0xa9, 0x65, 0xe6, 0xdb, 0x67, - 0x6e, 0xb6, 0xcf, 0x74, 0x36, 0xdb, 0xd7, 0x7d, 0xb0, 0xc8, 0x74, 0x74, 0xf1, 0x53, 0x47, 0xf6, - 0xa3, 0x6d, 0xb1, 0x48, 0x2b, 0x5f, 0x11, 0x6e, 0x26, 0x9c, 0xfa, 0xe4, 0xef, 0xab, 0x7f, 0x5f, - 0xda, 0x49, 0xd7, 0x76, 0x9e, 0x87, 0x14, 0x46, 0xd3, 0xa1, 0xe9, 0xb3, 0xc8, 0x5a, 0xdf, 0xd8, - 0x13, 0xc6, 0xc3, 0xcd, 0xd9, 0x3a, 0x7f, 0x61, 0x4d, 0x81, 0x4e, 0xd2, 0xdc, 0xe9, 0x19, 0x27, - 0x7e, 0x8f, 0xf8, 0xe2, 0x75, 0x97, 0xfd, 0xf7, 0xf6, 0x75, 0x97, 0x65, 0x0d, 0xbb, 0x21, 0xc3, - 0xc5, 0x5d, 0xeb, 0xbe, 0x59, 0x2c, 0x35, 0x74, 0xb9, 0xd4, 0xd0, 0xaf, 0xa5, 0x86, 0x2e, 0x56, - 0x5a, 0xe5, 0x72, 0xa5, 0x55, 0xbe, 0xaf, 0xb4, 0xca, 0xc7, 0x93, 0x9b, 0xc9, 0x66, 0xf9, 0x37, - 0x65, 0x9e, 0x90, 0x74, 0x58, 0x93, 0xd3, 0x39, 0xfd, 0x13, 0x00, 0x00, 0xff, 0xff, 0x15, 0x52, - 0xbe, 0xc6, 0xf5, 0x04, 0x00, 0x00, + // 601 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x4f, 0x8b, 0xd3, 0x4e, + 0x18, 0xc7, 0x3b, 0xbf, 0xfe, 0xdc, 0x3f, 0x53, 0x74, 0xd9, 0xd0, 0x85, 0xec, 0x0a, 0xc9, 0x1a, + 0x10, 0xf6, 0xb2, 0x09, 0xb8, 0x0a, 0x22, 0x9e, 0x96, 0x82, 0x14, 0xad, 0x96, 0x90, 0x93, 0x97, + 0x90, 0x26, 0x63, 0x3a, 0xb4, 0xc9, 0x84, 0xc9, 0xd3, 0xa5, 0xf5, 0xe8, 0xc1, 0xf3, 0xbe, 0x0a, + 0xaf, 0xbe, 0x8d, 0xde, 0xdc, 0xa3, 0x78, 0x88, 0xd2, 0xde, 0x3c, 0xf6, 0x15, 0xc8, 0x4c, 0xda, + 0x6d, 0x62, 0xa3, 0xcb, 0xe2, 0xa9, 0x33, 0xcf, 0xf7, 0x3b, 0x79, 0x3e, 0xcf, 0x3c, 0xf3, 0x14, + 0x3f, 0x8c, 0xc9, 0x08, 0x38, 0x8b, 0xad, 0x80, 0x8c, 0xad, 0x21, 0x8d, 0x28, 0xb8, 0x8c, 0x07, + 0x84, 0xbb, 0xc0, 0xbd, 0xd8, 0xef, 0x13, 0x33, 0xe1, 0x0c, 0x98, 0xd2, 0x58, 0xda, 0xcc, 0x80, + 0x8c, 0x8f, 0x9a, 0x21, 0x0b, 0x99, 0x8c, 0x5b, 0x62, 0x95, 0x5b, 0x8e, 0xf4, 0x90, 0xb1, 0x70, + 0x48, 0x2c, 0xb9, 0xeb, 0x8d, 0xde, 0x59, 0x40, 0x23, 0x92, 0x82, 0x17, 0x25, 0x4b, 0xc3, 0x61, + 0x31, 0x55, 0xe2, 0x51, 0xee, 0xd2, 0x60, 0x75, 0xb6, 0x28, 0x01, 0xf7, 0x02, 0xe2, 0x96, 0x0c, + 0xc6, 0x67, 0x84, 0x9b, 0xaf, 0x04, 0xdd, 0x1b, 0x01, 0xe7, 0xe4, 0x6c, 0x2f, 0xc9, 0x44, 0x79, + 0x8e, 0xef, 0x96, 0xfc, 0x2a, 0x3a, 0x46, 0x27, 0x8d, 0x47, 0xaa, 0x59, 0x00, 0x36, 0x1d, 0xe1, + 0xe8, 0x7a, 0x94, 0xb7, 0x5b, 0x76, 0x03, 0xae, 0x37, 0x81, 0xf2, 0x14, 0x1f, 0x02, 0xf5, 0x07, + 0x2e, 0x8d, 0x03, 0x32, 0x76, 0xc1, 0x1b, 0x88, 0xc2, 0x99, 0x1b, 0x89, 0x85, 0xfa, 0xdf, 0x31, + 0x3a, 0xa9, 0xdb, 0x07, 0xc2, 0xd0, 0x16, 0xba, 0x23, 0xa2, 0x0e, 0xeb, 0x88, 0x1f, 0x45, 0xc7, + 0x8d, 0xe5, 0x0d, 0xb9, 0x03, 0x32, 0x51, 0xeb, 0xc7, 0xe8, 0x64, 0xd7, 0xc6, 0x70, 0x0d, 0x66, + 0x7c, 0xd9, 0xc6, 0xfb, 0x1b, 0xc4, 0xca, 0x19, 0xae, 0x0b, 0x7b, 0x0e, 0xf9, 0xa0, 0x04, 0x59, + 0x55, 0x9e, 0x2d, 0xdc, 0xca, 0x47, 0x84, 0x9b, 0x9c, 0xa4, 0x84, 0x5f, 0x90, 0x34, 0x67, 0x73, + 0x03, 0x12, 0xb3, 0x48, 0x12, 0xee, 0x9e, 0x3b, 0xd3, 0x4c, 0xaf, 0x7d, 0xcb, 0xf4, 0x03, 0x9f, + 0xa5, 0x11, 0x4b, 0xd3, 0x60, 0x60, 0x52, 0x66, 0x45, 0x1e, 0xf4, 0xcd, 0x76, 0x0c, 0x3f, 0x33, + 0xbd, 0xf2, 0xf0, 0x22, 0xd3, 0xef, 0x4f, 0xbc, 0x68, 0xf8, 0xcc, 0xa8, 0x52, 0x0d, 0x5b, 0x59, + 0x85, 0x65, 0xbd, 0x2d, 0x11, 0x2c, 0x83, 0x40, 0x01, 0xa4, 0x7e, 0x5b, 0x10, 0xf8, 0x2b, 0x08, + 0x54, 0x82, 0x38, 0x6b, 0x90, 0xf7, 0x78, 0x1f, 0x18, 0x78, 0xc3, 0xd2, 0x6d, 0xfc, 0x2f, 0x21, + 0x5e, 0xdf, 0x04, 0xb1, 0x79, 0x72, 0x91, 0xe9, 0x6a, 0x4e, 0xb0, 0x21, 0x19, 0xf6, 0x9e, 0x8c, + 0x75, 0x2a, 0x72, 0x17, 0x2f, 0xe0, 0xce, 0xad, 0x72, 0xc3, 0x9f, 0x73, 0xc3, 0x66, 0xee, 0x42, + 0xdd, 0x1d, 0xbc, 0x47, 0xc6, 0x09, 0xe5, 0x1e, 0x50, 0x16, 0xbb, 0x62, 0xc0, 0xd4, 0x2d, 0xf9, + 0x94, 0x8e, 0xcc, 0x7c, 0xfa, 0xcc, 0xd5, 0xf4, 0x99, 0xce, 0x6a, 0xfa, 0xce, 0x77, 0xa6, 0x99, + 0x8e, 0x2e, 0xbf, 0xeb, 0xc8, 0xbe, 0xb7, 0x3e, 0x2c, 0x64, 0xe5, 0x13, 0xc2, 0xcd, 0x84, 0x53, + 0x9f, 0xfc, 0xfe, 0xf4, 0xb7, 0x65, 0x39, 0xa3, 0x65, 0x39, 0x8f, 0x43, 0x0a, 0xfd, 0x51, 0xcf, + 0xf4, 0x59, 0x64, 0x2d, 0x5f, 0xec, 0x29, 0xe3, 0xe1, 0x6a, 0x6d, 0x5d, 0x3c, 0xb1, 0x46, 0x40, + 0x87, 0x69, 0x5e, 0x69, 0x97, 0x13, 0xbf, 0x45, 0x7c, 0xd1, 0xee, 0xaa, 0x6f, 0xaf, 0xdb, 0x5d, + 0xa5, 0x1a, 0x2a, 0xb2, 0xf7, 0xa5, 0x50, 0x9a, 0xb6, 0x0f, 0x08, 0x37, 0xf2, 0xae, 0x48, 0x4d, + 0xdd, 0x91, 0x7c, 0xde, 0x3f, 0xf2, 0x15, 0x3f, 0xb9, 0xc8, 0x74, 0x25, 0xc7, 0x2a, 0x04, 0x0d, + 0x1b, 0xcb, 0x5d, 0x57, 0x6c, 0xce, 0x5f, 0x4c, 0x67, 0x1a, 0xba, 0x9a, 0x69, 0xe8, 0xc7, 0x4c, + 0x43, 0x97, 0x73, 0xad, 0x76, 0x35, 0xd7, 0x6a, 0x5f, 0xe7, 0x5a, 0xed, 0xed, 0xe9, 0xcd, 0x00, + 0xe3, 0xfc, 0xaf, 0x6d, 0x92, 0x90, 0xb4, 0xb7, 0x25, 0x9b, 0x74, 0xf6, 0x2b, 0x00, 0x00, 0xff, + 0xff, 0xda, 0xfd, 0x1f, 0x44, 0x7c, 0x05, 0x00, 0x00, } func (m *LimitOrderTrancheKey) Marshal() (dAtA []byte, err error) { @@ -265,6 +270,16 @@ func (m *LimitOrderTranche) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.MakerPrice.Size() + i -= size + if _, err := m.MakerPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLimitOrderTranche(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 { size := m.PriceTakerToMaker.Size() i -= size @@ -395,6 +410,8 @@ func (m *LimitOrderTranche) Size() (n int) { } l = m.PriceTakerToMaker.Size() n += 1 + l + sovLimitOrderTranche(uint64(l)) + l = m.MakerPrice.Size() + n += 1 + l + sovLimitOrderTranche(uint64(l)) return n } @@ -812,6 +829,40 @@ func (m *LimitOrderTranche) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MakerPrice", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLimitOrderTranche + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLimitOrderTranche + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLimitOrderTranche + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MakerPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLimitOrderTranche(dAtA[iNdEx:]) diff --git a/x/dex/types/limit_order_tranche_key.go b/x/dex/types/limit_order_tranche_key.go index cb19c1650..5dfc78395 100644 --- a/x/dex/types/limit_order_tranche_key.go +++ b/x/dex/types/limit_order_tranche_key.go @@ -31,12 +31,12 @@ func (p LimitOrderTrancheKey) KeyMarshal() []byte { return key } -func (p LimitOrderTrancheKey) PriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec, err error) { +func (p LimitOrderTrancheKey) Price() (priceTakerToMaker math_utils.PrecDec, err error) { return CalcPrice(p.TickIndexTakerToMaker) } -func (p LimitOrderTrancheKey) MustPriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec) { - price, err := p.PriceTakerToMaker() +func (p LimitOrderTrancheKey) MustPrice() (priceTakerToMaker math_utils.PrecDec) { + price, err := p.Price() if err != nil { panic(err) } diff --git a/x/dex/types/pool.go b/x/dex/types/pool.go index 96a717eac..5a8cd9476 100644 --- a/x/dex/types/pool.go +++ b/x/dex/types/pool.go @@ -85,7 +85,7 @@ func (p *Pool) Swap( return math.ZeroInt(), math.ZeroInt() } - maxOutGivenTakerIn := makerReserves.PriceTakerToMaker.MulInt(maxAmountTakerIn).TruncateInt() + maxOutGivenTakerIn := math_utils.NewPrecDecFromInt(maxAmountTakerIn).Quo(makerReserves.MakerPrice).TruncateInt() possibleAmountsMakerOut := []math.Int{makerReserves.ReservesMakerDenom, maxOutGivenTakerIn} if maxAmountMakerOut != nil { possibleAmountsMakerOut = append(possibleAmountsMakerOut, *maxAmountMakerOut) @@ -97,10 +97,7 @@ func (p *Pool) Swap( // c) The maximum amount the user wants out (maxAmountOut1) amountMakerOut = utils.MinIntArr(possibleAmountsMakerOut) - amountTakerIn = math_utils.NewPrecDecFromInt(amountMakerOut). - Quo(makerReserves.PriceTakerToMaker). - Ceil(). - TruncateInt() + amountTakerIn = makerReserves.MakerPrice.MulInt(amountMakerOut).Ceil().TruncateInt() takerReserves.ReservesMakerDenom = takerReserves.ReservesMakerDenom.Add(amountTakerIn) makerReserves.ReservesMakerDenom = makerReserves.ReservesMakerDenom.Sub(amountMakerOut) @@ -158,10 +155,10 @@ func (p *Pool) GetPoolDenom() string { func (p *Pool) Price(tradePairID *TradePairID) math_utils.PrecDec { if tradePairID.IsTakerDenomToken0() { - return p.UpperTick1.PriceTakerToMaker + return p.UpperTick1.MakerPrice } - return p.LowerTick0.PriceTakerToMaker + return p.LowerTick0.MakerPrice } func (p *Pool) MustCalcPrice1To0Center() math_utils.PrecDec { @@ -203,7 +200,7 @@ func (p *Pool) CalcResidualSharesMinted( valueMintedToken0, err := CalcResidualValue( residualAmount0, residualAmount1, - p.LowerTick0.PriceTakerToMaker, + p.LowerTick0.MakerPrice, fee, ) if err != nil { @@ -269,16 +266,16 @@ func CalcGreatestMatchingRatio( func CalcResidualValue( amount0, amount1 math.Int, - priceLowerTakerToMaker math_utils.PrecDec, + makerPriceToken0 math_utils.PrecDec, fee int64, ) (math_utils.PrecDec, error) { - // ResidualValue = Amount0 * (Price1to0Center / Price1to0Upper) + Amount1 * Price1to0Lower + // ResidualValue = Amount0 * (Price1to0Center / Price1to0Upper) + Amount1 / MakerPriceToken0 amount0Discount, err := CalcPrice(-fee) if err != nil { return math_utils.ZeroPrecDec(), err } - return amount0Discount.MulInt(amount0).Add(priceLowerTakerToMaker.MulInt(amount1)), nil + return amount0Discount.MulInt(amount0).Add(math_utils.NewPrecDecFromInt(amount1).Quo(makerPriceToken0)), nil } func CalcFee(upperTickIndex, lowerTickIndex int64) int64 { @@ -287,6 +284,7 @@ func CalcFee(upperTickIndex, lowerTickIndex int64) int64 { func CalcAmountAsToken0(amount0, amount1 math.Int, price1To0 math_utils.PrecDec) math_utils.PrecDec { amount0Dec := math_utils.NewPrecDecFromInt(amount0) + amount1Dec := math_utils.NewPrecDecFromInt(amount1) - return amount0Dec.Add(price1To0.MulInt(amount1)) + return amount0Dec.Add(amount1Dec.Quo(price1To0)) } diff --git a/x/dex/types/pool_reserves.go b/x/dex/types/pool_reserves.go index d4f29eff6..7e924ddfe 100644 --- a/x/dex/types/pool_reserves.go +++ b/x/dex/types/pool_reserves.go @@ -2,6 +2,8 @@ package types import ( "cosmossdk.io/math" + + math_utils "github.com/neutron-org/neutron/v5/utils/math" ) func (p PoolReserves) HasToken() bool { @@ -12,10 +14,13 @@ func NewPoolReservesFromCounterpart( counterpart *PoolReserves, ) *PoolReserves { thisID := counterpart.Key.Counterpart() + // Pool tickIndex has already been validated so this will never throw + makerPrice := MustCalcPrice(thisID.TickIndexTakerToMaker) return &PoolReserves{ Key: thisID, ReservesMakerDenom: math.ZeroInt(), - PriceTakerToMaker: counterpart.PriceOppositeTakerToMaker, + MakerPrice: makerPrice, + PriceTakerToMaker: math_utils.OnePrecDec().Quo(makerPrice), PriceOppositeTakerToMaker: counterpart.PriceTakerToMaker, } } @@ -23,7 +28,7 @@ func NewPoolReservesFromCounterpart( func NewPoolReserves( poolReservesID *PoolReservesKey, ) (*PoolReserves, error) { - priceTakerToMaker, err := poolReservesID.PriceTakerToMaker() + makerPrice, err := poolReservesID.Price() if err != nil { return nil, err } @@ -36,7 +41,8 @@ func NewPoolReserves( return &PoolReserves{ Key: poolReservesID, ReservesMakerDenom: math.ZeroInt(), - PriceTakerToMaker: priceTakerToMaker, + MakerPrice: makerPrice, + PriceTakerToMaker: math_utils.OnePrecDec().Quo(makerPrice), PriceOppositeTakerToMaker: priceOppositeTakerToMaker, }, nil } diff --git a/x/dex/types/pool_reserves.pb.go b/x/dex/types/pool_reserves.pb.go index 2cafc125e..12ae8621c 100644 --- a/x/dex/types/pool_reserves.pb.go +++ b/x/dex/types/pool_reserves.pb.go @@ -86,10 +86,15 @@ func (m *PoolReservesKey) GetFee() uint64 { } type PoolReserves struct { - Key *PoolReservesKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - ReservesMakerDenom cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=reserves_maker_denom,json=reservesMakerDenom,proto3,customtype=cosmossdk.io/math.Int" json:"reserves_maker_denom" yaml:"reserves_maker_denom"` - PriceTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,3,opt,name=price_taker_to_maker,json=priceTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_taker_to_maker" yaml:"price_taker_to_maker"` - PriceOppositeTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,4,opt,name=price_opposite_taker_to_maker,json=priceOppositeTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_opposite_taker_to_maker" yaml:"price_opposite_taker_to_maker"` + Key *PoolReservesKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + ReservesMakerDenom cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=reserves_maker_denom,json=reservesMakerDenom,proto3,customtype=cosmossdk.io/math.Int" json:"reserves_maker_denom" yaml:"reserves_maker_denom"` + // DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. + PriceTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,3,opt,name=price_taker_to_maker,json=priceTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_taker_to_maker" yaml:"price_taker_to_maker"` // Deprecated: Do not use. + // DEPRECATED: price_opposite_taker_maker was an internal implementation detail and will be removed in a future release. + // It is being kept strictly for backwards compatibility. The actual field value is unused. + PriceOppositeTakerToMaker github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,4,opt,name=price_opposite_taker_to_maker,json=priceOppositeTakerToMaker,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"price_opposite_taker_to_maker" yaml:"price_opposite_taker_to_maker"` // Deprecated: Do not use. + // This is the price of the PoolReserves denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) + MakerPrice github_com_neutron_org_neutron_v5_utils_math.PrecDec `protobuf:"bytes,5,opt,name=maker_price,json=makerPrice,proto3,customtype=github.com/neutron-org/neutron/v5/utils/math.PrecDec" json:"maker_price" yaml:"maker_price"` } func (m *PoolReserves) Reset() { *m = PoolReserves{} } @@ -140,36 +145,38 @@ func init() { func init() { proto.RegisterFile("neutron/dex/pool_reserves.proto", fileDescriptor_f0fe9f734c7ad538) } var fileDescriptor_f0fe9f734c7ad538 = []byte{ - // 463 bytes of a gzipped FileDescriptorProto + // 494 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0x4f, 0x8b, 0xd3, 0x40, - 0x18, 0xc6, 0x3b, 0xa6, 0x08, 0x4e, 0x15, 0x35, 0x74, 0xa1, 0xbb, 0x6a, 0x52, 0x82, 0x87, 0x5e, - 0x36, 0x01, 0xff, 0x80, 0x88, 0xa7, 0xa5, 0x20, 0x45, 0xc4, 0x12, 0x7a, 0xf2, 0x12, 0xb2, 0x99, - 0xd7, 0xee, 0xd0, 0x26, 0x6f, 0x98, 0x99, 0x2e, 0xed, 0x07, 0xd0, 0xb3, 0x77, 0xaf, 0x7e, 0x09, - 0xbf, 0x41, 0x8f, 0x7b, 0x14, 0x0f, 0x41, 0xda, 0x9b, 0xc7, 0xfd, 0x04, 0x32, 0x93, 0x54, 0xdb, - 0x1a, 0xf5, 0xb0, 0xa7, 0xbc, 0x79, 0x9e, 0x27, 0xef, 0xfc, 0xde, 0x09, 0x2f, 0x75, 0x33, 0x98, - 0x29, 0x81, 0x59, 0xc0, 0x60, 0x1e, 0xe4, 0x88, 0xd3, 0x48, 0x80, 0x04, 0x71, 0x0e, 0xd2, 0xcf, - 0x05, 0x2a, 0xb4, 0x5b, 0x55, 0xc0, 0x67, 0x30, 0x3f, 0x6a, 0x8f, 0x71, 0x8c, 0x46, 0x0f, 0x74, - 0x55, 0x46, 0x8e, 0x76, 0x7a, 0x28, 0x11, 0x33, 0x88, 0xf2, 0x98, 0x8b, 0x88, 0xb3, 0x32, 0xe0, - 0x7d, 0x22, 0xf4, 0xf6, 0x10, 0x71, 0x1a, 0x56, 0xad, 0x5f, 0xc1, 0xc2, 0x7e, 0x41, 0x6f, 0xed, - 0x44, 0x3b, 0xa4, 0x4b, 0x7a, 0xad, 0x47, 0x1d, 0x7f, 0xeb, 0x3c, 0x7f, 0xa4, 0x13, 0xc3, 0x98, - 0x8b, 0x41, 0x3f, 0x6c, 0xa9, 0x5f, 0x2f, 0xcc, 0x7e, 0x46, 0x0f, 0x15, 0x4f, 0x26, 0x11, 0xcf, - 0x18, 0xcc, 0x23, 0x15, 0x4f, 0x40, 0x44, 0x0a, 0xa3, 0x54, 0x17, 0x9d, 0x6b, 0x5d, 0xd2, 0xb3, - 0xc2, 0x03, 0x1d, 0x18, 0x68, 0x7f, 0xa4, 0xd5, 0x11, 0xbe, 0xd6, 0x0f, 0xfb, 0x0e, 0xb5, 0xde, - 0x01, 0x74, 0xac, 0x2e, 0xe9, 0x35, 0x43, 0x5d, 0x7a, 0x5f, 0x9a, 0xf4, 0xe6, 0x36, 0x9d, 0xed, - 0x53, 0x6b, 0x02, 0x8b, 0x0a, 0xe8, 0xfe, 0x0e, 0xd0, 0xde, 0x14, 0xa1, 0x0e, 0xda, 0x1f, 0x08, - 0x6d, 0x6f, 0x6e, 0xad, 0x44, 0x88, 0x18, 0x64, 0x98, 0x1a, 0x90, 0x1b, 0x27, 0xa3, 0x65, 0xe1, - 0x36, 0xbe, 0x15, 0xee, 0x41, 0x82, 0x32, 0x45, 0x29, 0xd9, 0xc4, 0xe7, 0x18, 0xa4, 0xb1, 0x3a, - 0xf3, 0x07, 0x99, 0xfa, 0x51, 0xb8, 0xb5, 0x1f, 0x5f, 0x16, 0xee, 0xbd, 0x45, 0x9c, 0x4e, 0x9f, - 0x7b, 0x75, 0xae, 0x17, 0xda, 0x1b, 0xd9, 0x8c, 0xd5, 0xd7, 0xa2, 0xfd, 0x99, 0xd0, 0x76, 0x2e, - 0x78, 0x02, 0xfb, 0x37, 0x62, 0x19, 0x10, 0x59, 0x81, 0x3c, 0x19, 0x73, 0x75, 0x36, 0x3b, 0xf5, - 0x13, 0x4c, 0x83, 0x6a, 0xb8, 0x63, 0x14, 0xe3, 0x4d, 0x1d, 0x9c, 0x3f, 0x0d, 0x66, 0x8a, 0x4f, - 0x65, 0xc9, 0x38, 0x14, 0x90, 0xf4, 0x21, 0xd1, 0x9c, 0x75, 0xbd, 0x7f, 0x73, 0xd6, 0xb9, 0x5e, - 0x78, 0xd7, 0xc8, 0x3b, 0xbf, 0x60, 0x49, 0xe8, 0x83, 0x32, 0x8c, 0x79, 0x8e, 0x92, 0xab, 0x3f, - 0x78, 0x9b, 0x86, 0xf7, 0x3d, 0xb9, 0x22, 0xf0, 0xbf, 0x4f, 0xb9, 0x2c, 0xdc, 0x87, 0xdb, 0xe4, - 0x7f, 0x89, 0x79, 0xe1, 0xa1, 0xf1, 0xdf, 0x54, 0xf6, 0xf6, 0x28, 0x27, 0x2f, 0x97, 0x2b, 0x87, - 0x5c, 0xac, 0x1c, 0xf2, 0x7d, 0xe5, 0x90, 0x8f, 0x6b, 0xa7, 0x71, 0xb1, 0x76, 0x1a, 0x5f, 0xd7, - 0x4e, 0xe3, 0xed, 0xf1, 0xff, 0x99, 0xe7, 0xe5, 0xc2, 0x2c, 0x72, 0x90, 0xa7, 0xd7, 0xcd, 0xa6, - 0x3c, 0xfe, 0x19, 0x00, 0x00, 0xff, 0xff, 0xbe, 0xc4, 0x40, 0x24, 0x90, 0x03, 0x00, 0x00, + 0x14, 0xc0, 0x3b, 0x66, 0x57, 0x70, 0xaa, 0xa8, 0x43, 0x17, 0xb2, 0xab, 0x26, 0x25, 0x78, 0xe8, + 0x65, 0x13, 0xf0, 0x0f, 0x88, 0x78, 0x5a, 0x0a, 0x52, 0x44, 0x2c, 0xa1, 0x27, 0x2f, 0x21, 0x9b, + 0x3c, 0xbb, 0x43, 0x9b, 0xbc, 0x30, 0x99, 0x2e, 0xed, 0xd5, 0x83, 0x5e, 0xbd, 0x7b, 0xf7, 0x83, + 0x78, 0x5a, 0x3c, 0xed, 0x51, 0x3c, 0x04, 0x69, 0x6f, 0x1e, 0xfb, 0x09, 0x64, 0x26, 0xa9, 0x26, + 0x6b, 0xd5, 0xc3, 0x9e, 0xf2, 0xe6, 0xbd, 0x5f, 0xde, 0xfc, 0xde, 0x0c, 0x43, 0xed, 0x14, 0x66, + 0x52, 0x60, 0xea, 0xc5, 0x30, 0xf7, 0x32, 0xc4, 0x69, 0x20, 0x20, 0x07, 0x71, 0x0a, 0xb9, 0x9b, + 0x09, 0x94, 0xc8, 0xda, 0x15, 0xe0, 0xc6, 0x30, 0x3f, 0xe8, 0x8c, 0x71, 0x8c, 0x3a, 0xef, 0xa9, + 0xa8, 0x44, 0x0e, 0x1a, 0x3d, 0xa4, 0x08, 0x63, 0x08, 0xb2, 0x90, 0x8b, 0x80, 0xc7, 0x25, 0xe0, + 0x7c, 0x24, 0xf4, 0xe6, 0x10, 0x71, 0xea, 0x57, 0xad, 0x5f, 0xc0, 0x82, 0x3d, 0xa3, 0x37, 0x1a, + 0xa8, 0x49, 0xba, 0xa4, 0xd7, 0x7e, 0x60, 0xba, 0xb5, 0xfd, 0xdc, 0x91, 0x22, 0x86, 0x21, 0x17, + 0x83, 0xbe, 0xdf, 0x96, 0xbf, 0x16, 0x31, 0x7b, 0x42, 0xf7, 0x25, 0x8f, 0x26, 0x01, 0x4f, 0x63, + 0x98, 0x07, 0x32, 0x9c, 0x80, 0x08, 0x24, 0x06, 0x89, 0x0a, 0xcc, 0x2b, 0x5d, 0xd2, 0x33, 0xfc, + 0x3d, 0x05, 0x0c, 0x54, 0x7d, 0xa4, 0xb2, 0x23, 0x7c, 0xa9, 0x3e, 0xec, 0x16, 0x35, 0xde, 0x00, + 0x98, 0x46, 0x97, 0xf4, 0x76, 0x7c, 0x15, 0x3a, 0x9f, 0x77, 0xe9, 0xf5, 0xba, 0x1d, 0x73, 0xa9, + 0x31, 0x81, 0x45, 0x25, 0x74, 0xb7, 0x21, 0x74, 0x61, 0x0a, 0x5f, 0x81, 0xec, 0x1d, 0xa1, 0x9d, + 0xcd, 0xa9, 0x95, 0x0a, 0x41, 0x0c, 0x29, 0x26, 0x5a, 0xe4, 0xda, 0xd1, 0xe8, 0xac, 0xb0, 0x5b, + 0xdf, 0x0a, 0x7b, 0x2f, 0xc2, 0x3c, 0xc1, 0x3c, 0x8f, 0x27, 0x2e, 0x47, 0x2f, 0x09, 0xe5, 0x89, + 0x3b, 0x48, 0xe5, 0x8f, 0xc2, 0xde, 0xfa, 0xf3, 0xba, 0xb0, 0xef, 0x2c, 0xc2, 0x64, 0xfa, 0xd4, + 0xd9, 0x56, 0x75, 0x7c, 0xb6, 0x49, 0xeb, 0xb1, 0xfa, 0x2a, 0xc9, 0x3e, 0x11, 0xda, 0xc9, 0x04, + 0x8f, 0xe0, 0xe2, 0x89, 0x18, 0x5a, 0x64, 0x56, 0x89, 0x3c, 0x1a, 0x73, 0x79, 0x32, 0x3b, 0x76, + 0x23, 0x4c, 0xbc, 0x6a, 0xb8, 0x43, 0x14, 0xe3, 0x4d, 0xec, 0x9d, 0x3e, 0xf6, 0x66, 0x92, 0x4f, + 0xf3, 0xd2, 0x71, 0x28, 0x20, 0xea, 0x43, 0xa4, 0x3c, 0xb7, 0xf5, 0xfe, 0xed, 0xb9, 0xad, 0xea, + 0x98, 0xc4, 0xbf, 0xad, 0x0b, 0x8d, 0x4b, 0xf8, 0x42, 0xe8, 0xbd, 0x12, 0xc7, 0x2c, 0xc3, 0x9c, + 0xcb, 0x3f, 0x8c, 0x77, 0xb4, 0xf1, 0x7b, 0x72, 0x49, 0xe5, 0x7f, 0xef, 0xb2, 0x2e, 0xec, 0xfb, + 0x75, 0xf7, 0xbf, 0x60, 0x6a, 0x88, 0x7d, 0x4d, 0xbc, 0xaa, 0x80, 0xc6, 0x30, 0x6f, 0x09, 0x6d, + 0x97, 0x57, 0xa3, 0x19, 0x73, 0x57, 0xab, 0x87, 0x97, 0x34, 0xaf, 0xb7, 0x5c, 0x17, 0x36, 0x2b, + 0x3d, 0x6b, 0x49, 0xc7, 0xa7, 0x7a, 0x35, 0x54, 0x8b, 0xa3, 0xe7, 0x67, 0x4b, 0x8b, 0x9c, 0x2f, + 0x2d, 0xf2, 0x7d, 0x69, 0x91, 0x0f, 0x2b, 0xab, 0x75, 0xbe, 0xb2, 0x5a, 0x5f, 0x57, 0x56, 0xeb, + 0xf5, 0xe1, 0xff, 0x05, 0xe6, 0xe5, 0xcb, 0x5d, 0x64, 0x90, 0x1f, 0x5f, 0xd5, 0x4f, 0xf6, 0xe1, + 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbe, 0x9f, 0x7e, 0x24, 0x19, 0x04, 0x00, 0x00, } func (m *PoolReservesKey) Marshal() (dAtA []byte, err error) { @@ -237,6 +244,16 @@ func (m *PoolReserves) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.MakerPrice.Size() + i -= size + if _, err := m.MakerPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintPoolReserves(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size := m.PriceOppositeTakerToMaker.Size() i -= size @@ -328,6 +345,8 @@ func (m *PoolReserves) Size() (n int) { n += 1 + l + sovPoolReserves(uint64(l)) l = m.PriceOppositeTakerToMaker.Size() n += 1 + l + sovPoolReserves(uint64(l)) + l = m.MakerPrice.Size() + n += 1 + l + sovPoolReserves(uint64(l)) return n } @@ -628,6 +647,40 @@ func (m *PoolReserves) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MakerPrice", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPoolReserves + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPoolReserves + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPoolReserves + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MakerPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPoolReserves(dAtA[iNdEx:]) diff --git a/x/dex/types/pool_reserves_key.go b/x/dex/types/pool_reserves_key.go index bb1f7a9c9..aabbb2c97 100644 --- a/x/dex/types/pool_reserves_key.go +++ b/x/dex/types/pool_reserves_key.go @@ -44,14 +44,18 @@ func (p PoolReservesKey) Counterpart() *PoolReservesKey { } } -func (p PoolReservesKey) PriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec, err error) { +func (p PoolReservesKey) Price() (priceTakerToMaker math_utils.PrecDec, err error) { return CalcPrice(p.TickIndexTakerToMaker) } -func (p PoolReservesKey) MustPriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec) { - price, err := p.PriceTakerToMaker() +func (p PoolReservesKey) MustPrice() (priceTakerToMaker math_utils.PrecDec) { + price, err := p.Price() if err != nil { panic(err) } return price } + +func (p PoolReservesKey) PriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec, err error) { + return CalcPrice(-p.TickIndexTakerToMaker) +} diff --git a/x/dex/types/precomputed_prices.gob b/x/dex/types/precomputed_prices.gob index fa2bbfad0..706e8e790 100644 Binary files a/x/dex/types/precomputed_prices.gob and b/x/dex/types/precomputed_prices.gob differ diff --git a/x/dex/types/price.go b/x/dex/types/price.go index 2feafece6..3d553f420 100644 --- a/x/dex/types/price.go +++ b/x/dex/types/price.go @@ -54,21 +54,21 @@ func loadPrecomputedPricesFromFile() error { } // Calculates the price for a swap from token 0 to token 1 given a relative tick -// tickIndex refers to the index of a specified tick such that x * 1.0001 ^(-1 * t) = y +// tickIndex refers to the index of a specified tick such that x * 1.0001 ^(1 * t) = y // Lower ticks offer better prices. func CalcPrice(relativeTickIndex int64) (math_utils.PrecDec, error) { if IsTickOutOfRange(relativeTickIndex) { return math_utils.ZeroPrecDec(), ErrTickOutsideRange } if relativeTickIndex < 0 { - return utils.BasePrice().Power(uint64(-1 * relativeTickIndex)), nil + return math_utils.OnePrecDec().Quo(PrecomputedPrices[-relativeTickIndex]), nil } // else - return math_utils.OnePrecDec().Quo(utils.BasePrice().Power(uint64(relativeTickIndex))), nil + return PrecomputedPrices[relativeTickIndex], nil } func BinarySearchPriceToTick(price math_utils.PrecDec) uint64 { - if price.GT(math_utils.OnePrecDec()) { + if price.LT(math_utils.OnePrecDec()) { panic("Can only lookup prices <= 1") } var left uint64 // = 0 @@ -80,9 +80,9 @@ func BinarySearchPriceToTick(price math_utils.PrecDec) uint64 { case PrecomputedPrices[mid].Equal(price): return mid case PrecomputedPrices[mid].LT(price): - right = mid - 1 - default: left = mid + 1 + default: + right = mid - 1 } } @@ -96,8 +96,8 @@ func CalcTickIndexFromPrice(price math_utils.PrecDec) (int64, error) { return 0, ErrPriceOutsideRange } - if price.GT(math_utils.OnePrecDec()) { - // We only have a lookup table for prices <= 1 + if price.LT(math_utils.OnePrecDec()) { + // We only have a lookup table for prices >= 1 // So we invert the price for the lookup invPrice := math_utils.OnePrecDec().Quo(price) tick := BinarySearchPriceToTick(invPrice) @@ -141,20 +141,21 @@ func ValidateTickFee(tick int64, fee uint64) error { } func ValidateFairOutput(amountIn math.Int, price math_utils.PrecDec) error { - amountOut := price.MulInt(amountIn) + amountOut := math_utils.NewPrecDecFromInt(amountIn).Quo(price) if amountOut.LT(math_utils.OnePrecDec()) { return errors.Wrapf(ErrTradeTooSmall, "True output for %v tokens at price %v is %v", amountIn, price, amountOut) } return nil } -// Used for generating the precomputedPrice.gob file +// // Used for generating the precomputedPrice.gob file +// const PrecomputedPricesFile = "../types/precomputed_prices.gob" // func generatePrecomputedPrices() []math_utils.PrecDec { // precomputedPowers := make([]math_utils.PrecDec, MaxTickExp+1) // precomputedPowers[0] = math_utils.OnePrecDec() // 1.0001^0 = 1 // for i := 1; i <= int(MaxTickExp); i++ { -// precomputedPowers[i] = precomputedPowers[i-1].Quo(utils.BasePrice()) +// precomputedPowers[i] = precomputedPowers[i-1].Mul(utils.BasePrice()) // } // return precomputedPowers // } diff --git a/x/dex/types/tick_liquidity.go b/x/dex/types/tick_liquidity.go index 26ca3ab96..fa0f39f53 100644 --- a/x/dex/types/tick_liquidity.go +++ b/x/dex/types/tick_liquidity.go @@ -10,10 +10,10 @@ import ( func (t TickLiquidity) Price() math_utils.PrecDec { switch liquidity := t.Liquidity.(type) { case *TickLiquidity_LimitOrderTranche: - return liquidity.LimitOrderTranche.PriceTakerToMaker + return liquidity.LimitOrderTranche.MakerPrice case *TickLiquidity_PoolReserves: - return liquidity.PoolReserves.PriceTakerToMaker + return liquidity.PoolReserves.MakerPrice default: panic("Tick does not contain valid liqudityType") } diff --git a/x/dex/types/tick_liquidity_key.go b/x/dex/types/tick_liquidity_key.go index fe33d7899..3949f52f2 100644 --- a/x/dex/types/tick_liquidity_key.go +++ b/x/dex/types/tick_liquidity_key.go @@ -4,5 +4,5 @@ import math_utils "github.com/neutron-org/neutron/v5/utils/math" type TickLiquidityKey interface { KeyMarshal() []byte - PriceTakerToMaker() (priceTakerToMaker math_utils.PrecDec, err error) + Price() (priceTakerToMaker math_utils.PrecDec, err error) } diff --git a/x/dex/types/trade_pair_id.go b/x/dex/types/trade_pair_id.go index 2f6f33c23..388ab7cdf 100644 --- a/x/dex/types/trade_pair_id.go +++ b/x/dex/types/trade_pair_id.go @@ -91,12 +91,12 @@ func (p TradePairID) TickIndexNormalized(tickIndexTakerToMaker int64) int64 { return p.TickIndexTakerToMaker(tickIndexTakerToMaker) } -func (p TradePairID) PriceTakerToMaker(tickIndexNormalized int64) (priceTakerToMaker math_utils.PrecDec, err error) { +func (p TradePairID) MakerPrice(tickIndexNormalized int64) (priceTakerToMaker math_utils.PrecDec, err error) { return CalcPrice(p.TickIndexTakerToMaker(tickIndexNormalized)) } -func (p TradePairID) MustPriceTakerToMaker(tickIndexNormalized int64) (priceTakerToMaker math_utils.PrecDec) { - price, err := p.PriceTakerToMaker(tickIndexNormalized) +func (p TradePairID) MustMakerPrice(tickIndexNormalized int64) (priceTakerToMaker math_utils.PrecDec) { + price, err := p.MakerPrice(tickIndexNormalized) if err != nil { panic(err) }