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

feat(affiliates): Add table affiliate-referee-stats and implement table methods #2685

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

teddyding
Copy link
Contributor

@teddyding teddyding commented Jan 13, 2025

Changelist

  • Add table affiliate-referee-stats which records per referee stats (similar/parallel to existing table affilaite-info which aggregates per-affiliate
  • Implemented table methods

Test Plan

Unit tests

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

  • New Features

    • Added support for tracking and managing affiliate referee statistics
    • Introduced new database table and model for storing detailed affiliate referral metrics
    • Implemented functions to create, update, and retrieve affiliate statistics
  • Database Changes

    • Created new affiliate_referee_stats table with comprehensive tracking of referral performance
    • Added migration for setting up the new database schema
  • Technical Improvements

    • Enhanced data validation and type safety for affiliate statistics
    • Implemented pagination and querying capabilities for affiliate data

@teddyding teddyding marked this pull request as ready for review January 13, 2025 17:31
@teddyding teddyding requested a review from a team as a code owner January 13, 2025 17:31
Copy link
Contributor

coderabbitai bot commented Jan 13, 2025

Walkthrough

This pull request introduces a comprehensive implementation for tracking and managing affiliate referee statistics in a PostgreSQL database. The changes span multiple files and include creating a new database migration, defining a model and types for affiliate statistics, implementing store functions for querying and updating these statistics, and adding test cases to validate the functionality. The implementation allows for detailed tracking of affiliate performance metrics such as earnings, trades, volumes, and fees.

Changes

File Change Summary
indexer/packages/postgres/__tests__/helpers/constants.ts Added new constants for affiliate statistics and a default address
indexer/packages/postgres/__tests__/stores/affiliate-referee-stats-table.test.ts Added comprehensive test suite for affiliate referee stats functionality
indexer/packages/postgres/src/db/migrations/20241126173317_affiliate_referee_stats.ts Created migration to add affiliate_referee_stats table
indexer/packages/postgres/src/helpers/db-helpers.ts Added affiliate_referee_stats to layer1Tables
indexer/packages/postgres/src/models/affiliate-referee-stats-model.ts Created AffiliateRefereeStatsModel with JSON schema and type definitions
indexer/packages/postgres/src/stores/affiliate-referee-stats-table.ts Implemented functions for querying, creating, and updating affiliate stats
indexer/packages/postgres/src/types/... Added new interfaces and types for affiliate referee statistics

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Store as AffiliateRefereeStatsStore
    participant DB as PostgreSQL Database

    App->>Store: updateStats(windowStartTs, windowEndTs)
    Store->>DB: Construct complex query with CTEs
    DB-->>Store: Aggregate affiliate statistics
    Store->>DB: Upsert aggregated stats
    DB-->>Store: Confirm update
    Store-->>App: Return updated stats
Loading

Possibly related PRs

Suggested labels

indexer

Suggested reviewers

  • vincentwschau

Poem

🐰 Hop, hop, hooray for stats so bright!
Affiliate data, now in clear sight
Tracking trades and fees with glee
A database dance of metrics free
CodeRabbit's magic, pure delight! 🎉

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

const transaction: Knex.Transaction | undefined = Transaction.get(txId);

const query = `
-- Get metadata for all affiliates
Copy link
Contributor Author

@teddyding teddyding Jan 13, 2025

Choose a reason for hiding this comment

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

Ran query on internal mainnet indexer DB and confirmed result is consistent with output of current affiliate-infotable.

Copy link
Contributor

Choose a reason for hiding this comment

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

How fast is the query?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (7)
indexer/packages/postgres/src/stores/affiliate-referee-stats-table.ts (1)

304-328: Consider Using Cursor-Based Pagination for Efficiency

The paginatedFindWithAffiliateAddress function uses offset-based pagination, which can become inefficient with large datasets. Consider implementing cursor-based pagination using affiliateEarnings and refereeAddress as cursors to improve performance.

Example implementation:

// Modify the function signature to include cursor parameters
export async function paginatedFindWithAffiliateAddress(
  affiliateAddress: string,
  afterCursor: string | null,
  limit: number,
  options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<AffiliateRefereeStatsFromDatabase[]> {
  let baseQuery = setupBaseQuery<AffiliateRefereeStatsModel>(
    AffiliateRefereeStatsModel,
    options,
  );

  baseQuery = baseQuery.where(AffiliateRefereeStatsColumns.affiliateAddress, affiliateAddress);

  if (afterCursor) {
    baseQuery = baseQuery.where('refereeAddress', '>', afterCursor);
  }

  baseQuery = baseQuery.orderBy('refereeAddress', Ordering.ASC).limit(limit);

  return baseQuery.returning('*');
}
indexer/packages/postgres/src/types/affiliate-referee-stats-types.ts (1)

1-27: Ensure Consistent Mapping Between Interface and Database Columns

The AffiliateRefereeStatsCreateObject interface uses camelCase for property names, while database columns are typically snake_case. Ensure that the model correctly maps these properties to the corresponding database columns to maintain consistency and prevent any mapping issues.

Example adjustment in the model:

static get columnNameMappers() {
  return snakeCaseMappers();
}
indexer/packages/postgres/src/models/affiliate-referee-stats-model.ts (2)

14-44: Consider adding field descriptions in the JSON schema.

While the schema is comprehensive with proper validation patterns, adding descriptions for each field would improve maintainability and documentation.

 properties: {
-  refereeAddress: { type: 'string' },
+  refereeAddress: { 
+    type: 'string',
+    description: 'The address of the referee user'
+  },
   // Add descriptions for other fields similarly
 }

33-41: Consider using constants for numeric patterns.

The validation patterns for numeric fields are repeated. Consider extracting them into named constants for better maintainability.

+ const EARNINGS_PATTERN = NonNegativeNumericPattern;
+ const FEES_PATTERN = NonNegativeNumericPattern;
+ const REBATES_PATTERN = NumericPattern;

 properties: {
   // ...
-  affiliateEarnings: { type: 'string', pattern: NonNegativeNumericPattern },
+  affiliateEarnings: { type: 'string', pattern: EARNINGS_PATTERN },
   // Apply similar changes to other numeric fields
 }
indexer/packages/postgres/src/types/db-model-types.ts (1)

286-298: Consider grouping related interfaces together.

The interface looks good with proper types. Consider moving it next to other affiliate-related interfaces (like AffiliateInfoFromDatabase) for better code organization.

Move this interface next to other affiliate-related interfaces around line 300.

indexer/packages/postgres/__tests__/stores/affiliate-referee-stats-table.test.ts (2)

229-229: Remove or implement the TODO comment.

The TODO comment suggests a potential improvement to the query functionality.

Would you like me to help implement the query improvement or create a GitHub issue to track this task?


304-403: Consider refactoring the test helper function.

The populateFillsAndReferrals function is quite long and handles multiple responsibilities. Consider breaking it down into smaller, focused helper functions for better maintainability.

Example refactor:

-async function populateFillsAndReferrals(): Promise<DateTime> {
+async function createReferrals(referredAtBlock: number): Promise<void> {
+  await AffiliateReferredUsersTable.create({
+    affiliateAddress: affiliateStatDefaultAddrReferredByAddr2.affiliateAddress,
+    refereeAddress: affiliateStatDefaultAddrReferredByAddr2.refereeAddress,
+    referredAtBlock: `${referredAtBlock}`,
+  });
+  // ... other referral creations
+}
+
+async function createFills(referenceDt: DateTime, referredAtBlock: number): Promise<void> {
+  await Promise.all([
+    createTakerFill(referenceDt, referredAtBlock),
+    createMakerFill(referenceDt, referredAtBlock),
+    // ... other fill creations
+  ]);
+}
+
+async function populateFillsAndReferrals(): Promise<DateTime> {
+  const referenceDt = DateTime.utc();
+  const referredAtBlock = 1;
+
+  await seedData();
+  await createReferrals(referredAtBlock);
+  await createFills(referenceDt, referredAtBlock);
+
+  return referenceDt;
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 949b46c and 21fc5bd.

📒 Files selected for processing (10)
  • indexer/packages/postgres/__tests__/helpers/constants.ts (3 hunks)
  • indexer/packages/postgres/__tests__/stores/affiliate-referee-stats-table.test.ts (1 hunks)
  • indexer/packages/postgres/src/db/migrations/migration_files/20241126173317_affiliate_referee_stats.ts (1 hunks)
  • indexer/packages/postgres/src/helpers/db-helpers.ts (1 hunks)
  • indexer/packages/postgres/src/models/affiliate-referee-stats-model.ts (1 hunks)
  • indexer/packages/postgres/src/stores/affiliate-referee-stats-table.ts (1 hunks)
  • indexer/packages/postgres/src/types/affiliate-referee-stats-types.ts (1 hunks)
  • indexer/packages/postgres/src/types/db-model-types.ts (1 hunks)
  • indexer/packages/postgres/src/types/index.ts (1 hunks)
  • indexer/packages/postgres/src/types/query-types.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Summary
🔇 Additional comments (10)
indexer/packages/postgres/src/stores/affiliate-referee-stats-table.ts (2)

78-87: Verify the Use of .upsert() Method

The use of .upsert() on line 83 may not be a standard method provided by Objection.js. Ensure that there is a custom implementation for AffiliateRefereeStatsModel or adjust the code to use supported methods like insert with onConflict().merge() to achieve upsert functionality.

Please confirm that the upsert method exists or modify the code to use an alternative approach. For example:

await AffiliateRefereeStatsModel.query(Transaction.get(options.txId))
  .insert(AffiliateRefereeStatsToUpsert)
  .onConflict('refereeAddress')
  .merge()
  .returning('*');

38-66: Validate orderBy Parameters to Prevent SQL Injection

In the findAll function, the options.orderBy values are directly used in the query builder. Ensure that the column names and order directions are validated against a whitelist of allowed values to prevent potential SQL injection attacks.

Consider implementing validation for options.orderBy:

const allowedColumns = Object.values(AffiliateRefereeStatsColumns);
const allowedOrderings = [Ordering.ASC, Ordering.DESC];

for (const [column, order] of options.orderBy) {
  if (!allowedColumns.includes(column) || !allowedOrderings.includes(order)) {
    throw new Error('Invalid orderBy parameters');
  }
  baseQuery = baseQuery.orderBy(column, order);
}
indexer/packages/postgres/src/types/index.ts (1)

34-34: LGTM!

The new export statement is correctly placed and maintains the alphabetical ordering of the exports.

indexer/packages/postgres/src/models/affiliate-referee-stats-model.ts (2)

5-12: LGTM!

The model configuration is correct with proper table name and primary key definition.


50-50: Address the TODO comment regarding schema validation.

The TODO comment (IND-239) indicates a need to ensure consistency between jsonSchema, sqlToJsonConversions, and model fields.

Run this script to verify the consistency:

✅ Verification successful

Schema validation is complete - all components are consistent

The TODO comment can be safely removed as the verification confirms perfect consistency between:

  • All field names and their order in jsonSchema(), sqlToJsonConversions(), and model fields
  • Type mappings across all three sections (string/int types properly aligned)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check consistency between schema, conversions, and model fields

# Extract field names from different sections
echo "Analyzing AffiliateRefereeStatsModel..."

# Get fields from jsonSchema
echo "Fields in jsonSchema:"
rg -A 30 'jsonSchema.*properties:' | rg '^\s+\w+:'

# Get fields from sqlToJsonConversions
echo "Fields in sqlToJsonConversions:"
rg -A 15 'sqlToJsonConversions.*return' | rg '^\s+\w+:'

# Get model property declarations
echo "Model property declarations:"
rg -A 30 'QueryBuilderType!' | rg '^\s+\w+!:'

Length of output: 440


Script:

#!/bin/bash
# View the complete file content to analyze schema consistency
cat "indexer/packages/postgres/src/models/affiliate-referee-stats-model.ts"

Length of output: 2772

indexer/packages/postgres/src/helpers/db-helpers.ts (1)

35-35: LGTM!

The addition of 'affiliate_referee_stats' to layer1Tables is correct and maintains consistency with the database schema.

indexer/packages/postgres/src/types/query-types.ts (1)

344-346: LGTM! The interface follows the established pattern.

The new AffiliateRefereeStatsQueryConfig interface is well-structured and consistent with other query configuration interfaces in the file.

indexer/packages/postgres/__tests__/stores/affiliate-referee-stats-table.test.ts (2)

1-38: LGTM! Test setup is well-structured.

The test file has proper lifecycle hooks for database setup and cleanup.


39-124: LGTM! Comprehensive CRUD test coverage.

The test cases cover essential scenarios:

  • Creation of stats
  • Duplicate prevention
  • Upsert functionality
  • Retrieval by ID and query parameters
indexer/packages/postgres/__tests__/helpers/constants.ts (1)

76-76: LGTM! Well-structured test constants.

The new constants are:

  • Well-organized and properly grouped
  • Include realistic test values
  • Cover different scenarios for affiliate stats

Also applies to: 1020-1060

Comment on lines +117 to +289
filtered_fills
INNER JOIN
affiliate_referred_subaccounts
ON
filtered_fills."subaccountId" = affiliate_referred_subaccounts."subaccountId"
WHERE
filtered_fills."createdAtHeight" >= affiliate_referred_subaccounts."referredAtBlock"
),

-- Step 2d: Aggregate stats per affiliate-referee tuple
affiliate_stats_per_referee AS (
SELECT
affiliate_fills."affiliateAddress",
affiliate_fills."refereeAddress",
SUM(affiliate_fills."fee") AS "totalReferredFees",
SUM(affiliate_fills."affiliateRevShare") AS "affiliateEarnings",
SUM(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.MAKER}' AND affiliate_fills."fee" > 0 THEN affiliate_fills."fee" ELSE 0 END) AS "referredMakerFees",
SUM(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.TAKER}' AND affiliate_fills."type" = '${FillType.LIMIT}' THEN affiliate_fills."fee" ELSE 0 END) AS "referredTakerFees",
SUM(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.MAKER}' AND affiliate_fills."fee" < 0 THEN affiliate_fills."fee" ELSE 0 END) AS "referredMakerRebates",
SUM(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.TAKER}' AND affiliate_fills."type" = '${FillType.LIQUIDATED}' THEN affiliate_fills."fee" ELSE 0 END) AS "referredLiquidationFees",
COUNT(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.MAKER}' THEN 1 END) AS "referredMakerTrades",
COUNT(CASE WHEN affiliate_fills."liquidity" = '${Liquidity.TAKER}' THEN 1 END) AS "referredTakerTrades",
SUM(affiliate_fills."price" * affiliate_fills."size") AS "referredTotalVolume"
FROM
affiliate_fills
GROUP BY
affiliate_fills."affiliateAddress",
affiliate_fills."refereeAddress"
),

-- Step 3a: Prepare data for updating or inserting into the affiliate_referee_stats table
-- Combine metadata with aggregated stats for each affiliate-referee pair
affiliate_referee_stats_update AS (
SELECT
affiliate_metadata_per_referee."affiliateAddress",
affiliate_metadata_per_referee."refereeAddress",
affiliate_metadata_per_referee."referralBlockHeight",
COALESCE(affiliate_stats_per_referee."affiliateEarnings", 0) AS "affiliateEarnings",
COALESCE(affiliate_stats_per_referee."referredMakerTrades", 0) AS "referredMakerTrades",
COALESCE(affiliate_stats_per_referee."referredTakerTrades", 0) AS "referredTakerTrades",
COALESCE(affiliate_stats_per_referee."referredMakerFees", 0) AS "referredMakerFees",
COALESCE(affiliate_stats_per_referee."referredTakerFees", 0) AS "referredTakerFees",
COALESCE(affiliate_stats_per_referee."referredLiquidationFees", 0) AS "referredLiquidationFees",
COALESCE(affiliate_stats_per_referee."referredMakerRebates", 0) AS "referredMakerRebates",
COALESCE(affiliate_stats_per_referee."referredTotalVolume", 0) AS "referredTotalVolume"
FROM
affiliate_metadata_per_referee
LEFT JOIN
affiliate_stats_per_referee
ON
affiliate_metadata_per_referee."affiliateAddress" = affiliate_stats_per_referee."affiliateAddress"
AND affiliate_metadata_per_referee."refereeAddress" = affiliate_stats_per_referee."refereeAddress"
)

-- Step 3b: Insert or update the affiliate_referee_stats table
-- Update existing rows with new data or insert new rows if they don't exist
INSERT INTO affiliate_referee_stats (
"affiliateAddress",
"refereeAddress",
"referralBlockHeight",
"affiliateEarnings",
"referredMakerTrades",
"referredTakerTrades",
"referredMakerFees",
"referredTakerFees",
"referredLiquidationFees",
"referredMakerRebates",
"referredTotalVolume"
)
SELECT
"affiliateAddress",
"refereeAddress",
"referralBlockHeight",
"affiliateEarnings",
"referredMakerTrades",
"referredTakerTrades",
"referredMakerFees",
"referredTakerFees",
"referredLiquidationFees",
"referredMakerRebates",
"referredTotalVolume"
FROM
affiliate_referee_stats_update
ON CONFLICT ("refereeAddress")
DO UPDATE SET
"referralBlockHeight" = EXCLUDED."referralBlockHeight",
"affiliateEarnings" = affiliate_referee_stats."affiliateEarnings" + EXCLUDED."affiliateEarnings",
"referredMakerTrades" = affiliate_referee_stats."referredMakerTrades" + EXCLUDED."referredMakerTrades",
"referredTakerTrades" = affiliate_referee_stats."referredTakerTrades" + EXCLUDED."referredTakerTrades",
"referredMakerFees" = affiliate_referee_stats."referredMakerFees" + EXCLUDED."referredMakerFees",
"referredTakerFees" = affiliate_referee_stats."referredTakerFees" + EXCLUDED."referredTakerFees",
"referredLiquidationFees" = affiliate_referee_stats."referredLiquidationFees" + EXCLUDED."referredLiquidationFees",
"referredMakerRebates" = affiliate_referee_stats."referredMakerRebates" + EXCLUDED."referredMakerRebates",
"referredTotalVolume" = affiliate_referee_stats."referredTotalVolume" + EXCLUDED."referredTotalVolume";
`;

return transaction
? knexPrimary.raw(query).transacting(transaction)
: knexPrimary.raw(query);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential SQL Injection Vulnerability in updateStats Function

The updateStats function constructs a raw SQL query by directly interpolating windowStartTs and windowEndTs into the query string. If these parameters are not properly sanitized, this can lead to SQL injection vulnerabilities. It's recommended to use parameterized queries to safely include these values.

Apply this diff to use parameterized queries:

-      fills."createdAt" > '${windowStartTs}'
-          AND fills."createdAt" <= '${windowEndTs}'
+      fills."createdAt" > ? AND fills."createdAt" <= ?

Then, pass the parameters securely when executing the query:

const query = `
  -- Your SQL query with placeholders for windowStartTs and windowEndTs
`;

const bindings = [windowStartTs, windowEndTs];

return transaction
  ? knexPrimary.raw(query, bindings).transacting(transaction)
  : knexPrimary.raw(query, bindings);

Comment on lines +5 to +6
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Reconsider the Primary Key Definition

Setting refereeAddress as the primary key may not accurately represent the relationship if a referee can be associated with multiple affiliates. Consider using a composite primary key consisting of both affiliateAddress and refereeAddress.

Apply this diff to adjust the primary key:

-    table.string('refereeAddress').primary().notNullable();
-    table.string('affiliateAddress').notNullable();
+    table.string('refereeAddress').notNullable();
+    table.string('affiliateAddress').notNullable();
+    table.primary(['affiliateAddress', 'refereeAddress']);

This change ensures that each affiliate-referee pair is unique and correctly modeled.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
table.string('refereeAddress').notNullable();
table.string('affiliateAddress').notNullable();
table.primary(['affiliateAddress', 'refereeAddress']);

Comment on lines +5 to +15
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
table.decimal('affiliateEarnings', null).notNullable().defaultTo(0);
table.integer('referredMakerTrades').notNullable().defaultTo(0);
table.integer('referredTakerTrades').notNullable().defaultTo(0);
table.decimal('referredTotalVolume', null).notNullable().defaultTo(0);
table.bigInteger('referralBlockHeight').notNullable();
table.decimal('referredTakerFees', null).notNullable().defaultTo(0);
table.decimal('referredMakerFees', null).notNullable().defaultTo(0);
table.decimal('referredMakerRebates', null).notNullable().defaultTo(0);
table.decimal('referredLiquidationFees', null).notNullable().defaultTo(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Specify Precision and Scale for Decimal Columns

Passing null for precision in table.decimal() may lead to inconsistent behavior across different database systems. It's recommended to specify precision and scale explicitly for decimal columns to ensure data consistency.

Apply this diff to define precision and scale:

-    table.decimal('affiliateEarnings', null).notNullable().defaultTo(0);
+    table.decimal('affiliateEarnings', 38, 18).notNullable().defaultTo(0);

Repeat for all decimal columns, setting appropriate precision and scale based on your requirements.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
table.decimal('affiliateEarnings', null).notNullable().defaultTo(0);
table.integer('referredMakerTrades').notNullable().defaultTo(0);
table.integer('referredTakerTrades').notNullable().defaultTo(0);
table.decimal('referredTotalVolume', null).notNullable().defaultTo(0);
table.bigInteger('referralBlockHeight').notNullable();
table.decimal('referredTakerFees', null).notNullable().defaultTo(0);
table.decimal('referredMakerFees', null).notNullable().defaultTo(0);
table.decimal('referredMakerRebates', null).notNullable().defaultTo(0);
table.decimal('referredLiquidationFees', null).notNullable().defaultTo(0);
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
table.decimal('affiliateEarnings', 38, 18).notNullable().defaultTo(0);
table.integer('referredMakerTrades').notNullable().defaultTo(0);
table.integer('referredTakerTrades').notNullable().defaultTo(0);
table.decimal('referredTotalVolume', 38, 18).notNullable().defaultTo(0);
table.bigInteger('referralBlockHeight').notNullable();
table.decimal('referredTakerFees', 38, 18).notNullable().defaultTo(0);
table.decimal('referredMakerFees', 38, 18).notNullable().defaultTo(0);
table.decimal('referredMakerRebates', 38, 18).notNullable().defaultTo(0);
table.decimal('referredLiquidationFees', 38, 18).notNullable().defaultTo(0);

});
});

// TODO(CT-1341): Add paginated query tests similar to `affiliate-info-table.test.ts`
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Add pagination tests as mentioned in the TODO comment.

The reference file affiliate-info-table.test.ts contains a comprehensive test suite for the paginatedFindWithAddressFilter functionality that should be replicated in this file. The tests should cover filtering, sorting, pagination (offset/limit), and edge cases.

🔗 Analysis chain

Address the TODO comment for pagination tests.

The comment references ticket CT-1341 for adding pagination query tests.

Let's verify if this ticket exists and its status:

Would you like me to help implement the pagination tests or create a GitHub issue if it doesn't exist?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the ticket exists in GitHub issues
gh issue list --search "CT-1341 in:title,body"

Length of output: 48


Script:

#!/bin/bash
# Find and show the referenced test file content
fd -t f "affiliate-info-table.test.ts" --exec cat {}

# Show the current test file content for comparison
cat indexer/packages/postgres/__tests__/stores/affiliate-referee-stats-table.test.ts

Length of output: 30046

-- Step 2c: Inner join filtered_fills with affiliate_referred_subaccounts and filter
affiliate_fills AS (
SELECT
filtered_fills."subaccountId",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Could simply the query by using filtered_fills.* to select all columns from the subquery.

Choose a reason for hiding this comment

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

I think that's a smell for prod.

Copy link
Contributor

@vincentwschau vincentwschau Jan 13, 2025

Choose a reason for hiding this comment

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

Valid point, could go either way, * leads to less failures due to changes to fields, but also hides issues due to schema changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

3 participants