Skip to content

Commit

Permalink
[IND-503]: Add historical trading reward block and aggregation endpoi…
Browse files Browse the repository at this point in the history
…nts (#954)
  • Loading branch information
Christopher-Li authored Jan 11, 2024
1 parent 2d20eac commit 6385473
Show file tree
Hide file tree
Showing 14 changed files with 1,151 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export async function findAll(
startedAtHeight,
period,
limit,
startedAtBeforeOrAt,
startedAtHeightBeforeOrAt,
}: TradingRewardAggregationQueryConfig,
requiredFields: QueryableField[],
options: Options = DEFAULT_POSTGRES_OPTIONS,
Expand All @@ -45,6 +47,8 @@ export async function findAll(
startedAtHeight,
period,
limit,
startedAtBeforeOrAt,
startedAtHeightBeforeOrAt,
} as QueryConfig,
requiredFields,
);
Expand All @@ -71,6 +75,18 @@ export async function findAll(
baseQuery = baseQuery.where(TradingRewardAggregationColumns.period, period);
}

if (startedAtBeforeOrAt) {
baseQuery = baseQuery.where(TradingRewardAggregationColumns.startedAt, '<=', startedAtBeforeOrAt);
}

if (startedAtHeightBeforeOrAt) {
baseQuery = baseQuery.where(
TradingRewardAggregationColumns.startedAtHeight,
'<=',
startedAtHeightBeforeOrAt,
);
}

if (options.orderBy !== undefined) {
for (const [column, order] of options.orderBy) {
baseQuery = baseQuery.orderBy(
Expand Down
6 changes: 6 additions & 0 deletions indexer/packages/postgres/src/stores/trading-reward-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export async function findAll(
blockTimeBeforeOrAt,
blockTimeAfterOrAt,
blockTimeBefore,
blockHeightBeforeOrAt,
limit,
}: TradingRewardQueryConfig,
requiredFields: QueryableField[],
Expand All @@ -40,6 +41,7 @@ export async function findAll(
blockTimeBeforeOrAt,
blockTimeAfterOrAt,
blockTimeBefore,
blockHeightBeforeOrAt,
limit,
} as QueryConfig,
requiredFields,
Expand Down Expand Up @@ -70,6 +72,10 @@ export async function findAll(
baseQuery = baseQuery.where(TradingRewardColumns.blockTime, '<', blockTimeBefore);
}

if (blockHeightBeforeOrAt) {
baseQuery = baseQuery.where(TradingRewardColumns.blockHeight, '<=', blockHeightBeforeOrAt);
}

if (options.orderBy !== undefined) {
for (const [column, order] of options.orderBy) {
baseQuery = baseQuery.orderBy(
Expand Down
6 changes: 6 additions & 0 deletions indexer/packages/postgres/src/types/query-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export enum QueryableField {
BLOCK_TIME_AFTER_OR_AT = 'blockTimeAfterOrAt',
BLOCK_TIME_BEFORE = 'blockTimeBefore',
ADDRESSES = 'addresses',
BLOCK_HEIGHT_BEFORE_OR_AT = 'blockHeightBeforeOrAt',
STARTED_AT_BEFORE_OR_AT = 'startedAtBeforeOrAt',
STARTED_AT_HEIGHT_BEFORE_OR_AT = 'startedAtHeightBeforeOrAt',
}

export interface QueryConfig {
Expand Down Expand Up @@ -280,6 +283,7 @@ export interface TradingRewardQueryConfig extends QueryConfig {
[QueryableField.BLOCK_TIME_BEFORE_OR_AT]?: IsoString;
[QueryableField.BLOCK_TIME_AFTER_OR_AT]?: IsoString;
[QueryableField.BLOCK_TIME_BEFORE]?: IsoString;
[QueryableField.BLOCK_HEIGHT_BEFORE_OR_AT]?: IsoString;
}

export interface TradingRewardAggregationQueryConfig extends QueryConfig {
Expand All @@ -288,4 +292,6 @@ export interface TradingRewardAggregationQueryConfig extends QueryConfig {
[QueryableField.STARTED_AT_HEIGHT]?: string;
[QueryableField.STARTED_AT_HEIGHT_OR_AFTER]?: string;
[QueryableField.PERIOD]?: TradingRewardAggregationPeriod;
[QueryableField.STARTED_AT_BEFORE_OR_AT]?: IsoString;
[QueryableField.STARTED_AT_HEIGHT_BEFORE_OR_AT]?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {
HistoricalBlockTradingReward,
HistoricalBlockTradingRewardsResponse,
HistoricalTradingRewardAggregation,
HistoricalTradingRewardAggregationsResponse,
RequestMethod,
} from '../../../../src/types';
import { getQueryString, sendRequest } from '../../../helpers/helpers';
import {
TradingRewardCreateObject,
TradingRewardFromDatabase,
TradingRewardTable,
dbHelpers,
testConstants,
testConversionHelpers,
testMocks,
} from '@dydxprotocol-indexer/postgres';
import { stats } from '@dydxprotocol-indexer/base';
import request from 'supertest';
import { tradingRewardToResponse } from '../../../../src/request-helpers/request-transformer';

describe('historical-block-trading-reward-controller#V4', () => {
beforeAll(async () => {
await dbHelpers.migrate();
jest.spyOn(stats, 'increment');
jest.spyOn(stats, 'timing');
});

beforeEach(async () => {
await testMocks.seedData();
await Promise.all([
TradingRewardTable.create(defaultTradingRewardCreate),
TradingRewardTable.create(defaultTradingRewardCreate2),
]);

const rewards: TradingRewardFromDatabase[] = await TradingRewardTable.findAll({}, []);

defaultTradingReward = rewards[1];
defaultTradingReward2 = rewards[0];
});

afterAll(async () => {
await dbHelpers.teardown();
});

afterEach(async () => {
await dbHelpers.clearData();
});

const defaultTradingRewardCreate: TradingRewardCreateObject = {
address: testConstants.defaultAddress,
blockTime: testConstants.defaultBlock.time,
blockHeight: testConstants.defaultBlock.blockHeight,
amount: testConversionHelpers.convertToDenomScale('10'),
};
let defaultTradingReward: TradingRewardFromDatabase;
const defaultTradingRewardCreate2: TradingRewardCreateObject = {
address: testConstants.defaultAddress,
blockTime: testConstants.defaultBlock2.time,
blockHeight: testConstants.defaultBlock2.blockHeight,
amount: testConversionHelpers.convertToDenomScale('5'),
};
let defaultTradingReward2: TradingRewardFromDatabase;

describe('GET', () => {
it('Get /historicalBlockTradingReward/:address returns all valid rewards', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalBlockTradingRewards/${testConstants.defaultAddress}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(2);
console.log(JSON.stringify(rewards));
expect(rewards[0]).toEqual(tradingRewardToResponse(
defaultTradingReward2,
));
expect(rewards[1]).toEqual(tradingRewardToResponse(
defaultTradingReward,
));
});

it('Get /historicalBlockTradingRewards/:address returns all valid rewards with limit', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalBlockTradingRewards/${testConstants.defaultAddress}` +
`?${getQueryString({ limit: 1 })}`,
});

const responseBody: HistoricalBlockTradingRewardsResponse = response.body;
const rewards: HistoricalBlockTradingReward[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardToResponse(
defaultTradingReward2,
));
});

it('Get /historicalBlockTradingRewards/:address returns no rewards when none exist', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: '/v4/historicalBlockTradingRewards/fakeAddress',
});

const responseBody: HistoricalBlockTradingRewardsResponse = response.body;
const rewards: HistoricalBlockTradingReward[] = responseBody.rewards;
expect(rewards.length).toEqual(0);
});

it('Get /historicalBlockTradingRewards/:address returns rewards with blockTimeBeforeOrAt', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalBlockTradingRewards/${testConstants.defaultAddress}` +
`?${getQueryString({ startingBeforeOrAt: testConstants.defaultBlock.time })}`,
});

const responseBody: HistoricalBlockTradingRewardsResponse = response.body;
const rewards: HistoricalBlockTradingReward[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardToResponse(
defaultTradingReward,
));
});

it('Get /historicalBlockTradingRewards/:address returns rewards with blockHeightBeforeOrAt', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalBlockTradingRewards/${testConstants.defaultAddress}` +
`?${getQueryString({ startingBeforeOrAtHeight: testConstants.defaultBlock.blockHeight })}`,
});

const responseBody: HistoricalBlockTradingRewardsResponse = response.body;
const rewards: HistoricalBlockTradingReward[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardToResponse(
defaultTradingReward,
));
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import {
HistoricalTradingRewardAggregation,
HistoricalTradingRewardAggregationsResponse,
RequestMethod,
} from '../../../../src/types';
import { getQueryString, sendRequest } from '../../../helpers/helpers';
import {
TradingRewardAggregationCreateObject,
TradingRewardAggregationFromDatabase,
TradingRewardAggregationPeriod,
TradingRewardAggregationTable,
dbHelpers,
testConstants,
testConversionHelpers,
testMocks,
} from '@dydxprotocol-indexer/postgres';
import { stats } from '@dydxprotocol-indexer/base';
import { DateTime } from 'luxon';
import request from 'supertest';
import { tradingRewardAggregationToResponse } from '../../../../src/request-helpers/request-transformer';

describe('historical-trading-reward-aggregations-controller#V4', () => {
beforeAll(async () => {
await dbHelpers.migrate();
jest.spyOn(stats, 'increment');
jest.spyOn(stats, 'timing');
});

beforeEach(async () => {
await testMocks.seedData();
await Promise.all([
TradingRewardAggregationTable.create(defaultCompletedTradingRewardAggregationCreate),
TradingRewardAggregationTable.create(defaultIncompleteTradingRewardAggregationCreate),
]);
const aggregations:
TradingRewardAggregationFromDatabase[] = await TradingRewardAggregationTable.findAll({}, []);

defaultCompletedTradingRewardAggregation = aggregations[0];
defaultIncompleteTradingRewardAggregation = aggregations[1];
});

afterAll(async () => {
await dbHelpers.teardown();
});

afterEach(async () => {
await dbHelpers.clearData();
});

const startedAt: DateTime = testConstants.createdDateTime.startOf('month').toUTC();
const startedAt2: DateTime = startedAt.plus({ month: 1 });
const defaultCompletedTradingRewardAggregationCreate: TradingRewardAggregationCreateObject = {
address: testConstants.defaultAddress,
startedAt: startedAt.toISO(),
startedAtHeight: testConstants.defaultBlock.blockHeight,
endedAt: startedAt2.toISO(),
endedAtHeight: '10000', // ignored field for the purposes of this test
period: TradingRewardAggregationPeriod.MONTHLY,
amount: testConversionHelpers.convertToDenomScale('10'),
};
let defaultCompletedTradingRewardAggregation: TradingRewardAggregationFromDatabase;
const defaultIncompleteTradingRewardAggregationCreate: TradingRewardAggregationCreateObject = {
address: testConstants.defaultAddress,
startedAt: startedAt2.toISO(),
startedAtHeight: testConstants.defaultBlock2.blockHeight,
period: TradingRewardAggregationPeriod.MONTHLY,
amount: testConversionHelpers.convertToDenomScale('20'),
};
let defaultIncompleteTradingRewardAggregation: TradingRewardAggregationFromDatabase;

describe('GET', () => {
it('Get /historicalTradingRewardAggregations/:address returns all valid aggregations', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalTradingRewardAggregations/${testConstants.defaultAddress}` +
`?${getQueryString({ period: TradingRewardAggregationPeriod.MONTHLY })}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(2);
expect(rewards[0]).toEqual(tradingRewardAggregationToResponse(
defaultIncompleteTradingRewardAggregation,
));
expect(rewards[1]).toEqual(tradingRewardAggregationToResponse(
defaultCompletedTradingRewardAggregation,
));
});

it('Get /historicalTradingRewardAggregations/:address returns all valid aggregations with limit', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalTradingRewardAggregations/${testConstants.defaultAddress}` +
`?${getQueryString({ period: TradingRewardAggregationPeriod.MONTHLY, limit: 1 })}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardAggregationToResponse(
defaultIncompleteTradingRewardAggregation,
));
});

it('Get /historicalTradingRewardAggregations/:address returns no aggregations when none exist', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalTradingRewardAggregations/${testConstants.defaultAddress}` +
`?${getQueryString({ period: TradingRewardAggregationPeriod.DAILY })}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(0);
});

it('Get /historicalTradingRewardAggregations/:address returns aggregations with startedAtBeforeOrAt', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalTradingRewardAggregations/${testConstants.defaultAddress}` +
`?${getQueryString({
period: TradingRewardAggregationPeriod.MONTHLY,
startingBeforeOrAt: startedAt.toISO(),
})}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardAggregationToResponse(
defaultCompletedTradingRewardAggregation,
));
});

it('Get /historicalTradingRewardAggregations/:address returns aggregations with startedAtHeightBeforeOrAt', async () => {
const response: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/historicalTradingRewardAggregations/${testConstants.defaultAddress}` +
`?${getQueryString({
period: TradingRewardAggregationPeriod.MONTHLY,
startingBeforeOrAtHeight: testConstants.defaultBlock.blockHeight,
})}`,
});

const responseBody: HistoricalTradingRewardAggregationsResponse = response.body;
const rewards: HistoricalTradingRewardAggregation[] = responseBody.rewards;
expect(rewards.length).toEqual(1);
expect(rewards[0]).toEqual(tradingRewardAggregationToResponse(
defaultCompletedTradingRewardAggregation,
));
});
});
});
Loading

0 comments on commit 6385473

Please sign in to comment.