diff --git a/protocol/x/clob/keeper/match_state.go b/protocol/x/clob/keeper/match_state.go index d5c2535712..e83ae5887f 100644 --- a/protocol/x/clob/keeper/match_state.go +++ b/protocol/x/clob/keeper/match_state.go @@ -7,12 +7,49 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" ) -// GetLastTradePriceForPerpetual gets the last trade price for a perpetual. -func (k Keeper) GetLastTradePriceForPerpetual( +// GetTradePricesForPerpetual gets the maximum and minimum traded prices for a perpetual for the +// current block. +// These prices are intended to be used for improved conditional order triggering in the EndBlocker. +func (k Keeper) GetTradePricesForPerpetual( + ctx sdk.Context, + perpetualId uint32, +) ( + minTradePriceSubticks types.Subticks, + maxTradePriceSubticks types.Subticks, + found bool, +) { + minTradePriceSubticks, found = k.getMinTradePriceForPerpetual(ctx, perpetualId) + if !found { + return 0, 0, false + } + + maxTradePriceSubticks, found = k.getMaxTradePriceForPerpetual(ctx, perpetualId) + return minTradePriceSubticks, maxTradePriceSubticks, found +} + +// getMinTradePriceForPerpetual gets the min trade price for a perpetual. +func (k Keeper) getMinTradePriceForPerpetual( + ctx sdk.Context, + perpetualId uint32, +) (subticks types.Subticks, found bool) { + store := k.GetMinTradePriceStore(ctx) + + b := store.Get(lib.Uint32ToKey(perpetualId)) + if b == nil { + return 0, false + } + + result := gogotypes.UInt64Value{Value: 0} + k.cdc.MustUnmarshal(b, &result) + return types.Subticks(result.Value), true +} + +// getMaxTradePriceForPerpetual gets the max trade price for a perpetual. +func (k Keeper) getMaxTradePriceForPerpetual( ctx sdk.Context, perpetualId uint32, ) (subticks types.Subticks, found bool) { - store := k.GetLastTradePriceStore(ctx) + store := k.GetMaxTradePriceStore(ctx) b := store.Get(lib.Uint32ToKey(perpetualId)) if b == nil { @@ -24,17 +61,29 @@ func (k Keeper) GetLastTradePriceForPerpetual( return types.Subticks(result.Value), true } -// SetLastTradePriceForPerpetual sets the last trade price for a perpetual. -func (k Keeper) SetLastTradePriceForPerpetual( +// SetTradePricesForPerpetual sets the maximum and minimum traded prices for a perpetual for the current +// block. +// Note that this method updates the transient store and is meant to be called in `DeliverTx` when +// matches are persisted to state. +func (k Keeper) SetTradePricesForPerpetual( ctx sdk.Context, perpetualId uint32, subticks types.Subticks, ) { - store := k.GetLastTradePriceStore(ctx) - + minTradePriceSubticks, maxTradePriceSubticks, found := k.GetTradePricesForPerpetual(ctx, perpetualId) + key := lib.Uint32ToKey(perpetualId) value := gogotypes.UInt64Value{Value: subticks.ToUint64()} - store.Set( - lib.Uint32ToKey(perpetualId), - k.cdc.MustMarshal(&value), - ) + b := k.cdc.MustMarshal(&value) + + // Update the min price if not previously saved or if the new price is lower. + if !found || subticks < minTradePriceSubticks { + store := k.GetMinTradePriceStore(ctx) + store.Set(key, b) + } + + // Update the max price if not previously saved or if the new price is higher. + if !found || subticks > maxTradePriceSubticks { + store := k.GetMaxTradePriceStore(ctx) + store.Set(key, b) + } } diff --git a/protocol/x/clob/keeper/match_state_test.go b/protocol/x/clob/keeper/match_state_test.go index b5b0aafaf3..f8c27e49cc 100644 --- a/protocol/x/clob/keeper/match_state_test.go +++ b/protocol/x/clob/keeper/match_state_test.go @@ -10,21 +10,39 @@ import ( "github.com/stretchr/testify/require" ) -func TestGetSetLastTradePrice(t *testing.T) { +func TestGetSetLastTradePrices(t *testing.T) { // Setup keeper state and test parameters. memClob := memclob.NewMemClobPriceTimePriority(false) ks := keepertest.NewClobKeepersTestContext(t, memClob, &mocks.BankKeeper{}, &mocks.IndexerEventManager{}) // Get non-existent last trade price. - price, found := ks.ClobKeeper.GetLastTradePriceForPerpetual(ks.Ctx, 0) - require.Equal(t, price, types.Subticks(0)) + minTradePriceSubticks, maxTradePriceSubticks, found := ks.ClobKeeper.GetTradePricesForPerpetual(ks.Ctx, 0) + require.Equal(t, minTradePriceSubticks, types.Subticks(0)) + require.Equal(t, maxTradePriceSubticks, types.Subticks(0)) require.False(t, found) - // Set last trade price. - ks.ClobKeeper.SetLastTradePriceForPerpetual(ks.Ctx, 0, types.Subticks(17)) + // Set trade prices. + ks.ClobKeeper.SetTradePricesForPerpetual(ks.Ctx, 0, types.Subticks(17)) - // Get the last trade price, which should now exist. - price, found = ks.ClobKeeper.GetLastTradePriceForPerpetual(ks.Ctx, 0) - require.Equal(t, price, types.Subticks(17)) + // Get the min and max trade prices, which should now exist. + minTradePriceSubticks, maxTradePriceSubticks, found = ks.ClobKeeper.GetTradePricesForPerpetual(ks.Ctx, 0) + require.Equal(t, minTradePriceSubticks, types.Subticks(17)) + require.Equal(t, maxTradePriceSubticks, types.Subticks(17)) + require.True(t, found) + + // Update the min price. + ks.ClobKeeper.SetTradePricesForPerpetual(ks.Ctx, 0, types.Subticks(13)) + + minTradePriceSubticks, maxTradePriceSubticks, found = ks.ClobKeeper.GetTradePricesForPerpetual(ks.Ctx, 0) + require.Equal(t, minTradePriceSubticks, types.Subticks(13)) + require.Equal(t, maxTradePriceSubticks, types.Subticks(17)) + require.True(t, found) + + // Update the max price. + ks.ClobKeeper.SetTradePricesForPerpetual(ks.Ctx, 0, types.Subticks(23)) + + minTradePriceSubticks, maxTradePriceSubticks, found = ks.ClobKeeper.GetTradePricesForPerpetual(ks.Ctx, 0) + require.Equal(t, minTradePriceSubticks, types.Subticks(13)) + require.Equal(t, maxTradePriceSubticks, types.Subticks(23)) require.True(t, found) } diff --git a/protocol/x/clob/keeper/stores.go b/protocol/x/clob/keeper/stores.go index e1a63e7eea..e3b00d35d4 100644 --- a/protocol/x/clob/keeper/stores.go +++ b/protocol/x/clob/keeper/stores.go @@ -1,9 +1,10 @@ package keeper import ( - storetypes "cosmossdk.io/store/types" "fmt" + storetypes "cosmossdk.io/store/types" + "github.com/dydxprotocol/v4-chain/protocol/lib" "cosmossdk.io/store/prefix" @@ -152,11 +153,20 @@ func (k Keeper) fetchStateStoresForOrder( ) } -// GetLastTradePriceStore fetches a mem store used for reading and updating the -// last trade prices for perpetuals. -func (k Keeper) GetLastTradePriceStore(ctx sdk.Context) prefix.Store { +// GetMinTradePriceStore fetches a transient store used for reading and updating the +// min trade prices for perpetuals. +func (k Keeper) GetMinTradePriceStore(ctx sdk.Context) prefix.Store { return prefix.NewStore( - ctx.KVStore(k.storeKey), - []byte(types.LastTradePricePrefix), + ctx.KVStore(k.transientStoreKey), + []byte(types.MinTradePricePrefix), + ) +} + +// GetMaxTradePriceStore fetches a transient store used for reading and updating the +// max trade prices for perpetuals. +func (k Keeper) GetMaxTradePriceStore(ctx sdk.Context) prefix.Store { + return prefix.NewStore( + ctx.KVStore(k.transientStoreKey), + []byte(types.MaxTradePricePrefix), ) } diff --git a/protocol/x/clob/types/keys.go b/protocol/x/clob/types/keys.go index 4fe9ce747b..c4707a6def 100644 --- a/protocol/x/clob/types/keys.go +++ b/protocol/x/clob/types/keys.go @@ -52,10 +52,6 @@ const ( // StatefulOrdersTimeSlicePrefix is the key to retrieve a unique list of the stateful orders that // expire at a given timestamp, sorted by order ID. StatefulOrdersTimeSlicePrefix = "ExpTm:" - - // LastTradePricePrefix is the key prefix to retrieve the last trade price for a perpetual. - // This is meant to be used for improved conditional order triggering. - LastTradePricePrefix = "LastTrade:" ) // Store / Memstore @@ -109,6 +105,14 @@ const ( // are stored in a transient store. This count represents the number of uncommitted stateful // `placements - cancellations`. UncommittedStatefulOrderCountPrefix = "NumUncmtSO:" + + // MinTradePricePrefix is the key prefix to retrieve the min trade price for a perpetual. + // This is meant to be used for improved conditional order triggering. + MinTradePricePrefix = "MinTrade:" + + // MaxTradePricePrefix is the key prefix to retrieve the max trade price for a perpetual. + // This is meant to be used for improved conditional order triggering. + MaxTradePricePrefix = "MaxTrade:" ) // Module Accounts