-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CT-856] Add order replacement to fix vault causing orderbook flickering #1602
Changes from 8 commits
2196eb5
53f021b
feddcca
e087c19
557be43
751bae7
5f3e64e
b534c21
92e19d3
078b321
83cc892
fc81475
a35fe85
ff5df30
a114fac
0ae3f40
adddd22
be7e698
1f18e20
31acb76
176d37c
062ff0d
c43565a
4d13eb5
c1388bb
23f0c10
6ea9d26
6f0ab7d
66a55c9
cf1f888
fe03736
7a2afea
8dee4c9
53fe081
915e31f
e4bb2dd
15061e2
25a445e
1489677
b332cd9
e99086c
366e7de
747ffaa
4f0415e
a61803a
aff6855
4ca5ed4
060ba2e
27641a4
2993902
e2f6aa1
ee19948
dbb28d8
5c6f5ba
2be3558
61022c6
4e6c761
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1088,6 +1088,7 @@ func New( | |
app.PricesKeeper, | ||
app.SendingKeeper, | ||
app.SubaccountsKeeper, | ||
app.IndexerEventManager, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add documentation for The addition of |
||
[]string{ | ||
lib.GovModuleAddress.String(), | ||
delaymsgmoduletypes.ModuleAddress.String(), | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ func (k msgServer) CancelOrder( | |
) (resp *types.MsgCancelOrderResponse, err error) { | ||
ctx := lib.UnwrapSDKContext(goCtx, types.ModuleName) | ||
|
||
if err := k.Keeper.HandleMsgCancelOrder(ctx, msg); err != nil { | ||
if err := k.Keeper.HandleMsgCancelOrder(ctx, msg, false); err != nil { | ||
return nil, err | ||
} | ||
|
||
|
@@ -37,6 +37,7 @@ func (k msgServer) CancelOrder( | |
func (k Keeper) HandleMsgCancelOrder( | ||
ctx sdk.Context, | ||
msg *types.MsgCancelOrder, | ||
isInternalOrder bool, | ||
chenyaoy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) (err error) { | ||
lib.AssertDeliverTxMode(ctx) | ||
|
||
|
@@ -111,17 +112,19 @@ func (k Keeper) HandleMsgCancelOrder( | |
k.MustSetProcessProposerMatchesEvents(ctx, processProposerMatchesEvents) | ||
|
||
// 4. Add the relevant on-chain Indexer event for the cancellation. | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewStatefulOrderRemovalEvent( | ||
msg.OrderId, | ||
indexershared.OrderRemovalReason_ORDER_REMOVAL_REASON_USER_CANCELED, | ||
if !isInternalOrder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment explaining why you are special casing here, and note that vault order indexer event logic is handled elsewhere There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add cmt |
||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewStatefulOrderRemovalEvent( | ||
msg.OrderId, | ||
indexershared.OrderRemovalReason_ORDER_REMOVAL_REASON_USER_CANCELED, | ||
), | ||
), | ||
), | ||
) | ||
) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,13 +88,15 @@ func (k Keeper) HandleMsgPlaceOrder( | |
|
||
// 2. Return an error if an associated cancellation or removal already exists in the current block. | ||
processProposerMatchesEvents := k.GetProcessProposerMatchesEvents(ctx) | ||
cancelledOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.PlacedStatefulCancellationOrderIds) | ||
if _, found := cancelledOrderIds[order.GetOrderId()]; found { | ||
return errorsmod.Wrapf( | ||
types.ErrStatefulOrderPreviouslyCancelled, | ||
"PlaceOrder: order (%+v)", | ||
order, | ||
) | ||
if !isInternalOrder { // If vault order, we allow the order to replace a cancelled order with the same order ID | ||
cancelledOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.PlacedStatefulCancellationOrderIds) | ||
if _, found := cancelledOrderIds[order.GetOrderId()]; found { | ||
return errorsmod.Wrapf( | ||
types.ErrStatefulOrderPreviouslyCancelled, | ||
"PlaceOrder: order (%+v)", | ||
order, | ||
) | ||
} | ||
} | ||
removedOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.RemovedStatefulOrderIds) | ||
if _, found := removedOrderIds[order.GetOrderId()]; found { | ||
|
@@ -115,31 +117,35 @@ func (k Keeper) HandleMsgPlaceOrder( | |
|
||
// 4. Emit the new order placement indexer event. | ||
if order.IsConditionalOrder() { | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewConditionalOrderPlacementEvent( | ||
order, | ||
if !isInternalOrder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this logic right? we never emit any order placements for vault orders? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by emitting a replacement event (instead of placement), does indexer / front-end behavior change in terms of storing / displaying vault orders? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tqin7 would ordersToCancel[i] ever be nil here or will There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewConditionalOrderPlacementEvent( | ||
order, | ||
), | ||
), | ||
), | ||
) | ||
) | ||
} | ||
processProposerMatchesEvents.PlacedConditionalOrderIds = append( | ||
processProposerMatchesEvents.PlacedConditionalOrderIds, | ||
order.OrderId, | ||
) | ||
} else { | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewLongTermOrderPlacementEvent( | ||
order, | ||
if !isInternalOrder { | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewLongTermOrderPlacementEvent( | ||
order, | ||
), | ||
), | ||
), | ||
) | ||
) | ||
} | ||
processProposerMatchesEvents.PlacedLongTermOrderIds = append( | ||
processProposerMatchesEvents.PlacedLongTermOrderIds, | ||
order.OrderId, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,9 @@ import ( | |
|
||
errorsmod "cosmossdk.io/errors" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" | ||
"github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" | ||
indexershared "github.com/dydxprotocol/v4-chain/protocol/indexer/shared/types" | ||
"github.com/dydxprotocol/v4-chain/protocol/lib" | ||
"github.com/dydxprotocol/v4-chain/protocol/lib/log" | ||
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics" | ||
|
@@ -24,6 +27,7 @@ func (k Keeper) RefreshAllVaultOrders(ctx sdk.Context) { | |
defer totalSharesIterator.Close() | ||
for ; totalSharesIterator.Valid(); totalSharesIterator.Next() { | ||
vaultId, err := types.GetVaultIdFromStateKey(totalSharesIterator.Key()) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding metrics or more detailed logging for vaults that are skipped. This could provide better visibility into the operation of this function, especially when diagnosing issues in production environments. |
||
if err != nil { | ||
log.ErrorLogWithError(ctx, "Failed to get vault ID from state key", err) | ||
continue | ||
|
@@ -84,7 +88,7 @@ func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) ( | |
err := k.clobKeeper.HandleMsgCancelOrder(ctx, clobtypes.NewMsgCancelOrderStateful( | ||
order.OrderId, | ||
uint32(ctx.BlockTime().Unix())+orderExpirationSeconds, | ||
)) | ||
), true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for refreshing CLOB orders is complex but correctly handles the cancellation and placement of orders. Consider adding more detailed logging for the steps involved, especially around the conditions that lead to early returns. Also applies to: 107-159 |
||
if err != nil { | ||
log.ErrorLogWithError(ctx, "Failed to cancel order", err, "order", order, "vaultId", vaultId) | ||
} | ||
|
@@ -97,11 +101,12 @@ func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) ( | |
|
||
// Place new CLOB orders. | ||
ordersToPlace, err := k.GetVaultClobOrders(ctx, vaultId) | ||
|
||
if err != nil { | ||
log.ErrorLogWithError(ctx, "Failed to get vault clob orders to place", err, "vaultId", vaultId) | ||
return err | ||
} | ||
for _, order := range ordersToPlace { | ||
for i, order := range ordersToPlace { | ||
err := k.PlaceVaultClobOrder(ctx, order) | ||
if err != nil { | ||
log.ErrorLogWithError(ctx, "Failed to place order", err, "order", order, "vaultId", vaultId) | ||
|
@@ -110,8 +115,46 @@ func (k Keeper) RefreshVaultClobOrders(ctx sdk.Context, vaultId types.VaultId) ( | |
metrics.VaultPlaceOrder, | ||
metrics.GetLabelForBoolValue(metrics.Success, err == nil), | ||
) | ||
} | ||
|
||
// Send indexer messages. | ||
// If the price of the old and new orders are different, send a cancel and a place message | ||
// Otherwise, send an order place message only. | ||
replacedOrder := ordersToCancel[i] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as discussed above, might be good to check whether |
||
if replacedOrder.Subticks != order.Subticks { | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewStatefulOrderRemovalEvent( | ||
replacedOrder.OrderId, | ||
indexershared.OrderRemovalReason_ORDER_REMOVAL_REASON_REPLACED, | ||
), | ||
), | ||
) | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewLongTermOrderPlacementEvent( | ||
*order, | ||
), | ||
), | ||
) | ||
} else { | ||
k.GetIndexerEventManager().AddTxnEvent( | ||
ctx, | ||
indexerevents.SubtypeStatefulOrder, | ||
indexerevents.StatefulOrderEventVersion, | ||
indexer_manager.GetBytes( | ||
indexerevents.NewLongTermOrderPlacementEvent( | ||
*order, | ||
), | ||
), | ||
) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
|
@@ -335,11 +378,6 @@ func (k Keeper) GetVaultClobOrders( | |
// GetVaultClobOrderClientId returns the client ID for a CLOB order where | ||
// - 1st bit is `side-1` (subtract 1 as buy_side = 1, sell_side = 2) | ||
// | ||
// - 2nd bit is `block height % 2` | ||
// - block height bit alternates between 0 and 1 to ensure that client IDs | ||
// are different in two consecutive blocks (otherwise, order placement would | ||
// fail because the same order IDs are already marked for cancellation) | ||
// | ||
// - next 8 bits are `layer` | ||
func (k Keeper) GetVaultClobOrderClientId( | ||
ctx sdk.Context, | ||
|
@@ -349,12 +387,9 @@ func (k Keeper) GetVaultClobOrderClientId( | |
sideBit := uint32(side - 1) | ||
sideBit <<= 31 | ||
|
||
blockHeightBit := uint32(ctx.BlockHeight() % 2) | ||
blockHeightBit <<= 30 | ||
|
||
layerBits := uint32(layer) << 22 | ||
layerBits := uint32(layer) << 23 | ||
|
||
return sideBit | blockHeightBit | layerBits | ||
return sideBit | layerBits | ||
} | ||
|
||
// PlaceVaultClobOrder places a vault CLOB order as an order internal to the protocol, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: remove