Skip to content

Commit

Permalink
[CLOB-807] - SetClobPairStatus (#82)
Browse files Browse the repository at this point in the history
* add mustGetClobPair and SetClobPairStatus functions to clob keeper

* add tests for SetClobPairStatus

* format

* add mocked indexer event for creating new perp market in test

* update if statements and comments

* format

* remove nolint line

* format
  • Loading branch information
jakob-dydx authored Aug 25, 2023
1 parent b03ea0b commit 42fee77
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 4 deletions.
53 changes: 51 additions & 2 deletions protocol/x/clob/keeper/clob_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (k Keeper) CreatePerpetualClobPair(
// - SubticksPerTick:
// - Must be greater than zero.
func (k Keeper) validateClobPair(ctx sdk.Context, clobPair *types.ClobPair) error {
if isSupported := types.IsSupportedClobPairStatus(clobPair.Status); !isSupported {
if !types.IsSupportedClobPairStatus(clobPair.Status) {
return sdkerrors.Wrapf(
types.ErrInvalidClobPairParameter,
"CLOB pair (%+v) has unsupported status %+v",
Expand Down Expand Up @@ -246,7 +246,9 @@ func (k Keeper) validateOrderAgainstClobPairStatus(
order types.Order,
clobPair types.ClobPair,
) error {
if isSupported := types.IsSupportedClobPairStatus(clobPair.Status); !isSupported {
if !types.IsSupportedClobPairStatus(clobPair.Status) {
// Validation should only be called against ClobPairs in state, implying we have a ClobPair with
// an unsupported status in state.
panic(
fmt.Sprintf(
"validateOrderAgainstClobPairStatus: clob pair status %v is not supported",
Expand Down Expand Up @@ -310,3 +312,50 @@ func (k Keeper) validateOrderAgainstClobPairStatus(

return nil
}

// mustGetClobPair fetches a ClobPair from state given its id.
// This function panics if the ClobPair is not found.
func (k Keeper) mustGetClobPair(
ctx sdk.Context,
clobPairId types.ClobPairId,
) types.ClobPair {
clobPair, found := k.GetClobPair(ctx, clobPairId)
if !found {
panic(
fmt.Sprintf(
"mustGetClobPair: ClobPair with id %+v not found",
clobPairId,
),
)
}
return clobPair
}

// SetClobPairStatus fetches a ClobPair by id and sets its
// Status property equal to the provided ClobPair_Status. This function returns
// an error if the proposed status transition is not supported.
func (k Keeper) SetClobPairStatus(
ctx sdk.Context,
clobPairId types.ClobPairId,
clobPairStatus types.ClobPair_Status,
) error {
clobPair := k.mustGetClobPair(ctx, clobPairId)

if !types.IsSupportedClobPairStatusTransition(clobPair.Status, clobPairStatus) {
return sdkerrors.Wrapf(
types.ErrInvalidClobPairStatusTransition,
"Cannot transition from status %+v to status %+v",
clobPair.Status,
clobPairStatus,
)
}

clobPair.Status = clobPairStatus
if err := k.validateClobPair(ctx, &clobPair); err != nil {
return err
}

k.setClobPair(ctx, clobPair)

return nil
}
142 changes: 140 additions & 2 deletions protocol/x/clob/keeper/clob_pair_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package keeper_test

import (
indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events"
"github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager"
"strconv"
"testing"

indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events"
"github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
Expand Down Expand Up @@ -432,3 +433,140 @@ func TestClobPairGetAll(t *testing.T) {
nullify.Fill(ks.ClobKeeper.GetAllClobPair(ks.Ctx)), //nolint:staticcheck
)
}

func TestSetClobPairStatus(t *testing.T) {
testCases := map[string]struct {
setup func(t *testing.T, ks keepertest.ClobKeepersTestContext, manager *mocks.IndexerEventManager)
status types.ClobPair_Status
expectedErr string
expectedPanic string
}{
"Succeeds with valid status transition": {
setup: func(t *testing.T, ks keepertest.ClobKeepersTestContext, mockIndexerEventManager *mocks.IndexerEventManager) {
// write a clob pair to the store with status initializing
registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
store := prefix.NewStore(ks.Ctx.KVStore(ks.StoreKey), types.KeyPrefix(types.ClobPairKeyPrefix))

clobPair := constants.ClobPair_Btc
clobPair.Status = types.ClobPair_STATUS_INITIALIZING
b := cdc.MustMarshal(&clobPair)
store.Set(types.ClobPairKey(
types.ClobPairId(clobPair.Id),
), b)
},
status: types.ClobPair_STATUS_ACTIVE,
},
"Panics with missing clob pair": {
setup: func(t *testing.T, ks keepertest.ClobKeepersTestContext, mockIndexerEventManager *mocks.IndexerEventManager) {
},
status: types.ClobPair_STATUS_ACTIVE,
expectedPanic: "mustGetClobPair: ClobPair with id 0 not found",
},
"Errors with unsupported transition to supported status": {
setup: func(t *testing.T, ks keepertest.ClobKeepersTestContext, mockIndexerEventManager *mocks.IndexerEventManager) {
clobPair := constants.ClobPair_Btc
mockIndexerEventManager.On("AddTxnEvent",
ks.Ctx,
indexerevents.SubtypePerpetualMarket,
indexer_manager.GetB64EncodedEventMessage(
indexerevents.NewPerpetualMarketCreateEvent(
0,
0,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.Ticker,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketId,
clobPair.Status,
clobPair.QuantumConversionExponent,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.AtomicResolution,
clobPair.SubticksPerTick,
clobPair.MinOrderBaseQuantums,
clobPair.StepBaseQuantums,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier,
),
),
).Once().Return()

_, err := ks.ClobKeeper.CreatePerpetualClobPair(
ks.Ctx,
clobtest.MustPerpetualId(clobPair),
satypes.BaseQuantums(clobPair.MinOrderBaseQuantums),
satypes.BaseQuantums(clobPair.StepBaseQuantums),
clobPair.QuantumConversionExponent,
clobPair.SubticksPerTick,
clobPair.Status,
)
require.NoError(t, err)
},
status: types.ClobPair_STATUS_INITIALIZING,
expectedErr: "Cannot transition from status STATUS_ACTIVE to status STATUS_INITIALIZING",
},
"Errors with unsupported transition to unsupported status": {
setup: func(t *testing.T, ks keepertest.ClobKeepersTestContext, mockIndexerEventManager *mocks.IndexerEventManager) {
clobPair := constants.ClobPair_Btc
mockIndexerEventManager.On("AddTxnEvent",
ks.Ctx,
indexerevents.SubtypePerpetualMarket,
indexer_manager.GetB64EncodedEventMessage(
indexerevents.NewPerpetualMarketCreateEvent(
0,
0,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.Ticker,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketId,
clobPair.Status,
clobPair.QuantumConversionExponent,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.AtomicResolution,
clobPair.SubticksPerTick,
clobPair.MinOrderBaseQuantums,
clobPair.StepBaseQuantums,
constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier,
),
),
).Once().Return()

_, err := ks.ClobKeeper.CreatePerpetualClobPair(
ks.Ctx,
clobtest.MustPerpetualId(clobPair),
satypes.BaseQuantums(clobPair.MinOrderBaseQuantums),
satypes.BaseQuantums(clobPair.StepBaseQuantums),
clobPair.QuantumConversionExponent,
clobPair.SubticksPerTick,
clobPair.Status,
)
require.NoError(t, err)
},
status: types.ClobPair_Status(100),
expectedErr: "Cannot transition from status STATUS_ACTIVE to status 100",
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
memClob := memclob.NewMemClobPriceTimePriority(false)
mockIndexerEventManager := &mocks.IndexerEventManager{}
ks := keepertest.NewClobKeepersTestContext(t, memClob, &mocks.BankKeeper{}, mockIndexerEventManager)
prices.InitGenesis(ks.Ctx, *ks.PricesKeeper, constants.Prices_DefaultGenesisState)
perpetuals.InitGenesis(ks.Ctx, *ks.PerpetualsKeeper, constants.Perpetuals_DefaultGenesisState)

tc.setup(t, ks, mockIndexerEventManager)

if tc.expectedPanic != "" {
require.PanicsWithValue(
t,
tc.expectedPanic,
func() {
err := ks.ClobKeeper.SetClobPairStatus(ks.Ctx, 0, tc.status)
require.NoError(t, err)
},
)
} else {
err := ks.ClobKeeper.SetClobPairStatus(ks.Ctx, 0, tc.status)

if tc.expectedErr != "" {
require.ErrorContains(t, err, tc.expectedErr)
} else {
require.NoError(t, err)
}
}
})
}
}
5 changes: 5 additions & 0 deletions protocol/x/clob/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ var (
33,
"Order conflicts with ClobPair status",
)
ErrInvalidClobPairStatusTransition = sdkerrors.Register(
ModuleName,
34,
"Invalid ClobPair status transition",
)

// Liquidations errors.
ErrInvalidLiquidationsConfig = sdkerrors.Register(
Expand Down

0 comments on commit 42fee77

Please sign in to comment.