Skip to content
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

Include getting main subaccount equity / pnl for megavault PnL query. (backport #2376) #2378

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions indexer/packages/postgres/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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[],
Expand All @@ -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(),
Expand Down Expand Up @@ -449,8 +464,10 @@ describe('vault-controller#V4', () => {
});
});

async function createPnlTicks(): Promise<PnlTicksFromDatabase[]> {
return Promise.all([
async function createPnlTicks(
createMainSubaccountPnlTicks: boolean = false,
): Promise<PnlTicksFromDatabase[]> {
const createdTicks: PnlTicksFromDatabase[] = await Promise.all([
PnlTicksTable.create(testConstants.defaultPnlTick),
PnlTicksTable.create({
...testConstants.defaultPnlTick,
Expand Down Expand Up @@ -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;
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
PnlTickInterval,
VaultTable,
VaultFromDatabase,
MEGAVAULT_SUBACCOUNT_ID,
} from '@dydxprotocol-indexer/postgres';
import Big from 'big.js';
import express from 'express';
Expand Down Expand Up @@ -70,18 +71,24 @@ class VaultController extends Controller {
@Query() resolution?: PnlTickInterval,
): Promise<MegavaultHistoricalPnlResponse> {
const vaultSubaccounts: VaultMapping = await getVaultMapping();
const vaultSubaccountIdsWithMainSubaccount: string[] = _
.keys(vaultSubaccounts)
.concat([MEGAVAULT_SUBACCOUNT_ID]);
const [
vaultPnlTicks,
vaultPositions,
latestBlock,
mainSubaccountEquity,
] : [
PnlTicksFromDatabase[],
Map<string, VaultPosition>,
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
Expand All @@ -92,15 +99,15 @@ 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()),
latestBlock,
);

return {
megavaultPnl: pnlTicksWithCurrentTick.map(
megavaultPnl: _.sortBy(pnlTicksWithCurrentTick, 'blockTime').map(
(pnlTick: PnlTicksFromDatabase) => {
return pnlTicksToResponseObject(pnlTick);
}),
Expand All @@ -121,7 +128,7 @@ class VaultController extends Controller {
Map<string, VaultPosition>,
BlockFromDatabase,
] = await Promise.all([
getVaultSubaccountPnlTicks(vaultSubaccounts, resolution),
getVaultSubaccountPnlTicks(_.keys(vaultSubaccounts), resolution),
getVaultPositions(vaultSubaccounts),
BlockTable.getLatest(),
]);
Expand Down Expand Up @@ -286,10 +293,9 @@ router.get(
});

async function getVaultSubaccountPnlTicks(
vaultSubaccounts: VaultMapping,
vaultSubaccountIds: string[],
resolution?: PnlTickInterval,
): Promise<PnlTicksFromDatabase[]> {
const vaultSubaccountIds: string[] = _.keys(vaultSubaccounts);
if (vaultSubaccountIds.length === 0) {
return [];
}
Expand Down Expand Up @@ -437,6 +443,15 @@ async function getVaultPositions(
));
}

async function getMainSubaccountEquity(): Promise<string> {
// 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[],
Expand Down
Loading