-
Notifications
You must be signed in to change notification settings - Fork 16
Mature Price Models for "v1" ENSAnalytics #1562
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
a01edc6
mature price models + ens-referrals adapted to versioning
Goader 9917b53
Merge remote-tracking branch 'origin' into feat/mature-price-models
Goader d973ec5
try with source imports
Goader 7da4740
moving to a separate v1 directory
Goader 8cf4b77
linter issues
Goader 2960921
Merge remote-tracking branch 'origin' into feat/mature-price-models
Goader a2867bf
some problems fixed
Goader 2712c0d
v1 internal entry point
Goader a9beb17
fix: use explicit shared imports inside ENSNode SDK (#1572)
tk-o 3bac4e1
proper v1 cache building
Goader 020c6f5
Merge remote-tracking branch 'origin' into feat/mature-price-models
Goader 0bfa0ed
docs(changeset):
Goader 5a64b12
changesets
Goader 5607554
correct endpoint paths in client
Goader d39ad42
Merge branch 'main' into feat/mature-price-models
lightwalker-eth 38c9f1e
review applied
Goader adb9427
Merge branch 'feat/mature-price-models' of github.com:namehash/ensnod…
Goader f68ba30
coderabbit suggestions
Goader 6b507fd
reject untrimmed values explicitly
Goader File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database-v1.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import { | ||
| buildReferrerMetrics, | ||
| type ReferralProgramRules, | ||
| type ReferrerMetrics, | ||
| } from "@namehash/ens-referrals/v1"; | ||
| import { and, count, desc, eq, gte, isNotNull, lte, ne, sql, sum } from "drizzle-orm"; | ||
| import { type Address, zeroAddress } from "viem"; | ||
|
|
||
| import * as schema from "@ensnode/ensnode-schema"; | ||
| import { deserializeDuration, formatAccountId, priceEth } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import { db } from "@/lib/db"; | ||
| import logger from "@/lib/logger"; | ||
|
|
||
| /** | ||
| * Get Referrer Metrics from the database (V1 API). | ||
| * | ||
| * @param rules - The referral program rules for filtering registrar actions | ||
| * @returns A promise that resolves to an array of {@link ReferrerMetrics} values. | ||
| * @throws Error if the database query fails. | ||
| */ | ||
| export const getReferrerMetrics = async ( | ||
| rules: ReferralProgramRules, | ||
| ): Promise<ReferrerMetrics[]> => { | ||
| /** | ||
| * Step 1: Filter for referrals matching the provided rules: | ||
| * - timestamp is between startDate and endDate (inclusive) | ||
| * - decodedReferrer is not null and not the zero address | ||
| * - subregistryId matches the provided subregistryId | ||
| * | ||
| * Step 2: Group by decodedReferrer and calculate: | ||
| * - Sum total incrementalDuration for each decodedReferrer | ||
| * - Count of qualified referrals for each decodedReferrer | ||
| * - Sum total cost (revenue contribution) for each decodedReferrer | ||
| * | ||
| * Step 3: Sort by sum total incrementalDuration from highest to lowest | ||
| */ | ||
|
|
||
| try { | ||
| const records = await db | ||
| .select({ | ||
| referrer: schema.registrarActions.decodedReferrer, | ||
| totalReferrals: count().as("total_referrals"), | ||
| totalIncrementalDuration: sum(schema.registrarActions.incrementalDuration).as( | ||
| "total_incremental_duration", | ||
| ), | ||
| // Note: Using raw SQL for COALESCE because Drizzle doesn't natively support it yet. | ||
| // See: https://github.com/drizzle-team/drizzle-orm/issues/3708 | ||
| totalRevenueContribution: | ||
| sql<string>`COALESCE(SUM(${schema.registrarActions.total}), 0)`.as( | ||
| "total_revenue_contribution", | ||
| ), | ||
| }) | ||
| .from(schema.registrarActions) | ||
| .where( | ||
| and( | ||
| // Filter by timestamp range | ||
| gte(schema.registrarActions.timestamp, BigInt(rules.startTime)), | ||
| lte(schema.registrarActions.timestamp, BigInt(rules.endTime)), | ||
| // Filter by decodedReferrer not null | ||
| isNotNull(schema.registrarActions.decodedReferrer), | ||
| // Filter by decodedReferrer not zero address | ||
| ne(schema.registrarActions.decodedReferrer, zeroAddress), | ||
| // Filter by subregistryId matching the provided subregistryId | ||
| eq(schema.registrarActions.subregistryId, formatAccountId(rules.subregistryId)), | ||
| ), | ||
| ) | ||
| .groupBy(schema.registrarActions.decodedReferrer) | ||
| .orderBy(desc(sql`total_incremental_duration`)); | ||
|
|
||
| // Type assertion: The WHERE clause in the query above guarantees non-null values for: | ||
| // 1. `referrer` is guaranteed to be non-null due to isNotNull filter | ||
| // 2. `totalIncrementalDuration` is guaranteed to be non-null as it is the sum of non-null bigint values | ||
| // 3. `totalRevenueContribution` is guaranteed to be non-null due to COALESCE with 0 | ||
| interface NonNullRecord { | ||
| referrer: Address; | ||
| totalReferrals: number; | ||
| totalIncrementalDuration: string; | ||
| totalRevenueContribution: string; | ||
| } | ||
|
|
||
| return (records as NonNullRecord[]).map((record) => { | ||
| return buildReferrerMetrics( | ||
| record.referrer, | ||
| record.totalReferrals, | ||
| deserializeDuration(record.totalIncrementalDuration), | ||
| priceEth(BigInt(record.totalRevenueContribution)), | ||
| ); | ||
| }); | ||
|
lightwalker-eth marked this conversation as resolved.
|
||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : "Unknown error"; | ||
| logger.error({ error }, "Failed to fetch referrer metrics from database"); | ||
| throw new Error(`Failed to fetch referrer metrics from database: ${errorMessage}`); | ||
| } | ||
| }; | ||
25 changes: 25 additions & 0 deletions
25
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referrer-leaderboard-v1.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { | ||
| buildReferrerLeaderboard, | ||
| type ReferralProgramRules, | ||
| type ReferrerLeaderboard, | ||
| } from "@namehash/ens-referrals/v1"; | ||
|
|
||
| import type { UnixTimestamp } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import { getReferrerMetrics } from "./database-v1"; | ||
|
|
||
| /** | ||
| * Builds a `ReferralLeaderboard` from the database using the provided referral program rules (V1 API). | ||
| * | ||
| * @param rules - The referral program rules for filtering registrar actions | ||
| * @param accurateAsOf - The {@link UnixTimestamp} of when the data used to build the {@link ReferrerLeaderboard} was accurate as of. | ||
| * @returns A promise that resolves to a {@link ReferrerLeaderboard} | ||
| * @throws Error if the database query fails | ||
| */ | ||
| export async function getReferrerLeaderboard( | ||
| rules: ReferralProgramRules, | ||
| accurateAsOf: UnixTimestamp, | ||
| ): Promise<ReferrerLeaderboard> { | ||
| const allReferrers = await getReferrerMetrics(rules); | ||
| return buildReferrerLeaderboard(allReferrers, rules, accurateAsOf); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.