Skip to content

Commit

Permalink
fix: group activity by transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
jackmellis committed Jan 26, 2024
1 parent 68d7928 commit 51d35f3
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 77 deletions.
61 changes: 40 additions & 21 deletions packages/core/src/vaults/__tests__/fetchVaultActivity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,72 +36,72 @@ beforeEach(() => {
// Sell
{
...defaultActivityEvent,
id: 'MINT-0x0',
id: 'MINT-0x1',
type: 'ZapSell',
mintIds: ['1'],
},
// Rdeem
{
...defaultActivityEvent,
id: 'REDEEM-0x0',
id: 'REDEEM-0x2',
type: 'Redeem',
redeemIds: ['1'],
},
// Buy
{
...defaultActivityEvent,
id: 'REDEEM-0x0',
id: 'REDEEM-0x3',
type: 'ZapBuy',
redeemIds: ['1'],
},
// Swap
{
...defaultActivityEvent,
id: 'SWAP-0x0',
id: 'SWAP-0x4',
type: 'Swap',
swapMintIds: ['1'],
swapRedeemIds: ['2'],
},
// LP Stake
{
...defaultActivityEvent,
id: 'DEPOSIT-0x0',
id: 'DEPOSIT-0x5',
type: 'AddLiquidity',
},
// IP Stake
{
...defaultActivityEvent,
id: 'DEPOSIT-0x0',
id: 'DEPOSIT-0x6',
type: 'InventoryDeposit',
},
// LP Unstake
{
...defaultActivityEvent,
id: 'WITHDRAW-0x0',
id: 'WITHDRAW-0x7',
type: 'RemoveLiquidity',
},
// IP Unstake
{
...defaultActivityEvent,
id: 'WITHDRAW-0x0',
id: 'WITHDRAW-0x8',
type: 'InventoryWithdraw',
},
// Created
{
...defaultActivityEvent,
id: 'VAULT_CREATED-0x0',
id: 'VAULT_CREATED-0x9',
type: 'VaultCreated',
},
// Updated
{
...defaultActivityEvent,
id: 'VAULT_FEE_UPDATE-0x0',
id: 'VAULT_FEE_UPDATE-0x10',
type: 'VaultFeeUpdate',
},
// Shutdown
{
...defaultActivityEvent,
id: 'VAULT_SHUTDOWN-0x0',
id: 'VAULT_SHUTDOWN-0x11',
type: 'VaultShutdown',
},
],
Expand Down Expand Up @@ -137,7 +137,7 @@ it('returns a list of vault activity', async () => {
eventType: 'ZapSell',
source: 'source',
tokenIds: ['1'],
txId: '0x0',
txId: '0x1',
feeAmount: '1',
},
{
Expand All @@ -148,7 +148,7 @@ it('returns a list of vault activity', async () => {
eventType: 'Redeem',
source: 'source',
tokenIds: ['1'],
txId: '0x0',
txId: '0x2',
feeAmount: '1',
},
{
Expand All @@ -159,7 +159,7 @@ it('returns a list of vault activity', async () => {
eventType: 'ZapBuy',
source: 'source',
tokenIds: ['1'],
txId: '0x0',
txId: '0x3',
feeAmount: '1',
},
{
Expand All @@ -170,7 +170,7 @@ it('returns a list of vault activity', async () => {
eventType: 'Swap',
source: 'source',
tokenIds: ['1'],
txId: '0x0',
txId: '0x4',
feeAmount: '1',
swapTokenIds: ['2'],
},
Expand All @@ -181,7 +181,7 @@ it('returns a list of vault activity', async () => {
date: 1,
eventType: 'AddLiquidity',
source: 'source',
txId: '0x0',
txId: '0x5',
stakeType: 'liquidity',
amount: '0',
},
Expand All @@ -192,7 +192,7 @@ it('returns a list of vault activity', async () => {
date: 1,
eventType: 'InventoryDeposit',
source: 'source',
txId: '0x0',
txId: '0x6',
stakeType: 'inventory',
amount: '0',
},
Expand All @@ -202,6 +202,20 @@ it('returns a list of vault activity', async () => {
expect(formatJson(result)).toEqual(expected);
});

it('groups activity by transaction id', async () => {
subgraphResponse.activityEvents.forEach((event: any) => {
const [type] = event.id.split('-');

event.id = [type, '0x0'].join('-');
});

const result = await run();

expect(result).toHaveLength(1);
expect(result[0].type).toBe('stake');
expect(result[0].eventType).toBe('AddLiquidity');
});

describe('when includeAllActivity is true', () => {
beforeEach(() => {
args.includeAllActivity = true;
Expand All @@ -218,7 +232,7 @@ describe('when includeAllActivity is true', () => {
date: 1,
eventType: 'VaultCreated',
source: 'source',
txId: '0x0',
txId: '0x9',
},
{
type: 'update',
Expand All @@ -227,7 +241,7 @@ describe('when includeAllActivity is true', () => {
date: 1,
eventType: 'VaultFeeUpdate',
source: 'source',
txId: '0x0',
txId: '0x10',
},
{
type: 'shutdown',
Expand All @@ -236,7 +250,7 @@ describe('when includeAllActivity is true', () => {
date: 1,
eventType: 'VaultShutdown',
source: 'source',
txId: '0x0',
txId: '0x11',
},
];

Expand All @@ -248,7 +262,12 @@ describe('when there are more than 1000 events', () => {
beforeEach(() => {
querySubgraph.mockResolvedValueOnce({
...subgraphResponse,
activityEvents: Array(1000).fill(subgraphResponse.activityEvents[0]),
activityEvents: Array(1000)
.fill(subgraphResponse.activityEvents[0])
.map((event, i) => ({
...event,
id: `MINT-0x000${i}`,
})),
});
});

Expand Down
150 changes: 94 additions & 56 deletions packages/core/src/vaults/fetchVaultActivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ type Type =
| 'INVENTORY_DEPOSIT'
| 'INVENTORY_DEPOSIT_WITH_NFT';

const typePrecedence: VaultActivityType[] = [
'stake',
'unstake',
'buy',
'sell',
'swap',
'redeem',
'mint',
'create',
'update',
'shutdown',
];

type Response = NftxV3.Query & {
activityEvents: NftxV3.ActivityEvent &
{
Expand Down Expand Up @@ -240,7 +253,6 @@ export const makeFetchVaultActivity = ({
async function fetchVaultActivity({
fromTimestamp,
includeAllActivity = false,
lastId,
network = config.network,
toTimestamp,
vaultAddresses,
Expand All @@ -252,67 +264,93 @@ export const makeFetchVaultActivity = ({
vaultAddresses?: Address[];
vaultIds?: string[];
includeAllActivity?: boolean;
lastId?: string;
}): Promise<VaultActivity[]> {
const query = createQuery<Response>()
.activityEvents.first(1000)
.orderBy('id')
.where((w) => [
w.id.gt(lastId),
w.date.gt(fromTimestamp == null ? null : `${fromTimestamp}`),
w.date.lte(toTimestamp == null ? null : `${toTimestamp}`),
w.vault.in(vaultAddresses),
w.vault((v) => [v.vaultId.in(vaultIds)]),
])
.select((s) => [
s.id,
s.source,
s.type,
s.date,
s.vault((v) => [v.id, v.vaultId]),
s.on<NftxV3.Mint>('Mint', (s) => [
s.nftIds.as('mintIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
s.on<NftxV3.Redeem>('Redeem', (s) => [
s.targetCount,
s.nftIds.as('redeemIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
s.on<NftxV3.Swap>('Swap', (s) => [
s.mintedIds.as('swapMintIds'),
s.specificIds.as('swapRedeemIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
]);
let lastId: string | undefined;
const activity: VaultActivity[] = [];

const response = await querySubgraph({
url: getChainConstant(config.subgraph.NFTX_SUBGRAPH, network),
query,
});
do {
const query = createQuery<Response>()
.activityEvents.first(1000)
.orderBy('id')
.where((w) => [
w.id.gt(lastId),
w.date.gt(fromTimestamp == null ? null : `${fromTimestamp}`),
w.date.lte(toTimestamp == null ? null : `${toTimestamp}`),
w.vault.in(vaultAddresses),
w.vault((v) => [v.vaultId.in(vaultIds)]),
])
.select((s) => [
s.id,
s.source,
s.type,
s.date,
s.vault((v) => [v.id, v.vaultId]),
s.on<NftxV3.Mint>('Mint', (s) => [
s.nftIds.as('mintIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
s.on<NftxV3.Redeem>('Redeem', (s) => [
s.targetCount,
s.nftIds.as('redeemIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
s.on<NftxV3.Swap>('Swap', (s) => [
s.mintedIds.as('swapMintIds'),
s.specificIds.as('swapRedeemIds'),
s.feeReceipt((r) => [r.transfers((t) => [t.amount])]),
]),
]);

const allActivity = response.activityEvents.map((e) =>
activityEventToActivity(e as any, includeAllActivity)
);
const response = await querySubgraph({
url: getChainConstant(config.subgraph.NFTX_SUBGRAPH, network),
query,
});

let activity = allActivity.filter(isDefined);
const allActivity = response.activityEvents
.map((e) => activityEventToActivity(e as any, includeAllActivity))
.filter(isDefined);

if (response.activityEvents.length === 1000) {
const lastId =
response.activityEvents[response.activityEvents.length - 1].id;
const moreActivity = await fetchVaultActivity({
fromTimestamp,
includeAllActivity,
network,
toTimestamp,
vaultAddresses,
vaultIds,
lastId,
});
activity = [...activity, ...moreActivity];
}
activity.push(...allActivity);

if (response.activityEvents.length === 1000) {
lastId = response.activityEvents[response.activityEvents.length - 1].id;
} else {
lastId = undefined;
}
} while (lastId);

return Object.values(
activity.reduce((acc, activity) => {
const { txId } = activity;
let arr = acc[txId];
if (!arr) {
arr = acc[txId] = [];
}
arr.push(activity);

return acc;
}, {} as Record<string, VaultActivity[]>)
)
.map((activitySet) => {
activitySet.sort((a, b) => {
const aType = typePrecedence.indexOf(a.type);
const bType = typePrecedence.indexOf(b.type);

if (aType === bType) {
return a.date - b.date;
}

return aType - bType;
});

return activity.sort((a, b) => a.date - b.date);
return activitySet.reduce((acc, activity) => {
return {
...activity,
...acc,
};
}, activitySet[0]);
})
.sort((a, b) => a.date - b.date);
};

const fetchVaultActivity = makeFetchVaultActivity({ querySubgraph });
Expand Down

0 comments on commit 51d35f3

Please sign in to comment.