From b23a22c767d3bf6ca33a915a5ba1d646772c2220 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 10:35:04 +0200 Subject: [PATCH 01/12] Empty commit From 8af8c472836eeeeed9349f712b6b0b4e91c011f7 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 11:52:44 +0200 Subject: [PATCH 02/12] add debug info log --- .../lib/indexing-status-builder/indexing-status-builder.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index 66f56a1627..aabe5fb044 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -31,6 +31,8 @@ import { RangeTypeIds, } from "@ensnode/ponder-sdk"; +import { logger } from "@/lib/logger"; + export class IndexingStatusBuilder { /** * Immutable Indexing Config @@ -130,6 +132,11 @@ export class IndexingStatusBuilder { const { checkpointBlock } = chainIndexingStatus; const { indexedBlockrange } = chainIndexingConfig; + logger.info({ + msg: `Building indexing status snapshot for chain`, + payload: { chainIndexingConfig, chainIndexingMetrics, chainIndexingStatus }, + }); + // In omnichain ordering, if the startBlock is the same as the // status block, the chain has not started yet. if (isBlockRefEqualTo(indexedBlockrange.startBlock, checkpointBlock)) { From dd5e137b0be25dd06b520f1cb35f17e6b41a44dd Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 12:46:04 +0200 Subject: [PATCH 03/12] Fix `buildIndexedBlockranges` to include global end block setting --- .../ensindexer/src/lib/local-ponder-client.ts | 6 +- .../shared/config/indexed-blockranges.test.ts | 78 ++++++++++++++++++- .../src/shared/config/indexed-blockranges.ts | 11 ++- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/apps/ensindexer/src/lib/local-ponder-client.ts b/apps/ensindexer/src/lib/local-ponder-client.ts index fcfe293a92..8d62f21cd2 100644 --- a/apps/ensindexer/src/lib/local-ponder-client.ts +++ b/apps/ensindexer/src/lib/local-ponder-client.ts @@ -10,7 +10,11 @@ import { getPluginsAllDatasourceNames } from "@/lib/plugin-helpers"; import { localPonderContext } from "./local-ponder-context"; const pluginsAllDatasourceNames = getPluginsAllDatasourceNames(config.plugins); -const indexedBlockranges = buildIndexedBlockranges(config.namespace, pluginsAllDatasourceNames); +const indexedBlockranges = buildIndexedBlockranges( + config.namespace, + config.globalBlockrange.endBlock, + pluginsAllDatasourceNames, +); export const localPonderClient = new LocalPonderClient( config.indexedChainIds, diff --git a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts index 908ab4cc63..15717b3483 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts @@ -68,7 +68,11 @@ describe("buildIndexedBlockranges()", () => { ]); // Act - const result = buildIndexedBlockranges(ENSNamespaceIds.Mainnet, pluginsRequiredDatasourceNames); + const result = buildIndexedBlockranges( + ENSNamespaceIds.Mainnet, + undefined, + pluginsRequiredDatasourceNames, + ); const expectedEntries = new Map([ [1, buildBlockNumberRange(80, undefined)], @@ -104,7 +108,11 @@ describe("buildIndexedBlockranges()", () => { ]); // Act - const result = buildIndexedBlockranges(ENSNamespaceIds.Mainnet, pluginsRequiredDatasourceNames); + const result = buildIndexedBlockranges( + ENSNamespaceIds.Mainnet, + undefined, + pluginsRequiredDatasourceNames, + ); // Assert @@ -137,7 +145,11 @@ describe("buildIndexedBlockranges()", () => { ]); // Act - const result = buildIndexedBlockranges(ENSNamespaceIds.Mainnet, pluginsRequiredDatasourceNames); + const result = buildIndexedBlockranges( + ENSNamespaceIds.Mainnet, + undefined, + pluginsRequiredDatasourceNames, + ); // Assert expect(result).toStrictEqual(new Map([[8453, buildBlockNumberRange(17571480, undefined)]])); @@ -150,9 +162,67 @@ describe("buildIndexedBlockranges()", () => { const pluginsDatasourceNames = new Map([[PluginName.Subgraph, [DatasourceNames.Seaport]]]); // Act - const result = buildIndexedBlockranges(ENSNamespaceIds.Mainnet, pluginsDatasourceNames); + const result = buildIndexedBlockranges( + ENSNamespaceIds.Mainnet, + undefined, + pluginsDatasourceNames, + ); // Assert expect(result).toStrictEqual(new Map()); }); + + it("applies global end block to contracts without end block and skips contracts starting after global end block", () => { + // Arrange + const ensrootDatasourceConfig: unknown = { + chain: { id: 1 }, + contracts: { + registry: { startBlock: 100 }, // no endBlock, should use global end block (500) + resolver: { startBlock: 80, endBlock: 200 }, // has endBlock, should keep it + registrar: { startBlock: 600 }, // startBlock > global end block, should be skipped + }, + }; + + const basenamesDatasourceConfig: unknown = { + chain: { id: 8453 }, + contracts: { + registry: { startBlock: 5 }, // no endBlock, should use global end block (500) + }, + }; + + const datasourcesByName: Partial< + Record> + > = { + [DatasourceNames.ENSRoot]: datasourceMock(ensrootDatasourceConfig), + [DatasourceNames.Basenames]: datasourceMock(basenamesDatasourceConfig), + }; + + maybeGetDatasourceMock.mockImplementation( + (_namespace, datasourceName) => datasourcesByName[datasourceName as DatasourceName], + ); + + const pluginsRequiredDatasourceNames = new Map([ + [PluginName.Subgraph, [DatasourceNames.ENSRoot]], + [PluginName.Basenames, [DatasourceNames.Basenames]], + ]); + + const globalBlockrangeEndBlock = 500; + + // Act + const result = buildIndexedBlockranges( + ENSNamespaceIds.Mainnet, + globalBlockrangeEndBlock, + pluginsRequiredDatasourceNames, + ); + + // Assert + const expectedEntries = new Map([ + // Chain 1: min startBlock = 80, max endBlock = max(500 from registry, 200 from resolver) = 500 + [1, buildBlockNumberRange(80, 500)], + // Chain 8453: startBlock = 5, endBlock = 500 (from global) + [8453, buildBlockNumberRange(5, 500)], + ]); + + expect(result).toStrictEqual(expectedEntries); + }); }); diff --git a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts index 3611d71d2f..cefb442142 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts @@ -9,6 +9,7 @@ import { import type { PluginName } from "../../ensindexer/config/types"; import { + type BlockNumberRange, type BlockNumberRangeWithStartBlock, buildBlockNumberRange, mergeBlockNumberRanges, @@ -22,6 +23,7 @@ import { */ export function buildIndexedBlockranges( namespace: ENSNamespaceId, + globalBlockrangeEndBlock: BlockNumberRange["endBlock"], pluginsDatasourceNames: Map, ): Map { const indexedBlockranges = new Map(); @@ -39,9 +41,16 @@ export function buildIndexedBlockranges( for (const datasourceContract of datasourceContracts) { const currentChainIndexedBlockrange = indexedBlockranges.get(datasourceChainId); + if (globalBlockrangeEndBlock && datasourceContract.startBlock > globalBlockrangeEndBlock) { + // If the contract's start block is greater than the global end block, + // then this contract is not indexed at all, so we can skip it from + // consideration in the indexed blockrange. + continue; + } + const contractIndexedBlockrange = buildBlockNumberRange( datasourceContract.startBlock, - datasourceContract.endBlock, + datasourceContract.endBlock || globalBlockrangeEndBlock, ); const indexedBlockrange = currentChainIndexedBlockrange From 588558169d6b03b55aec8a76343ef01c703d4e6c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 12:50:40 +0200 Subject: [PATCH 04/12] Improve debug info --- .../init-indexing-onchain-events.ts | 17 +++++- .../indexing-status-builder.ts | 54 +++++++++++-------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts b/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts index cf8c22fe44..5d73753138 100644 --- a/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts +++ b/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts @@ -4,6 +4,8 @@ * event handlers. */ +import pRetry from "p-retry"; + import { migrateEnsNodeSchema } from "@/lib/ensdb/migrate-ensnode-schema"; import { ensDbClient } from "@/lib/ensdb/singleton"; import { startEnsDbWriterWorker } from "@/lib/ensdb-writer-worker/singleton"; @@ -15,7 +17,20 @@ import { indexingMetadataContextBuilder } from "@/lib/indexing-metadata-context- import { logger } from "@/lib/logger"; async function upsertIndexingMetadataContextRecord(): Promise { - const indexingMetadataContext = await indexingMetadataContextBuilder.getIndexingMetadataContext(); + // indexingMetadataContextBuilder may face transient errors while trying to + // build the Indexing Metadata Context, so we allow to retry this operation + // a few times before giving up and crashing the ENSIndexer instance. + const indexingMetadataContext = await pRetry( + () => indexingMetadataContextBuilder.getIndexingMetadataContext(), + { + retries: 5, + onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => { + logger.info({ + msg: ` Building Indexing Metadata Context attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`, + }); + }, + }, + ); logger.info({ msg: `Upserting Indexing Metadata Context Initialized`, diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index aabe5fb044..4769d55066 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -92,22 +92,30 @@ export class IndexingStatusBuilder { const chainIndexingStatus = chainIndexingStatuses.get(chainId); const chainIndexingConfig = chainsIndexingConfig.get(chainId); - // Invariants ensuring required data is available. - if (!chainIndexingStatus) { - throw new Error(`Indexing status not found for chain ID ${chainId}`); + try { + // Invariants ensuring required data is available. + if (!chainIndexingStatus) { + throw new Error(`Indexing status not found for chain ID ${chainId}`); + } + + if (!chainIndexingConfig) { + throw new Error(`Indexing config not found for chain ID ${chainId}`); + } + + const chainStatusSnapshot = this.buildChainIndexingStatusSnapshot( + chainIndexingMetric, + chainIndexingStatus, + chainIndexingConfig, + ); + + chainStatusSnapshots.set(chainId, chainStatusSnapshot); + } catch (error) { + logger.error({ + msg: `Building indexing status snapshot for chain ID ${chainId} failed`, + payload: { chainIndexingConfig, chainIndexingMetrics, chainIndexingStatus }, + }); + throw error; } - - if (!chainIndexingConfig) { - throw new Error(`Indexing config not found for chain ID ${chainId}`); - } - - const chainStatusSnapshot = this.buildChainIndexingStatusSnapshot( - chainIndexingMetric, - chainIndexingStatus, - chainIndexingConfig, - ); - - chainStatusSnapshots.set(chainId, chainStatusSnapshot); } return chainStatusSnapshots; @@ -132,11 +140,6 @@ export class IndexingStatusBuilder { const { checkpointBlock } = chainIndexingStatus; const { indexedBlockrange } = chainIndexingConfig; - logger.info({ - msg: `Building indexing status snapshot for chain`, - payload: { chainIndexingConfig, chainIndexingMetrics, chainIndexingStatus }, - }); - // In omnichain ordering, if the startBlock is the same as the // status block, the chain has not started yet. if (isBlockRefEqualTo(indexedBlockrange.startBlock, checkpointBlock)) { @@ -174,6 +177,15 @@ export class IndexingStatusBuilder { } case ChainIndexingStates.Historical: { + // // Metrics and status are fetched concurrently — the checkpoint block + // // can briefly advance past the backfill end block. Clamp to maintain + // // the invariant: latestIndexedBlock <= backfillEndBlock. + // const latestIndexedBlock = + // chainIndexingConfig.backfillEndBlock && + // isBlockRefBeforeOrEqualTo(chainIndexingConfig.backfillEndBlock, checkpointBlock) + // ? chainIndexingConfig.backfillEndBlock + // : checkpointBlock; + return validateChainIndexingStatusSnapshot({ chainStatus: ChainIndexingStatusIds.Backfill, latestIndexedBlock: checkpointBlock, @@ -281,7 +293,7 @@ export class IndexingStatusBuilder { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new Error( - `Error fetching block for chain ID ${chainId} at block number ${blockNumber}: ${errorMessage}`, + `Error fetching block for chain ID ${chainId} at block number ${blockNumber.toString()}: ${errorMessage}`, ); } } From 1486237435f21ed988f26c7704f86e2b3a3ea56c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 12:55:40 +0200 Subject: [PATCH 05/12] Fix failing test --- .../lib/indexing-status-builder/indexing-status-builder.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts index 9b30d72e94..25b63753c4 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts @@ -1,5 +1,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import "@/lib/__test__/mockLogger"; + import { ChainIndexingStatusIds, type ChainIndexingStatusSnapshotBackfill, From a52f1cdbf0d8b6fb7f0a40e4fa215c1560e8d950 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 13:08:07 +0200 Subject: [PATCH 06/12] fix typos --- .../src/lib/indexing-status-builder/indexing-status-builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index 4769d55066..0ec5d9e565 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -293,7 +293,7 @@ export class IndexingStatusBuilder { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new Error( - `Error fetching block for chain ID ${chainId} at block number ${blockNumber.toString()}: ${errorMessage}`, + `Error fetching block for chain ID ${chainId} at block number ${blockNumber}: ${errorMessage}`, ); } } From e99d9569388fc14d7a1f2ce69bf5519fd9a482bf Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 13:37:34 +0200 Subject: [PATCH 07/12] Fix backfill end block invarinat issue after restarts --- .../init-indexing-onchain-events.ts | 22 +++++-------------- .../indexing-status-builder.ts | 18 +++++++-------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts b/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts index 5d73753138..f8f32b3679 100644 --- a/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts +++ b/apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts @@ -4,8 +4,6 @@ * event handlers. */ -import pRetry from "p-retry"; - import { migrateEnsNodeSchema } from "@/lib/ensdb/migrate-ensnode-schema"; import { ensDbClient } from "@/lib/ensdb/singleton"; import { startEnsDbWriterWorker } from "@/lib/ensdb-writer-worker/singleton"; @@ -17,20 +15,7 @@ import { indexingMetadataContextBuilder } from "@/lib/indexing-metadata-context- import { logger } from "@/lib/logger"; async function upsertIndexingMetadataContextRecord(): Promise { - // indexingMetadataContextBuilder may face transient errors while trying to - // build the Indexing Metadata Context, so we allow to retry this operation - // a few times before giving up and crashing the ENSIndexer instance. - const indexingMetadataContext = await pRetry( - () => indexingMetadataContextBuilder.getIndexingMetadataContext(), - { - retries: 5, - onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => { - logger.info({ - msg: ` Building Indexing Metadata Context attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`, - }); - }, - }, - ); + const indexingMetadataContext = await indexingMetadataContextBuilder.getIndexingMetadataContext(); logger.info({ msg: `Upserting Indexing Metadata Context Initialized`, @@ -63,6 +48,11 @@ async function upsertIndexingMetadataContextRecord(): Promise { * so we need to make sure that this does not cause any unexpected side effects. */ export async function initIndexingOnchainEvents(): Promise { + logger.info({ + msg: "Initializing indexing of onchain events", + module: "init-indexing-onchain-events", + }); + try { // Ensure ENSDb instance is healthy before trying to run any queries against it. const isEnsDbHealthy = await ensDbClient.isHealthy(); diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index 0ec5d9e565..99094e7fab 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -177,18 +177,18 @@ export class IndexingStatusBuilder { } case ChainIndexingStates.Historical: { - // // Metrics and status are fetched concurrently — the checkpoint block - // // can briefly advance past the backfill end block. Clamp to maintain - // // the invariant: latestIndexedBlock <= backfillEndBlock. - // const latestIndexedBlock = - // chainIndexingConfig.backfillEndBlock && - // isBlockRefBeforeOrEqualTo(chainIndexingConfig.backfillEndBlock, checkpointBlock) - // ? chainIndexingConfig.backfillEndBlock - // : checkpointBlock; + // Metrics and status are fetched concurrently — the checkpoint block + // can briefly advance past the backfill end block. Clamp to maintain + // the invariant: latestIndexedBlock <= backfillEndBlock. + const latestIndexedBlock = + chainIndexingConfig.backfillEndBlock && + isBlockRefBeforeOrEqualTo(chainIndexingConfig.backfillEndBlock, checkpointBlock) + ? chainIndexingConfig.backfillEndBlock + : checkpointBlock; return validateChainIndexingStatusSnapshot({ chainStatus: ChainIndexingStatusIds.Backfill, - latestIndexedBlock: checkpointBlock, + latestIndexedBlock, backfillEndBlock: chainIndexingConfig.backfillEndBlock as Unvalidated, config: indexedBlockrange as Unvalidated, } satisfies Unvalidated); From e8372f4a5d667fa49abf5b25e8f7d0b4a776f37f Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 14:02:38 +0200 Subject: [PATCH 08/12] docs(changeset): Added `globalBlockrangeEndBlock` param to `buildIndexedBlockranges`. --- .changeset/clever-numbers-change.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/clever-numbers-change.md diff --git a/.changeset/clever-numbers-change.md b/.changeset/clever-numbers-change.md new file mode 100644 index 0000000000..10395ca6dc --- /dev/null +++ b/.changeset/clever-numbers-change.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-sdk": minor +--- + +Added `globalBlockrangeEndBlock` param to `buildIndexedBlockranges`. From a6719ce8b23c9a88b2abc36b6ebefc585b2d6075 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 14:06:58 +0200 Subject: [PATCH 09/12] Fix feature toggle for protocol acceleration feature Follow up change for PR #2097 --- apps/ensapi/src/middleware/can-accelerate.middleware.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/ensapi/src/middleware/can-accelerate.middleware.ts b/apps/ensapi/src/middleware/can-accelerate.middleware.ts index b14d91154e..3e43cd647c 100644 --- a/apps/ensapi/src/middleware/can-accelerate.middleware.ts +++ b/apps/ensapi/src/middleware/can-accelerate.middleware.ts @@ -1,5 +1,6 @@ import config from "@/config"; +import { DatasourceNames, maybeGetDatasource } from "@ensnode/datasources"; import { PluginName } from "@ensnode/ensnode-sdk"; import { factory, producing } from "@/lib/hono-factory"; @@ -35,7 +36,10 @@ export const canAccelerateMiddleware = producing( /// Temporary ENSv2 Bailout //////////////////////////// // TODO: re-enable acceleration for ensv2 once implemented - if (config.ensIndexerPublicConfig.plugins.includes(PluginName.ENSv2)) { + // NOTE: gate on the namespace containing an ENSv2Root datasource rather than the ENSv2 + // plugin being configured — a namespace may be ENSv1-only even when the ENSv2 plugin is + // defined, and forward resolution must follow the ENSv1 path in that case. + if (maybeGetDatasource(config.namespace, DatasourceNames.ENSv2Root)) { if (!didWarnCannotAccelerateENSv2) { logger.warn( `ENSApi is temporarily unable to accelerate Resolution API requests while indexing ENSv2. Protocol Acceleration is DISABLED.`, From 9a3e28141d1c300f6bbc8375b0e3c63e13ca2dca Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 14:12:55 +0200 Subject: [PATCH 10/12] docs(changeset): Protocol accelertation is now available for data indexed from ENSv1 namespaces. ENSv2 namespaces will be supported in the future. --- .changeset/fifty-humans-wait.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fifty-humans-wait.md diff --git a/.changeset/fifty-humans-wait.md b/.changeset/fifty-humans-wait.md new file mode 100644 index 0000000000..dbd134e530 --- /dev/null +++ b/.changeset/fifty-humans-wait.md @@ -0,0 +1,5 @@ +--- +"ensapi": minor +--- + +Protocol accelertation is now available for data indexed from ENSv1 namespaces. ENSv2 namespaces will be supported in the future. From c2f2d22c90b447d432f9b5dc097d97f56fffcb96 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 14:14:24 +0200 Subject: [PATCH 11/12] Apply AI PR feedback --- .../lib/indexing-status-builder/indexing-status-builder.ts | 1 + .../ensnode-sdk/src/shared/config/indexed-blockranges.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index 99094e7fab..293495eb22 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -113,6 +113,7 @@ export class IndexingStatusBuilder { logger.error({ msg: `Building indexing status snapshot for chain ID ${chainId} failed`, payload: { chainIndexingConfig, chainIndexingMetrics, chainIndexingStatus }, + error, }); throw error; } diff --git a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts index cefb442142..9429c44a4d 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts @@ -41,7 +41,10 @@ export function buildIndexedBlockranges( for (const datasourceContract of datasourceContracts) { const currentChainIndexedBlockrange = indexedBlockranges.get(datasourceChainId); - if (globalBlockrangeEndBlock && datasourceContract.startBlock > globalBlockrangeEndBlock) { + if ( + typeof globalBlockrangeEndBlock === "number" && + datasourceContract.startBlock > globalBlockrangeEndBlock + ) { // If the contract's start block is greater than the global end block, // then this contract is not indexed at all, so we can skip it from // consideration in the indexed blockrange. From fd5a73b34326f712343f79e2194e05ac821b6f5f Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 13 May 2026 16:00:57 +0200 Subject: [PATCH 12/12] Apply PR feedback --- .changeset/fifty-humans-wait.md | 2 +- .../indexing-status-builder.test.ts | 60 +++++++++++++++++++ .../indexing-status-builder.ts | 7 ++- .../src/shared/config/indexed-blockranges.ts | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/.changeset/fifty-humans-wait.md b/.changeset/fifty-humans-wait.md index dbd134e530..49085ab7da 100644 --- a/.changeset/fifty-humans-wait.md +++ b/.changeset/fifty-humans-wait.md @@ -2,4 +2,4 @@ "ensapi": minor --- -Protocol accelertation is now available for data indexed from ENSv1 namespaces. ENSv2 namespaces will be supported in the future. +Continue to support protocol acceleration for data indexed from ENSv1 namespaces. Protocol acceleration for data indexed from ENSv2 namespaces will be supported in the near future. diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts index 25b63753c4..4ed3609894 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.test.ts @@ -476,6 +476,66 @@ describe("IndexingStatusBuilder", () => { }); describe("Race condition handling", () => { + it("clamps latestIndexedBlock to backfillEndBlock when checkpointBlock advances past it in historical", async () => { + // Arrange — simulate a race where the checkpoint block has advanced + // past the backfill end block between concurrent fetches. + const publicClientMock = buildPublicClientMock(); + + // checkpointBlock (laterBlockRef/1025) is ahead of backfillEndBlock (earlierBlockRef/1024) + const localMetrics = buildLocalChainsIndexingMetrics( + new Map([ + [ + chainId, + { + state: ChainIndexingStates.Historical, + latestSyncedBlock: latestBlockRef, + historicalTotalBlocks: 100, + backfillEndBlock: earlierBlockRef.number, // 1024 + } satisfies LocalChainIndexingMetricsHistorical, + ], + ]), + ); + + const localStatus: PonderIndexingStatus = { + chains: new Map([[chainId, { checkpointBlock: laterBlockRef }]]), // 1025, ahead of backfillEndBlock + }; + + const localPonderClientMock = buildLocalPonderClientMock({ + metrics: vi.fn().mockResolvedValue(localMetrics), + status: vi.fn().mockResolvedValue(localStatus), + getIndexedBlockrange: vi + .fn() + .mockReturnValue(buildBlockNumberRange(earliestBlockRef.number, undefined)), + getCachedPublicClient: vi.fn().mockReturnValue(publicClientMock), + }); + + const builder = new IndexingStatusBuilder(localPonderClientMock as LocalPonderClient); + + // Act + const result = await builder.getOmnichainIndexingStatusSnapshot(); + + // Assert — latestIndexedBlock should be clamped to backfillEndBlock (earlierBlockRef) + // instead of the checkpointBlock (laterBlockRef) + expect(result).toStrictEqual({ + omnichainStatus: OmnichainIndexingStatusIds.Backfill, + chains: new Map([ + [ + chainId, + { + chainStatus: ChainIndexingStatusIds.Backfill, + latestIndexedBlock: earlierBlockRef, // clamped to backfillEndBlock + backfillEndBlock: earlierBlockRef, + config: { + rangeType: RangeTypeIds.LeftBounded, + startBlock: earliestBlockRef, + }, + } satisfies ChainIndexingStatusSnapshotBackfill, + ], + ]), + omnichainIndexingCursor: earlierBlockRef.timestamp, + } satisfies OmnichainIndexingStatusSnapshot); + }); + it("clamps latestKnownBlock when checkpointBlock is ahead of latestSyncedBlock in realtime", async () => { // Arrange — simulate a race where the checkpoint block has advanced // past the synced block metric between concurrent fetches. diff --git a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts index 293495eb22..303df076aa 100644 --- a/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts +++ b/apps/ensindexer/src/lib/indexing-status-builder/indexing-status-builder.ts @@ -112,7 +112,12 @@ export class IndexingStatusBuilder { } catch (error) { logger.error({ msg: `Building indexing status snapshot for chain ID ${chainId} failed`, - payload: { chainIndexingConfig, chainIndexingMetrics, chainIndexingStatus }, + payload: { + chainId, + chainIndexingConfig, + chainIndexingMetrics, + chainIndexingStatus, + }, error, }); throw error; diff --git a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts index 9429c44a4d..1574fa0ab4 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts @@ -53,7 +53,7 @@ export function buildIndexedBlockranges( const contractIndexedBlockrange = buildBlockNumberRange( datasourceContract.startBlock, - datasourceContract.endBlock || globalBlockrangeEndBlock, + datasourceContract.endBlock ?? globalBlockrangeEndBlock, ); const indexedBlockrange = currentChainIndexedBlockrange