Skip to content

Commit

Permalink
[IND-494]: Create trading_rewards postgres table
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher-Li committed Nov 29, 2023
1 parent b274f0a commit d41d94a
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 3 deletions.
10 changes: 10 additions & 0 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
SubaccountCreateObject,
TendermintEventCreateObject,
TimeInForce,
TradingRewardCreateObject,
TransactionCreateObject,
TransferCreateObject,
WalletCreateObject,
Expand Down Expand Up @@ -581,3 +582,12 @@ export const nonBlockedComplianceData: ComplianceDataCreateObject = {
riskScore: '10.00',
updatedAt: createdDateTime.plus(1).toISO(),
};

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

export const defaultTradingReward: TradingRewardCreateObject = {
address: defaultAddress,
blockHeight: createdHeight,
blockTime: createdDateTime.toISO(),
amount: '1.00',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { TradingRewardFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import { defaultTradingReward } from '../helpers/constants';
import * as TradingRewardTable from '../../src/stores/trading-reward-table';

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

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

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

it('Successfully creates a TradingReward', async () => {
await TradingRewardTable.create(defaultTradingReward);
});

it('Successfully finds all TradingRewards', async () => {
await Promise.all([
TradingRewardTable.create(defaultTradingReward),
TradingRewardTable.create({
...defaultTradingReward,
blockHeight: '20',
}),
]);

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

expect(tradingRewards.length).toEqual(2);
expect(tradingRewards[0]).toEqual(expect.objectContaining(defaultTradingReward));
expect(tradingRewards[1]).toEqual(expect.objectContaining({
...defaultTradingReward,
blockHeight: '20',
}));
});

it('Successfully finds a TradingReward', async () => {
await TradingRewardTable.create(defaultTradingReward);

const tradingReward: TradingRewardFromDatabase | undefined = await TradingRewardTable.findById(
TradingRewardTable.uuid(defaultTradingReward.address, defaultTradingReward.blockHeight),
);

expect(tradingReward).toEqual(expect.objectContaining(defaultTradingReward));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex
.schema
.createTable('trading_rewards', (table) => {
table.uuid('id').primary();
table.string('address').notNullable();
table.timestamp('blockTime').notNullable();
table.bigInteger('blockHeight').notNullable();
table.decimal('amount').notNullable();

// Foreign
table.foreign('address').references('wallet.address');

// Indices
table.index(['address']);
table.index(['blockTime']);
table.index(['blockHeight']);
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.dropTableIfExists('trading_rewards');
}
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 @@ -23,6 +23,7 @@ const layer1Tables = [
'liquidity_tiers',
'wallets',
'compliance_data',
'trading_rewards',
];

/**
Expand Down
75 changes: 75 additions & 0 deletions indexer/packages/postgres/src/models/trading-reward-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import path from 'path';

import { Model } from 'objection';

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

export default class TradingRewardModel extends Model {
static get tableName() {
return 'trading_rewards';
}

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

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

static get jsonSchema() {
return {
type: 'object',
required: [
'id',
'address',
'blockTime',
'blockHeight',
'amount',
],
properties: {
id: { type: 'string', format: 'uuid' },
address: { type: 'string' },
blockTime: { type: 'string', format: 'date-time' },
blockHeight: { type: 'string', pattern: IntegerPattern },
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',
blockTime: 'date-time',
blockHeight: 'string',
amount: 'string',
};
}

QueryBuilderType!: UpsertQueryBuilder<this>;

id!: string;

address!: string;

blockTime!: string;

blockHeight!: string;

amount!: string;
}
35 changes: 32 additions & 3 deletions indexer/packages/postgres/src/models/wallet-model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { NumericPattern } from '../lib/validators';
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,32 @@ export default class WalletModel extends BaseModel {
return 'address';
}

static relationMappings = {};
static relationMappings = {
transferRecipientWallet: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'transfer-model'),
join: {
from: 'wallets.address',
to: 'transfers.recipientWalletAddress',
},
},
transferSenderWallet: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'transfer-model'),
join: {
from: 'wallets.address',
to: 'transfers.senderWalletAddress',
},
},
tradingRewards: {
relation: Model.HasManyRelation,
modelClass: path.join(__dirname, 'trading-reward-model'),
join: {
from: 'wallets.address',
to: 'trading_rewards.address',
},
},
};

static get jsonSchema() {
return {
Expand All @@ -22,7 +51,7 @@ export default class WalletModel extends BaseModel {
],
properties: {
address: { type: 'string' },
totalTradingRewards: { type: 'string', pattern: NumericPattern },
totalTradingRewards: { type: 'string', pattern: NonNegativeNumericPattern },
},
};
}
Expand Down
105 changes: 105 additions & 0 deletions indexer/packages/postgres/src/stores/trading-reward-table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { QueryBuilder } from 'objection';

import { BUFFER_ENCODING_UTF_8, DEFAULT_POSTGRES_OPTIONS } from '../constants';
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers';
import Transaction from '../helpers/transaction';
import { getUuid } from '../helpers/uuid';
import TradingRewardModel from '../models/trading-reward-model';
import {
Options,
Ordering,
QueryableField,
QueryConfig,
TradingRewardColumns,
TradingRewardCreateObject,
TradingRewardFromDatabase,
TradingRewardQueryConfig,
} from '../types';

export function uuid(address: string, blockHeight: string): string {
// TODO(IND-483): Fix all uuid string substitutions to use Array.join.
return getUuid(Buffer.from(`${address}-${blockHeight}`, BUFFER_ENCODING_UTF_8));
}

export async function findAll(
{
address,
blockHeight,
blockTimeBeforeOrAt,
limit,
}: TradingRewardQueryConfig,
requiredFields: QueryableField[],
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<TradingRewardFromDatabase[]> {
verifyAllRequiredFields(
{
address,
blockHeight,
blockTimeBeforeOrAt,
limit,
} as QueryConfig,
requiredFields,
);

let baseQuery: QueryBuilder<TradingRewardModel> = setupBaseQuery<TradingRewardModel>(
TradingRewardModel,
options,
);

if (address) {
baseQuery = baseQuery.where(TradingRewardColumns.address, address);
}

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

if (blockTimeBeforeOrAt) {
baseQuery = baseQuery.where(TradingRewardColumns.blockTime, '<=', blockTimeBeforeOrAt);
}

if (options.orderBy !== undefined) {
for (const [column, order] of options.orderBy) {
baseQuery = baseQuery.orderBy(
column,
order,
);
}
} else {
baseQuery = baseQuery.orderBy(
TradingRewardColumns.blockHeight,
Ordering.ASC,
);
}

if (limit) {
baseQuery = baseQuery.limit(limit);
}

return baseQuery.returning('*');
}

export async function create(
tradingRewardToCreate: TradingRewardCreateObject,
options: Options = { txId: undefined },
): Promise<TradingRewardFromDatabase> {
return TradingRewardModel.query(
Transaction.get(options.txId),
).insert({
id: uuid(tradingRewardToCreate.address, tradingRewardToCreate.blockHeight),
...tradingRewardToCreate,
}).returning('*');
}

export async function findById(
address: string,
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<TradingRewardFromDatabase | undefined> {
const baseQuery: QueryBuilder<TradingRewardModel> = setupBaseQuery<TradingRewardModel>(
TradingRewardModel,
options,
);
return baseQuery
.findById(address)
.returning('*');
}
8 changes: 8 additions & 0 deletions indexer/packages/postgres/src/types/db-model-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ export interface ComplianceDataFromDatabase {
updatedAt: string;
}

export interface TradingRewardFromDatabase {
id: string;
address: string;
blockTime: IsoString;
blockHeight: string;
amount: string;
}

export type SubaccountAssetNetTransferMap = { [subaccountId: string]:
{ [assetId: string]: string } };
export type SubaccountToPerpetualPositionsMap = { [subaccountId: string]:
Expand Down
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export * from './funding-index-updates-types';
export * from './liquidity-tiers-types';
export * from './wallet-types';
export * from './compliance-data-types';
export * from './trading-reward-types';
export { PositionSide } from './position-types';
7 changes: 7 additions & 0 deletions indexer/packages/postgres/src/types/query-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export enum QueryableField {
UPDATED_ON_OR_AFTER = 'updatedOnOrAfter',
PROVIDER = 'provider',
BLOCKED = 'blocked',
BLOCK_TIME_BEFORE_OR_AT = 'blockTimeBeforeOrAt',
}

export interface QueryConfig {
Expand Down Expand Up @@ -263,3 +264,9 @@ export interface ComplianceDataQueryConfig extends QueryConfig {
[QueryableField.PROVIDER]?: string;
[QueryableField.BLOCKED]?: boolean;
}

export interface TradingRewardQueryConfig extends QueryConfig {
[QueryableField.ADDRESS]?: string;
[QueryableField.BLOCK_HEIGHT]?: string;
[QueryableField.BLOCK_TIME_BEFORE_OR_AT]?: IsoString;
}
Loading

0 comments on commit d41d94a

Please sign in to comment.