diff --git a/protocol/testutil/constants/orders.go b/protocol/testutil/constants/orders.go index 0e83d1002e..92aee84893 100644 --- a/protocol/testutil/constants/orders.go +++ b/protocol/testutil/constants/orders.go @@ -414,6 +414,20 @@ var ( Subticks: 10000, GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 22}, } + Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 8, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 5, + Subticks: 10, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 22}, + } + Order_Bob_Num0_Id8_Clob1_Sell20_Price10_GTB22 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 8, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 20, + Subticks: 10, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 22}, + } Order_Bob_Num0_Id8_Clob0_Sell20_Price10_GTB22 = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 8, ClobPairId: 0}, Side: clobtypes.Order_SIDE_SELL, @@ -435,6 +449,13 @@ var ( Subticks: 10000, GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 22}, } + Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 11, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 5, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + } Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20 = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 11, ClobPairId: 1}, Side: clobtypes.Order_SIDE_BUY, @@ -442,6 +463,20 @@ var ( Subticks: 40, GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, } + Order_Bob_Num0_Id12_Clob0_Buy5_Price5_GTB20 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 5, + Subticks: 5, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + } + Order_Bob_Num0_Id12_Clob0_Buy5_Price40_GTB20 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 5, + Subticks: 40, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + } Order_Bob_Num0_Id12_Clob1_Buy5_Price40_GTB20 = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 1}, Side: clobtypes.Order_SIDE_BUY, @@ -456,6 +491,20 @@ var ( Subticks: 40, GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 32}, } + Order_Bob_Num0_Id12_Clob0_Sell20_Price5_GTB20 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 20, + Subticks: 5, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + } + Order_Bob_Num0_Id12_Clob0_Sell20_Price15_GTB20 = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 20, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + } Order_Bob_Num0_Id12_Clob0_Sell20_Price35_GTB32 = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Bob_Num0, ClientId: 12, ClobPairId: 0}, Side: clobtypes.Order_SIDE_SELL, @@ -879,6 +928,38 @@ var ( } // IOC orders. + Order_Alice_Num0_Id1_Clob1_Buy5_Price15_GTB20_IOC = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 5, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_IOC, + } + Order_Alice_Num0_Id1_Clob1_Sell5_Price15_GTB20_IOC = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 5, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_IOC, + } + Order_Alice_Num0_Id1_Clob1_Buy10_Price15_GTB20_IOC = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 10, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_IOC, + } + Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 10, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_IOC, + } Order_Alice_Num1_Id1_Clob1_Sell10_Price15_GTB20_IOC = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Alice_Num1, ClientId: 1, ClobPairId: 1}, Side: clobtypes.Order_SIDE_SELL, @@ -897,14 +978,30 @@ var ( } // Fill-or-kill orders. + Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 0, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 10, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_FILL_OR_KILL, + } Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK = clobtypes.Order{ - OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 0, ClobPairId: 0}, + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 0, ClobPairId: 1}, Side: clobtypes.Order_SIDE_BUY, Quantums: 10, Subticks: 15, GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, TimeInForce: clobtypes.Order_TIME_IN_FORCE_FILL_OR_KILL, } + Order_Alice_Num0_Id0_Clob1_Buy20_Price15_GTB20_FOK = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 0, ClobPairId: 1}, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 20, + Subticks: 15, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_FILL_OR_KILL, + } Order_Alice_Num1_Id1_Clob1_Sell10_Price15_GTB20_FOK = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Alice_Num1, ClientId: 1, ClobPairId: 1}, Side: clobtypes.Order_SIDE_SELL, @@ -1071,12 +1168,20 @@ var ( } // Post-only orders. + Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO = clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 15, + Subticks: 10, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 18}, + TimeInForce: clobtypes.Order_TIME_IN_FORCE_POST_ONLY, + } Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO = clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: Alice_Num0, ClientId: 1, ClobPairId: 0}, Side: clobtypes.Order_SIDE_BUY, Quantums: 15, Subticks: 10, - GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{GoodTilBlockTime: 18}, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 18}, TimeInForce: clobtypes.Order_TIME_IN_FORCE_POST_ONLY, } Order_Alice_Num1_Id1_Clob1_Sell10_Price15_GTB20_PO = clobtypes.Order{ diff --git a/protocol/testutil/msgs/util.go b/protocol/testutil/msgs/util.go index 183e445c72..294d75d0d8 100644 --- a/protocol/testutil/msgs/util.go +++ b/protocol/testutil/msgs/util.go @@ -13,8 +13,9 @@ type TestBlockWithMsgs struct { } type TestSdkMsg struct { - Msg sdk.Msg - ExpectedIsOk bool + Msg sdk.Msg + ExpectedIsOk bool + ExpectedRespCode uint32 } // SampleMsg is a struct containing a sample msg and its name. diff --git a/protocol/x/clob/e2e/short_term_orders_test.go b/protocol/x/clob/e2e/short_term_orders_test.go index b6bfc6518f..702b58e5f0 100644 --- a/protocol/x/clob/e2e/short_term_orders_test.go +++ b/protocol/x/clob/e2e/short_term_orders_test.go @@ -17,6 +17,7 @@ import ( 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" + testmsgs "github.com/dydxprotocol/v4-chain/protocol/testutil/msgs" 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" @@ -1227,3 +1228,574 @@ func TestCancelShortTermOrder(t *testing.T) { }) } } + +func TestShortTermAdvancedOrders(t *testing.T) { + tests := map[string]struct { + blocks []testmsgs.TestBlockWithMsgs + + expectedOrderIdsInMemclob map[clobtypes.OrderId]bool + expectedOrderFillAmounts map[clobtypes.OrderId]uint64 + }{ + "IOC sell fully matches": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Sell5_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: false, + constants.Order_Alice_Num0_Id1_Clob1_Sell5_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: 5000, // full size of scaled orders + constants.Order_Alice_Num0_Id1_Clob1_Sell5_Price15_GTB20_IOC.OrderId: 5000, + }, + }, + "IOC buy fully matches": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Buy5_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20.OrderId: false, + constants.Order_Alice_Num0_Id1_Clob1_Buy5_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20.OrderId: 5000, + constants.Order_Alice_Num0_Id1_Clob1_Buy5_Price15_GTB20_IOC.OrderId: 5000, + }, + }, + "IOC sell partially matches and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: false, + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: 5000, + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC.OrderId: 5000, + }, + }, + "IOC buy partially matches and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Buy10_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20.OrderId: false, + constants.Order_Alice_Num0_Id1_Clob1_Buy10_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Sell5_Price15_GTB20.OrderId: 5000, + constants.Order_Alice_Num0_Id1_Clob1_Buy10_Price15_GTB20_IOC.OrderId: 5000, + }, + }, + "IOC fails CheckTx if previously filled": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + { + Block: 3, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrImmediateExecutionOrderAlreadyFilled.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: false, + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: 5000, + constants.Order_Alice_Num0_Id1_Clob1_Sell10_Price15_GTB20_IOC.OrderId: 5000, + }, + }, + "FOK buy fully matches": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id8_Clob1_Sell20_Price10_GTB22, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id8_Clob1_Sell20_Price10_GTB22.OrderId: true, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id8_Clob1_Sell20_Price10_GTB22.OrderId: 10_000, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: 10_000, + }, + }, + "FOK sell fully matches": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id4_Clob1_Buy20_Price35_GTB22, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id4_Clob1_Buy20_Price35_GTB22.OrderId: true, + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id4_Clob1_Buy20_Price35_GTB22.OrderId: 10_000, + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK.OrderId: 10_000, + }, + }, + "FOK buy partially matches, fails, and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrFokOrderCouldNotBeFullyFilled.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22.OrderId: true, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22.OrderId: 0, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: 0, + }, + }, + "FOK sell partially matches, fails, and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrFokOrderCouldNotBeFullyFilled.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: true, + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id11_Clob1_Buy5_Price40_GTB20.OrderId: 0, + constants.Order_Alice_Num0_Id0_Clob1_Sell10_Price15_GTB20_FOK.OrderId: 0, + }, + }, + "FOK fails CheckTx if previously filled": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id8_Clob1_Sell20_Price10_GTB22, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + { + Block: 3, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id0_Clob1_Buy20_Price15_GTB20_FOK, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrImmediateExecutionOrderAlreadyFilled.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22.OrderId: true, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id8_Clob1_Sell5_Price10_GTB22.OrderId: 10_000, + constants.Order_Alice_Num0_Id0_Clob1_Buy10_Price15_GTB20_FOK.OrderId: 10_000, + }, + }, + "Post-only buy does not cross and is placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price15_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price15_GTB20.OrderId: true, + constants.Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO.OrderId: true, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price15_GTB20.OrderId: 0, + constants.Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO.OrderId: 0, + }, + }, + "Post-only sell does not cross and is placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price5_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price5_GTB20.OrderId: true, + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO.OrderId: true, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price5_GTB20.OrderId: 0, + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO.OrderId: 0, + }, + }, + "Post-only buy crosses and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price5_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrPostOnlyWouldCrossMakerOrder.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price5_GTB20.OrderId: true, + constants.Order_Alice_Num0_Id1_Clob1_Sell5_Price15_GTB20_IOC.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id12_Clob0_Sell20_Price5_GTB20.OrderId: 0, + constants.Order_Alice_Num0_Id1_Clob0_Buy15_Price10_GTB18_PO.OrderId: 0, + }, + }, + "Post-only sell crosses and is not placed on the book": { + blocks: []testmsgs.TestBlockWithMsgs{ + { + Block: 2, + Msgs: []testmsgs.TestSdkMsg{ + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price40_GTB20, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: true, + }, + { + Msg: clobtypes.NewMsgPlaceOrder( + MustScaleOrder( + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO, + testapp.DefaultGenesis(), + ), + ), + ExpectedIsOk: false, + ExpectedRespCode: clobtypes.ErrPostOnlyWouldCrossMakerOrder.ABCICode(), + }, + }, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price40_GTB20.OrderId: true, + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO.OrderId: false, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + constants.Order_Bob_Num0_Id12_Clob0_Buy5_Price40_GTB20.OrderId: 0, + constants.Order_Alice_Num0_Id1_Clob0_Sell15_Price10_GTB18_PO.OrderId: 0, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + + for _, block := range tc.blocks { + for _, order := range block.Msgs { + msgPlaceOrder, ok := order.Msg.(*clobtypes.MsgPlaceOrder) + if !ok { + t.Error("Expected MsgPlaceOrder") + } + for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, *msgPlaceOrder) { + resp := tApp.CheckTx(checkTx) + require.Equal(t, order.ExpectedIsOk, resp.IsOK(), "Response was not as expected: %+v", resp.Log) + require.Equal( + t, + order.ExpectedRespCode, + resp.Code, + "Response code was not as expected", + ) + } + } + ctx = tApp.AdvanceToBlock(block.Block, testapp.AdvanceToBlockOptions{}) + } + + for orderId, shouldHaveOrder := range tc.expectedOrderIdsInMemclob { + _, exists := tApp.App.ClobKeeper.MemClob.GetOrder(ctx, orderId) + require.Equal(t, shouldHaveOrder, exists) + } + + for orderId, expectedFillAmount := range tc.expectedOrderFillAmounts { + _, fillAmount, _ := tApp.App.ClobKeeper.GetOrderFillAmount(ctx, orderId) + require.Equal(t, expectedFillAmount, fillAmount.ToUint64()) + } + }) + } +}