Skip to content

Award Accounting#1998

Open
Goader wants to merge 10 commits intomainfrom
feature/award-accounting
Open

Award Accounting#1998
Goader wants to merge 10 commits intomainfrom
feature/award-accounting

Conversation

@Goader
Copy link
Copy Markdown
Member

@Goader Goader commented Apr 26, 2026

Award Accounting

pushes forward: #1797

Summary

  • New endpoint returning a CSV file with all the events related to the rev-share-cap edition
  • ENSApi caches now produce whole snapshots containing leaderboards and potentially other things (accounting trace in case of rev-share-cap editions)
  • Updated the leaderboard building to actually produce the accounting trace, along with all the required types
  • Example CSV for my local Sepolia instance

Why


Testing

  • Automatic CI and manual validation
  • New tests

Notes for Reviewer (Optional)

  • From the ENSDb schema, it seems like we need both registrars and subgraph plugins activated to be able to gather the required information (otherwise new innerJoins will drop everything silently). Should we add them as required for all the ENSAnalytics APIs?
  • Other unresolved comments
  • Should we add registrarActionId to the CSV?

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

@Goader Goader self-assigned this Apr 26, 2026
Copilot AI review requested due to automatic review settings April 26, 2026 22:05
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 26, 2026

🦋 Changeset detected

Latest commit: 9cf0a7c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 24 packages
Name Type
@namehash/ens-referrals Major
ensapi Major
ensindexer Major
ensadmin Major
ensrainbow Major
fallback-ensapi Major
enssdk Major
enscli Major
enskit Major
ensskills Major
@ensnode/datasources Major
@ensnode/ensrainbow-sdk Major
@ensnode/ensdb-sdk Major
@ensnode/ensnode-react Major
@ensnode/ensnode-sdk Major
@ensnode/integration-test-env Major
@ensnode/ponder-sdk Major
@ensnode/ponder-subgraph Major
@ensnode/shared-configs Major
@docs/ensnode Major
@docs/ensrainbow Major
@namehash/namehash-ui Major
@ensnode/ensindexer-perf-testing Major
@ensnode/enskit-react-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Apr 29, 2026 1:18am
ensnode.io Ready Ready Preview, Comment Apr 29, 2026 1:18am
ensrainbow.io Ready Ready Preview, Comment Apr 29, 2026 1:18am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 26, 2026

Warning

Rate limit exceeded

@Goader has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 57 minutes and 39 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6e18fb1-cf36-4fae-8b5e-f9c06b0b63a6

📥 Commits

Reviewing files that changed from the base of the PR and between 24b20a6 and 9cf0a7c.

📒 Files selected for processing (3)
  • apps/ensapi/src/cache/referral-edition-snapshots.cache.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts
📝 Walkthrough

Walkthrough

This PR replaces per-edition leaderboard caching with snapshot-based caching and adds per-event accounting trace support for rev-share-cap editions, including CSV export endpoint, enriched referral events, updated builders that return snapshots (leaderboard + accountingRecords), middleware/typing updates, and tests.

Changes

Cohort / File(s) Summary
Release Metadata
/.changeset/purple-frogs-dream.md, /.changeset/shiny-pandas-account.md
Adds changeset entries documenting minor bumps and the new snapshot/accounting features and CSV route.
Caching & Middleware Layer
apps/ensapi/src/cache/referral-edition-snapshots.cache.ts, apps/ensapi/src/middleware/referral-edition-snapshots-caches.middleware.ts, apps/ensapi/src/lib/hono-factory.ts, apps/ensapi/src/index.ts
Replaces leaderboard caches with referral-edition-snapshots caches, renames types/initializers, updates produced middleware variables and shutdown cache destruction.
API Routes & Handlers
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.routes.ts, apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts, docs/ensnode.io/ensapi-openapi.json
Adds GET /v1/ensanalytics/accounting?edition={slug} returning RFC-4180 CSV for rev-share-cap editions; wires new route, OpenAPI metadata, prerequisite gating, and explicit 400/404/503/500 responses.
API Tests
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts
Mocks and asserts against snapshot caches; adds prerequisites short-circuit tests and a comprehensive /accounting test block covering 404/400/200 and 503 modes plus CSV formatting checks.
Referral Event Enrichment & DB
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts, packages/ens-referrals/src/award-models/rev-share-cap/referral-event.ts
Joins registrar lifecycle data to include name, actionType, transactionHash, and registrant on referral events; runtime null/type checks added.
Accounting Types & CSV Formatter
packages/ens-referrals/src/award-models/rev-share-cap/accounting.ts, apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts, packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts
Adds accounting types (TentativeReferralAwardRevShareCap, ReferralAccountingRecordRevShareCap), implements CSV formatter with proper escaping/ordering, and introduces extensive accounting tests.
Award Model Snapshots & Builders
packages/ens-referrals/src/award-models/pie-split/leaderboard.ts, packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts, packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.test.ts, apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.ts, apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.test.ts
Renames builders to buildReferralEditionSnapshot*, introduces snapshot return shapes wrapping leaderboard and (for rev-share-cap) accountingRecords, and updates tests to assert new shapes.
Types & Public API Exports
packages/ens-referrals/src/leaderboard.ts, packages/ens-referrals/src/index.ts, packages/ens-referrals/src/api/index.ts, packages/ens-referrals/src/api/prerequisites.ts
Adds ReferralEditionSnapshot union type, re-exports accounting/prerequisites modules, and implements prerequisite checks for ENSAnalytics config/indexing status; exports new middleware variable types.
Middleware: ENSAnalytics Prerequisites
apps/ensapi/src/middleware/ensanalytics.middleware.ts, apps/ensapi/src/lib/hono-factory.ts
Adds ensanalyticsApiMiddleware that validates config and indexing-status support and exposes ensAnalyticsPrerequisites to handlers; integrates into produced middleware variables.
Misc Tests/Adjustments
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.test.ts, other updated tests
Adjusts tests to new snapshot shapes and updated import/describe names.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as API Handler
    participant Cache as Snapshot Cache
    participant DB as Database
    participant Builder as AwardModel Builder
    participant Formatter as CSV Formatter

    Client->>Handler: GET /v1/ensanalytics/accounting?edition=slug
    Handler->>Handler: ensure prerequisites supported
    Handler->>Cache: cache.read(edition)
    alt Cache Hit
        Cache-->>Handler: ReferralEditionSnapshot
    else Cache Miss
        Cache->>DB: getReferralEvents(edition)
        DB-->>DB: enrich events with name, actionType,<br/>transactionHash, registrant
        DB-->>Builder: ReferralEvent[]
        Builder->>Builder: compute leaderboard & per-event accounting
        Builder-->>Cache: ReferralEditionSnapshot (leaderboard + accountingRecords)
        Cache-->>Handler: ReferralEditionSnapshot
    end
    Handler->>Handler: assert awardModel === RevShareCap
    Handler->>Formatter: formatAccountingCsv(accountingRecords)
    Formatter-->>Handler: CSV string
    Handler-->>Client: 200 text/csv (CSV payload)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

ensanalytics

Poem

🐇 I hop through snapshots, fields in tow,
Events in order, each record on show,
CSV carrots, neat rows so bright,
Rev-share tales in morning light,
🥕📜

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Award Accounting' is vague and does not clearly convey the primary changes in the pull request. Use a more descriptive title that better captures the main changes, such as 'Add rev-share-cap accounting CSV endpoint and snapshot-based caching' to provide reviewers with clearer context about the changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description covers the main objectives and includes all required template sections: Summary, Why, Testing, and Notes for Reviewers, though some sections are brief.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/award-accounting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 57 minutes and 39 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the ENS referrals “rev-share-cap” pipeline to produce a per-event award accounting trace, exposes that trace via a new ENSApi CSV endpoint, and refactors leaderboard builders to return an edition “snapshot” object.

Changes:

  • Introduce ReferralEditionSnapshot builders (buildReferralEditionSnapshot*) bundling leaderboards with additional computed state (rev-share-cap includes per-event accounting records).
  • Add rev-share-cap accounting types + tests, and extend ReferralEvent to carry audit fields needed for reporting/export.
  • Add GET /v1/ensanalytics/accounting?edition={slug} returning a CSV dump of per-event accounting for rev-share-cap editions.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/ens-referrals/src/leaderboard.ts Exposes new ReferralEditionSnapshot union alongside leaderboards.
packages/ens-referrals/src/index.ts Re-exports rev-share-cap accounting types.
packages/ens-referrals/src/award-models/rev-share-cap/referral-event.ts Extends ReferralEvent with name/action/txHash/registrant for audit/export.
packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts Refactors rev-share-cap builder to return a snapshot + emits per-event accounting records.
packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.test.ts Updates tests for the new snapshot-returning builder.
packages/ens-referrals/src/award-models/rev-share-cap/accounting.ts Adds per-event accounting record + award trace interfaces.
packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts Adds targeted tests for per-event accounting trace behavior.
packages/ens-referrals/src/award-models/pie-split/leaderboard.ts Refactors pie-split builder to return a snapshot wrapper.
apps/ensapi/src/middleware/referral-edition-snapshots-caches.middleware.ts Renames/rewires middleware to provide snapshot caches instead of leaderboards.
apps/ensapi/src/lib/hono-factory.ts Updates middleware variable typing to new snapshot-caches middleware.
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.ts Replaces leaderboard builder dispatcher with snapshot builder dispatcher.
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.test.ts Updates tests for snapshot return shape.
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts Adds CSV formatter for rev-share-cap accounting records.
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts Extends rev-share-cap event query to fetch audit fields + domain name.
apps/ensapi/src/index.ts Updates graceful shutdown to destroy snapshot caches.
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts Uses snapshot caches for existing endpoints and adds /accounting CSV endpoint.
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts Updates cache mocks for snapshot shape + adds basic accounting endpoint tests.
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.routes.ts Adds OpenAPI route definition for /accounting CSV endpoint.
apps/ensapi/src/cache/referral-edition-snapshots.cache.ts Refactors cache to store snapshots (and use new builder).
.changeset/shiny-pandas-account.md Changeset for ensapi: adds accounting CSV endpoint.
.changeset/purple-frogs-dream.md Changeset for ens-referrals: adds snapshot + per-event accounting trace.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .changeset/purple-frogs-dream.md
Copy link
Copy Markdown
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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/ensapi/src/index.ts (1)

61-61: ⚠️ Potential issue | 🟡 Minor

Stale log label after rename.

The log message still references the pre-rename referralLeaderboardEditionsCache. For consistency with the rest of the PR-wide migration to snapshot-based caching (e.g., the import on Line 6 and the variable editionsCaches), update the label to reflect the new name.

📝 Suggested fix
-        logger.info(`Destroyed referralLeaderboardEditionsCache for ${editionSlug}`);
+        logger.info(`Destroyed referralEditionSnapshotsCache for ${editionSlug}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensapi/src/index.ts` at line 61, The log message uses an outdated label
"referralLeaderboardEditionsCache"; update the logger.info call that references
editionSlug to use the new snapshot-based name (e.g., "editionsCaches" or
"editionSnapshots" consistent with the import and variable `editionsCaches`) so
the message matches the PR-wide rename—find the logger.info(`Destroyed
referralLeaderboardEditionsCache for ${editionSlug}`) and change the text to
reflect the new cache name.
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts (1)

134-159: ⚠️ Potential issue | 🟠 Major

Replace innerJoin with leftJoin to detect missing data explicitly instead of silently dropping referral events.

The query chains registrarActions → registrationLifecycles (on node) → subgraph_domain, both via innerJoin. This means any registrarAction row without a corresponding registrationLifecycles or subgraph_domain.name will vanish from results without surfacing an error. The runtime checks in the map function (lines 164–172) only validate rows the query returns; they cannot catch rows silently dropped by the joins.

While the schema documentation states registrationLifecycles are guaranteed to exist and subgraph_domain.name will "in practice never be null," these guarantees are not enforced at the schema level. If an indexer bug or data gap violates these assumptions, the leaderboard will silently omit referral events rather than fail loudly.

Consider using leftJoin on both tables and adding explicit error throws if the joins fail to produce expected rows. This ensures data integrity is validated explicitly and any gaps surface clearly rather than causing silent revenue loss.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts` around
lines 134 - 159, Change the two innerJoin calls on
ensIndexerSchema.registrationLifecycles and ensIndexerSchema.subgraph_domain to
leftJoin so registrarActions rows are not silently dropped, then after executing
the query (the mapping logic that currently validates rows returned by the
query) add explicit null checks for registrationLifecycles and subgraph_domain
(e.g., verify the joined lifecycle and domain.name exist for each row) and throw
a descriptive error when either is missing so missing joins surface loudly;
adjust any downstream logic that assumed non-null values accordingly
(references: the query built from ensIndexerSchema.registrarActions, the joins
to ensIndexerSchema.registrationLifecycles and ensIndexerSchema.subgraph_domain,
and the post-query mapping/validation code that checks decodedReferrer).
apps/ensapi/src/cache/referral-edition-snapshots.cache.ts (1)

41-178: 🧹 Nitpick | 🔵 Trivial

Stale "leaderboard" wording in log messages and JSDoc after the snapshot rename.

The cache now produces and caches ReferralEditionSnapshot (which contains both leaderboard and accountingRecords), but the human-readable strings throughout this file still say "leaderboard". This hurts grep-ability/observability (the logger category is referral-edition-snapshots-cache but the messages talk about leaderboards) and the JSDoc no longer accurately describes the cache contents.

Affected spots: lines 41–44 (JSDoc), 57 (@returns), 87, 90, 97, 107, 112, 125, 168, and 177.

♻️ Suggested wording updates
 /**
  * The list of {`@link` OmnichainIndexingStatusId} values that are supported for generating
- * referrer leaderboards.
+ * referral edition snapshots.
  *
- * Other values indicate that we are not ready to generate leaderboards yet.
+ * Other values indicate that we are not ready to generate edition snapshots yet.
  */
@@
- * `@returns` A function that builds the leaderboard for the given edition
+ * `@returns` A function that builds the {`@link` ReferralEditionSnapshot} for the given edition
@@
-      logger.error(
-        { error: indexingStatus, editionSlug },
-        `Failed to read indexing status cache while generating referral leaderboard for ${editionSlug}. Cannot proceed without valid indexing status.`,
-      );
-      throw new Error(
-        `Unable to generate referral leaderboard for ${editionSlug}. indexingStatusCache must have been successfully initialized.`,
-      );
+      logger.error(
+        { error: indexingStatus, editionSlug },
+        `Failed to read indexing status cache while generating referral edition snapshot for ${editionSlug}. Cannot proceed without valid indexing status.`,
+      );
+      throw new Error(
+        `Unable to generate referral edition snapshot for ${editionSlug}. indexingStatusCache must have been successfully initialized.`,
+      );
@@
-        `Unable to generate referrer leaderboard for ${editionSlug}. Omnichain indexing status is currently ${omnichainIndexingStatus} but must be ${supportedOmnichainIndexingStatuses.join(" or ")}.`,
+        `Unable to generate referral edition snapshot for ${editionSlug}. Omnichain indexing status is currently ${omnichainIndexingStatus} but must be ${supportedOmnichainIndexingStatuses.join(" or ")}.`,
@@
-        `Unable to generate referrer leaderboard for ${editionSlug}. Latest indexed block ref for chain ${editionConfig.rules.subregistryId.chainId} is null.`,
+        `Unable to generate referral edition snapshot for ${editionSlug}. Latest indexed block ref for chain ${editionConfig.rules.subregistryId.chainId} is null.`,
@@
-      `Building referrer leaderboard for ${editionSlug} with rules:\n${JSON.stringify(
+      `Building referral edition snapshot for ${editionSlug} with rules:\n${JSON.stringify(
@@
-      `Successfully built referrer leaderboard for ${editionSlug} with ${snapshot.leaderboard.referrers.size} referrers`,
+      `Successfully built referral edition snapshot for ${editionSlug} with ${snapshot.leaderboard.referrers.size} referrers and ${snapshot.accountingRecords.length} accounting records`,
@@
-    logger.info(`Initialized leaderboard cache for ${editionSlug}`);
+    logger.info(`Initialized referral edition snapshot cache for ${editionSlug}`);
@@
 /**
- * Gets the cached instance of referral leaderboard editions caches.
+ * Gets the cached instance of referral edition snapshots caches.
  * Returns null if not yet initialized.

As per coding guidelines: "Do not add JSDoc @returns tags that merely restate the method summary; remove such redundancy during PR review." If the updated @returns on createEditionSnapshotBuilder ends up being just a restatement of the summary, drop it instead of rewording.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensapi/src/cache/referral-edition-snapshots.cache.ts` around lines 41 -
178, Update wording to refer to "ReferralEditionSnapshot" / "edition snapshot"
instead of "leaderboard" in the JSDoc and all log messages: adjust the JSDoc for
createEditionSnapshotBuilder to describe that the cache holds
ReferralEditionSnapshot (remove the redundant `@returns` if it only repeats the
summary), change logger.debug, logger.info and error strings inside
createEditionSnapshotBuilder to say "snapshot" or "edition snapshot" (e.g., the
debug that currently says "Edition is immutably closed, returning cached data
without re-fetching", the info that logs the rules, and the info after building
that references snapshot.leaderboard.referrers.size should say snapshot/edition
snapshot and can still reference snapshot.leaderboard.referrers.size in code),
and change the initializeReferralEditionSnapshotsCaches init log ("Initialized
leaderboard cache for") to "Initialized edition snapshot cache for" (or
similar); ensure the references to functions/classes remain the same:
createEditionSnapshotBuilder, initializeReferralEditionSnapshotsCaches,
cachedInstance, and getReferralEditionSnapshot so the replacements are localized
to message/JSDoc strings only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts`:
- Around line 987-997: The helper mockSnapshotCache returns an object cast to
ReferralEditionSnapshot but omits required fields like leaderboard and
accountingRecords (required by ReferralEditionSnapshotPieSplit and
ReferralEditionSnapshotRevShareCap); update mockSnapshotCache to either (a)
document this limitation with a brief comment stating it intentionally produces
minimal snapshots only valid for early-exit awardModel checks, or (b) extend its
signature to accept optional partial overrides for leaderboard and
accountingRecords and merge them into the returned object so the mock can
produce structurally valid ReferralEditionSnapshot instances when tests exercise
success paths; reference mockSnapshotCache, ReferralEditionSnapshot,
ReferralEditionSnapshotPieSplit, ReferralEditionSnapshotRevShareCap, and
SWRCache when making the change.
- Around line 986-1034: Add tests to cover the /accounting success and
cache-error 503 paths: reuse mockSnapshotCache and setupAccountingMocks to
inject a cache map via
editionsCachesMiddleware.referralEditionSnapshotsCachesMiddleware; add a test
where mockSnapshotCache returns awardModel
ReferralProgramAwardModels.RevShareCap and the mocked
ReferralEditionSnapshot.read() resolves to an object with accountingRecords
(assert response status 200, Content-Type "text/csv; charset=utf-8",
Content-Disposition includes filename "accounting-${edition}.csv", and body
contains CSV header row plus one sample record row produced by
formatAccountingCsv); add another test where setupAccountingMocks injects a map
or cache whose value is an Error or whose read() resolves to an Error and assert
the endpoint returns 503. Ensure you reference the
app.request("/accounting?edition=...") call and the
ReferralEditionSnapshot.read() behavior when stubbing responses.

In `@apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts`:
- Around line 338-383: Add response caching headers, operational logging, and
avoid building the entire CSV in memory: after the rev-share check and before
returning the body in getAccountingCsvRoute, log the edition and record count
via logger.info({ edition, recordCount: cached.accountingRecords.length }), and
set a long-lived Cache-Control (e.g. "public, max-age=31536000, immutable") when
the snapshot is known immutable (use the cached snapshot metadata/flag available
on the cached object), by calling c.header("Cache-Control", "..."); finally
replace the immediate c.body(formatAccountingCsv(cached.accountingRecords), 200)
with a streaming response that converts cached.accountingRecords into CSV rows
(e.g. a ReadableStream or Node stream) so you don’t build the whole CSV in
memory — reuse formatAccountingCsv for row formatting or implement an async
iterator that writes rows to the stream and call c.body(stream, 200); keep
existing error handling and headers (Content-Type/Content-Disposition) intact.

In `@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts`:
- Around line 174-184: The exhaustiveness check using "const _exhaustive: never
= record.actionType" is unsafe if the DB-typed column widens; instead perform a
runtime validation against the known enum values by checking
Object.values(RegistrarActionTypes).includes(record.actionType as
RegistrarActionType) in the switch/default path (or before the switch) and, if
it returns false, throw the same Error (the one referencing getReferralEvents
and record.id); remove the _exhaustive assignment so the guard is a pure runtime
check independent of static narrowing.

In
`@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts`:
- Around line 74-82: The CSV produced by formatAccountingCsv can contain
non-ASCII ENS names and should include a UTF-8 BOM to avoid Excel mojibake;
update formatAccountingCsv so it prepends "\uFEFF" before the header row (i.e.,
before CSV_COLUMNS.map((c) => c.header).join(",")) while leaving the rest of the
join logic intact and still returning the final string with "\r\n" line endings;
reference CSV_COLUMNS and formatAccountingCsv to locate where to add the BOM.
- Around line 4-13: The CSV-escaping helper csvCell should be applied to every
exported cell to avoid future CSV injection/corruption; update the CSV writer so
that all output values (not just name and disqualificationReason) are passed
through csvCell — e.g., wrap actionType, hex/hash/address columns,
integer/string counts, and any other columns the formatter emits with csvCell
before joining/printing — leaving csvCell implementation unchanged and ensuring
the join logic still produces the same column order.

In `@packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts`:
- Around line 264-287: Update the test description and inline comment to remove
references to a "wrapper" or "with-accounting function" and reflect that the
test now directly verifies the leaderboard returned by
buildReferralEditionSnapshotRevShareCap; rename the it(...) string to something
like "leaderboard in snapshot matches expected spot-checks" and delete or
replace the misleading comment "// Verify that the wrapper and the
with-accounting function produce the same leaderboard." so the test (using
leaderboard and rules variables and function
buildReferralEditionSnapshotRevShareCap) clearly documents it's spot-checking
fields of the returned leaderboard.

In `@packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts`:
- Around line 80-89: The new interface ReferralEditionSnapshotRevShareCap lacks
per-field JSDoc consistent with its sibling ReferrerLeaderboardRevShareCap;
update ReferralEditionSnapshotRevShareCap to add JSDoc on each field: document
awardModel (assert it equals leaderboard.awardModel and rules.awardModel),
document leaderboard (type ReferrerLeaderboardRevShareCap and that its
awardModel must match), and document accountingRecords (assert its length equals
the number of processed onchain events and that entries are in chronological
onchain order), mirroring the detailed `@invariant-style` comments used for
ReferrerLeaderboardRevShareCap so the file’s comment style stays consistent.

In `@packages/ens-referrals/src/leaderboard.ts`:
- Around line 17-22: Add a short doc-comment to ReferralEditionSnapshot
mirroring ReferrerLeaderboard that notes the union is discriminated by the
awardModel field; update the comment above the ReferralEditionSnapshot type to
mention "Discriminated by awardModel" (or similar) so consumers know to switch
on awardModel when handling ReferralEditionSnapshot variants like
ReferralEditionSnapshotPieSplit and ReferralEditionSnapshotRevShareCap.

---

Outside diff comments:
In `@apps/ensapi/src/cache/referral-edition-snapshots.cache.ts`:
- Around line 41-178: Update wording to refer to "ReferralEditionSnapshot" /
"edition snapshot" instead of "leaderboard" in the JSDoc and all log messages:
adjust the JSDoc for createEditionSnapshotBuilder to describe that the cache
holds ReferralEditionSnapshot (remove the redundant `@returns` if it only repeats
the summary), change logger.debug, logger.info and error strings inside
createEditionSnapshotBuilder to say "snapshot" or "edition snapshot" (e.g., the
debug that currently says "Edition is immutably closed, returning cached data
without re-fetching", the info that logs the rules, and the info after building
that references snapshot.leaderboard.referrers.size should say snapshot/edition
snapshot and can still reference snapshot.leaderboard.referrers.size in code),
and change the initializeReferralEditionSnapshotsCaches init log ("Initialized
leaderboard cache for") to "Initialized edition snapshot cache for" (or
similar); ensure the references to functions/classes remain the same:
createEditionSnapshotBuilder, initializeReferralEditionSnapshotsCaches,
cachedInstance, and getReferralEditionSnapshot so the replacements are localized
to message/JSDoc strings only.

In `@apps/ensapi/src/index.ts`:
- Line 61: The log message uses an outdated label
"referralLeaderboardEditionsCache"; update the logger.info call that references
editionSlug to use the new snapshot-based name (e.g., "editionsCaches" or
"editionSnapshots" consistent with the import and variable `editionsCaches`) so
the message matches the PR-wide rename—find the logger.info(`Destroyed
referralLeaderboardEditionsCache for ${editionSlug}`) and change the text to
reflect the new cache name.

In `@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts`:
- Around line 134-159: Change the two innerJoin calls on
ensIndexerSchema.registrationLifecycles and ensIndexerSchema.subgraph_domain to
leftJoin so registrarActions rows are not silently dropped, then after executing
the query (the mapping logic that currently validates rows returned by the
query) add explicit null checks for registrationLifecycles and subgraph_domain
(e.g., verify the joined lifecycle and domain.name exist for each row) and throw
a descriptive error when either is missing so missing joins surface loudly;
adjust any downstream logic that assumed non-null values accordingly
(references: the query built from ensIndexerSchema.registrarActions, the joins
to ensIndexerSchema.registrationLifecycles and ensIndexerSchema.subgraph_domain,
and the post-query mapping/validation code that checks decodedReferrer).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ab1fd3ce-fb34-46b8-ab77-4d0356b0aeb9

📥 Commits

Reviewing files that changed from the base of the PR and between aa26180 and 1fde48e.

📒 Files selected for processing (21)
  • .changeset/purple-frogs-dream.md
  • .changeset/shiny-pandas-account.md
  • apps/ensapi/src/cache/referral-edition-snapshots.cache.ts
  • apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.routes.ts
  • apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts
  • apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts
  • apps/ensapi/src/index.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.test.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/get-referral-edition-snapshot.ts
  • apps/ensapi/src/lib/hono-factory.ts
  • apps/ensapi/src/middleware/referral-edition-snapshots-caches.middleware.ts
  • packages/ens-referrals/src/award-models/pie-split/leaderboard.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/accounting.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.test.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/referral-event.ts
  • packages/ens-referrals/src/index.ts
  • packages/ens-referrals/src/leaderboard.ts

Comment thread apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts
Comment thread apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts
Comment thread apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts
Comment thread apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts
Comment thread apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts Outdated
Comment on lines +74 to +82
export function formatAccountingCsv(
records: ReadonlyArray<ReferralAccountingRecordRevShareCap>,
): string {
const lines = [
CSV_COLUMNS.map((c) => c.header).join(","),
...records.map((r) => CSV_COLUMNS.map((c) => c.value(r)).join(",")),
];
return `${lines.join("\r\n")}\r\n`;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider a UTF-8 BOM for Excel compatibility.

Names returned by ENS (r.name is InterpretedName) can contain non-ASCII characters (emoji, Cyrillic, etc.). When users open a UTF-8 CSV directly in Excel without an explicit import step, Excel will mis-decode bytes and show mojibake. Prepending a UTF-8 BOM ("\uFEFF") before the header row is the standard mitigation, with no impact on RFC-4180-compliant parsers. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts`
around lines 74 - 82, The CSV produced by formatAccountingCsv can contain
non-ASCII ENS names and should include a UTF-8 BOM to avoid Excel mojibake;
update formatAccountingCsv so it prepends "\uFEFF" before the header row (i.e.,
before CSV_COLUMNS.map((c) => c.header).join(",")) while leaving the rest of the
join logic intact and still returning the final string with "\r\n" line endings;
reference CSV_COLUMNS and formatAccountingCsv to locate where to add the BOM.

Comment thread packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts Outdated
Comment thread packages/ens-referrals/src/leaderboard.ts
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 27, 2026 18:07 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 27, 2026 18:07 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 27, 2026 18:07 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 27, 2026 18:20 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 27, 2026 18:20 Inactive
@Goader Goader marked this pull request as ready for review April 27, 2026 18:22
@Goader Goader requested a review from a team as a code owner April 27, 2026 18:22
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

This PR introduces a new GET /v1/ensanalytics/accounting?edition={slug} endpoint that returns a per-event CSV accounting dump for rev-share-cap editions, and refactors the cache layer from storing bare ReferrerLeaderboard objects to ReferralEditionSnapshot wrappers that bundle the leaderboard with the chronological accounting trace.

Previously flagged P1 issues are addressed: INNER JOINs are replaced by LEFT JOINs with explicit post-query null checks that throw with the specific registrarActionId, and transactionHash/registrant now have the same null-guard treatment as referrer and domainName. The new ensanalyticsApiMiddleware centralises the plugin-support and indexing-status gate, and the same gate is duplicated in the cache builder to protect the proactivelyInitialize startup path before any request has arrived.

Confidence Score: 4/5

Safe to merge; both previously flagged P1s (INNER JOIN silent truncation, unchecked null casts) are resolved and the new accounting pipeline is well-tested.

No new P0 or P1 issues found. The two outside-diff P1 comments from the prior review round are addressed: JOINs are now LEFT with explicit null-throw guards, and transactionHash/registrant receive the same null checks as referrer. The remaining P2 items are non-blocking style concerns. Score held at 4 rather than 5 given the complexity of the sequential race accounting algorithm and the financial-correctness implications of the new CSV export.

packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts (race algorithm + catch-up award math) and apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts (LEFT JOIN null-throw guards)

Important Files Changed

Filename Overview
packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts Core race algorithm refactored to produce ReferralEditionSnapshotRevShareCap (leaderboard + per-event accounting records); catch-up award math and hasQualified latch logic are correct
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts Switched from INNER JOINs to LEFT JOINs with explicit post-query null throws for every joined column; transactionHash and registrant now null-checked before cast; previous P1 concerns addressed
apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts New RFC-4180 CSV formatter with column-definition table pattern; csvCell escaping is correct; registrarActionId exported as first column named referralId
apps/ensapi/src/middleware/ensanalytics.middleware.ts New prerequisite middleware cleanly checks plugin config and indexing status; does not short-circuit itself — each handler owns its 503 shape
apps/ensapi/src/cache/referral-edition-snapshots.cache.ts Cache builder now guards plugin-support and indexing-status at startup (proactivelyInitialize path); prevents stale/incomplete snapshots from being cached for closed editions
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts New /accounting endpoint correctly narrows snapshot type via awardModel discriminant before accessing accountingRecords; plain-text error responses are intentional for CSV endpoint
packages/ens-referrals/src/api/prerequisites.ts New shared prerequisite helpers for config and indexing-status checks; requires registrars, subgraph, basenames, and lineanames plugins to prevent silent data gaps
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts Comprehensive handler-level tests added: prerequisite short-circuit 503s, 400 for non-rev-share-cap edition, 404 for unknown slug, 503 on cache read error, 200 with CSV shape validation

Sequence Diagram

sequenceDiagram
    participant Client
    participant EnsApi
    participant IndexingStatusMiddleware
    participant EnsanalyticsMiddleware
    participant SnapshotCachesMiddleware
    participant SWRCache
    participant DB

    Client->>EnsApi: GET /v1/ensanalytics/accounting?edition=slug
    EnsApi->>IndexingStatusMiddleware: sets indexingStatus
    EnsApi->>EnsanalyticsMiddleware: checks plugin config + indexing status
    alt prerequisites not met
        EnsanalyticsMiddleware-->>EnsApi: ensAnalyticsPrerequisites {supported:false}
        EnsApi-->>Client: 503 text/plain
    end
    EnsApi->>SnapshotCachesMiddleware: resolves SWRCache map
    EnsApi->>SWRCache: cache.read()
    alt cache cold or stale
        SWRCache->>DB: LEFT JOIN registrarActions + registrationLifecycles + subgraph_domain
        DB-->>SWRCache: ReferralEvent[]
        SWRCache->>SWRCache: buildReferralEditionSnapshotRevShareCap()
        SWRCache-->>EnsApi: ReferralEditionSnapshot {leaderboard, accountingRecords}
    else cache warm
        SWRCache-->>EnsApi: cached ReferralEditionSnapshot
    end
    alt cached instanceof Error
        EnsApi-->>Client: 503 text/plain
    else awardModel != RevShareCap
        EnsApi-->>Client: 400 text/plain
    end
    EnsApi->>EnsApi: formatAccountingCsv(accountingRecords)
    EnsApi-->>Client: 200 text/csv (RFC-4180 CRLF)
Loading

Reviews (6): Last reviewed commit: "Merge remote-tracking branch 'origin' in..." | Re-trigger Greptile

@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 27, 2026 18:58 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 27, 2026 18:58 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 27, 2026 18:58 Inactive
@Goader Goader requested a review from lightwalker-eth April 27, 2026 19:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 28, 2026 14:20 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 28, 2026 14:20 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 28, 2026 14:20 Inactive
Copy link
Copy Markdown
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: 6

♻️ Duplicate comments (1)
apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts (1)

1124-1137: ⚠️ Potential issue | 🟡 Minor

Make the rev-share-cap happy-path cache mock a real snapshot.

mockRevShareCapAccountingCache() casts { awardModel, accountingRecords } to ReferralEditionSnapshot, but the rev-share-cap variant also requires leaderboard. The test currently passes only because /accounting never reads that field, so this helper can hide snapshot-shape regressions. Prefer returning a minimal valid leaderboard instead of relying on the cast.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts` around lines
1124 - 1137, The mockRevShareCapAccountingCache helper returns an object cast to
ReferralEditionSnapshot but omits the required leaderboard field for the
RevShareCap variant; update mockRevShareCapAccountingCache to return a real
minimal snapshot including awardModel: ReferralProgramAwardModels.RevShareCap,
accountingRecords, and a valid minimal leaderboard (e.g., an empty or default
leaderboard structure that satisfies ReferralEditionSnapshot) so the returned
SWRCache<ReferralEditionSnapshot> matches the real shape and prevents hidden
regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ensapi/src/cache/referral-edition-snapshots.cache.ts`:
- Around line 40-42: Remove the redundant JSDoc `@returns` tags that simply
restate the summary: edit the JSDoc for the function that accepts editionConfig
(the factory that "builds the edition snapshot for the given edition") and
delete its `@returns` entry, and also remove the duplicate `@returns` block at the
later JSDoc (the one around lines 167-172) so only concise summary and param
descriptions remain; keep any meaningful `@returns` only if it adds new
information beyond the summary.

In `@apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts`:
- Around line 37-43: The ENSAnalytics sub-app is using the global errorResponse
hook via createApp which serializes validation errors into the generic {message,
details} shape; change the createApp invocation in ensanalytics-api.ts to not
install the shared errorResponse hook and instead register a
ENSAnalytics-specific validation/error hook (e.g., ensAnalyticsErrorResponse or
ensAnalyticsValidationHook) that returns the route-specific envelope with
responseCode for endpoints like /referral-leaderboard, /referrer/:referrer,
/editions and /accounting; update the middleware/list of hooks passed to
createApp (and remove or override errorResponse) so validation errors are
transformed into the responseCode-keyed shape expected by ENSReferralsClient.

In `@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts`:
- Around line 135-142: The two innerJoin calls joining
ensIndexerSchema.registrationLifecycles and ensIndexerSchema.subgraph_domain in
the query used by getReferralEvents() cause registrar actions without matching
enrichment rows to be dropped; change those innerJoin(...) calls to
leftJoin(...) (the joins on ensIndexerSchema.registrarActions.node →
registrationLifecycles.node and registrationLifecycles.node →
subgraph_domain.id) and add explicit null-handling/fallbacks in
getReferralEvents() (and any downstream use in
buildReferralEditionSnapshotRevShareCap) so missing
registrationLifecycles/subgraph_domain rows don't silently exclude events—either
coalesce missing fields to defaults or gate snapshot construction until
enrichment exists.

In
`@apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts`:
- Around line 24-25: Rename the CSV header from "referralId" to
"registrarActionId" so the exported column matches the value accessor
r.registrarActionId; update the header entry in format-accounting-csv (the array
that contains { header: "referralId", value: (r) => r.registrarActionId }) to {
header: "registrarActionId", value: (r) => r.registrarActionId } so consumers
can correlate rows back to the
ReferralAccountingRecordRevShareCap/registrarActionId field.
- Around line 7-11: csvCell currently only handles RFC-4180 quoting but doesn't
neutralize spreadsheet formulas; ensure values that start with "=", "+", "-", or
"@" are prefixed to prevent formula injection before applying the existing CSV
quoting. Update csvCell(value: string) to check value[0] for those characters
and, if present, prepend a safe prefix (e.g., a single quote) to the value, then
perform the current quote/escape logic; make sure callers that pass
user-controlled fields (notably name and disqualificationReason) use this
hardened csvCell so exported CSVs can't trigger spreadsheet formula execution.

In `@apps/ensapi/src/middleware/ensanalytics.middleware.ts`:
- Around line 54-75: The middleware currently sets ensAnalyticsPrerequisites
then calls await next(), allowing downstream expensive middlewares (like
referralProgramEditionConfigSetMiddleware and
referralEditionSnapshotsCachesMiddleware) to run even when prerequisites fail;
change the three unsupported branches (where hasEnsAnalyticsConfigSupport,
c.var.indexingStatus instanceof Error, and hasEnsAnalyticsIndexingStatusSupport
are checked) to short-circuit by returning a route-aware 503 response (or
otherwise halting the middleware chain) instead of calling await next(), or
alternatively move those expensive downstream middlewares behind a handler-level
check that only runs when ensAnalyticsPrerequisites.supported is true; reference
the functions hasEnsAnalyticsConfigSupport, hasEnsAnalyticsIndexingStatusSupport
and the ensAnalyticsPrerequisites context key to locate the changes.

---

Duplicate comments:
In `@apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts`:
- Around line 1124-1137: The mockRevShareCapAccountingCache helper returns an
object cast to ReferralEditionSnapshot but omits the required leaderboard field
for the RevShareCap variant; update mockRevShareCapAccountingCache to return a
real minimal snapshot including awardModel:
ReferralProgramAwardModels.RevShareCap, accountingRecords, and a valid minimal
leaderboard (e.g., an empty or default leaderboard structure that satisfies
ReferralEditionSnapshot) so the returned SWRCache<ReferralEditionSnapshot>
matches the real shape and prevents hidden regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3440732d-fdee-4f7b-a1f6-dd49591a679c

📥 Commits

Reviewing files that changed from the base of the PR and between 1fde48e and 24b20a6.

📒 Files selected for processing (16)
  • apps/ensapi/src/cache/referral-edition-snapshots.cache.ts
  • apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.test.ts
  • apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts
  • apps/ensapi/src/index.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts
  • apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/format-accounting-csv.ts
  • apps/ensapi/src/lib/hono-factory.ts
  • apps/ensapi/src/middleware/ensanalytics.middleware.ts
  • docs/ensnode.io/ensapi-openapi.json
  • packages/ens-referrals/src/api/index.ts
  • packages/ens-referrals/src/api/prerequisites.ts
  • packages/ens-referrals/src/award-models/pie-split/leaderboard.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/accounting.test.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.test.ts
  • packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts
  • packages/ens-referrals/src/leaderboard.ts

Comment thread apps/ensapi/src/cache/referral-edition-snapshots.cache.ts
Comment thread apps/ensapi/src/handlers/ensanalytics/ensanalytics-api.ts
Comment thread apps/ensapi/src/lib/ensanalytics/referrer-leaderboard/database.ts Outdated
Comment thread apps/ensapi/src/middleware/ensanalytics.middleware.ts
Copilot AI review requested due to automatic review settings April 28, 2026 14:47
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 28, 2026 14:47 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 28, 2026 14:47 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 28, 2026 14:47 Inactive
Comment thread packages/ens-referrals/src/award-models/rev-share-cap/leaderboard.ts Outdated
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 29, 2026 01:16 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 29, 2026 01:16 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 29, 2026 01:16 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants