-
Notifications
You must be signed in to change notification settings - Fork 15
[WIP] Integration of Indexing Status Builder into ENSIndexer API #1614
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
base: feat/indexing-status-builder-2
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,171 @@ | ||||||
| /** | ||||||
| * This file is about parsing the object that is exported by `ponder.config.ts`. | ||||||
| * | ||||||
| * Each Ponder datasource defined in the aforementioned Ponder Config object | ||||||
| * can include information about startBlock and endBlock. This is to let | ||||||
| * Ponder know which blockrange to index for a particular Ponder Datasource. | ||||||
| * | ||||||
| * ENSIndexer, however, needs a blockrange for each indexed chain. This is why | ||||||
| * we examine Ponder Config object, looking for the "lowest" startBlock, and | ||||||
| * the "highest" endBlock defined for each of the indexed chains. | ||||||
| */ | ||||||
|
|
||||||
| import type { AddressConfig, ChainConfig, CreateConfigReturnType } from "ponder"; | ||||||
|
|
||||||
| import { | ||||||
| type BlockNumber, | ||||||
| type Blockrange, | ||||||
| type ChainId, | ||||||
| type ChainIdString, | ||||||
| deserializeBlockNumber, | ||||||
| deserializeBlockrange, | ||||||
| deserializeChainId, | ||||||
| } from "@ensnode/ensnode-sdk"; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder config datasource with a flat `chain` value. | ||||||
| */ | ||||||
| export type PonderConfigDatasourceFlat = { | ||||||
| chain: ChainIdString; | ||||||
| } & AddressConfig & | ||||||
| Blockrange; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder config datasource with a nested `chain` value. | ||||||
| */ | ||||||
| export type PonderConfigDatasourceNested = { | ||||||
| chain: Record<ChainIdString, AddressConfig & Blockrange>; | ||||||
| }; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder config datasource | ||||||
| */ | ||||||
| export type PonderConfigDatasource = PonderConfigDatasourceFlat | PonderConfigDatasourceNested; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder config datasource | ||||||
| */ | ||||||
| type PonderConfigDatasources = { | ||||||
| [datasourceId: string]: PonderConfigDatasource; | ||||||
| }; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder chains config | ||||||
| * | ||||||
| * Chain config for each indexed chain. | ||||||
| */ | ||||||
| type PonderConfigChains = { | ||||||
| [chainId: ChainIdString]: ChainConfig; | ||||||
| }; | ||||||
|
|
||||||
| /** | ||||||
| * Ponder Config | ||||||
| * | ||||||
| * A utility type describing Ponder Config. | ||||||
| */ | ||||||
| export type PonderConfigType = CreateConfigReturnType< | ||||||
| PonderConfigChains, | ||||||
| PonderConfigDatasources, | ||||||
| PonderConfigDatasources, | ||||||
| PonderConfigDatasources | ||||||
| >; | ||||||
|
|
||||||
| /** | ||||||
| * Ensure the `ponderDatasource` is {@link PonderConfigDatasourceFlat}. | ||||||
| */ | ||||||
| function isPonderDatasourceFlat( | ||||||
| ponderDatasource: PonderConfigDatasource, | ||||||
| ): ponderDatasource is PonderConfigDatasourceFlat { | ||||||
| return typeof ponderDatasource.chain === "string"; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Ensure the `ponderDatasource` is {@link PonderConfigDatasourceNested}. | ||||||
| */ | ||||||
| function isPonderDatasourceNested( | ||||||
| ponderDatasource: PonderConfigDatasource, | ||||||
| ): ponderDatasource is PonderConfigDatasourceNested { | ||||||
| return typeof ponderDatasource.chain === "object"; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Build {@link Blockrange} for each indexed chain. | ||||||
| * | ||||||
| * Invariants: | ||||||
| * - every chain include a startBlock, | ||||||
|
||||||
| * - every chain include a startBlock, | |
| * - every chain includes a startBlock, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buildChainsBlockrange introduces non-trivial parsing/invariant logic (flat vs nested datasources, min start block, conditional end block). There are existing Vitest suites for indexing-status/ponder-metadata; adding focused unit tests for this function (including mixed flat/nested configs and partial endBlock definitions) would help prevent regressions.
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isEndBlockForChainAllowed is computed as chainEndBlocks.length === chainStartBlocks.length, but chainStartBlocks only counts sources where startBlock is a number. This can incorrectly allow an endBlock when some datasources for the chain use non-numeric startBlock (e.g. "latest") or otherwise weren’t counted. Track the total number of datasources that match the chain separately and only allow endBlock if all matching datasources provide a numeric endBlock (and consider failing fast if any matching datasource has a non-numeric startBlock, since it’s described as unsupported).
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: endBLock should be endBlock.
| // 3.b) Get the highest endBLock for the chain. | |
| // 3.b) Get the highest endBlock for the chain. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,70 @@ | ||||||
| import config from "@/config"; | ||||||
|
|
||||||
| import { publicClients as ponderPublicClients } from "ponder:api"; | ||||||
| import type { PublicClient } from "viem"; | ||||||
|
|
||||||
| import { deserializeChainId } from "@ensnode/ensnode-sdk"; | ||||||
| import { type ChainId, PonderClient } from "@ensnode/ponder-sdk"; | ||||||
|
|
||||||
| import type { ChainBlockRefs } from "@/lib/indexing-status/ponder-metadata"; | ||||||
|
||||||
| import type { ChainBlockRefs } from "@/lib/indexing-status/ponder-metadata"; | |
| import type { ChainBlockRefs } from "@/lib/indexing-status-builder/chain-block-refs"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cachedChainsBlockRefs is initialized using a top-level await, which performs network requests (Ponder metrics + RPC block fetches) at module import time. If any of these calls fail, the whole module import fails and the API server may not start. Consider lazy initialization (e.g., an async getter with memoization) and/or moving initialization into the server startup sequence with explicit retry/backoff and a clear failure mode.
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Object.freeze(chainsBlockRefs) does not make a Map immutable (callers can still mutate via .set()/.delete()). If immutability is required, avoid exporting a mutable Map instance (e.g., return a ReadonlyMap reference that isn’t shared, or wrap access behind functions and keep the mutable map private).
| return Object.freeze(chainsBlockRefs); | |
| return chainsBlockRefs as Readonly<Map<ChainId, ChainBlockRefs>>; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code duplication in Ponder config parsing logic across two files creates maintenance burden and risk of divergence