diff --git a/x/dex/keeper/migrations.go b/x/dex/keeper/migrations.go index f08b14797..ef9a8c2bb 100644 --- a/x/dex/keeper/migrations.go +++ b/x/dex/keeper/migrations.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" v3 "github.com/neutron-org/neutron/v4/x/dex/migrations/v3" + v4 "github.com/neutron-org/neutron/v4/x/dex/migrations/v4" ) // Migrator is a struct for handling in-place store migrations. @@ -20,3 +21,8 @@ func NewMigrator(keeper Keeper) Migrator { func (m Migrator) Migrate2to3(ctx sdk.Context) error { return v3.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) } + +// Migrate2to3 migrates from version 3 to 4. +func (m Migrator) Migrate3to4(ctx sdk.Context) error { + return v4.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) +} diff --git a/x/dex/migrations/v3/store.go b/x/dex/migrations/v3/store.go index 3afae19a8..bb26392f1 100644 --- a/x/dex/migrations/v3/store.go +++ b/x/dex/migrations/v3/store.go @@ -23,15 +23,6 @@ func MigrateStore(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.St if err := migrateLimitOrderExpirations(ctx, cdc, storeKey); err != nil { return err } - - if err := migrateTickLiquidityPrices(ctx, cdc, storeKey); err != nil { - return err - } - - if err := migrateInactiveTranchePrices(ctx, cdc, storeKey); err != nil { - return err - } - return nil } @@ -105,90 +96,3 @@ func migrateLimitOrderExpirations(ctx sdk.Context, cdc codec.BinaryCodec, storeK return nil } - -type migrationUpdate struct { - key []byte - val []byte -} - -func migrateTickLiquidityPrices(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { - // Due to change in precision of PrecDec between v2 and v3 we need to recompute all PrecDecs in the kvstore - 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) - // Recalculate all prices - switch liquidity := tickLiq.Liquidity.(type) { - case *types.TickLiquidity_LimitOrderTranche: - 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() - 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 { - // Due to change in precision of PrecDec between v2 and v3 we need to recompute all PrecDecs in the kvstore - 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) - // Recalculate price - tranche.PriceTakerToMaker = 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/v3/store_test.go b/x/dex/migrations/v3/store_test.go index 5f9e4a7ba..7dc44dc37 100644 --- a/x/dex/migrations/v3/store_test.go +++ b/x/dex/migrations/v3/store_test.go @@ -117,56 +117,3 @@ func (suite *V3DexMigrationTestSuite) TestLimitOrderExpirationUpgrade() { allExp := app.DexKeeper.GetAllLimitOrderExpiration(ctx) suite.Require().Equal(len(lOExpirations), len(allExp)) } - -func (suite *V3DexMigrationTestSuite) TestPriceUpdates() { - var ( - app = suite.GetNeutronZoneApp(suite.ChainA) - storeKey = app.GetKey(types.StoreKey) - ctx = suite.ChainA.GetContext() - cdc = app.AppCodec() - ) - - // Write tranche with incorrect price - trancheKey := &types.LimitOrderTrancheKey{ - TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), - TickIndexTakerToMaker: -50, - 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 with incorrect prices - poolKey := &types.PoolReservesKey{ - TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), - TickIndexTakerToMaker: 60000, - Fee: 1, - } - poolReserves := &types.PoolReserves{ - Key: poolKey, - PriceTakerToMaker: math.ZeroPrecDec(), - PriceOppositeTakerToMaker: math.ZeroPrecDec(), - } - app.DexKeeper.SetPoolReserves(ctx, poolReserves) - - // Run migration - suite.NoError(v3.MigrateStore(ctx, cdc, storeKey)) - - // 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 - inactiveTranche, _ := app.DexKeeper.GetInactiveLimitOrderTranche(ctx, trancheKey) - suite.True(inactiveTranche.PriceTakerToMaker.Equal(math.MustNewPrecDecFromStr("1.005012269623051203500693815"))) - - // 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"))) -} diff --git a/x/dex/migrations/v4/store.go b/x/dex/migrations/v4/store.go new file mode 100644 index 000000000..89766ff25 --- /dev/null +++ b/x/dex/migrations/v4/store.go @@ -0,0 +1,112 @@ +package v4 + +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/v4/x/dex/types" +) + +// MigrateStore performs in-place store migrations. +// The migration adds new dex params -- GoodTilPurgeAllowance & MaxJITsPerBlock// for handling JIT orders. +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 { + // Due to change in precision of PrecDec between v2 and v3 we need to recompute all PrecDecs in the kvstore + 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) + // Recalculate all prices + switch liquidity := tickLiq.Liquidity.(type) { + case *types.TickLiquidity_LimitOrderTranche: + 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() + 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 { + // Due to change in precision of PrecDec between v2 and v3 we need to recompute all PrecDecs in the kvstore + 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) + // Recalculate price + tranche.PriceTakerToMaker = 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/v4/store_test.go b/x/dex/migrations/v4/store_test.go new file mode 100644 index 000000000..10aa1bded --- /dev/null +++ b/x/dex/migrations/v4/store_test.go @@ -0,0 +1,73 @@ +package v4_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/neutron-org/neutron/v4/testutil" + "github.com/neutron-org/neutron/v4/utils/math" + v4 "github.com/neutron-org/neutron/v4/x/dex/migrations/v4" + "github.com/neutron-org/neutron/v4/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 tranche with incorrect price + trancheKey := &types.LimitOrderTrancheKey{ + TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), + TickIndexTakerToMaker: -50, + 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 with incorrect prices + poolKey := &types.PoolReservesKey{ + TradePairId: types.MustNewTradePairID("TokenA", "TokenB"), + TickIndexTakerToMaker: 60000, + Fee: 1, + } + poolReserves := &types.PoolReserves{ + Key: poolKey, + PriceTakerToMaker: math.ZeroPrecDec(), + PriceOppositeTakerToMaker: math.ZeroPrecDec(), + } + app.DexKeeper.SetPoolReserves(ctx, poolReserves) + + // Run migration + suite.NoError(v4.MigrateStore(ctx, cdc, storeKey)) + + // 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 + inactiveTranche, _ := app.DexKeeper.GetInactiveLimitOrderTranche(ctx, trancheKey) + suite.True(inactiveTranche.PriceTakerToMaker.Equal(math.MustNewPrecDecFromStr("1.005012269623051203500693815"))) + + // 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"))) +} diff --git a/x/dex/module.go b/x/dex/module.go index 3e1e31136..f2fa6cb52 100644 --- a/x/dex/module.go +++ b/x/dex/module.go @@ -158,6 +158,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3); err != nil { panic(fmt.Sprintf("failed to migrate x/dex from version 2 to 3: %v", err)) } + + 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)) + } } // RegisterInvariants registers the capability module's invariants.