Skip to content

Commit b5d4e8a

Browse files
[IND-496]: Create trading_reward_aggregations postgres table (#825)
* [IND-496]: Create trading_reward_aggregations postgres table * rabbit recommendations * nit
1 parent 207b903 commit b5d4e8a

15 files changed

+484
-2
lines changed

indexer/packages/postgres/__tests__/helpers/constants.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import * as OrderTable from '../../src/stores/order-table';
1313
import * as PerpetualPositionTable from '../../src/stores/perpetual-position-table';
1414
import * as SubaccountTable from '../../src/stores/subaccount-table';
1515
import * as TendermintEventTable from '../../src/stores/tendermint-event-table';
16+
import * as TradingRewardAggregationTable from '../../src/stores/trading-reward-aggregation-table';
1617
import * as TransactionTable from '../../src/stores/transaction-table';
1718
import * as TransferTable from '../../src/stores/transfer-table';
1819
import {
@@ -43,6 +44,8 @@ import {
4344
SubaccountCreateObject,
4445
TendermintEventCreateObject,
4546
TimeInForce,
47+
TradingRewardAggregationCreateObject,
48+
TradingRewardAggregationPeriod,
4649
TradingRewardCreateObject,
4750
TransactionCreateObject,
4851
TransferCreateObject,
@@ -591,3 +594,18 @@ export const defaultTradingReward: TradingRewardCreateObject = {
591594
blockTime: createdDateTime.toISO(),
592595
amount: '1.00',
593596
};
597+
598+
// ========= Trading Reward Aggregation Data ==========
599+
600+
export const defaultTradingRewardAggregation: TradingRewardAggregationCreateObject = {
601+
address: defaultAddress,
602+
startedAtHeight: createdHeight,
603+
startedAt: createdDateTime.toISO(),
604+
period: TradingRewardAggregationPeriod.DAILY,
605+
amount: '1.00',
606+
};
607+
export const defaultTradingRewardAggregationId: string = TradingRewardAggregationTable.uuid(
608+
defaultTradingRewardAggregation.address,
609+
defaultTradingRewardAggregation.period,
610+
defaultTradingRewardAggregation.startedAtHeight,
611+
);
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { TradingRewardAggregationFromDatabase } from '../../src/types';
2+
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
3+
import {
4+
defaultSubaccountId,
5+
defaultTradingRewardAggregation,
6+
defaultTradingRewardAggregationId,
7+
defaultWallet,
8+
} from '../helpers/constants';
9+
import * as TradingRewardAggregationTable from '../../src/stores/trading-reward-aggregation-table';
10+
import { WalletTable } from '../../src';
11+
import { seedData } from '../helpers/mock-generators';
12+
13+
describe('TradingRewardAggregation store', () => {
14+
beforeAll(async () => {
15+
await migrate();
16+
});
17+
18+
beforeEach(async () => {
19+
await seedData();
20+
await WalletTable.create(defaultWallet);
21+
});
22+
23+
afterEach(async () => {
24+
await clearData();
25+
});
26+
27+
afterAll(async () => {
28+
await teardown();
29+
});
30+
31+
it('Successfully creates a TradingRewardAggregation', async () => {
32+
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);
33+
});
34+
35+
it('Successfully finds all TradingRewardAggregations', async () => {
36+
await Promise.all([
37+
TradingRewardAggregationTable.create(defaultTradingRewardAggregation),
38+
TradingRewardAggregationTable.create({
39+
...defaultTradingRewardAggregation,
40+
startedAtHeight: '1',
41+
}),
42+
]);
43+
44+
const tradingRewardAggregations:
45+
TradingRewardAggregationFromDatabase[] = await TradingRewardAggregationTable.findAll(
46+
{},
47+
[],
48+
{ readReplica: true },
49+
);
50+
51+
expect(tradingRewardAggregations.length).toEqual(2);
52+
expect(tradingRewardAggregations[0]).toEqual(expect.objectContaining({
53+
...defaultTradingRewardAggregation,
54+
startedAtHeight: '1',
55+
}));
56+
expect(tradingRewardAggregations[1]).toEqual(
57+
expect.objectContaining(defaultTradingRewardAggregation),
58+
);
59+
});
60+
61+
it('Successfully finds a TradingRewardAggregation', async () => {
62+
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);
63+
64+
const tradingRewardAggregation:
65+
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.findById(
66+
defaultTradingRewardAggregationId,
67+
);
68+
69+
expect(tradingRewardAggregation).toEqual(
70+
expect.objectContaining(defaultTradingRewardAggregation),
71+
);
72+
});
73+
74+
it('Successfully returns undefined when updating a nonexistent TradingRewardAggregation', async () => {
75+
const fakeUpdate:
76+
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.update({
77+
id: defaultSubaccountId,
78+
});
79+
expect(fakeUpdate).toBeUndefined();
80+
});
81+
82+
it('Successfully updates an existing TradingRewardAggregation', async () => {
83+
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);
84+
85+
const amount: string = '100000.00';
86+
const endedAt: string = '2021-01-01T00:00:00.000Z';
87+
const endedAtHeight: string = '1000';
88+
const update:
89+
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.update({
90+
id: defaultTradingRewardAggregationId,
91+
endedAt,
92+
endedAtHeight,
93+
amount,
94+
});
95+
expect(update).toEqual({
96+
...defaultTradingRewardAggregation,
97+
id: defaultTradingRewardAggregationId,
98+
endedAt,
99+
endedAtHeight,
100+
amount,
101+
});
102+
});
103+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as Knex from 'knex';
2+
3+
export async function up(knex: Knex): Promise<void> {
4+
return knex
5+
.schema
6+
.createTable('trading_reward_aggregations', (table) => {
7+
table.uuid('id').primary();
8+
table.string('address').notNullable();
9+
table.timestamp('startedAt').notNullable();
10+
table.bigInteger('startedAtHeight').notNullable();
11+
table.timestamp('endedAt').nullable();
12+
table.bigInteger('endedAtHeight').nullable();
13+
table.enum(
14+
'period',
15+
[
16+
'DAILY',
17+
'WEEKLY',
18+
'MONTHLY',
19+
],
20+
).notNullable();
21+
table.decimal('amount').notNullable();
22+
23+
// Foreign
24+
table.foreign('address').references('wallets.address');
25+
table.foreign('startedAtHeight').references('blocks.blockHeight');
26+
27+
// Indices
28+
table.index(['address', 'startedAtHeight']);
29+
table.index(['period', 'startedAtHeight']);
30+
});
31+
}
32+
33+
export async function down(knex: Knex): Promise<void> {
34+
return knex.schema.dropTableIfExists('trading_reward_aggregations');
35+
}

indexer/packages/postgres/src/helpers/db-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const layer1Tables = [
2424
'wallets',
2525
'compliance_data',
2626
'trading_rewards',
27+
'trading_reward_aggregations',
2728
];
2829

2930
/**

indexer/packages/postgres/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export * as FundingIndexUpdatesTable from './stores/funding-index-updates-table'
3333
export * as LiquidityTiersTable from './stores/liquidity-tiers-table';
3434
export * as WalletTable from './stores/wallet-table';
3535
export * as ComplianceTable from './stores/compliance-table';
36+
export * as TradingRewardTable from './stores/trading-reward-table';
37+
export * as TradingRewardAggregationTable from './stores/trading-reward-aggregation-table';
3638

3739
export * as perpetualMarketRefresher from './loops/perpetual-market-refresher';
3840
export * as assetRefresher from './loops/asset-refresher';

indexer/packages/postgres/src/models/block-model.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ export default class BlockModel extends Model {
4747
to: 'oracle_prices.effectiveAtHeight',
4848
},
4949
},
50+
tradingRewardAggregations: {
51+
relation: Model.HasManyRelation,
52+
modelClass: path.join(__dirname, 'trading-reward-aggregation-model'),
53+
join: {
54+
from: 'blocks.blockHeight',
55+
to: 'trading_reward_aggregations.startedAtHeight',
56+
},
57+
},
5058
};
5159

5260
static get jsonSchema() {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import path from 'path';
2+
3+
import { Model } from 'objection';
4+
5+
import { IntegerPattern, NonNegativeNumericPattern } from '../lib/validators';
6+
import UpsertQueryBuilder from '../query-builders/upsert';
7+
import { IsoString } from '../types';
8+
import { TradingRewardAggregationPeriod } from '../types/trading-reward-aggregation-types';
9+
10+
export default class TradingRewardAggregationModel extends Model {
11+
static get tableName() {
12+
return 'trading_reward_aggregations';
13+
}
14+
15+
static get idColumn() {
16+
return 'id';
17+
}
18+
19+
static relationMappings = {
20+
wallets: {
21+
relation: Model.BelongsToOneRelation,
22+
modelClass: path.join(__dirname, 'wallet-model'),
23+
join: {
24+
from: 'trading_reward_aggregations.address',
25+
to: 'wallets.address',
26+
},
27+
},
28+
blocks: {
29+
relation: Model.BelongsToOneRelation,
30+
modelClass: path.join(__dirname, 'block-model'),
31+
join: {
32+
from: 'trading_reward_aggregations.startedAtHeight',
33+
to: 'blocks.height',
34+
},
35+
},
36+
};
37+
38+
static get jsonSchema() {
39+
return {
40+
type: 'object',
41+
required: [
42+
'id', // Generated from `address` and `startedAt` and `period`
43+
'address',
44+
'startedAt',
45+
'startedAtHeight',
46+
'period',
47+
'amount', // amount of token rewards earned by address in the period starting with startedAt
48+
],
49+
properties: {
50+
id: { type: 'string', format: 'uuid' },
51+
address: { type: 'string' },
52+
startedAt: { type: 'string', format: 'date-time' }, // Inclusive
53+
startedAtHeight: { type: 'string', pattern: IntegerPattern }, // Inclusive
54+
endedAt: { type: ['string', 'null'], format: 'date-time' }, // Inclusive
55+
endedAtHeight: { type: ['string', 'null'], pattern: IntegerPattern }, // Inclusive
56+
period: { type: 'string', enum: [...Object.values(TradingRewardAggregationPeriod)] },
57+
amount: { type: 'string', pattern: NonNegativeNumericPattern },
58+
},
59+
};
60+
}
61+
62+
/**
63+
* A mapping from column name to JSON conversion expected.
64+
* See getSqlConversionForDydxModelTypes for valid conversions.
65+
*
66+
* TODO(IND-239): Ensure that jsonSchema() / sqlToJsonConversions() / model fields match.
67+
*/
68+
static get sqlToJsonConversions() {
69+
return {
70+
id: 'string',
71+
address: 'string',
72+
startedAt: 'date-time',
73+
startedAtHeight: 'string',
74+
endedAt: 'date-time',
75+
endedAtHeight: 'string',
76+
period: 'string',
77+
amount: 'string',
78+
};
79+
}
80+
81+
QueryBuilderType!: UpsertQueryBuilder<this>;
82+
83+
id!: string;
84+
85+
address!: string;
86+
87+
startedAt!: IsoString;
88+
89+
startedAtHeight!: string;
90+
91+
endedAt!: IsoString;
92+
93+
endedAtHeight!: string;
94+
95+
period!: TradingRewardAggregationPeriod;
96+
97+
amount!: string;
98+
}

indexer/packages/postgres/src/models/trading-reward-model.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Model } from 'objection';
44

55
import { IntegerPattern, NonNegativeNumericPattern } from '../lib/validators';
66
import UpsertQueryBuilder from '../query-builders/upsert';
7+
import { IsoString } from '../types';
78

89
export default class TradingRewardModel extends Model {
910
static get tableName() {
@@ -67,7 +68,7 @@ export default class TradingRewardModel extends Model {
6768

6869
address!: string;
6970

70-
blockTime!: string;
71+
blockTime!: IsoString;
7172

7273
blockHeight!: string;
7374

indexer/packages/postgres/src/models/wallet-model.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import path from 'path';
2+
3+
import { Model } from 'objection';
4+
15
import { NonNegativeNumericPattern } from '../lib/validators';
26
import UpsertQueryBuilder from '../query-builders/upsert';
37
import BaseModel from './base-model';
@@ -11,7 +15,24 @@ export default class WalletModel extends BaseModel {
1115
return 'address';
1216
}
1317

14-
static relationMappings = {};
18+
static relationMappings = {
19+
tradingRewardAggregations: {
20+
relation: Model.HasManyRelation,
21+
modelClass: path.join(__dirname, 'trading-reward-aggregation-model'),
22+
join: {
23+
from: 'wallets.address',
24+
to: 'trading_reward_aggregations.address',
25+
},
26+
},
27+
tradingRewards: {
28+
relation: Model.HasManyRelation,
29+
modelClass: path.join(__dirname, 'trading-reward-model'),
30+
join: {
31+
from: 'wallets.address',
32+
to: 'trading_rewards.address',
33+
},
34+
},
35+
};
1536

1637
static get jsonSchema() {
1738
return {

0 commit comments

Comments
 (0)