Skip to content

Commit

Permalink
[TRA-478] compute only order IDs when cancelling vault orders (#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
tqin7 authored Jul 5, 2024
1 parent 5db6e89 commit ae0bf05
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 20 deletions.
79 changes: 59 additions & 20 deletions protocol/x/vault/keeper/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,23 @@ func (k Keeper) RefreshAllVaultOrders(ctx sdk.Context) {
// RefreshVaultClobOrders refreshes orders of a CLOB vault.
func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) (err error) {
// Cancel CLOB orders from last block.
ordersToCancel, err := k.GetVaultClobOrders(
orderIdsToCancel, err := k.GetVaultClobOrderIds(
ctx.WithBlockHeight(ctx.BlockHeight()-1),
vaultId,
)
if err != nil {
log.ErrorLogWithError(ctx, "Failed to get vault clob orders to cancel", err, "vaultId", vaultId)
log.ErrorLogWithError(ctx, "Failed to get vault clob order IDs to cancel", err, "vaultId", vaultId)
return err
}
orderExpirationSeconds := k.GetParams(ctx).OrderExpirationSeconds
for _, order := range ordersToCancel {
if _, exists := k.clobKeeper.GetLongTermOrderPlacement(ctx, order.OrderId); exists {
for _, orderId := range orderIdsToCancel {
if _, exists := k.clobKeeper.GetLongTermOrderPlacement(ctx, *orderId); exists {
err := k.clobKeeper.HandleMsgCancelOrder(ctx, clobtypes.NewMsgCancelOrderStateful(
order.OrderId,
*orderId,
uint32(ctx.BlockTime().Unix())+orderExpirationSeconds,
), true)
if err != nil {
log.ErrorLogWithError(ctx, "Failed to cancel order", err, "order", order, "vaultId", vaultId)
log.ErrorLogWithError(ctx, "Failed to cancel order", err, "orderId", orderId, "vaultId", vaultId)
}
vaultId.IncrCounterWithLabels(
metrics.VaultCancelOrder,
Expand All @@ -116,10 +116,10 @@ func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) (
metrics.GetLabelForBoolValue(metrics.Success, err == nil),
)

// Send indexer messages. We expect ordersToCancel and ordersToPlace to have the same length
// Send indexer messages. We expect orderIdsToCancel and ordersToPlace to have the same length
// and the order to place at each index to be a replacement of the order to cancel at the same index.
replacedOrder := ordersToCancel[i]
if replacedOrder == nil {
replacedOrderId := orderIdsToCancel[i]
if replacedOrderId == nil {
k.GetIndexerEventManager().AddTxnEvent(
ctx,
indexerevents.SubtypeStatefulOrder,
Expand All @@ -137,7 +137,7 @@ func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) (
indexerevents.StatefulOrderEventVersion,
indexer_manager.GetBytes(
indexerevents.NewLongTermOrderReplacementEvent(
replacedOrder.OrderId,
*replacedOrderId,
*order,
),
),
Expand Down Expand Up @@ -192,8 +192,6 @@ func (k Keeper) GetVaultClobOrders(
)
}

// Get vault (subaccount 0 of corresponding module account).
vault := vaultId.ToSubaccountId()
// Calculate leverage = open notional / equity.
equity, err := k.GetVaultEquity(ctx, vaultId)
if err != nil {
Expand Down Expand Up @@ -267,6 +265,7 @@ func (k Keeper) GetVaultClobOrders(
constructOrder := func(
side clobtypes.Order_Side,
layer uint32,
orderId *clobtypes.OrderId,
) *clobtypes.Order {
// Ask: leverage_i = leverage - i * order_size_pct
// Bid: leverage_i = leverage + i * order_size_pct
Expand Down Expand Up @@ -332,31 +331,71 @@ func (k Keeper) GetVaultClobOrders(
)

return &clobtypes.Order{
OrderId: clobtypes.OrderId{
SubaccountId: *vault,
ClientId: k.GetVaultClobOrderClientId(ctx, side, uint8(layer)),
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: clobPair.Id,
},
OrderId: *orderId,
Side: side,
Quantums: orderSize.Uint64(), // Validated to be a uint64 above.
Subticks: subticksRounded,
GoodTilOneof: goodTilBlockTime,
}
}

orderIds, err := k.GetVaultClobOrderIds(ctx, vaultId)
if err != nil {
return orders, err
}
orders = make([]*clobtypes.Order, 2*params.Layers)
for i := uint32(0); i < params.Layers; i++ {
// Construct ask at this layer.
orders[2*i] = constructOrder(clobtypes.Order_SIDE_SELL, i)
orders[2*i] = constructOrder(clobtypes.Order_SIDE_SELL, i, orderIds[2*i])

// Construct bid at this layer.
orders[2*i+1] = constructOrder(clobtypes.Order_SIDE_BUY, i)
orders[2*i+1] = constructOrder(clobtypes.Order_SIDE_BUY, i, orderIds[2*i+1])
}

return orders, nil
}

// GetVaultClobOrderIds returns a list of order IDs for a given CLOB vault.
// Let n be number of layers, then the function returns order IDs
// [a_0, b_0, a_1, b_1, ..., a_{n-1}, b_{n-1}] where a_i and b_i are respectively
// ask and bid order IDs at the i-th layer.
func (k Keeper) GetVaultClobOrderIds(
ctx sdk.Context,
vaultId types.VaultId,
) (orderIds []*clobtypes.OrderId, err error) {
clobPair, exists := k.clobKeeper.GetClobPair(ctx, clobtypes.ClobPairId(vaultId.Number))
if !exists {
return orderIds, errorsmod.Wrap(
types.ErrClobPairNotFound,
fmt.Sprintf("VaultId: %v", vaultId),
)
}
vault := vaultId.ToSubaccountId()
constructOrderId := func(
side clobtypes.Order_Side,
layer uint32,
) *clobtypes.OrderId {
return &clobtypes.OrderId{
SubaccountId: *vault,
ClientId: k.GetVaultClobOrderClientId(ctx, side, uint8(layer)),
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: clobPair.Id,
}
}

layers := k.GetParams(ctx).Layers
orderIds = make([]*clobtypes.OrderId, 2*layers)
for i := uint32(0); i < layers; i++ {
// Construct ask order ID at this layer.
orderIds[2*i] = constructOrderId(clobtypes.Order_SIDE_SELL, i)

// Construct bid order ID at this layer.
orderIds[2*i+1] = constructOrderId(clobtypes.Order_SIDE_BUY, i)
}

return orderIds, nil
}

// GetVaultClobOrderClientId returns the client ID for a CLOB order where
// - 1st bit is `side-1` (subtract 1 as buy_side = 1, sell_side = 2)
//
Expand Down
76 changes: 76 additions & 0 deletions protocol/x/vault/keeper/orders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,82 @@ func TestGetVaultClobOrders(t *testing.T) {
}
}

func TestGetVaultClobOrderIds(t *testing.T) {
tests := map[string]struct {
/* --- Setup --- */
// Vault ID.
vaultId vaulttypes.VaultId
// Layers.
layers uint32

/* --- Expectations --- */
// Expected error, if any.
expectedErr error
}{
"Vault Clob 0, 2 layers": {
vaultId: constants.Vault_Clob0,
layers: 2,
},
"Vault Clob 1, 7 layers": {
vaultId: constants.Vault_Clob1,
layers: 7,
},
"Vault Clob 0, 0 layers": {
vaultId: constants.Vault_Clob0,
layers: 0,
},
"Vault Clob 797 (non-existent clob pair), 2 layers": {
vaultId: vaulttypes.VaultId{
Type: vaulttypes.VaultType_VAULT_TYPE_CLOB,
Number: 797,
},
layers: 2,
expectedErr: vaulttypes.ErrClobPairNotFound,
},
}

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

// Set number of layers.
params := k.GetParams(ctx)
params.Layers = tc.layers
err := k.SetParams(ctx, params)
require.NoError(t, err)

// Construct expected order IDs.
expectedOrderIds := make([]*clobtypes.OrderId, tc.layers*2)
for i := uint32(0); i < tc.layers; i++ {
expectedOrderIds[2*i] = &clobtypes.OrderId{
SubaccountId: *tc.vaultId.ToSubaccountId(),
ClientId: tApp.App.VaultKeeper.GetVaultClobOrderClientId(ctx, clobtypes.Order_SIDE_SELL, uint8(i)),
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: tc.vaultId.Number,
}
expectedOrderIds[2*i+1] = &clobtypes.OrderId{
SubaccountId: *tc.vaultId.ToSubaccountId(),
ClientId: tApp.App.VaultKeeper.GetVaultClobOrderClientId(ctx, clobtypes.Order_SIDE_BUY, uint8(i)),
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: tc.vaultId.Number,
}
}

// Verify order IDs.
orderIds, err := k.GetVaultClobOrderIds(ctx, tc.vaultId)
if tc.expectedErr != nil {
require.ErrorContains(t, err, tc.expectedErr.Error())
require.Empty(t, orderIds)
} else {
require.NoError(t, err)
require.Equal(t, expectedOrderIds, orderIds)
}
})
}
}

func TestGetVaultClobOrderClientId(t *testing.T) {
tests := map[string]struct {
/* --- Setup --- */
Expand Down

0 comments on commit ae0bf05

Please sign in to comment.