Integration of Indexing Status Builder into ENSIndexer API#1614
Integration of Indexing Status Builder into ENSIndexer API#1614
Conversation
This module aims to host an abstraction layer that primarly relies on `viem` and `@ensnode/ponder-sdk` abstractions. It is still using `@ensnode/ensnode-sdk` for convenience, but leave the possiblity to iterate and remove `@ensnode/ensnode-sdk` dependency in the future.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 3 Skipped Deployments
|
|
📝 WalkthroughWalkthroughThe PR introduces a local Ponder client architecture with retry logic for ENSIndexer. It refactors the indexing status handler to use a new LocalPonderClient singleton that manages per-chain metadata (blocks and indexing status). Dependencies are updated to use catalog management, and the type system is split to separate immutable and dynamic indexing metadata. Changes
Sequence Diagram(s)sequenceDiagram
participant App as ENSIndexer App
participant Init as LocalPonderClient.init()
participant PonderApp as Ponder App
participant Config as Ponder Config
participant Metrics as Metrics/Status
participant Handler as /indexing-status Handler
App->>Init: LocalPonderClient.init(ponderAppUrl)
Init->>PonderApp: fetch config
Init->>Config: parse ponderConfig
Init->>Init: buildPublicClientsMap()
Init->>Init: buildChainsIndexingMetadataFixed()
Init->>PonderApp: fetch metrics & status
Init-->>App: return initialized client
Handler->>App: request indexing status
App->>Handler: getLocalPonderClient()
Handler->>Handler: chainsIndexingMetadata()
Handler->>PonderApp: fetch dynamic metrics/status
Handler->>Handler: merge fixed + dynamic metadata
Handler->>Handler: buildOmnichainIndexingStatusSnapshot()
Handler-->>App: return omnichain snapshot
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Integrates the new “Indexing Status Builder” layer into the ENSIndexer (Ponder) HTTP API by moving /indexing-status snapshot construction onto builder + PonderClient-fetched metadata.
Changes:
- Added
fetchAndBuildCrossChainIndexingStatusSnapshot()to fetch Ponder/metrics+/status, compute chain block refs, and build an omnichain cross-chain snapshot. - Added a Ponder-config parsing helper to derive per-chain blockranges from datasource start/end blocks.
- Updated
/indexing-statushandler to use the new snapshot builder flow.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts | New builder integration: fetch Ponder metadata, compute block refs, build cross-chain snapshot. |
| apps/ensindexer/ponder/src/lib/chains-config-blockrange.ts | New helper to derive per-chain blockranges from ponder.config.ts datasources. |
| apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts | Switches /indexing-status to the new fetch+build snapshot function. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
apps/ensindexer/ponder/src/lib/cross-chain-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
17e224c to
8ac1c11
Compare
apps/ensindexer/ponder/src/lib/omnichian-indexing-status-snapshot.ts
Outdated
Show resolved
Hide resolved
8ac1c11 to
6303636
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Simplify code, merge input params for `buildOmnichainIndexingStatusSnapshot` into a single input object with chain metadata.
6303636 to
f5ecab1
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/ensindexer/ponder/src/api/lib/local-ponder-client.ts`:
- Line 57: The two bare console.log calls inside the function
buildChainsIndexingMetadataFixed should be replaced with structured logger calls
(e.g., processLogger.info / processLogger.debug or the module's logger) so logs
remain JSON/structured in production; locate the console.log usages in
buildChainsIndexingMetadataFixed and swap them for the project's logger, include
a descriptive message and any relevant context values (e.g., variables being
logged) rather than free-form console output, and remove the console.log at the
second occurrence as well (the one mirrored later in the file).
---
Duplicate comments:
In `@apps/ensindexer/ponder/src/api/lib/local-ponder-client.ts`:
- Around line 77-81: The current truthy check on
chainConfigBlockrange.startBlock wrongly treats block 0 as missing; change the
condition in local-ponder-client.ts to an explicit nullish check (e.g., check
startBlock == null or startBlock === null || startBlock === undefined) so that a
startBlock of 0 is accepted, keeping the same Error thrown that references
chainId and chainConfigBlockrange.startBlock.
- Around line 127-161: buildChainsIndexingMetadataDynamic currently calls
ponderClient.metrics() and ponderClient.status() on every
chainsIndexingMetadata() invocation causing two HTTP round-trips per request;
add a short-lived (e.g. 2–5s) module-level TTL cache for the combined dynamic
result to reduce load. Implement a simple cached value and expiry timestamp at
module scope keyed to the set of indexed chains (or a single global cache if
indexedChainIds are stable), have chainsIndexingMetadata() return the cached Map
when not expired, otherwise refresh by calling ponderClient.metrics() and
ponderClient.status() once and store the result and new expiry; ensure
concurrent refreshes are serialized (e.g. a single in-flight Promise stored in
the cache) so buildChainsIndexingMetadataDynamic, ponderClient.metrics(), and
ponderClient.status() are not invoked repeatedly during refresh.
- Around line 87-91: The current throw when chainIndexingMetrics.state !==
ChainIndexingStates.Historical is treated as a hard failure but is actually
transient during startup; change the thrown error to a retryable/transient error
so pRetry callers can retry. Replace the plain Error with a specific transient
error type (e.g., RetryableError or TransientError) or an Error instance that
sets a retry flag/property (e.g., error.retryable = true) and preserve the
existing message including chainId and chainIndexingMetrics.state; update any
imports or declare the new error class (RetryableError) in the module if needed
and throw that from the block that builds ChainsIndexingMetadataFixed when state
is not Historical.
|
@greptile review |
|
@greptile review |
|
@greptile review |
Additional Comments (1)
The old |
lightwalker-eth
left a comment
There was a problem hiding this comment.
@tk-o Thanks. Shared a few questions appreciate your advice.
apps/ensindexer/tsconfig.json
Outdated
| "paths": { | ||
| "@/*": ["./src/*"] | ||
| "@/*": ["./src/*"], | ||
| "@/ponder/*": ["./ponder/src/*"] |
There was a problem hiding this comment.
@tk-o Appreciate background info that motivated this update. Thanks
There was a problem hiding this comment.
This update was required so we could use the following style of imports:
import { LocalPonderClient } from "@/ponder/api/lib/local-ponder-client";
import ponderConfig from "@/ponder/config";
Without this update, we'd need to use relative paths, which would also work. However, this @/ponder/* alias is quite nice to apply. Please feel free to share your thoughts on that motivation 👍
| import { createCrossChainIndexingStatusSnapshotOmnichain } from "@/lib/indexing-status/build-index-status"; | ||
| import { fetchAndBuildOmnichainIndexingStatusSnapshot } from "@/ponder/lib/omnichian-indexing-status-snapshot"; | ||
|
|
||
| const publicClients = new Map<ChainId, PublicClient>( |
There was a problem hiding this comment.
This will be a great utility function to move into the "local ponder client"
There was a problem hiding this comment.
I created publicClient(chainId: ChainId): PublicClient method on LocalPonderClient to allow access to Ponder cached RPC clients 👍
|
|
||
| // Ponder API dependencies: Ponder config, and PonderClient | ||
| // TODO: move into a separate file | ||
| const indexedChainIds = Object.keys(ponderConfig.chains).map((maybeChainId) => |
There was a problem hiding this comment.
Is there a special reason we read this from the ponder config and not the ENSIndexer config object?
There was a problem hiding this comment.
Great point, I think we could use just ENSIndexer config here 👍
| /** | ||
| * Build a list of indexed chain IDs. | ||
| */ | ||
| export function buildIndexedChainIds(publicClients: Map<ChainId, PublicClient>): ChainId[] { |
There was a problem hiding this comment.
Not clear on some things that are happening here.
I thought I saw some other code that was getting the set of indexed chain ids from the ponder config?
But now this is another function that seems to be getting the set of indexed chain ids from a different source: from a map of ChainId / PublicClient.
Are we doing things here right?
Is there potentially a mixup here caused by how there's a distinction in Ponder between:
- The set of RPCs (PublicClients) that are configured.
- The set of chains configured for indexing.
Where these two might not be exactly the same? If so we need more precision with our terminology and how we communicate these ideas.
It also seems strange to me to see this function all alone in this file operating on such a specific data structure. Maybe it should live somewhere else?
Appreciate your advice.
There was a problem hiding this comment.
I think you are right, we don't have to build a list of indexed chain IDs based on any Ponder API. All we need is to pass the set of indexed chain IDs from ENSIndexer config into the Local Ponder Client.
| * @throws Error if any of the required data cannot be fetched or is invalid, | ||
| * or if invariants are violated. | ||
| */ | ||
| async function buildChainsIndexingMetadataFixed( |
There was a problem hiding this comment.
What does "fixed" mean? Does it mean "immutable"?
There was a problem hiding this comment.
Yes, it means "immutable", which I think suits here better than "fixed". Will apply renames accordingly 👍
| * @returns The initialized LocalPonderClient instance. | ||
| * @throws Error if the client fails to initialize after the specified number of retries. | ||
| */ | ||
| localPonderClientPromise = pRetry(() => LocalPonderClient.init(config.ensIndexerUrl), { |
There was a problem hiding this comment.
Hmm. Why are we trying to force the local ponder client to init this way upon process startup?
I assume we're doing this because of a strategy to eagerly load some state into the local ponder client that requires network fetches? But why do we need such an eager strategy? Why wouldn't a lazy strategy work nice?
Updates LocalPonderClient dependency to favour Ponder SDK types
Remove unused module
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 12 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function buildChainsBlockrange( | ||
| ponderConfig: PonderConfigType, | ||
| ): Map<ChainId, BlockrangeWithStartBlock> { | ||
| const chainsBlockrange = new Map<ChainId, BlockrangeWithStartBlock>(); |
There was a problem hiding this comment.
buildChainsBlockrange re-implements the existing getChainsBlockrange logic (which has a dedicated test suite). Since this is a new module in a different path, it currently has no direct coverage; adding unit tests mirroring apps/ensindexer/src/lib/indexing-status/ponder-metadata/ponder-metadata.test.ts would help prevent subtle divergences/regressions in blockrange derivation.
| import type { | ||
| ChainIndexingMetadata, | ||
| ChainIndexingMetadataDynamic, | ||
| ChainIndexingMetadataFixed, | ||
| } from "@/lib/indexing-status-builder/chain-indexing-metadata"; |
There was a problem hiding this comment.
ChainIndexingMetadataFixed is imported/used here, but chain-indexing-metadata.ts no longer exports that name (it exports ChainIndexingMetadataImmutable/ChainIndexingMetadataDynamic and ChainIndexingMetadata). This will fail to typecheck/build. Align the type names (e.g., rename the import/usages to ChainIndexingMetadataImmutable, or re-export ChainIndexingMetadataFixed as an alias for the immutable/fixed metadata type).
| let localPonderClientPromise: Promise<LocalPonderClient>; | ||
|
|
||
| /** | ||
| * Get the singleton LocalPonderClient instance for the ENSIndexer app. | ||
| * | ||
| * This function initializes the LocalPonderClient on the first call by | ||
| * connecting to the local Ponder app and fetching the necessary data to | ||
| * build the client's state. The initialized client is cached and returned | ||
| * on subsequent calls. | ||
| * | ||
| * @returns The singleton LocalPonderClient instance. | ||
| * @throws Error if the client fails to initialize after | ||
| * the specified number of retries. | ||
| */ | ||
| export async function getLocalPonderClient(): Promise<LocalPonderClient> { | ||
| // Return the cached client instance if it has already been initialized. | ||
| if (localPonderClientPromise) { | ||
| return localPonderClientPromise; | ||
| } |
There was a problem hiding this comment.
localPonderClientPromise is declared without an initializer but read in the if (localPonderClientPromise) check. With TS control-flow analysis this is a “used before being assigned” error, and at runtime it starts as undefined anyway. Declare it as Promise<LocalPonderClient> | undefined (or use a definite assignment assertion) so the singleton cache pattern is type-safe.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts`:
- Around line 43-53: The JSDoc for PonderConfigDatasources duplicates the
singular comment used for PonderConfigDatasource; update the comment above the
type PonderConfigDatasources to a distinct, plural description (e.g., "Ponder
config datasources" or "Collection of Ponder config datasources") so it
accurately documents the map type instead of repeating the singular description
for PonderConfigDatasource.
- Line 152: Fix the typo in the inline comment that currently reads "Get the
highest endBLock for the chain." — change "endBLock" to "endBlock" so the
comment correctly references the endBlock concept used in the
chains-config-blockrange logic; ensure any other nearby occurrences of the same
misspelling in the same file are corrected to "endBlock".
- Around line 175-181: The second guard checking typeof blockrange.startBlock
=== "undefined" is unreachable; remove that throw and instead assert the type of
the returned value from deserializeBlockrange so TypeScript knows startBlock is
present. Replace the runtime guard with a type assertion/cast to
BlockrangeWithStartBlock (or add an inline assertion comment) when calling
chainsBlockrange.set(chainId, ...) after
deserializeBlockrange(chainLowestStartBlock, ...), referencing
deserializeBlockrange, blockrange, chainLowestStartBlock,
BlockrangeWithStartBlock, chainsBlockrange.set and serializedChainId to locate
the code.
- Around line 88-92: The type guard isPonderDatasourceNested incorrectly treats
null as an object; update it to explicitly exclude null (and arrays if
applicable) before returning true. Change the implementation to check
ponderDatasource.chain !== null && typeof ponderDatasource.chain === "object"
(and add && !Array.isArray(ponderDatasource.chain) if chain should be a plain
object) so downstream access like ponderSource.chain[serializedChainId] is safe.
In `@apps/ensindexer/src/lib/indexing-status-builder/chain-indexing-metadata.ts`:
- Around line 53-60: The JSDoc for the exported type ChainIndexingMetadata still
uses the outdated term "fixed metadata"; update that comment to say "immutable
metadata" to match the renamed type ChainIndexingMetadataImmutable and ensure
consistency — e.g., replace "fixed metadata (backfill scope and indexing
config)" with "immutable metadata (backfill scope and indexing config)" in the
block above the ChainIndexingMetadata declaration.
---
Duplicate comments:
In `@apps/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts`:
- Around line 110-114: The inline cast on ponderSources hides type errors —
remove the blanket "as PonderConfigDatasource[]" and ensure each source is
correctly typed or filtered: either change
ponderConfig.accounts/blocks/contracts to have values of PonderConfigDatasource,
or create a type‑guard function (e.g. isPonderConfigDatasource) and build
ponderSources by concatenating Object.values(...) and filtering with that guard
so only valid PonderConfigDatasource items remain; reference symbols:
ponderSources, ponderConfig, PonderConfigDatasource, and the type‑guard you add.
In `@apps/ensindexer/src/lib/indexing-status-builder/chain-indexing-metadata.ts`:
- Around line 30-51: No changes required—the JSDoc title was corrected and the
interface ChainIndexingMetadataDynamic with non-optional fields indexingMetrics
and indexingStatus is correct; leave the interface and its doc-comment as-is.
| /** | ||
| * Ponder config datasource | ||
| */ | ||
| export type PonderConfigDatasource = PonderConfigDatasourceFlat | PonderConfigDatasourceNested; | ||
|
|
||
| /** | ||
| * Ponder config datasource | ||
| */ | ||
| type PonderConfigDatasources = { | ||
| [datasourceId: string]: PonderConfigDatasource; | ||
| }; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Duplicate JSDoc comment text for PonderConfigDatasources.
The comment on lines 48–50 is identical to the one on lines 43–45 ("Ponder config datasource"). Since this comment describes the collection type PonderConfigDatasources, it should be distinct and use the plural form.
✏️ Proposed fix
/**
- * Ponder config datasource
+ * Ponder config datasources
*/
type PonderConfigDatasources = {📝 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.
| /** | |
| * Ponder config datasource | |
| */ | |
| export type PonderConfigDatasource = PonderConfigDatasourceFlat | PonderConfigDatasourceNested; | |
| /** | |
| * Ponder config datasource | |
| */ | |
| type PonderConfigDatasources = { | |
| [datasourceId: string]: PonderConfigDatasource; | |
| }; | |
| /** | |
| * Ponder config datasource | |
| */ | |
| export type PonderConfigDatasource = PonderConfigDatasourceFlat | PonderConfigDatasourceNested; | |
| /** | |
| * Ponder config datasources | |
| */ | |
| type PonderConfigDatasources = { | |
| [datasourceId: string]: PonderConfigDatasource; | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts` around lines
43 - 53, The JSDoc for PonderConfigDatasources duplicates the singular comment
used for PonderConfigDatasource; update the comment above the type
PonderConfigDatasources to a distinct, plural description (e.g., "Ponder config
datasources" or "Collection of Ponder config datasources") so it accurately
documents the map type instead of repeating the singular description for
PonderConfigDatasource.
| function isPonderDatasourceNested( | ||
| ponderDatasource: PonderConfigDatasource, | ||
| ): ponderDatasource is PonderConfigDatasourceNested { | ||
| return typeof ponderDatasource.chain === "object"; | ||
| } |
There was a problem hiding this comment.
typeof null === "object" makes the nested guard unsound.
typeof null === "object" is true in JavaScript. If ponderDatasource.chain is null (which cannot be excluded after the as PonderConfigDatasource[] cast on line 114), isPonderDatasourceNested returns true, and the subsequent access ponderSource.chain[serializedChainId] at line 130 throws a TypeError at runtime.
🛡️ Proposed fix
function isPonderDatasourceNested(
ponderDatasource: PonderConfigDatasource,
): ponderDatasource is PonderConfigDatasourceNested {
- return typeof ponderDatasource.chain === "object";
+ return typeof ponderDatasource.chain === "object" && ponderDatasource.chain !== null;
}📝 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.
| function isPonderDatasourceNested( | |
| ponderDatasource: PonderConfigDatasource, | |
| ): ponderDatasource is PonderConfigDatasourceNested { | |
| return typeof ponderDatasource.chain === "object"; | |
| } | |
| function isPonderDatasourceNested( | |
| ponderDatasource: PonderConfigDatasource, | |
| ): ponderDatasource is PonderConfigDatasourceNested { | |
| return typeof ponderDatasource.chain === "object" && ponderDatasource.chain !== null; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts` around lines
88 - 92, The type guard isPonderDatasourceNested incorrectly treats null as an
object; update it to explicitly exclude null (and arrays if applicable) before
returning true. Change the implementation to check ponderDatasource.chain !==
null && typeof ponderDatasource.chain === "object" (and add &&
!Array.isArray(ponderDatasource.chain) if chain should be a plain object) so
downstream access like ponderSource.chain[serializedChainId] is safe.
| // ponderSource for that chain has its respective `endBlock` defined. | ||
| const isEndBlockForChainAllowed = chainEndBlocks.length === chainStartBlocks.length; | ||
|
|
||
| // 3.b) Get the highest endBLock for the chain. |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Typo: "endBLock" → "endBlock".
- // 3.b) Get the highest endBLock for the chain.
+ // 3.b) Get the highest endBlock for the chain.📝 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.
| // 3.b) Get the highest endBLock for the chain. | |
| // 3.b) Get the highest endBlock for the chain. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts` at line 152,
Fix the typo in the inline comment that currently reads "Get the highest
endBLock for the chain." — change "endBLock" to "endBlock" so the comment
correctly references the endBlock concept used in the chains-config-blockrange
logic; ensure any other nearby occurrences of the same misspelling in the same
file are corrected to "endBlock".
| if (typeof blockrange.startBlock === "undefined") { | ||
| throw new Error( | ||
| `Invalid blockrange for chain '${serializedChainId}'. The blockrange must include a valid startBlock number.`, | ||
| ); | ||
| } | ||
|
|
||
| chainsBlockrange.set(chainId, blockrange as BlockrangeWithStartBlock); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
The second startBlock guard at lines 175–179 is unreachable at runtime.
After the throw at lines 161–165, chainLowestStartBlock is a valid BlockNumber, so deserializeBlockrange will either throw on schema failure or return with startBlock present. The if (typeof blockrange.startBlock === "undefined") branch can never execute.
The guard exists only to satisfy TypeScript's inferred return type of deserializeBlockrange (Blockrange with optional startBlock) before the cast on line 181. Consider using an assertion comment or a type assertion directly to make the intent clearer without the misleading dead throw:
♻️ Proposed simplification
- // Invariant: the blockrange must include a valid startBlock number.
- if (typeof blockrange.startBlock === "undefined") {
- throw new Error(
- `Invalid blockrange for chain '${serializedChainId}'. The blockrange must include a valid startBlock number.`,
- );
- }
-
- chainsBlockrange.set(chainId, blockrange as BlockrangeWithStartBlock);
+ // startBlock is guaranteed non-undefined: chainLowestStartBlock was validated above.
+ chainsBlockrange.set(chainId, blockrange as BlockrangeWithStartBlock);📝 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.
| if (typeof blockrange.startBlock === "undefined") { | |
| throw new Error( | |
| `Invalid blockrange for chain '${serializedChainId}'. The blockrange must include a valid startBlock number.`, | |
| ); | |
| } | |
| chainsBlockrange.set(chainId, blockrange as BlockrangeWithStartBlock); | |
| // startBlock is guaranteed non-undefined: chainLowestStartBlock was validated above. | |
| chainsBlockrange.set(chainId, blockrange as BlockrangeWithStartBlock); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensindexer/ponder/src/api/lib/chains-config-blockrange.ts` around lines
175 - 181, The second guard checking typeof blockrange.startBlock ===
"undefined" is unreachable; remove that throw and instead assert the type of the
returned value from deserializeBlockrange so TypeScript knows startBlock is
present. Replace the runtime guard with a type assertion/cast to
BlockrangeWithStartBlock (or add an inline assertion comment) when calling
chainsBlockrange.set(chainId, ...) after
deserializeBlockrange(chainLowestStartBlock, ...), referencing
deserializeBlockrange, blockrange, chainLowestStartBlock,
BlockrangeWithStartBlock, chainsBlockrange.set and serializedChainId to locate
the code.
| /** | ||
| * Complete indexing metadata for a single chain. | ||
| * | ||
| * Combines both the fixed metadata (backfill scope and indexing config) and | ||
| * the dynamic metadata (indexing metrics and indexing status) for | ||
| * a chain that is being indexed by a Ponder app. | ||
| */ | ||
| export type ChainIndexingMetadata = ChainIndexingMetadataImmutable & ChainIndexingMetadataDynamic; |
There was a problem hiding this comment.
Stale "fixed" terminology in JSDoc — should be "immutable".
Line 56 still says "fixed metadata" — a leftover from when the interface was named ChainIndexingMetadataFixed. It should now say "immutable metadata" to match the renamed ChainIndexingMetadataImmutable.
📝 Proposed fix
/**
* Complete indexing metadata for a single chain.
*
- * Combines both the fixed metadata (backfill scope and indexing config) and
+ * Combines both the immutable metadata (backfill scope and indexing config) and
* the dynamic metadata (indexing metrics and indexing status) for
* a chain that is being indexed by a Ponder app.
*/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensindexer/src/lib/indexing-status-builder/chain-indexing-metadata.ts`
around lines 53 - 60, The JSDoc for the exported type ChainIndexingMetadata
still uses the outdated term "fixed metadata"; update that comment to say
"immutable metadata" to match the renamed type ChainIndexingMetadataImmutable
and ensure consistency — e.g., replace "fixed metadata (backfill scope and
indexing config)" with "immutable metadata (backfill scope and indexing config)"
in the block above the ChainIndexingMetadata declaration.
Lite PR
Tip: Review docs on the ENSNode PR process
Summary
Indexing Status Buildermodule #1613 with ENSIndexer APILocalPonderClientto ensure initialization was executed exactly once during application startup.LocalPonderClientmay require retiries, as Ponder HTTP endpoints may not be available when the first client initialization is attempted.Why
OmnichainIndexingStatusSnapshotbased on abstractions fromviemand@ensnode/ponder-sdk(and if really needed, for the time being, from@ensnode/ensnode-sdk).Testing
Notes for Reviewer (Optional)
Indexing Status Buildermodule #1613Pre-Review Checklist (Blocking)