From 0502f73833dab40b85c97dd23e62df858cd56125 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:39:19 -0400 Subject: [PATCH] Include getting main subaccount equity / pnl for megavault PnL query. (backport #2376) (#2378) Co-authored-by: vincentwschau <99756290+vincentwschau@users.noreply.github.com> --- indexer/packages/postgres/src/constants.ts | 5 ++ .../api/v4/vault-controller.test.ts | 66 ++++++++++++++++--- .../controllers/api/v4/vault-controller.ts | 27 ++++++-- 3 files changed, 84 insertions(+), 14 deletions(-) diff --git a/indexer/packages/postgres/src/constants.ts b/indexer/packages/postgres/src/constants.ts index ac9a38e6cb..aa3dbc9bda 100644 --- a/indexer/packages/postgres/src/constants.ts +++ b/indexer/packages/postgres/src/constants.ts @@ -129,3 +129,8 @@ export const DEFAULT_POSTGRES_OPTIONS : Options = config.USE_READ_REPLICA export const MAX_PARENT_SUBACCOUNTS: number = 128; export const CHILD_SUBACCOUNT_MULTIPLIER: number = 1000; + +// From https://github.com/dydxprotocol/v4-chain/blob/protocol/v7.0.0-dev0/protocol/app/module_accounts_test.go#L41 +export const MEGAVAULT_MODULE_ADDRESS: string = 'dydx18tkxrnrkqc2t0lr3zxr5g6a4hdvqksylxqje4r'; +// Generated from the module address + subaccount number 0. +export const MEGAVAULT_SUBACCOUNT_ID: string = 'c7169f81-0c80-54c5-a41f-9cbb6a538fdf'; diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/vault-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/vault-controller.test.ts index 488435f8e7..e53a9b5b76 100644 --- a/indexer/services/comlink/__tests__/controllers/api/v4/vault-controller.test.ts +++ b/indexer/services/comlink/__tests__/controllers/api/v4/vault-controller.test.ts @@ -13,6 +13,8 @@ import { FundingIndexUpdatesTable, PnlTicksFromDatabase, VaultTable, + MEGAVAULT_MODULE_ADDRESS, + MEGAVAULT_SUBACCOUNT_ID, } from '@dydxprotocol-indexer/postgres'; import { RequestMethod, VaultHistoricalPnl } from '../../../../src/types'; import request from 'supertest'; @@ -32,6 +34,7 @@ describe('vault-controller#V4', () => { const initialFundingIndex: string = '10000'; const vault1Equity: number = 159500; const vault2Equity: number = 10000; + const mainVaultEquity: number = 10000; beforeAll(async () => { await dbHelpers.migrate(); @@ -69,6 +72,12 @@ describe('vault-controller#V4', () => { }), ]); await SubaccountTable.create(testConstants.vaultSubaccount); + await SubaccountTable.create({ + address: MEGAVAULT_MODULE_ADDRESS, + subaccountNumber: 0, + updatedAt: latestTime.toISO(), + updatedAtHeight: latestBlockHeight, + }); await Promise.all([ PerpetualPositionTable.create( testConstants.defaultPerpetualPosition, @@ -146,7 +155,7 @@ describe('vault-controller#V4', () => { ['no resolution', '', [1, 2]], ['daily resolution', '?resolution=day', [1, 2]], ['hourly resolution', '?resolution=hour', [1, 2, 3]], - ])('Get /megavault/historicalPnl with 2 vault subaccounts (%s)', async ( + ])('Get /megavault/historicalPnl with 2 vault subaccounts and main subaccount (%s)', async ( _name: string, queryParam: string, expectedTicksIndex: number[], @@ -162,22 +171,28 @@ describe('vault-controller#V4', () => { address: testConstants.vaultAddress, clobPairId: testConstants.defaultPerpetualMarket2.clobPairId, }), + AssetPositionTable.upsert({ + ...testConstants.defaultAssetPosition, + subaccountId: MEGAVAULT_SUBACCOUNT_ID, + }), ]); - const createdPnlTicks: PnlTicksFromDatabase[] = await createPnlTicks(); + const createdPnlTicks: PnlTicksFromDatabase[] = await createPnlTicks( + true, // createMainSubaccounPnlTicks + ); const response: request.Response = await sendRequest({ type: RequestMethod.GET, path: `/v4/vault/v1/megavault/historicalPnl${queryParam}`, }); const expectedPnlTickBase: any = { - equity: (parseFloat(testConstants.defaultPnlTick.equity) * 2).toString(), - totalPnl: (parseFloat(testConstants.defaultPnlTick.totalPnl) * 2).toString(), - netTransfers: (parseFloat(testConstants.defaultPnlTick.netTransfers) * 2).toString(), + equity: (parseFloat(testConstants.defaultPnlTick.equity) * 3).toString(), + totalPnl: (parseFloat(testConstants.defaultPnlTick.totalPnl) * 3).toString(), + netTransfers: (parseFloat(testConstants.defaultPnlTick.netTransfers) * 3).toString(), }; const finalTick: PnlTicksFromDatabase = { ...expectedPnlTickBase, - equity: Big(vault1Equity).add(vault2Equity).toFixed(), + equity: Big(vault1Equity).add(vault2Equity).add(mainVaultEquity).toFixed(), blockHeight: latestBlockHeight, blockTime: latestTime.toISO(), createdAt: latestTime.toISO(), @@ -449,8 +464,10 @@ describe('vault-controller#V4', () => { }); }); - async function createPnlTicks(): Promise { - return Promise.all([ + async function createPnlTicks( + createMainSubaccountPnlTicks: boolean = false, + ): Promise { + const createdTicks: PnlTicksFromDatabase[] = await Promise.all([ PnlTicksTable.create(testConstants.defaultPnlTick), PnlTicksTable.create({ ...testConstants.defaultPnlTick, @@ -496,5 +513,38 @@ describe('vault-controller#V4', () => { blockHeight: currentBlockHeight, }), ]); + + if (createMainSubaccountPnlTicks) { + const mainSubaccountTicks: PnlTicksFromDatabase[] = await Promise.all([ + PnlTicksTable.create({ + ...testConstants.defaultPnlTick, + subaccountId: MEGAVAULT_SUBACCOUNT_ID, + }), + PnlTicksTable.create({ + ...testConstants.defaultPnlTick, + subaccountId: MEGAVAULT_SUBACCOUNT_ID, + blockTime: twoDaysAgo.toISO(), + createdAt: twoDaysAgo.toISO(), + blockHeight: twoDayBlockHeight, + }), + PnlTicksTable.create({ + ...testConstants.defaultPnlTick, + subaccountId: MEGAVAULT_SUBACCOUNT_ID, + blockTime: twoHoursAgo.toISO(), + createdAt: twoHoursAgo.toISO(), + blockHeight: twoHourBlockHeight, + }), + PnlTicksTable.create({ + ...testConstants.defaultPnlTick, + subaccountId: MEGAVAULT_SUBACCOUNT_ID, + blockTime: currentTime.toISO(), + createdAt: currentTime.toISO(), + blockHeight: currentBlockHeight, + }), + ]); + createdTicks.push(...mainSubaccountTicks); + } + + return createdTicks; } }); diff --git a/indexer/services/comlink/src/controllers/api/v4/vault-controller.ts b/indexer/services/comlink/src/controllers/api/v4/vault-controller.ts index e377f40c9b..6b967f3872 100644 --- a/indexer/services/comlink/src/controllers/api/v4/vault-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/vault-controller.ts @@ -24,6 +24,7 @@ import { PnlTickInterval, VaultTable, VaultFromDatabase, + MEGAVAULT_SUBACCOUNT_ID, } from '@dydxprotocol-indexer/postgres'; import Big from 'big.js'; import express from 'express'; @@ -70,18 +71,24 @@ class VaultController extends Controller { @Query() resolution?: PnlTickInterval, ): Promise { const vaultSubaccounts: VaultMapping = await getVaultMapping(); + const vaultSubaccountIdsWithMainSubaccount: string[] = _ + .keys(vaultSubaccounts) + .concat([MEGAVAULT_SUBACCOUNT_ID]); const [ vaultPnlTicks, vaultPositions, latestBlock, + mainSubaccountEquity, ] : [ PnlTicksFromDatabase[], Map, BlockFromDatabase, + string, ] = await Promise.all([ - getVaultSubaccountPnlTicks(vaultSubaccounts, resolution), + getVaultSubaccountPnlTicks(vaultSubaccountIdsWithMainSubaccount, resolution), getVaultPositions(vaultSubaccounts), BlockTable.getLatest(), + getMainSubaccountEquity(), ]); // aggregate pnlTicks for all vault subaccounts grouped by blockHeight @@ -92,7 +99,7 @@ class VaultController extends Controller { return position.equity; }).reduce((acc: string, curr: string): string => { return (Big(acc).add(Big(curr))).toFixed(); - }, '0'); + }, mainSubaccountEquity); const pnlTicksWithCurrentTick: PnlTicksFromDatabase[] = getPnlTicksWithCurrentTick( currentEquity, Array.from(aggregatedPnlTicks.values()), @@ -100,7 +107,7 @@ class VaultController extends Controller { ); return { - megavaultPnl: pnlTicksWithCurrentTick.map( + megavaultPnl: _.sortBy(pnlTicksWithCurrentTick, 'blockTime').map( (pnlTick: PnlTicksFromDatabase) => { return pnlTicksToResponseObject(pnlTick); }), @@ -121,7 +128,7 @@ class VaultController extends Controller { Map, BlockFromDatabase, ] = await Promise.all([ - getVaultSubaccountPnlTicks(vaultSubaccounts, resolution), + getVaultSubaccountPnlTicks(_.keys(vaultSubaccounts), resolution), getVaultPositions(vaultSubaccounts), BlockTable.getLatest(), ]); @@ -286,10 +293,9 @@ router.get( }); async function getVaultSubaccountPnlTicks( - vaultSubaccounts: VaultMapping, + vaultSubaccountIds: string[], resolution?: PnlTickInterval, ): Promise { - const vaultSubaccountIds: string[] = _.keys(vaultSubaccounts); if (vaultSubaccountIds.length === 0) { return []; } @@ -437,6 +443,15 @@ async function getVaultPositions( )); } +async function getMainSubaccountEquity(): Promise { + // Main vault subaccount should only ever hold a USDC and never any perpetuals. + const usdcBalance: {[subaccountId: string]: Big} = await AssetPositionTable + .findUsdcPositionForSubaccounts( + [MEGAVAULT_SUBACCOUNT_ID], + ); + return usdcBalance[MEGAVAULT_SUBACCOUNT_ID]?.toFixed() || '0'; +} + function getPnlTicksWithCurrentTick( equity: string, pnlTicks: PnlTicksFromDatabase[],