Skip to content

Commit

Permalink
[CLOB-636] - add e2e tests for short term cancellations (#751)
Browse files Browse the repository at this point in the history
* add e2e tests for short term cancellations

* reorder imports

* add cancel replacement tests
  • Loading branch information
jakob-dydx authored Nov 7, 2023
1 parent 0385e41 commit 0cbde96
Showing 1 changed file with 212 additions and 5 deletions.
217 changes: 212 additions & 5 deletions protocol/x/clob/e2e/short_term_orders_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
package clob_test

import (
"github.com/dydxprotocol/v4-chain/protocol/indexer"
"testing"

"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"

"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/dydxprotocol/v4-chain/protocol/dtypes"
"github.com/dydxprotocol/v4-chain/protocol/lib"

"github.com/dydxprotocol/v4-chain/protocol/indexer"
indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events"
"github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager"
"github.com/dydxprotocol/v4-chain/protocol/indexer/msgsender"
"github.com/dydxprotocol/v4-chain/protocol/indexer/off_chain_updates"
"github.com/dydxprotocol/v4-chain/protocol/lib"
testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app"
clobtestutils "github.com/dydxprotocol/v4-chain/protocol/testutil/clob"
"github.com/dydxprotocol/v4-chain/protocol/testutil/constants"
testtx "github.com/dydxprotocol/v4-chain/protocol/testutil/tx"
assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types"
clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types"
"github.com/stretchr/testify/require"
)

func TestPlaceOrder(t *testing.T) {
Expand Down Expand Up @@ -1020,3 +1019,211 @@ func TestShortTermOrderReplacements(t *testing.T) {
})
}
}

func TestCancelShortTermOrder(t *testing.T) {
tests := map[string]struct {
firstBlockOrders []clobtypes.MsgPlaceOrder
firstBlockCancels []clobtypes.MsgCancelOrder
secondBlockOrders []clobtypes.MsgPlaceOrder
secondBlockCancels []clobtypes.MsgCancelOrder

expectedOrderIdsInMemclob map[clobtypes.OrderId]bool
expectedCancelExpirationsInMemclob map[clobtypes.OrderId]uint32
expectedOrderFillAmounts map[clobtypes.OrderId]uint64
}{
"Cancel unfilled short term order": {
firstBlockOrders: []clobtypes.MsgPlaceOrder{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5,
},
secondBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
},

expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false,
},
expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 0,
},
},
"Cancel partially filled short term order in same block": {
firstBlockOrders: []clobtypes.MsgPlaceOrder{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5,
*clobtypes.NewMsgPlaceOrder(MustScaleOrder(
clobtypes.Order{
OrderId: clobtypes.OrderId{SubaccountId: constants.Bob_Num0, ClientId: 0, ClobPairId: 0},
Side: clobtypes.Order_SIDE_SELL,
Quantums: 4,
Subticks: 10,
GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20},
},
testapp.DefaultGenesis(),
)),
},
firstBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
},

expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false,
},
expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 40,
},
},
"Cancel partially filled short term order in next block": {
firstBlockOrders: []clobtypes.MsgPlaceOrder{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5,
*clobtypes.NewMsgPlaceOrder(MustScaleOrder(
clobtypes.Order{
OrderId: clobtypes.OrderId{SubaccountId: constants.Bob_Num0, ClientId: 0, ClobPairId: 0},
Side: clobtypes.Order_SIDE_SELL,
Quantums: 4,
Subticks: 10,
GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20},
},
testapp.DefaultGenesis(),
)),
},
secondBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
},

expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false,
},
expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 40,
},
},
"Cancel succeeds for fully-filled order": {
firstBlockOrders: []clobtypes.MsgPlaceOrder{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5,
PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20,
},
secondBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
},

expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false,
},
expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 50,
},
},
"Cancel with GTB < existing order GTB does not remove order from memclob": {
firstBlockOrders: []clobtypes.MsgPlaceOrder{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20,
},
secondBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
},

expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{
PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20.Order.OrderId: true,
},
expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
},
"Cancel with GTB < existing cancel GTB is not placed on memclob": {
firstBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
*clobtypes.NewMsgCancelOrderShortTerm(
clobtypes.OrderId{
SubaccountId: constants.Alice_Num0,
ClientId: 0,
ClobPairId: 0,
},
4,
),
},

expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5,
},
},
"Cancel with GTB > existing cancel GTB is placed on memclob": {
firstBlockCancels: []clobtypes.MsgCancelOrder{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5,
*clobtypes.NewMsgCancelOrderShortTerm(
clobtypes.OrderId{
SubaccountId: constants.Alice_Num0,
ClientId: 0,
ClobPairId: 0,
},
6,
),
},

expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{
CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 6,
},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
tApp := testapp.NewTestAppBuilder(t).Build()
ctx := tApp.InitChain()

// Place first block orders and cancels
for _, order := range tc.firstBlockOrders {
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, order) {
resp := tApp.CheckTx(checkTx)
require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp)
}
}
for _, cancel := range tc.firstBlockCancels {
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, cancel) {
tApp.CheckTx(checkTx)
}
}

// Advance block
ctx = tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{})

// Place second block orders and cancels
for _, order := range tc.secondBlockOrders {
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, order) {
resp := tApp.CheckTx(checkTx)
require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp)
}
}
for _, orderCancel := range tc.secondBlockCancels {
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, orderCancel) {
resp := tApp.CheckTx(checkTx)
require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp)
}
}

// Verify expectations
for orderId, shouldHaveOrder := range tc.expectedOrderIdsInMemclob {
_, exists := tApp.App.ClobKeeper.MemClob.GetOrder(ctx, orderId)
require.Equal(t, shouldHaveOrder, exists)
}
for orderId, expectedCancelExpirationBlock := range tc.expectedCancelExpirationsInMemclob {
cancelExpirationBlock, exists := tApp.App.ClobKeeper.MemClob.GetCancelOrder(ctx, orderId)
require.True(t, exists)
require.Equal(t, expectedCancelExpirationBlock, cancelExpirationBlock)
}
for orderId, expectedFillAmount := range tc.expectedOrderFillAmounts {
_, fillAmount, _ := tApp.App.ClobKeeper.GetOrderFillAmount(ctx, orderId)
require.Equal(t, expectedFillAmount, fillAmount.ToUint64())
}
})
}
}

0 comments on commit 0cbde96

Please sign in to comment.