diff --git a/indexer/services/socks/__tests__/constants.ts b/indexer/services/socks/__tests__/constants.ts index 8d78444bc3..3c33c31ebf 100644 --- a/indexer/services/socks/__tests__/constants.ts +++ b/indexer/services/socks/__tests__/constants.ts @@ -6,6 +6,8 @@ import { TRADES_WEBSOCKET_MESSAGE_VERSION, } from '@dydxprotocol-indexer/kafka'; import { + TransferSubaccountMessageContents, + TransferType, MAX_PARENT_SUBACCOUNTS, } from '@dydxprotocol-indexer/postgres'; import { @@ -102,3 +104,20 @@ export const childSubaccountMessage: SubaccountMessage = { contents: defaultContentsString, version: SUBACCOUNTS_WEBSOCKET_MESSAGE_VERSION, }; + +export const defaultTransferContents: TransferSubaccountMessageContents = { + sender: { + address: defaultOwner, + subaccountNumber: defaultAccNumber, + }, + recipient: { + address: defaultOwner, + subaccountNumber: defaultChildAccNumber, + }, + symbol: 'USDC', + size: '1', + type: TransferType.TRANSFER_IN, + transactionHash: '0x1', + createdAt: '2023-10-05T14:48:00.000Z', + createdAtHeight: '10', +}; diff --git a/indexer/services/socks/__tests__/helpers/from-kafka-helpers.test.ts b/indexer/services/socks/__tests__/helpers/from-kafka-helpers.test.ts index 7415f12f18..d4ba610d6c 100644 --- a/indexer/services/socks/__tests__/helpers/from-kafka-helpers.test.ts +++ b/indexer/services/socks/__tests__/helpers/from-kafka-helpers.test.ts @@ -20,6 +20,7 @@ import { childSubaccountMessage, tradesMessage, defaultChildAccNumber, + defaultTransferContents, } from '../constants'; import { KafkaMessage } from 'kafkajs'; import { createKafkaMessage } from './kafka'; @@ -36,6 +37,8 @@ import { testMocks, perpetualMarketRefresher, CandleResolution, + TransferSubaccountMessageContents, + SubaccountMessageContents, TransferType, } from '@dydxprotocol-indexer/postgres'; describe('from-kafka-helpers', () => { @@ -153,6 +156,113 @@ describe('from-kafka-helpers', () => { expect(messageToForward.subaccountNumber).toEqual(defaultChildAccNumber); }); + it('filters out transfers between child subaccounts for parent subaccount channel', () => { + const transferContents: SubaccountMessageContents = { + transfers: { + ...defaultTransferContents, + sender: { + address: defaultOwner, + subaccountNumber: defaultAccNumber, + }, + recipient: { + address: defaultOwner, + subaccountNumber: defaultChildAccNumber, + }, + type: TransferType.TRANSFER_IN, + }, + }; + const message: KafkaMessage = createKafkaMessage( + Buffer.from(Uint8Array.from(SubaccountMessage.encode( + { + ...childSubaccountMessage, + contents: JSON.stringify(transferContents), + }, + ).finish())), + ); + const messageToForward: MessageToForward = getMessageToForward( + Channel.V4_PARENT_ACCOUNTS, + message, + ); + + expect(messageToForward.channel).toEqual(Channel.V4_PARENT_ACCOUNTS); + expect(messageToForward.id).toEqual(`${defaultOwner}/${defaultAccNumber}`); + expect(messageToForward.contents).toEqual({}); + expect(messageToForward.subaccountNumber).toBeDefined(); + expect(messageToForward.subaccountNumber).toEqual(defaultChildAccNumber); + }); + + it.each([ + [ + 'transfer between other parent/child subaccount', + { + ...defaultTransferContents, + sender: { + address: defaultOwner, + subaccountNumber: defaultAccNumber + 1, + }, + recipient: { + address: defaultOwner, + subaccountNumber: defaultChildAccNumber, + }, + }, + ], + [ + 'deposit', + { + ...defaultTransferContents, + sender: { + address: defaultOwner, + subaccountNumber: undefined, + }, + recipient: { + address: defaultOwner, + subaccountNumber: defaultChildAccNumber, + }, + type: TransferType.DEPOSIT, + }, + ], + [ + 'withdraw', + { + ...defaultTransferContents, + sender: { + address: defaultOwner, + subaccountNumber: defaultChildAccNumber, + }, + recipient: { + address: defaultOwner, + subaccountNumber: undefined, + }, + type: TransferType.WITHDRAWAL, + }, + ], + ])('does not filter out transfer message for (%s)', ( + _name: string, + transfer: TransferSubaccountMessageContents, + ) => { + const transferContents: SubaccountMessageContents = { + transfers: transfer, + }; + const message: KafkaMessage = createKafkaMessage( + Buffer.from(Uint8Array.from(SubaccountMessage.encode( + { + ...childSubaccountMessage, + contents: JSON.stringify(transferContents), + }, + ).finish())), + ); + const messageToForward: MessageToForward = getMessageToForward( + Channel.V4_PARENT_ACCOUNTS, + message, + ); + + expect(messageToForward.channel).toEqual(Channel.V4_PARENT_ACCOUNTS); + expect(messageToForward.id).toEqual(`${defaultOwner}/${defaultAccNumber}`); + expect(messageToForward.contents).toEqual(transferContents); + expect(messageToForward.subaccountNumber).toBeDefined(); + expect(messageToForward.subaccountNumber).toEqual(defaultChildAccNumber); + }); + it('throws InvalidForwardMessageError for empty message', () => { const message: KafkaMessage = createKafkaMessage(null); diff --git a/indexer/services/socks/src/helpers/from-kafka-helpers.ts b/indexer/services/socks/src/helpers/from-kafka-helpers.ts index 6577712435..38397fbe1b 100644 --- a/indexer/services/socks/src/helpers/from-kafka-helpers.ts +++ b/indexer/services/socks/src/helpers/from-kafka-helpers.ts @@ -3,13 +3,16 @@ import { perpetualMarketRefresher, PROTO_TO_CANDLE_RESOLUTION, parentSubaccountHelpers, + SubaccountMessageContents, } from '@dydxprotocol-indexer/postgres'; +import { getParentSubaccountNum } from '@dydxprotocol-indexer/postgres/build/src/lib/parent-subaccount-helpers'; import { CandleMessage, MarketMessage, OrderbookMessage, TradeMessage, - SubaccountMessage, CandleMessage_Resolution, + SubaccountMessage, + CandleMessage_Resolution, } from '@dydxprotocol-indexer/v4-protos'; import { KafkaMessage } from 'kafkajs'; @@ -91,7 +94,7 @@ export function getMessageToForward( channel, id: getParentSubaccountMessageId(subaccountMessage), subaccountNumber: subaccountMessage.subaccountId!.number, - contents: JSON.parse(subaccountMessage.contents), + contents: getParentSubaccountContents(subaccountMessage), version: subaccountMessage.version, }; } @@ -133,3 +136,35 @@ function getCandleMessageId(candleMessage: CandleMessage): string { } return `${ticker}/${PROTO_TO_CANDLE_RESOLUTION[candleMessage.resolution]}`; } + +function getParentSubaccountContents(msg: SubaccountMessage): SubaccountMessageContents { + // Filter out transfers between child subaccounts of the same parent subaccount. + const contents: SubaccountMessageContents = JSON.parse(msg.contents) as SubaccountMessageContents; + if (contents.transfers === undefined) { + return contents; + } + + const senderAddress: string = contents.transfers.sender.address; + const recipientAddress: string = contents.transfers.recipient.address; + + if (senderAddress !== recipientAddress) { + return contents; + } + + const senderSubaccount: number | undefined = contents.transfers.sender.subaccountNumber; + const recipientSubaccount: number | undefined = contents.transfers.recipient.subaccountNumber; + + if (senderSubaccount === undefined || recipientSubaccount === undefined) { + return contents; + } + + const senderParentSubaccountNumber: number = getParentSubaccountNum(senderSubaccount); + const recipientParentSubaccountNumber: number = getParentSubaccountNum(recipientSubaccount); + + if (senderParentSubaccountNumber !== recipientParentSubaccountNumber) { + return contents; + } + + delete contents.transfers; + return contents; +}