Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OTE-762] Affiliates comlink affiliate info #2269

Merged
merged 14 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const blockedAddress: string = 'dydx1f9k5qldwmqrnwy8hcgp4fw6heuvszt35egvt
export const vaultAddress: string = 'dydx1c0m5x87llaunl5sgv3q5vd7j5uha26d2q2r2q0';

// ============== Subaccounts ==============
export const defaultWalletAddress: string = 'defaultWalletAddress';

export const defaultSubaccount: SubaccountCreateObject = {
address: defaultAddress,
Expand All @@ -99,6 +100,14 @@ export const defaultSubaccount3: SubaccountCreateObject = {
updatedAtHeight: createdHeight,
};

// defaultWalletAddress belongs to defaultWallet2 and is different from defaultAddress
export const defaultSubaccountDefaultWalletAddress: SubaccountCreateObject = {
address: defaultWalletAddress,
subaccountNumber: 0,
updatedAt: createdDateTime.toISO(),
updatedAtHeight: createdHeight,
};

export const defaultSubaccountWithAlternateAddress: SubaccountCreateObject = {
address: defaultAddress2,
subaccountNumber: 0,
Expand Down Expand Up @@ -127,8 +136,6 @@ export const isolatedSubaccount2: SubaccountCreateObject = {
updatedAtHeight: createdHeight,
};

export const defaultWalletAddress: string = 'defaultWalletAddress';

export const defaultSubaccountId: string = SubaccountTable.uuid(
defaultAddress,
defaultSubaccount.subaccountNumber,
Expand All @@ -141,6 +148,10 @@ export const defaultSubaccountId3: string = SubaccountTable.uuid(
defaultAddress,
defaultSubaccount3.subaccountNumber,
);
export const defaultSubaccountIdDefaultWalletAddress: string = SubaccountTable.uuid(
defaultWalletAddress,
defaultSubaccountDefaultWalletAddress.subaccountNumber,
);
export const defaultSubaccountIdWithAlternateAddress: string = SubaccountTable.uuid(
defaultAddress2,
defaultSubaccountWithAlternateAddress.subaccountNumber,
Expand Down Expand Up @@ -908,6 +919,17 @@ export const duplicatedSubaccountUsername: SubaccountUsernamesCreateObject = {
subaccountId: defaultSubaccountId3,
};

// defaultWalletAddress belongs to defaultWallet2 and is different from defaultAddress
export const subaccountUsernameWithDefaultWalletAddress: SubaccountUsernamesCreateObject = {
username: 'EvilRaisin11',
subaccountId: defaultSubaccountIdDefaultWalletAddress,
};

export const subaccountUsernameWithAlternativeAddress: SubaccountUsernamesCreateObject = {
username: 'HonestRaisin32',
subaccountId: defaultSubaccountIdWithAlternateAddress,
};

// ============== Leaderboard pnl Data ==============

export const defaultLeaderboardPnlOneDay: LeaderboardPnlCreateObject = {
Expand Down Expand Up @@ -965,24 +987,38 @@ export const defaultKV2: PersistentCacheCreateObject = {

export const defaultAffiliateInfo: AffiliateInfoCreateObject = {
address: defaultAddress,
affiliateEarnings: '10.00',
affiliateEarnings: '10',
referredMakerTrades: 10,
referredTakerTrades: 20,
totalReferredFees: '10.00',
totalReferredFees: '10',
totalReferredUsers: 5,
referredNetProtocolEarnings: '20.00',
referredNetProtocolEarnings: '20',
firstReferralBlockHeight: '1',
referredTotalVolume: '1000',
};

export const defaultAffiliateInfo1: AffiliateInfoCreateObject = {
address: defaultAddress2,
affiliateEarnings: '11.00',
export const defaultAffiliateInfo2: AffiliateInfoCreateObject = {
address: defaultWalletAddress,
affiliateEarnings: '11',
referredMakerTrades: 11,
referredTakerTrades: 21,
totalReferredFees: '11.00',
totalReferredFees: '11',
totalReferredUsers: 5,
referredNetProtocolEarnings: '21.00',
referredNetProtocolEarnings: '21',
firstReferralBlockHeight: '11',
referredTotalVolume: '1000',
};

export const defaultAffiliateInfo3: AffiliateInfoCreateObject = {
address: defaultAddress2,
affiliateEarnings: '12',
referredMakerTrades: 12,
referredTakerTrades: 22,
totalReferredFees: '12',
totalReferredUsers: 10,
referredNetProtocolEarnings: '22',
firstReferralBlockHeight: '12',
referredTotalVolume: '1111111',
};

// ============== Tokens =============
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AffiliateInfoFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import { defaultAffiliateInfo, defaultAffiliateInfo1 } from '../helpers/constants';
import { defaultAffiliateInfo, defaultAffiliateInfo2 } from '../helpers/constants';
import * as AffiliateInfoTable from '../../src/stores/affiliate-info-table';

describe('Affiliate info store', () => {
Expand All @@ -27,20 +27,20 @@ describe('Affiliate info store', () => {

it('Can upsert affiliate info multiple times', async () => {
await AffiliateInfoTable.upsert(defaultAffiliateInfo);
let info: AffiliateInfoFromDatabase | undefined = await AffiliateInfoTable.findById(
let info: AffiliateInfoFromDatabase = await AffiliateInfoTable.findById(
defaultAffiliateInfo.address,
);
expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo));

await AffiliateInfoTable.upsert(defaultAffiliateInfo1);
info = await AffiliateInfoTable.findById(defaultAffiliateInfo1.address);
expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo1));
await AffiliateInfoTable.upsert(defaultAffiliateInfo2);
info = await AffiliateInfoTable.findById(defaultAffiliateInfo2.address);
expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo2));
});

it('Successfully finds all affiliate infos', async () => {
await Promise.all([
AffiliateInfoTable.create(defaultAffiliateInfo),
AffiliateInfoTable.create(defaultAffiliateInfo1),
AffiliateInfoTable.create(defaultAffiliateInfo2),
]);

const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable.findAll(
Expand All @@ -52,17 +52,126 @@ describe('Affiliate info store', () => {
expect(infos.length).toEqual(2);
expect(infos).toEqual(expect.arrayContaining([
expect.objectContaining(defaultAffiliateInfo),
expect.objectContaining(defaultAffiliateInfo1),
expect.objectContaining(defaultAffiliateInfo2),
]));
});

it('Successfully finds an affiliate info', async () => {
await AffiliateInfoTable.create(defaultAffiliateInfo);

const info: AffiliateInfoFromDatabase | undefined = await AffiliateInfoTable.findById(
const info: AffiliateInfoFromDatabase = await AffiliateInfoTable.findById(
defaultAffiliateInfo.address,
);

expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo));
});

describe('paginatedFindWithAddressFilter', () => {
beforeEach(async () => {
await migrate();
await Promise.all(
Array.from({ length: 10 }, (_, i) => AffiliateInfoTable.create({
...defaultAffiliateInfo,
address: `address_${i}`,
affiliateEarnings: i.toString(),
}),
),
);
});

it('Successfully filters by address', async () => {
const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable
.paginatedFindWithAddressFilter(
['address_0'],
0,
10,
false,
);
expect(infos).toBeDefined();
expect(infos!.length).toEqual(1);
expect(infos![0]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_0',
affiliateEarnings: '0',
}));
});

it('Successfully sorts by affiliate earning', async () => {
const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable
.paginatedFindWithAddressFilter(
[],
0,
10,
true,
);
expect(infos).toBeDefined();
expect(infos!.length).toEqual(10);
expect(infos![0]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_9',
affiliateEarnings: '9',
}));
expect(infos![9]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_0',
affiliateEarnings: '0',
}));
});

it('Successfully uses offset and limit', async () => {
const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable
.paginatedFindWithAddressFilter(
[],
5,
2,
false,
);
expect(infos).toBeDefined();
expect(infos!.length).toEqual(2);
expect(infos![0]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_5',
affiliateEarnings: '5',
}));
expect(infos![1]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_6',
affiliateEarnings: '6',
}));
});

it('Successfully filters, sorts, offsets, and limits', async () => {
const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable
.paginatedFindWithAddressFilter(
[],
3,
2,
true,
);
expect(infos).toBeDefined();
expect(infos!.length).toEqual(2);
expect(infos![0]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_6',
affiliateEarnings: '6',
}));
expect(infos![1]).toEqual(expect.objectContaining({
...defaultAffiliateInfo,
address: 'address_5',
affiliateEarnings: '5',
}));
});

it('Returns empty array if no results', async () => {
const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable
.paginatedFindWithAddressFilter(
['address_11'],
0,
10,
false,
);
expect(infos).toBeDefined();
expect(infos!.length).toEqual(0);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { SubaccountFromDatabase, SubaccountUsernamesFromDatabase, SubaccountsWithoutUsernamesResult } from '../../src/types';
import * as SubaccountUsernamesTable from '../../src/stores/subaccount-usernames-table';
import * as WalletTable from '../../src/stores/wallet-table';
import * as SubaccountsTable from '../../src/stores/subaccount-table';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import {
defaultSubaccountUsername,
defaultSubaccountUsername2,
defaultSubaccountWithAlternateAddress,
defaultWallet,
defaultWallet2,
duplicatedSubaccountUsername,
subaccountUsernameWithAlternativeAddress,
} from '../helpers/constants';
import { seedData } from '../helpers/mock-generators';

Expand Down Expand Up @@ -80,4 +85,24 @@ describe('SubaccountUsernames store', () => {
SubaccountUsernamesTable.getSubaccountsWithoutUsernames();
expect(subaccountIds.length).toEqual(subaccountLength - 1);
});

it('Get username using address', async () => {
await Promise.all([
// Add two usernames for defaultWallet
SubaccountUsernamesTable.create(defaultSubaccountUsername),
SubaccountUsernamesTable.create(defaultSubaccountUsername2),
// Add one username for alternativeWallet
WalletTable.create(defaultWallet2),
SubaccountsTable.create(defaultSubaccountWithAlternateAddress),
SubaccountUsernamesTable.create(subaccountUsernameWithAlternativeAddress),
]);

// Should only get username for defaultWallet's subaccount 0
const usernames = await SubaccountUsernamesTable.findByAddress([defaultWallet.address]);
expect(usernames.length).toEqual(1);
expect(usernames[0]).toEqual(expect.objectContaining({
address: defaultWallet.address,
username: defaultSubaccountUsername.username,
}));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex.schema.alterTable('affiliate_info', (table) => {
// null indicates variable precision whereas not specifying will result in 8,2 precision,scale
table.decimal('affiliateEarnings', null).alter();
table.decimal('totalReferredFees', null).alter();
table.decimal('referredNetProtocolEarnings', null).alter();
Comment on lines +6 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does variable precision mean? Thinking about this more, we probably want scale of 6 for all fees, thats the smallest unit of USDC

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For variable precision it will not append any 0s after the decimal. Eg. 2 stays 2 and 2.123 stays 2.123. Variable precision allows for up to the maximum postgres limit.

If you set the precision to 6. It will pad 0s so 2 -> 2.000000.

We can do it either way, the main change will be that our unit tests use the string conversion so we have to change those values. I prefer to stick with variable, as that is convention other columns in our tables use.


table.decimal('referredTotalVolume', null).notNullable();
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.alterTable('affiliate_info', (table) => {
table.decimal('affiliateEarnings').alter();
table.decimal('totalReferredFees').alter();
table.decimal('referredNetProtocolEarnings').alter();

table.dropColumn('referredTotalVolume');
});
}
2 changes: 2 additions & 0 deletions indexer/packages/postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { default as SubaccountUsernamesModel } from './models/subaccount-usernam
export { default as LeaderboardPnlModel } from './models/leaderboard-pnl-model';
export { default as PersistentCacheModel } from './models/persistent-cache-model';
export { default as AffiliateReferredUsersModel } from './models/affiliate-referred-users-model';
export { default as AffiliateInfoModel } from './models/affiliate-info-model';

export * as AssetTable from './stores/asset-table';
export * as AssetPositionTable from './stores/asset-position-table';
Expand Down Expand Up @@ -48,6 +49,7 @@ export * as SubaccountUsernamesTable from './stores/subaccount-usernames-table';
export * as PersistentCacheTable from './stores/persistent-cache-table';
export * as AffiliateReferredUsersTable from './stores/affiliate-referred-users-table';
export * as FirebaseNotificationTokenTable from './stores/firebase-notification-token-table';
export * as AffiliateInfoTable from './stores/affiliate-info-table';
export * as VaultTable from './stores/vault-table';

export * as perpetualMarketRefresher from './loops/perpetual-market-refresher';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class AffiliateInfoModel extends BaseModel {
'totalReferredUsers',
'referredNetProtocolEarnings',
'firstReferralBlockHeight',
'referredTotalVolume',
],
properties: {
address: { type: 'string' },
Expand All @@ -33,6 +34,7 @@ export default class AffiliateInfoModel extends BaseModel {
totalReferredUsers: { type: 'int' },
referredNetProtocolEarnings: { type: 'string', pattern: NonNegativeNumericPattern },
firstReferralBlockHeight: { type: 'string', pattern: NonNegativeNumericPattern },
referredTotalVolume: { type: 'string', pattern: NonNegativeNumericPattern },
},
};
}
Expand All @@ -53,6 +55,7 @@ export default class AffiliateInfoModel extends BaseModel {
totalReferredUsers: 'int',
referredNetProtocolEarnings: 'string',
firstReferralBlockHeight: 'string',
referredTotalVolume: 'string',
};
}

Expand All @@ -73,4 +76,6 @@ export default class AffiliateInfoModel extends BaseModel {
referredNetProtocolEarnings!: string;

firstReferralBlockHeight!: string;

referredTotalVolume!: string;
}
Loading
Loading