From 6fdc2d5a5bc32f561d743758a8144d1a11c43671 Mon Sep 17 00:00:00 2001 From: Will Liu Date: Wed, 1 Nov 2023 18:12:24 -0400 Subject: [PATCH] add/fix unit tests --- .../20220727134954_create_fills.ts | 2 + ...73444_update_fills_type_with_deleverage.ts | 23 ++ .../handlers/deleveraging-handler.test.ts | 329 ++++++++++++++++++ .../order-fills/liquidation-handler.test.ts | 4 + .../order-fills/order-handler.test.ts | 2 + .../ender/__tests__/helpers/constants.ts | 2 +- .../helpers/indexer-proto-helpers.ts | 39 ++- .../src/handlers/deleveraging-handler.ts | 12 +- .../abstract-order-fill-handler.ts | 3 +- .../ender/src/helpers/kafka-helper.ts | 4 + 10 files changed, 410 insertions(+), 10 deletions(-) create mode 100644 indexer/packages/postgres/src/db/migrations/migration_files/20231101173444_update_fills_type_with_deleverage.ts diff --git a/indexer/packages/postgres/src/db/migrations/migration_files/20220727134954_create_fills.ts b/indexer/packages/postgres/src/db/migrations/migration_files/20220727134954_create_fills.ts index d30b6348f8..0f4cb4d086 100644 --- a/indexer/packages/postgres/src/db/migrations/migration_files/20220727134954_create_fills.ts +++ b/indexer/packages/postgres/src/db/migrations/migration_files/20220727134954_create_fills.ts @@ -19,6 +19,8 @@ export async function up(knex: Knex): Promise { 'LIMIT', 'LIQUIDATED', 'LIQUIDATION', + 'DELEVERAGED', + 'OFFSETTING', ]).notNullable(); table.bigInteger('clobPairId').notNullable(); table.uuid('orderId').nullable(); diff --git a/indexer/packages/postgres/src/db/migrations/migration_files/20231101173444_update_fills_type_with_deleverage.ts b/indexer/packages/postgres/src/db/migrations/migration_files/20231101173444_update_fills_type_with_deleverage.ts new file mode 100644 index 0000000000..85a5075201 --- /dev/null +++ b/indexer/packages/postgres/src/db/migrations/migration_files/20231101173444_update_fills_type_with_deleverage.ts @@ -0,0 +1,23 @@ +import * as Knex from 'knex'; + +export async function up(knex: Knex): Promise { + return knex.raw(` + ALTER TABLE ONLY fills + DROP CONSTRAINT IF EXISTS fills_type_check; + + ALTER TABLE ONLY fills + ADD CONSTRAINT fills_type_check + CHECK (type = ANY (ARRAY['MARKET'::text, 'LIMIT'::text, 'LIQUIDATED'::text, 'LIQUIDATION'::text, 'DELEVERAGED'::text, 'OFFSETTING'::text])); + `); +} + +export async function down(knex: Knex): Promise { + return knex.raw(` + ALTER TABLE ONLY fills + DROP CONSTRAINT IF EXISTS fills_type_check; + + ALTER TABLE ONLY fills + ADD CONSTRAINT fills_type_check + CHECK (type = ANY (ARRAY['MARKET'::text, 'LIMIT'::text, 'LIQUIDATED'::text, 'LIQUIDATION'::text])); + `); +} diff --git a/indexer/services/ender/__tests__/handlers/deleveraging-handler.test.ts b/indexer/services/ender/__tests__/handlers/deleveraging-handler.test.ts index e69de29bb2..997f13b311 100644 --- a/indexer/services/ender/__tests__/handlers/deleveraging-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/deleveraging-handler.test.ts @@ -0,0 +1,329 @@ +import { logger, stats, STATS_FUNCTION_NAME } from '@dydxprotocol-indexer/base'; +import { redis } from '@dydxprotocol-indexer/redis'; +import { + assetRefresher, + dbHelpers, + FillTable, + FillType, + Liquidity, + OrderSide, + perpetualMarketRefresher, + PerpetualPositionCreateObject, + PerpetualPositionStatus, + PerpetualPositionTable, + PositionSide, + SubaccountCreateObject, + SubaccountTable, + TendermintEventTable, + testConstants, + testMocks, +} from '@dydxprotocol-indexer/postgres'; +import { updateBlockCache } from '../../src/caches/block-cache'; +import { defaultDeleveragingEvent, defaultPreviousHeight } from '../helpers/constants'; +import { clearCandlesMap } from '../../src/caches/candle-cache'; +import { createPostgresFunctions } from '../../src/helpers/postgres/postgres-functions'; +import { redisClient } from '../../src/helpers/redis/redis-controller'; +import { + DeleveragingEventV1, + IndexerSubaccountId, + IndexerTendermintBlock, + IndexerTendermintEvent, + Timestamp, +} from '@dydxprotocol-indexer/v4-protos'; +import { + createIndexerTendermintBlock, + createIndexerTendermintEvent, + createKafkaMessageFromDeleveragingEvent, + expectDefaultTradeKafkaMessageFromTakerFillId, + expectFillInDatabase, + expectFillSubaccountKafkaMessageFromLiquidationEvent, +} from '../helpers/indexer-proto-helpers'; +import { DydxIndexerSubtypes } from '../../src/lib/types'; +import { + DELEVERAGING_EVENT_TYPE, + MILLIS_IN_NANOS, + SECONDS_IN_MILLIS, + SUBACCOUNT_ORDER_FILL_EVENT_TYPE, +} from '../../src/constants'; +import { DateTime } from 'luxon'; +import Long from 'long'; +import { DeleveragingHandler } from '../../src/handlers/deleveraging-handler'; +import { KafkaMessage } from 'kafkajs'; +import { onMessage } from '../../src/lib/on-message'; +import { producer } from '@dydxprotocol-indexer/kafka'; +import { createdDateTime, createdHeight } from '@dydxprotocol-indexer/postgres/build/__tests__/helpers/constants'; + +describe('DeleveragingHandler', () => { + const offsettingSubaccount: SubaccountCreateObject = { + address: defaultDeleveragingEvent.offsetting!.owner, + subaccountNumber: defaultDeleveragingEvent.offsetting!.number, + updatedAt: createdDateTime.toISO(), + updatedAtHeight: createdHeight, + }; + + const deleveragedSubaccount: SubaccountCreateObject = { + address: defaultDeleveragingEvent.liquidated!.owner, + subaccountNumber: defaultDeleveragingEvent.liquidated!.number, + updatedAt: createdDateTime.toISO(), + updatedAtHeight: createdHeight, + }; + + beforeAll(async () => { + await dbHelpers.migrate(); + await createPostgresFunctions(); + jest.spyOn(stats, 'increment'); + jest.spyOn(stats, 'timing'); + jest.spyOn(stats, 'gauge'); + }); + + beforeEach(async () => { + await testMocks.seedData(); + await perpetualMarketRefresher.updatePerpetualMarkets(); + await assetRefresher.updateAssets(); + updateBlockCache(defaultPreviousHeight); + }); + + afterEach(async () => { + await dbHelpers.clearData(); + jest.clearAllMocks(); + clearCandlesMap(); + await redis.deleteAllAsync(redisClient); + }); + + afterAll(async () => { + await dbHelpers.teardown(); + jest.resetAllMocks(); + }); + + const defaultHeight: string = '3'; + const defaultDateTime: DateTime = DateTime.utc(2022, 6, 1, 12, 1, 1, 2); + const defaultTime: Timestamp = { + seconds: Long.fromValue(Math.floor(defaultDateTime.toSeconds()), true), + nanos: (defaultDateTime.toMillis() % SECONDS_IN_MILLIS) * MILLIS_IN_NANOS, + }; + const defaultTxHash: string = '0x32343534306431622d306461302d343831322d613730372d3965613162336162'; + + const offsettingPerpetualPosition: PerpetualPositionCreateObject = { + subaccountId: SubaccountTable.subaccountIdToUuid(defaultDeleveragingEvent.offsetting!), + perpetualId: testConstants.defaultPerpetualMarket.id, + side: PositionSide.LONG, + status: PerpetualPositionStatus.OPEN, + size: '10', + maxSize: '25', + sumOpen: '10', + entryPrice: '15000', + createdAt: DateTime.utc().toISO(), + createdAtHeight: '1', + openEventId: testConstants.defaultTendermintEventId, + lastEventId: testConstants.defaultTendermintEventId, + settledFunding: '200000', + }; + + it('getParallelizationIds', () => { + const offsettingSubaccountId: IndexerSubaccountId = defaultDeleveragingEvent.offsetting!; + const deleveragedSubaccountId: IndexerSubaccountId = defaultDeleveragingEvent.liquidated!; + const transactionIndex: number = 0; + const eventIndex: number = 0; + + const indexerTendermintEvent: IndexerTendermintEvent = createIndexerTendermintEvent( + DydxIndexerSubtypes.DELEVERAGING, + DeleveragingEventV1.encode(defaultDeleveragingEvent).finish(), + transactionIndex, + eventIndex, + ); + const block: IndexerTendermintBlock = createIndexerTendermintBlock( + 0, + defaultTime, + [indexerTendermintEvent], + [defaultTxHash], + ); + + const handler: DeleveragingHandler = new DeleveragingHandler( + block, + indexerTendermintEvent, + 0, + defaultDeleveragingEvent, + ); + + const offsettingSubaccountUuid: string = SubaccountTable.subaccountIdToUuid( + offsettingSubaccountId, + ); + const deleveragedSubaccountUuid: string = SubaccountTable.subaccountIdToUuid( + deleveragedSubaccountId, + ); + + expect(handler.getParallelizationIds()).toEqual([ + `${handler.eventType}_${offsettingSubaccountUuid}_${defaultDeleveragingEvent.clobPairId}`, + `${handler.eventType}_${deleveragedSubaccountUuid}_${defaultDeleveragingEvent.clobPairId}`, + // To ensure that SubaccountUpdateEvents and OrderFillEvents for the same subaccount are not + // processed in parallel + `${SUBACCOUNT_ORDER_FILL_EVENT_TYPE}_${offsettingSubaccountUuid}`, + `${SUBACCOUNT_ORDER_FILL_EVENT_TYPE}_${deleveragedSubaccountUuid}`, + // To ensure that StatefulOrderEvents and OrderFillEvents for the same order are not + // processed in parallel + `${DELEVERAGING_EVENT_TYPE}_${offsettingSubaccountUuid}`, + `${DELEVERAGING_EVENT_TYPE}_${deleveragedSubaccountUuid}`, + ]); + }); + + it('DeleveragingEvent fails validation', async () => { + const deleveragingEvent: DeleveragingEventV1 = DeleveragingEventV1 + .fromPartial({ // no liquidated subaccount + ...defaultDeleveragingEvent, + liquidated: undefined, + }); + const transactionIndex: number = 0; + const eventIndex: number = 0; + const kafkaMessage: KafkaMessage = createKafkaMessageFromDeleveragingEvent({ + deleveragingEvent, + transactionIndex, + eventIndex, + height: parseInt(defaultHeight, 10), + time: defaultTime, + txHash: defaultTxHash, + }); + const loggerCrit = jest.spyOn(logger, 'crit'); + await expect(onMessage(kafkaMessage)).rejects.toThrowError(); + + expect(loggerCrit).toHaveBeenCalledWith(expect.objectContaining({ + at: 'onMessage#onMessage', + message: 'Error: Unable to parse message, this must be due to a bug in V4 node', + })); + }); + + it('creates fills and updates perpetual positions', async () => { + const transactionIndex: number = 0; + const eventIndex: number = 0; + const kafkaMessage: KafkaMessage = createKafkaMessageFromDeleveragingEvent({ + deleveragingEvent: defaultDeleveragingEvent, + transactionIndex, + eventIndex, + height: parseInt(defaultHeight, 10), + time: defaultTime, + txHash: defaultTxHash, + }); + + // create initial Subaccounts + await Promise.all([ + SubaccountTable.create(offsettingSubaccount), + SubaccountTable.create(deleveragedSubaccount), + ]); + // create initial PerpetualPositions + await Promise.all([ + PerpetualPositionTable.create(offsettingPerpetualPosition), + PerpetualPositionTable.create({ + ...offsettingPerpetualPosition, + subaccountId: SubaccountTable.subaccountIdToUuid(defaultDeleveragingEvent.liquidated!), + }), + ]); + + const producerSendMock: jest.SpyInstance = jest.spyOn(producer, 'send'); + await onMessage(kafkaMessage); + + const eventId: Buffer = TendermintEventTable.createEventId( + defaultHeight, + transactionIndex, + eventIndex, + ); + + // This size should be in fixed-point notation rather than exponential notation. + const quoteAmount: string = '0.1'; // quote amount is price * fillAmount = 1e5 * 1e-6 = 1e-1 + const totalFilled: string = '0.000001'; // fillAmount in human = 1e4 * 1e-10 = 1e-6 + const price: string = '100000'; // 10^9*10^-8*10^-6/10^-10=10^5 + + await expectFillInDatabase({ + subaccountId: SubaccountTable.subaccountIdToUuid(defaultDeleveragingEvent.offsetting!), + clientId: '0', + liquidity: Liquidity.MAKER, + size: totalFilled, + price, + quoteAmount, + eventId, + transactionHash: defaultTxHash, + createdAt: defaultDateTime.toISO(), + createdAtHeight: defaultHeight, + type: FillType.OFFSETTING, + clobPairId: defaultDeleveragingEvent.clobPairId.toString(), + side: OrderSide.SELL, + orderFlags: '0', + clientMetadata: null, + hasOrderId: false, + fee: '0', + }); + await expectFillInDatabase({ + subaccountId: SubaccountTable.subaccountIdToUuid(defaultDeleveragingEvent.liquidated!), + clientId: '0', + liquidity: Liquidity.TAKER, + size: totalFilled, + price, + quoteAmount, + eventId, + transactionHash: defaultTxHash, + createdAt: defaultDateTime.toISO(), + createdAtHeight: defaultHeight, + type: FillType.DELEVERAGED, + clobPairId: defaultDeleveragingEvent.clobPairId.toString(), + side: OrderSide.BUY, + orderFlags: '0', + clientMetadata: null, + hasOrderId: false, + fee: '0', + }); + + await Promise.all([ + expectFillsAndPositionsSubaccountKafkaMessages( + producerSendMock, + eventId, + true, + ), + expectFillsAndPositionsSubaccountKafkaMessages( + producerSendMock, + eventId, + false, + ), + expectDefaultTradeKafkaMessageFromTakerFillId( + producerSendMock, + eventId, + ), + ]); + expectTimingStats(); + }); + + async function expectFillsAndPositionsSubaccountKafkaMessages( + producerSendMock: jest.SpyInstance, + eventId: Buffer, + deleveraged: boolean, + ) { + const subaccountId: IndexerSubaccountId = deleveraged + ? defaultDeleveragingEvent.liquidated! : defaultDeleveragingEvent.offsetting!; + const liquidity: Liquidity = deleveraged ? Liquidity.TAKER : Liquidity.MAKER; + const positionId: string = ( + await PerpetualPositionTable.findOpenPositionForSubaccountPerpetual( + SubaccountTable.subaccountIdToUuid(subaccountId), + testConstants.defaultPerpetualMarket.id, + ) + )!.id; + + await Promise.all([ + expectFillSubaccountKafkaMessageFromLiquidationEvent( + producerSendMock, + subaccountId, + FillTable.uuid(eventId, liquidity), + positionId, + ), + ]); + } +}); + +function expectTimingStats() { + expectTimingStat('create_fills'); + expectTimingStat('update_perpetual_positions'); +} + +function expectTimingStat(fnName: string) { + expect(stats.timing).toHaveBeenCalledWith( + `ender.${STATS_FUNCTION_NAME}.timing`, + expect.any(Number), + { className: 'DeleveragingHandler', eventType: 'DeleveragingEvent', fnName }, + ); +} diff --git a/indexer/services/ender/__tests__/handlers/order-fills/liquidation-handler.test.ts b/indexer/services/ender/__tests__/handlers/order-fills/liquidation-handler.test.ts index a88f11efdc..1845c4bc46 100644 --- a/indexer/services/ender/__tests__/handlers/order-fills/liquidation-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/order-fills/liquidation-handler.test.ts @@ -40,6 +40,7 @@ import { import { KafkaMessage } from 'kafkajs'; import { DateTime } from 'luxon'; import { + DELEVERAGING_EVENT_TYPE, MILLIS_IN_NANOS, SECONDS_IN_MILLIS, STATEFUL_ORDER_ORDER_FILL_EVENT_TYPE, @@ -197,6 +198,9 @@ describe('LiquidationHandler', () => { if (orderId !== undefined) { parallelizationIds.push(`${STATEFUL_ORDER_ORDER_FILL_EVENT_TYPE}_${orderId}`); } + parallelizationIds.push( + `${DELEVERAGING_EVENT_TYPE}_${SubaccountTable.subaccountIdToUuid(subaccountId)}`, + ); expect(handler.getParallelizationIds()).toEqual(parallelizationIds); }); }); diff --git a/indexer/services/ender/__tests__/handlers/order-fills/order-handler.test.ts b/indexer/services/ender/__tests__/handlers/order-fills/order-handler.test.ts index 8fb3468971..71d72cd845 100644 --- a/indexer/services/ender/__tests__/handlers/order-fills/order-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/order-fills/order-handler.test.ts @@ -42,6 +42,7 @@ import { import { KafkaMessage } from 'kafkajs'; import { DateTime } from 'luxon'; import { + DELEVERAGING_EVENT_TYPE, MILLIS_IN_NANOS, SECONDS_IN_MILLIS, STATEFUL_ORDER_ORDER_FILL_EVENT_TYPE, @@ -196,6 +197,7 @@ describe('OrderHandler', () => { `${handler.eventType}_${SubaccountTable.subaccountIdToUuid(subaccountId)}_${defaultOrderEvent.makerOrder!.orderId!.clobPairId}`, `${SUBACCOUNT_ORDER_FILL_EVENT_TYPE}_${SubaccountTable.subaccountIdToUuid(subaccountId)}`, `${STATEFUL_ORDER_ORDER_FILL_EVENT_TYPE}_${orderUuid}`, + `${DELEVERAGING_EVENT_TYPE}_${SubaccountTable.subaccountIdToUuid(subaccountId)}`, ]); }); }); diff --git a/indexer/services/ender/__tests__/helpers/constants.ts b/indexer/services/ender/__tests__/helpers/constants.ts index 8d73fda0f1..0b86c0c59b 100644 --- a/indexer/services/ender/__tests__/helpers/constants.ts +++ b/indexer/services/ender/__tests__/helpers/constants.ts @@ -283,7 +283,7 @@ export const defaultTransferEvent: TransferEventV1 = { export const defaultDeleveragingEvent: DeleveragingEventV1 = { liquidated: defaultSenderSubaccountId, offsetting: defaultRecipientSubaccountId, - clobPairId: 0, + clobPairId: 1, fillAmount: Long.fromValue(10_000, true), subticks: Long.fromValue(1_000_000_000, true), isBuy: true, diff --git a/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts b/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts index 576ee0e89e..11153077a3 100644 --- a/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts +++ b/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts @@ -48,6 +48,7 @@ import { OffChainUpdateV1, IndexerOrderId, PerpetualMarketCreateEventV1, + DeleveragingEventV1, } from '@dydxprotocol-indexer/v4-protos'; import { Message, ProducerRecord } from 'kafkajs'; import _ from 'lodash'; @@ -56,7 +57,7 @@ import { convertPerpetualPosition, generateFillSubaccountMessage, generatePerpetualMarketMessage, - generatePerpetualPositionsContents, + generatePerpetualPositionsContents, isDeleveraging, isLiquidation, } from '../../src/helpers/kafka-helper'; import { protoTimestampToDate } from '../../src/lib/helper'; @@ -471,6 +472,41 @@ export function createKafkaMessageFromOrderFillEvent({ return createKafkaMessage(Buffer.from(binaryBlock)); } +export function createKafkaMessageFromDeleveragingEvent({ + deleveragingEvent, + transactionIndex, + eventIndex, + height, + time, + txHash, +}: { + deleveragingEvent: DeleveragingEventV1, + transactionIndex: number, + eventIndex: number, + height: number, + time: Timestamp, + txHash: string, +}) { + const events: IndexerTendermintEvent[] = [ + createIndexerTendermintEvent( + DydxIndexerSubtypes.DELEVERAGING, + Uint8Array.from(DeleveragingEventV1.encode(deleveragingEvent).finish()), + transactionIndex, + eventIndex, + ), + ]; + + const block: IndexerTendermintBlock = createIndexerTendermintBlock( + height, + time, + events, + [txHash], + ); + + const binaryBlock: Uint8Array = Uint8Array.from(IndexerTendermintBlock.encode(block).finish()); + return createKafkaMessage(Buffer.from(binaryBlock)); +} + export function liquidationOrderToOrderSide( liquidationOrder: LiquidationOrderV1, ): OrderSide { @@ -771,6 +807,7 @@ export async function expectDefaultTradeKafkaMessageFromTakerFillId( side: takerFill!.side.toString(), createdAt: takerFill!.createdAt, liquidation: isLiquidation(takerFill!), + deleveraging: isDeleveraging(takerFill!), }, ], }; diff --git a/indexer/services/ender/src/handlers/deleveraging-handler.ts b/indexer/services/ender/src/handlers/deleveraging-handler.ts index 2097b514dd..9a0eb3ee55 100644 --- a/indexer/services/ender/src/handlers/deleveraging-handler.ts +++ b/indexer/services/ender/src/handlers/deleveraging-handler.ts @@ -21,11 +21,7 @@ import { import { DeleveragingEventV1, IndexerSubaccountId } from '@dydxprotocol-indexer/v4-protos'; import Big from 'big.js'; -import { - DELEVERAGING_EVENT_TYPE, - STATEFUL_ORDER_ORDER_FILL_EVENT_TYPE, - SUBACCOUNT_ORDER_FILL_EVENT_TYPE, -} from '../constants'; +import { DELEVERAGING_EVENT_TYPE, SUBACCOUNT_ORDER_FILL_EVENT_TYPE } from '../constants'; import { generateFillSubaccountMessage, generatePerpetualPositionsContents } from '../helpers/kafka-helper'; import { getWeightedAverage, @@ -95,6 +91,7 @@ export class DeleveragingHandler extends Handler { }; const offsettingSubaccountFill: FillCreateObject = { ...liquidatedSubaccountFill, + subaccountId: SubaccountTable.uuid(event.offsetting!.owner, event.offsetting!.number), side: event.isBuy ? OrderSide.SELL : OrderSide.BUY, liquidity: Liquidity.MAKER, type: FillType.OFFSETTING, @@ -249,6 +246,7 @@ export class DeleveragingHandler extends Handler { price: fill.price, side: fill.side.toString(), createdAt: fill.createdAt, + liquidation: false, deleveraging: true, }, ], @@ -278,7 +276,7 @@ export class DeleveragingHandler extends Handler { Promise.all( this.createFillsFromEvent(perpetualMarket, this.event), ), - this.generateTimingStatsOptions('create_fill'), + this.generateTimingStatsOptions('create_fills'), ); const positions: PerpetualPositionFromDatabase[] = await @@ -287,7 +285,7 @@ export class DeleveragingHandler extends Handler { this.updatePerpetualPosition(perpetualMarket, this.event, true), this.updatePerpetualPosition(perpetualMarket, this.event, false), ]), - this.generateTimingStatsOptions('update_perpetual_position'), + this.generateTimingStatsOptions('update_perpetual_positions'), ); const kafkaEvents: ConsolidatedKafkaEvent[] = [ this.generateConsolidatedKafkaEvent( diff --git a/indexer/services/ender/src/handlers/order-fills/abstract-order-fill-handler.ts b/indexer/services/ender/src/handlers/order-fills/abstract-order-fill-handler.ts index 670911c9a8..369376d0e1 100644 --- a/indexer/services/ender/src/handlers/order-fills/abstract-order-fill-handler.ts +++ b/indexer/services/ender/src/handlers/order-fills/abstract-order-fill-handler.ts @@ -43,7 +43,7 @@ import { DateTime } from 'luxon'; import { generateFillSubaccountMessage, generateOrderSubaccountMessage, - generatePerpetualPositionsContents, + generatePerpetualPositionsContents, isDeleveraging, isLiquidation, } from '../../helpers/kafka-helper'; import { @@ -425,6 +425,7 @@ export abstract class AbstractOrderFillHandler extends Handler { side: fill.side.toString(), createdAt: fill.createdAt, liquidation: isLiquidation(fill), + deleveraging: isDeleveraging(fill), }, ], }; diff --git a/indexer/services/ender/src/helpers/kafka-helper.ts b/indexer/services/ender/src/helpers/kafka-helper.ts index cfda119ef4..8e1f1fa579 100644 --- a/indexer/services/ender/src/helpers/kafka-helper.ts +++ b/indexer/services/ender/src/helpers/kafka-helper.ts @@ -278,6 +278,10 @@ export function isLiquidation(fill: FillFromDatabase): boolean { return fill.type === FillType.LIQUIDATION || fill.type === FillType.LIQUIDATED; } +export function isDeleveraging(fill: FillFromDatabase): boolean { + return fill.type === FillType.DELEVERAGED || fill.type === FillType.OFFSETTING; +} + export function generateFillSubaccountMessage( fill: FillFromDatabase, ticker: string,