diff --git a/indexer/services/vulcan/__tests__/handlers/order-place-handler.test.ts b/indexer/services/vulcan/__tests__/handlers/order-place-handler.test.ts index 2010a3adb7..8c187cac75 100644 --- a/indexer/services/vulcan/__tests__/handlers/order-place-handler.test.ts +++ b/indexer/services/vulcan/__tests__/handlers/order-place-handler.test.ts @@ -748,21 +748,18 @@ describe('order-place-handler', () => { 'good-til-block-time', redisTestConstants.defaultOrderGoodTilBlockTime, redisTestConstants.defaultRedisOrderGoodTilBlockTime, - redisTestConstants.defaultOrderUuidGoodTilBlockTime, dbOrderGoodTilBlockTime, ], [ 'conditional', redisTestConstants.defaultConditionalOrder, redisTestConstants.defaultRedisOrderConditional, - redisTestConstants.defaultOrderUuidConditional, dbConditionalOrder, ], ])('handles order place with OPEN placement status, exists initially (with %s)', async ( _name: string, orderToPlace: IndexerOrder, expectedRedisOrder: RedisOrder, - expectedOrderUuid: string, placedOrder: OrderFromDatabase, ) => { synchronizeWrapBackgroundTask(wrapBackgroundTask); @@ -812,6 +809,39 @@ describe('order-place-handler', () => { expectStats(); }); + it('handles unplaced and unreplaced order place with BEST_EFFORT_OPENED placement status', async () => { + synchronizeWrapBackgroundTask(wrapBackgroundTask); + const producerSendSpy: jest.SpyInstance = jest.spyOn(producer, 'send').mockReturnThis(); + // Handle the order place event for the initial order with BEST_EFFORT_OPENED + await handleInitialOrderPlace(redisTestConstants.orderPlace); + expectWebsocketMessagesSent( + producerSendSpy, + redisTestConstants.defaultRedisOrder, + dbDefaultOrder, + testConstants.defaultPerpetualMarket, + APIOrderStatusEnum.BEST_EFFORT_OPENED, + true, + ); + expectStats(); + // clear mocks + jest.clearAllMocks(); + + // Handle the order place with OPEN placement status + await handleInitialOrderPlace(redisTestConstants.orderPlace); + expectWebsocketMessagesSent( + producerSendSpy, + redisTestConstants.defaultRedisOrder, + dbDefaultOrder, + testConstants.defaultPerpetualMarket, + APIOrderStatusEnum.BEST_EFFORT_OPENED, + // Subaccount messages should be sent for stateful order with OPEN status + false, + ); + + expect(logger.error).not.toHaveBeenCalled(); + expectStats(); + }); + it.each([ [ 'missing order', diff --git a/indexer/services/vulcan/src/handlers/order-place-handler.ts b/indexer/services/vulcan/src/handlers/order-place-handler.ts index 6f9ba098a2..7514d8bac9 100644 --- a/indexer/services/vulcan/src/handlers/order-place-handler.ts +++ b/indexer/services/vulcan/src/handlers/order-place-handler.ts @@ -64,9 +64,10 @@ export class OrderPlaceHandler extends Handler { update, txHash: this.txHash, }); + const orderPlace: OrderPlaceV1 = update.orderPlace!; this.validateOrderPlace(update.orderPlace!); - const order: IndexerOrder = update.orderPlace!.order!; - const placementStatus: OrderPlaceV1_OrderPlacementStatus = update.orderPlace!.placementStatus; + const order: IndexerOrder = orderPlace.order!; + const placementStatus: OrderPlaceV1_OrderPlacementStatus = orderPlace.placementStatus; const perpetualMarket: PerpetualMarketFromDatabase | undefined = perpetualMarketRefresher .getPerpetualMarketFromClobPairId(order.orderId!.clobPairId.toString()); @@ -125,7 +126,7 @@ export class OrderPlaceHandler extends Handler { // TODO(CLOB-597): Remove this logic and log erorrs once best-effort-open is not sent for // stateful orders in the protocol - if (this.shouldSendSubaccountMessage(update.orderPlace!)) { + if (this.shouldSendSubaccountMessage(orderPlace, placeOrderResult, placementStatus)) { // TODO(IND-171): Determine whether we should always be sending a message, even when the cache // isn't updated. // For stateful and conditional orders, look the order up in the db for the createdAtHeight @@ -325,7 +326,11 @@ export class OrderPlaceHandler extends Handler { * @returns TODO(CLOB-597): Remove once best-effort-opened messages are not sent for stateful * orders. */ - protected shouldSendSubaccountMessage(orderPlace: OrderPlaceV1): boolean { + protected shouldSendSubaccountMessage( + orderPlace: OrderPlaceV1, + placeOrderResult: PlaceOrderResult, + placementStatus: OrderPlaceV1_OrderPlacementStatus, + ): boolean { const orderFlags: number = orderPlace.order!.orderId!.orderFlags; const status: OrderPlaceV1_OrderPlacementStatus = orderPlace.placementStatus; // Best-effort-opened status should only be sent for short-term orders @@ -335,6 +340,16 @@ export class OrderPlaceHandler extends Handler { ) { return false; } + + // In the case where a stateful orderPlace is opened with a more recent expiry than an + // existing order on the indexer, then the order will not have been placed or replaced and + // no subaccount message should be sent. + if (placeOrderResult.placed === false && + placeOrderResult.replaced === false && + placementStatus === + OrderPlaceV1_OrderPlacementStatus.ORDER_PLACEMENT_STATUS_BEST_EFFORT_OPENED) { + return false; + } return true; }