Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Add a migration to recalculate price to use higher prec_dec precision #582

Merged
merged 6 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 104 additions & 4 deletions x/dex/migrations/v3/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v3
import (
"errors"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -15,11 +16,23 @@ import (
// 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 {
err := migrateParams(ctx, cdc, storeKey)
if err != nil {
if err := migrateParams(ctx, cdc, storeKey); err != nil {
return err
}

if err := migrateLimitOrderExpirations(ctx, cdc, storeKey); err != nil {
return err
}

if err := migrateTickLiquidityPrices(ctx, cdc, storeKey); err != nil {
return err
}
return migrateLimitOrderExpirations(ctx, cdc, storeKey)

if err := migrateInactiveTranchePrices(ctx, cdc, storeKey); err != nil {
return err
}

return nil
}

func migrateParams(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error {
Expand Down Expand Up @@ -72,7 +85,7 @@ func migrateLimitOrderExpirations(ctx sdk.Context, cdc codec.BinaryCodec, storeK

err := iterator.Close()
if err != nil {
return err
return errorsmod.Wrap(err, "iterator failed to close during migration")
}

for i, key := range expirationKeys {
Expand All @@ -92,3 +105,90 @@ 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
}
53 changes: 53 additions & 0 deletions x/dex/migrations/v3/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,56 @@ 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")))
}
Loading