Skip to content

Commit

Permalink
[IND-496]: Create trading_reward_aggregations postgres table (#825)
Browse files Browse the repository at this point in the history
* [IND-496]: Create trading_reward_aggregations postgres table

* rabbit recommendations

* nit
  • Loading branch information
Christopher-Li authored Nov 30, 2023
1 parent 207b903 commit b5d4e8a
Show file tree
Hide file tree
Showing 15 changed files with 484 additions and 2 deletions.
18 changes: 18 additions & 0 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as OrderTable from '../../src/stores/order-table';
import * as PerpetualPositionTable from '../../src/stores/perpetual-position-table';
import * as SubaccountTable from '../../src/stores/subaccount-table';
import * as TendermintEventTable from '../../src/stores/tendermint-event-table';
import * as TradingRewardAggregationTable from '../../src/stores/trading-reward-aggregation-table';
import * as TransactionTable from '../../src/stores/transaction-table';
import * as TransferTable from '../../src/stores/transfer-table';
import {
Expand Down Expand Up @@ -43,6 +44,8 @@ import {
SubaccountCreateObject,
TendermintEventCreateObject,
TimeInForce,
TradingRewardAggregationCreateObject,
TradingRewardAggregationPeriod,
TradingRewardCreateObject,
TransactionCreateObject,
TransferCreateObject,
Expand Down Expand Up @@ -591,3 +594,18 @@ export const defaultTradingReward: TradingRewardCreateObject = {
blockTime: createdDateTime.toISO(),
amount: '1.00',
};

// ========= Trading Reward Aggregation Data ==========

export const defaultTradingRewardAggregation: TradingRewardAggregationCreateObject = {
address: defaultAddress,
startedAtHeight: createdHeight,
startedAt: createdDateTime.toISO(),
period: TradingRewardAggregationPeriod.DAILY,
amount: '1.00',
};
export const defaultTradingRewardAggregationId: string = TradingRewardAggregationTable.uuid(
defaultTradingRewardAggregation.address,
defaultTradingRewardAggregation.period,
defaultTradingRewardAggregation.startedAtHeight,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { TradingRewardAggregationFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import {
defaultSubaccountId,
defaultTradingRewardAggregation,
defaultTradingRewardAggregationId,
defaultWallet,
} from '../helpers/constants';
import * as TradingRewardAggregationTable from '../../src/stores/trading-reward-aggregation-table';
import { WalletTable } from '../../src';
import { seedData } from '../helpers/mock-generators';

describe('TradingRewardAggregation store', () => {
beforeAll(async () => {
await migrate();
});

beforeEach(async () => {
await seedData();
await WalletTable.create(defaultWallet);
});

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

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

it('Successfully creates a TradingRewardAggregation', async () => {
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);
});

it('Successfully finds all TradingRewardAggregations', async () => {
await Promise.all([
TradingRewardAggregationTable.create(defaultTradingRewardAggregation),
TradingRewardAggregationTable.create({
...defaultTradingRewardAggregation,
startedAtHeight: '1',
}),
]);

const tradingRewardAggregations:
TradingRewardAggregationFromDatabase[] = await TradingRewardAggregationTable.findAll(
{},
[],
{ readReplica: true },
);

expect(tradingRewardAggregations.length).toEqual(2);
expect(tradingRewardAggregations[0]).toEqual(expect.objectContaining({
...defaultTradingRewardAggregation,
startedAtHeight: '1',
}));
expect(tradingRewardAggregations[1]).toEqual(
expect.objectContaining(defaultTradingRewardAggregation),
);
});

it('Successfully finds a TradingRewardAggregation', async () => {
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);

const tradingRewardAggregation:
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.findById(
defaultTradingRewardAggregationId,
);

expect(tradingRewardAggregation).toEqual(
expect.objectContaining(defaultTradingRewardAggregation),
);
});

it('Successfully returns undefined when updating a nonexistent TradingRewardAggregation', async () => {
const fakeUpdate:
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.update({
id: defaultSubaccountId,
});
expect(fakeUpdate).toBeUndefined();
});

it('Successfully updates an existing TradingRewardAggregation', async () => {
await TradingRewardAggregationTable.create(defaultTradingRewardAggregation);

const amount: string = '100000.00';
const endedAt: string = '2021-01-01T00:00:00.000Z';
const endedAtHeight: string = '1000';
const update:
TradingRewardAggregationFromDatabase | undefined = await TradingRewardAggregationTable.update({
id: defaultTradingRewardAggregationId,
endedAt,
endedAtHeight,
amount,
});
expect(update).toEqual({
...defaultTradingRewardAggregation,
id: defaultTradingRewardAggregationId,
endedAt,
endedAtHeight,
amount,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex
.schema
.createTable('trading_reward_aggregations', (table) => {
table.uuid('id').primary();
table.string('address').notNullable();
table.timestamp('startedAt').notNullable();
table.bigInteger('startedAtHeight').notNullable();
table.timestamp('endedAt').nullable();
table.bigInteger('endedAtHeight').nullable();
table.enum(
'period',
[
'DAILY',
'WEEKLY',
'MONTHLY',
],
).notNullable();
table.decimal('amount').notNullable();

// Foreign
table.foreign('address').references('wallets.address');
table.foreign('startedAtHeight').references('blocks.blockHeight');

// Indices
table.index(['address', 'startedAtHeight']);
table.index(['period', 'startedAtHeight']);
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.dropTableIfExists('trading_reward_aggregations');
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/helpers/db-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const layer1Tables = [
'wallets',
'compliance_data',
'trading_rewards',
'trading_reward_aggregations',
];

/**
Expand Down
2 changes: 2 additions & 0 deletions indexer/packages/postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export * as FundingIndexUpdatesTable from './stores/funding-index-updates-table'
export * as LiquidityTiersTable from './stores/liquidity-tiers-table';
export * as WalletTable from './stores/wallet-table';
export * as ComplianceTable from './stores/compliance-table';
export * as TradingRewardTable from './stores/trading-reward-table';
export * as TradingRewardAggregationTable from './stores/trading-reward-aggregation-table';

export * as perpetualMarketRefresher from './loops/perpetual-market-refresher';
export * as assetRefresher from './loops/asset-refresher';
Expand Down
8 changes: 8 additions & 0 deletions indexer/packages/postgres/src/models/block-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export default class BlockModel extends Model {
to: 'oracle_prices.effectiveAtHeight',
},
},
tradingRewardAggregations: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'trading-reward-aggregation-model'),
join: {
from: 'blocks.blockHeight',
to: 'trading_reward_aggregations.startedAtHeight',
},
},
};

static get jsonSchema() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import path from 'path';

import { Model } from 'objection';

import { IntegerPattern, NonNegativeNumericPattern } from '../lib/validators';
import UpsertQueryBuilder from '../query-builders/upsert';
import { IsoString } from '../types';
import { TradingRewardAggregationPeriod } from '../types/trading-reward-aggregation-types';

export default class TradingRewardAggregationModel extends Model {
static get tableName() {
return 'trading_reward_aggregations';
}

static get idColumn() {
return 'id';
}

static relationMappings = {
wallets: {
relation: Model.BelongsToOneRelation,
modelClass: path.join(__dirname, 'wallet-model'),
join: {
from: 'trading_reward_aggregations.address',
to: 'wallets.address',
},
},
blocks: {
relation: Model.BelongsToOneRelation,
modelClass: path.join(__dirname, 'block-model'),
join: {
from: 'trading_reward_aggregations.startedAtHeight',
to: 'blocks.height',
},
},
};

static get jsonSchema() {
return {
type: 'object',
required: [
'id', // Generated from `address` and `startedAt` and `period`
'address',
'startedAt',
'startedAtHeight',
'period',
'amount', // amount of token rewards earned by address in the period starting with startedAt
],
properties: {
id: { type: 'string', format: 'uuid' },
address: { type: 'string' },
startedAt: { type: 'string', format: 'date-time' }, // Inclusive
startedAtHeight: { type: 'string', pattern: IntegerPattern }, // Inclusive
endedAt: { type: ['string', 'null'], format: 'date-time' }, // Inclusive
endedAtHeight: { type: ['string', 'null'], pattern: IntegerPattern }, // Inclusive
period: { type: 'string', enum: [...Object.values(TradingRewardAggregationPeriod)] },
amount: { type: 'string', pattern: NonNegativeNumericPattern },
},
};
}

/**
* A mapping from column name to JSON conversion expected.
* See getSqlConversionForDydxModelTypes for valid conversions.
*
* TODO(IND-239): Ensure that jsonSchema() / sqlToJsonConversions() / model fields match.
*/
static get sqlToJsonConversions() {
return {
id: 'string',
address: 'string',
startedAt: 'date-time',
startedAtHeight: 'string',
endedAt: 'date-time',
endedAtHeight: 'string',
period: 'string',
amount: 'string',
};
}

QueryBuilderType!: UpsertQueryBuilder<this>;

id!: string;

address!: string;

startedAt!: IsoString;

startedAtHeight!: string;

endedAt!: IsoString;

endedAtHeight!: string;

period!: TradingRewardAggregationPeriod;

amount!: string;
}
3 changes: 2 additions & 1 deletion indexer/packages/postgres/src/models/trading-reward-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Model } from 'objection';

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

export default class TradingRewardModel extends Model {
static get tableName() {
Expand Down Expand Up @@ -67,7 +68,7 @@ export default class TradingRewardModel extends Model {

address!: string;

blockTime!: string;
blockTime!: IsoString;

blockHeight!: string;

Expand Down
23 changes: 22 additions & 1 deletion indexer/packages/postgres/src/models/wallet-model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import path from 'path';

import { Model } from 'objection';

import { NonNegativeNumericPattern } from '../lib/validators';
import UpsertQueryBuilder from '../query-builders/upsert';
import BaseModel from './base-model';
Expand All @@ -11,7 +15,24 @@ export default class WalletModel extends BaseModel {
return 'address';
}

static relationMappings = {};
static relationMappings = {
tradingRewardAggregations: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'trading-reward-aggregation-model'),
join: {
from: 'wallets.address',
to: 'trading_reward_aggregations.address',
},
},
tradingRewards: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'trading-reward-model'),
join: {
from: 'wallets.address',
to: 'trading_rewards.address',
},
},
};

static get jsonSchema() {
return {
Expand Down
Loading

0 comments on commit b5d4e8a

Please sign in to comment.