diff --git a/.changeset/ensindexer-per-chain-end-blocks.md b/.changeset/ensindexer-per-chain-end-blocks.md new file mode 100644 index 0000000000..112858e66e --- /dev/null +++ b/.changeset/ensindexer-per-chain-end-blocks.md @@ -0,0 +1,5 @@ +--- +"ensindexer": minor +--- + +Replace the global `START_BLOCK`/`END_BLOCK` indexing-range configuration with per-chain end blocks via `END_BLOCK_` environment variables (e.g. `END_BLOCK_1`, `END_BLOCK_8453`), mirroring the `RPC_URL_` convention. Each constrains the indexing end block of its chain independently and MAY be set across any number of indexed chains (the legacy global blockrange was restricted to single-chain indexing). This enables deterministic, reproducible multichain checkpoints where every indexed chain stops at a block corresponding to a shared timestamp. diff --git a/.changeset/ensnode-sdk-indexed-blockranges-per-chain.md b/.changeset/ensnode-sdk-indexed-blockranges-per-chain.md new file mode 100644 index 0000000000..cdce9edf27 --- /dev/null +++ b/.changeset/ensnode-sdk-indexed-blockranges-per-chain.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-sdk": minor +--- + +`buildIndexedBlockranges` now takes a per-chain end-block map (`ReadonlyMap`) instead of a single global end block, supporting ENSIndexer's per-chain `END_BLOCK_` configuration. diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index a72fc1b3a5..fec335bffa 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -1,6 +1,7 @@ +import type { ChainId } from "enssdk"; import { prettifyError, ZodError, z } from "zod/v4"; -import { buildBlockNumberRange, PluginName, uniq } from "@ensnode/ensnode-sdk"; +import { PluginName, uniq } from "@ensnode/ensnode-sdk"; import { buildRpcConfigsFromEnv, ENSNamespaceSchema, @@ -19,7 +20,7 @@ import { applyDefaults, EnvironmentDefaults } from "@/config/environment-default import { derive_indexedChainIds } from "./derived-params"; import type { EnsIndexerConfig } from "./types"; import { - invariant_globalBlockrange, + invariant_chainEndBlocks, invariant_requiredDatasources, invariant_requiredDatasourcesSubsetOfAll, invariant_rpcConfigsSpecifiedForIndexedChains, @@ -38,24 +39,34 @@ const makeEnvStringBoolSchema = (envVarKey: string) => ) .transform((val) => val === "true"); -const makeBlockNumberSchema = (envVarKey: string) => - z.coerce - .number({ error: `${envVarKey} must be a positive integer.` }) - .int({ error: `${envVarKey} must be a positive integer.` }) - .min(0, { error: `${envVarKey} must be a positive integer.` }) - .optional(); +const ChainEndBlocksSchema = z.map(z.number().int().nonnegative(), z.number().int().nonnegative()); -const BlockrangeSchema = z - .object({ - startBlock: makeBlockNumberSchema("START_BLOCK"), - endBlock: makeBlockNumberSchema("END_BLOCK"), - }) - .refine( - (val) => - val.startBlock === undefined || val.endBlock === undefined || val.startBlock <= val.endBlock, - { error: "START_BLOCK must be less than or equal to END_BLOCK." }, - ) - .transform(({ startBlock, endBlock }) => buildBlockNumberRange(startBlock, endBlock)); +/** + * Parses chain-specific end blocks from `END_BLOCK_` environment variables into a map. + * + * Mirrors the `RPC_URL_` convention. Pins a deterministic end block per chain (e.g. all + * chains stopping at blocks corresponding to a shared timestamp) for reproducible checkpoints; may + * be set across any number of indexed chains. + * + * @throws if any `END_BLOCK_` value is not a non-negative integer + */ +export function buildChainEndBlocksFromEnv(env: ENSIndexerEnvironment): Map { + const chainEndBlocks = new Map(); + + for (const [key, value] of Object.entries(env)) { + const match = /^END_BLOCK_(\d+)$/.exec(key); + if (!match || value === undefined) continue; + + const endBlock = Number(value); + if (!Number.isInteger(endBlock) || endBlock < 0) { + throw new Error(`${key} must be a non-negative integer.`); + } + + chainEndBlocks.set(Number(match[1]), endBlock); + } + + return chainEndBlocks; +} const PluginsSchema = z.coerce .string() @@ -93,7 +104,7 @@ const ENSIndexerConfigSchema = z namespace: ENSNamespaceSchema, plugins: PluginsSchema, isSubgraphCompatible: IsSubgraphCompatibleSchema, - globalBlockrange: BlockrangeSchema, + chainEndBlocks: ChainEndBlocksSchema, ensRainbowUrl: EnsRainbowUrlSchema, clientLabelSet: LabelSetSchema, @@ -134,7 +145,7 @@ const ENSIndexerConfigSchema = z .check(invariant_unigraphRequiresProtocolAcceleration) .check(invariant_isSubgraphCompatibleRequirements) .check(invariant_rpcConfigsSpecifiedForIndexedChains) - .check(invariant_globalBlockrange); + .check(invariant_chainEndBlocks); /** * Builds the ENSIndexer configuration object from an ENSIndexerEnvironment object. @@ -174,10 +185,7 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde plugins: env.PLUGINS, isSubgraphCompatible: env.SUBGRAPH_COMPAT, - globalBlockrange: { - startBlock: env.START_BLOCK, - endBlock: env.END_BLOCK, - }, + chainEndBlocks: buildChainEndBlocksFromEnv(env), ensRainbowUrl: env.ENSRAINBOW_URL, clientLabelSet: { labelSetId: env.LABEL_SET_ID, diff --git a/apps/ensindexer/src/config/config.test.ts b/apps/ensindexer/src/config/config.test.ts index 7bd1e3aff1..7ed036b2a7 100644 --- a/apps/ensindexer/src/config/config.test.ts +++ b/apps/ensindexer/src/config/config.test.ts @@ -29,7 +29,7 @@ import { getENSNamespace, maybeGetDatasource, } from "@ensnode/datasources"; -import { buildBlockNumberRange, ENSNamespaceIds, PluginName } from "@ensnode/ensnode-sdk"; +import { ENSNamespaceIds, PluginName } from "@ensnode/ensnode-sdk"; import type { RpcConfig } from "@ensnode/ensnode-sdk/internal"; import { buildConfigFromEnvironment } from "@/config/config.schema"; @@ -88,7 +88,6 @@ describe("config (with base env)", () => { it("returns a valid config object using environment variables", async () => { const config = await getConfig(); expect(config.namespace).toBe("mainnet"); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(undefined, undefined)); expect(config.ensIndexerSchemaName).toBe("ensindexer_test"); expect(config.plugins).toEqual(["subgraph"]); expect(config.ensRainbowUrl).toStrictEqual(new URL("http://localhost:3223")); @@ -105,64 +104,45 @@ describe("config (with base env)", () => { }); }); - describe(".globalBlockrange", () => { - it("returns both startBlock and endBlock as numbers when both are set", async () => { - vi.stubEnv("START_BLOCK", "10"); - vi.stubEnv("END_BLOCK", "20"); + describe(".chainEndBlocks", () => { + it("defaults to an empty map when no END_BLOCK_ is set", async () => { const config = await getConfig(); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(10, 20)); + expect(config.chainEndBlocks).toEqual(new Map()); }); - it("returns only startBlock when only START_BLOCK is set", async () => { - vi.stubEnv("START_BLOCK", "5"); + it("parses END_BLOCK_ into the chainEndBlocks map", async () => { + vi.stubEnv("END_BLOCK_1", "100"); const config = await getConfig(); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(5, undefined)); + expect(config.chainEndBlocks).toEqual(new Map([[1, 100]])); }); - it("returns only endBlock when only END_BLOCK is set", async () => { - vi.stubEnv("END_BLOCK", "15"); - const config = await getConfig(); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(undefined, 15)); - }); - - it("returns both as undefined when neither is set", async () => { + it("allows per-chain end blocks across multiple indexed chains", async () => { + vi.stubEnv("PLUGINS", "subgraph,basenames"); + stubRpcUrlsForNamespace("mainnet"); + vi.stubEnv("END_BLOCK_1", "100"); + vi.stubEnv("END_BLOCK_8453", "200"); const config = await getConfig(); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(undefined, undefined)); - }); - - it("throws if START_BLOCK is negative", async () => { - vi.stubEnv("START_BLOCK", "-1"); - await expect(getConfig()).rejects.toThrow(/START_BLOCK must be a positive integer/i); - }); - - it("throws if END_BLOCK is negative", async () => { - vi.stubEnv("END_BLOCK", "-5"); - await expect(getConfig()).rejects.toThrow(/END_BLOCK must be a positive integer/i); - }); - - it("throws if START_BLOCK is not a number", async () => { - vi.stubEnv("START_BLOCK", "foo"); - await expect(getConfig()).rejects.toThrow(/START_BLOCK must be a positive integer/i); + expect(config.chainEndBlocks).toEqual( + new Map([ + [1, 100], + [8453, 200], + ]), + ); }); - it("throws if END_BLOCK is not a number", async () => { - vi.stubEnv("END_BLOCK", "bar"); - await expect(getConfig()).rejects.toThrow(/END_BLOCK must be a positive integer/i); + it("throws if END_BLOCK_ targets a chain that is not indexed", async () => { + vi.stubEnv("END_BLOCK_8453", "100"); + await expect(getConfig()).rejects.toThrow(/no active plugin indexes chain 8453/i); }); - it("throws if START_BLOCK > END_BLOCK", async () => { - vi.stubEnv("START_BLOCK", "100"); - vi.stubEnv("END_BLOCK", "50"); - await expect(getConfig()).rejects.toThrow( - /START_BLOCK must be less than or equal to END_BLOCK/i, - ); + it("throws if END_BLOCK_ is negative", async () => { + vi.stubEnv("END_BLOCK_1", "-5"); + await expect(getConfig()).rejects.toThrow(/END_BLOCK_1 must be a non-negative integer/i); }); - it("does not throw if START_BLOCK == END_BLOCK", async () => { - vi.stubEnv("START_BLOCK", "100"); - vi.stubEnv("END_BLOCK", "100"); - const config = await getConfig(); - expect(config.globalBlockrange).toEqual(buildBlockNumberRange(100, 100)); + it("throws if END_BLOCK_ is not a number", async () => { + vi.stubEnv("END_BLOCK_1", "foo"); + await expect(getConfig()).rejects.toThrow(/END_BLOCK_1 must be a non-negative integer/i); }); }); @@ -565,13 +545,6 @@ describe("config (with base env)", () => { vi.stubEnv("PLUGINS", "subgraph,basenames"); await expect(getConfig()).rejects.toThrow(/RPC_URL_\d+/i); }); - - it("cannot constrain blockrange with multiple chains", async () => { - vi.stubEnv("PLUGINS", "subgraph,basenames"); - stubRpcUrlsForNamespace("mainnet"); - vi.stubEnv("END_BLOCK", "1"); - await expect(getConfig()).rejects.toThrow(/multiple chains/i); - }); }); describe(".clientLabelSet", () => { diff --git a/apps/ensindexer/src/config/environment.ts b/apps/ensindexer/src/config/environment.ts index a764c271c5..c3c070f155 100644 --- a/apps/ensindexer/src/config/environment.ts +++ b/apps/ensindexer/src/config/environment.ts @@ -13,8 +13,9 @@ export type ENSIndexerEnvironment = EnsDbEnvironment & PLUGINS?: string; SUBGRAPH_COMPAT?: string; - START_BLOCK?: string; - END_BLOCK?: string; + // Chain-specific end blocks, keyed by chain id (e.g. END_BLOCK_1, END_BLOCK_8453). Mirrors the + // RPC_URL_ convention. See ENSIndexerConfig.chainEndBlocks. + [x: `END_BLOCK_${number}`]: string | undefined; ENSRAINBOW_URL?: string; LABEL_SET_ID?: string; diff --git a/apps/ensindexer/src/config/serialize.ts b/apps/ensindexer/src/config/serialize.ts index 563970289e..e22ecb5167 100644 --- a/apps/ensindexer/src/config/serialize.ts +++ b/apps/ensindexer/src/config/serialize.ts @@ -45,7 +45,6 @@ export function serializeRedactedENSIndexerConfig( ensDbUrl: redactedConfig.ensDbUrl, ensRainbowUrl: serializeUrl(redactedConfig.ensRainbowUrl), clientLabelSet: redactedConfig.clientLabelSet, - globalBlockrange: redactedConfig.globalBlockrange, indexedChainIds: serializeIndexedChainIds(redactedConfig.indexedChainIds), isSubgraphCompatible: redactedConfig.isSubgraphCompatible, namespace: redactedConfig.namespace, diff --git a/apps/ensindexer/src/config/serialized-types.ts b/apps/ensindexer/src/config/serialized-types.ts index 167b4d572e..d8376f1f5c 100644 --- a/apps/ensindexer/src/config/serialized-types.ts +++ b/apps/ensindexer/src/config/serialized-types.ts @@ -26,7 +26,10 @@ export interface SerializedRpcConfig extends Omit { + extends Omit< + ENSIndexerConfig, + "ensRainbowUrl" | "indexedChainIds" | "rpcConfigs" | "plugins" | "chainEndBlocks" + > { /** * Serialized representation of {@link ENSIndexerConfig.ensRainbowUrl}. */ diff --git a/apps/ensindexer/src/config/types.ts b/apps/ensindexer/src/config/types.ts index c41b68b951..0fecfdc33d 100644 --- a/apps/ensindexer/src/config/types.ts +++ b/apps/ensindexer/src/config/types.ts @@ -2,7 +2,7 @@ import type { ChainId } from "enssdk"; import type { ENSNamespaceId } from "@ensnode/datasources"; import type { EnsDbConfig } from "@ensnode/ensdb-sdk"; -import type { BlockNumberRange, PluginName } from "@ensnode/ensnode-sdk"; +import type { PluginName } from "@ensnode/ensnode-sdk"; import { RpcConfig, type RpcConfigs } from "@ensnode/ensnode-sdk/internal"; import type { EnsRainbowClientLabelSet } from "@ensnode/ensrainbow-sdk"; @@ -120,23 +120,19 @@ export interface EnsIndexerConfig { ensDbUrl: EnsDbConfig["ensDbUrl"]; /** - * Constrains the global blockrange for indexing, useful for testing purposes. + * Chain-specific end blocks, keyed by {@link ChainId}, parsed from `END_BLOCK_` env vars. * - * This is strictly designed for testing and development and its usage in production will result - * in incorrect or out-of-date indexes. - * - * ENSIndexer will constrain all indexed contracts to the provided {@link BlockNumberRange.startBlock} - * and {@link BlockNumberRange.endBlock} if specified. + * Constrains the indexing end block of each specified chain — strictly for testing/checkpoints; + * usage in production results in incorrect or out-of-date indexes. Designed for producing + * deterministic, reproducible checkpoints where every indexed chain stops at a block corresponding + * to a shared timestamp. Empty when no `END_BLOCK_` vars are set. * * Invariants: - * - both `startBlock` and `endBlock` are optional, and expected to be undefined - * - if defined, startBlock must be an integer greater than 0 - * - if defined, endBlock must be an integer greater than 0 - * - if defined, endBlock must be greater than startBlock - * - if either `startBlock` or `endBlock` are defined, the number of indexed chains described - * by {@link plugins} must be 1 + * - each key must be a {@link ChainId} indexed by the active {@link plugins} + * - each value must be a non-negative integer + * - may be set across any number of indexed chains */ - globalBlockrange: BlockNumberRange; + chainEndBlocks: Map; /** * A feature flag to enable/disable ENSIndexer's Subgraph Compatible Indexing Behavior. diff --git a/apps/ensindexer/src/config/validations.ts b/apps/ensindexer/src/config/validations.ts index cdc4a45854..df221de224 100644 --- a/apps/ensindexer/src/config/validations.ts +++ b/apps/ensindexer/src/config/validations.ts @@ -78,32 +78,18 @@ export function invariant_rpcConfigsSpecifiedForIndexedChains( } } -// Invariant: if a global blockrange is defined, only one chain is indexed -export function invariant_globalBlockrange( - ctx: ZodCheckFnInput< - Pick - >, +// Invariant: each END_BLOCK_ targets a chain that is actually indexed +export function invariant_chainEndBlocks( + ctx: ZodCheckFnInput>, ) { const { value: config } = ctx; - const { globalBlockrange } = config; - if (globalBlockrange.startBlock !== undefined || globalBlockrange.endBlock !== undefined) { - if (config.indexedChainIds.size > 1) { + for (const chainId of config.chainEndBlocks.keys()) { + if (!config.indexedChainIds.has(chainId)) { ctx.issues.push({ code: "custom", input: config, - message: `ENSIndexer's behavior when indexing _multiple chains_ with a _specific blockrange_ is considered undefined (for now). If you're using this feature, you're likely interested in snapshotting at a specific END_BLOCK, and may have unintentially activated plugins that source events from multiple chains. The config currently is: - - NAMESPACE=${config.namespace} - PLUGINS=${config.plugins.join(",")} - START_BLOCK=${globalBlockrange.startBlock || "n/a"} - END_BLOCK=${globalBlockrange.endBlock || "n/a"} - - The usage you're most likely interested in is: - NAMESPACE=(mainnet|sepolia) PLUGINS=subgraph END_BLOCK=x pnpm run start - which runs just the 'subgraph' plugin with a specific end block, suitable for snapshotting ENSNode and comparing to Subgraph snapshots. - - In the future, indexing multiple chains with chain-specific blockrange constraints may be possible.`, + message: `END_BLOCK_${chainId} is set, but no active plugin indexes chain ${chainId}.`, }); } } diff --git a/apps/ensindexer/src/lib/__test__/mockConfig.ts b/apps/ensindexer/src/lib/__test__/mockConfig.ts index 1c51471600..8c6fe03871 100644 --- a/apps/ensindexer/src/lib/__test__/mockConfig.ts +++ b/apps/ensindexer/src/lib/__test__/mockConfig.ts @@ -1,7 +1,5 @@ import { vi } from "vitest"; -import { buildBlockNumberRange } from "@ensnode/ensnode-sdk"; - import { buildConfigFromEnvironment } from "@/config/config.schema"; import type { ENSIndexerConfig } from "@/config/types"; import { deepClone } from "@/lib/lib-helpers"; @@ -119,19 +117,9 @@ export function setupConfigMock() { * @example * // In the test * updateMockConfig({ - * globalBlockrange: { startBlock: 100, endBlock: 200 } + * chainEndBlocks: new Map([[1, 200]]) * }); */ export function updateMockConfig(updates: Partial) { Object.assign(currentMockConfig, updates); } - -/** - * Sets up the global blockrange in the current mock config - * - * @param startBlock Optional start block - * @param endBlock Optional end block - */ -export function setGlobalBlockrange(startBlock?: number, endBlock?: number) { - updateMockConfig({ globalBlockrange: buildBlockNumberRange(startBlock, endBlock) }); -} diff --git a/apps/ensindexer/src/lib/local-ponder-client.ts b/apps/ensindexer/src/lib/local-ponder-client.ts index 8d62f21cd2..c2f2d3a791 100644 --- a/apps/ensindexer/src/lib/local-ponder-client.ts +++ b/apps/ensindexer/src/lib/local-ponder-client.ts @@ -12,7 +12,7 @@ import { localPonderContext } from "./local-ponder-context"; const pluginsAllDatasourceNames = getPluginsAllDatasourceNames(config.plugins); const indexedBlockranges = buildIndexedBlockranges( config.namespace, - config.globalBlockrange.endBlock, + config.chainEndBlocks, pluginsAllDatasourceNames, ); diff --git a/apps/ensindexer/src/lib/ponder-helpers.test.ts b/apps/ensindexer/src/lib/ponder-helpers.test.ts index 11dd145292..75ff8b2243 100644 --- a/apps/ensindexer/src/lib/ponder-helpers.test.ts +++ b/apps/ensindexer/src/lib/ponder-helpers.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { buildBlockNumberRange } from "@ensnode/ponder-sdk"; -import { constrainBlockrange } from "./ponder-helpers"; +import { blockrangeForChain, constrainBlockrange } from "./ponder-helpers"; const UNDEFINED_BLOCKRANGE = buildBlockNumberRange(undefined, undefined); const BLOCKRANGE_WITH_END = buildBlockNumberRange(undefined, 1234); @@ -90,4 +90,23 @@ describe("ponder helpers", () => { }); }); }); + + describe("blockrangeForChain", () => { + it("is unbounded when the chain has no end block", () => { + expect(blockrangeForChain(new Map(), 1)).toEqual(buildBlockNumberRange(undefined, undefined)); + }); + + it("is right-bounded at the chain's end block", () => { + expect(blockrangeForChain(new Map([[1, 100]]), 1)).toEqual( + buildBlockNumberRange(undefined, 100), + ); + }); + + it("applies the end block only to the matching chain", () => { + const chainEndBlocks = new Map([[1, 100]]); + expect(blockrangeForChain(chainEndBlocks, 8453)).toEqual( + buildBlockNumberRange(undefined, undefined), + ); + }); + }); }); diff --git a/apps/ensindexer/src/lib/ponder-helpers.ts b/apps/ensindexer/src/lib/ponder-helpers.ts index 732a187bd1..41f63b2f9e 100644 --- a/apps/ensindexer/src/lib/ponder-helpers.ts +++ b/apps/ensindexer/src/lib/ponder-helpers.ts @@ -118,18 +118,31 @@ export function chainsConnectionConfig( }; } +/** + * Resolves the {@link BlockNumberRange} for `chainId` from its chain-specific end block + * (`END_BLOCK_`). Unbounded if the chain has no end block set; otherwise right-bounded at + * that block. This is the mechanism behind deterministic checkpoints, where every chain stops at a + * block corresponding to a shared timestamp. + */ +export function blockrangeForChain( + chainEndBlocks: ReadonlyMap, + chainId: ChainId, +): BlockNumberRange { + return buildBlockNumberRange(undefined, chainEndBlocks.get(chainId)); +} + /** * Builds a `ponder#ContractConfig['chain']` given a contract's config, constraining the contract's - * indexing range by the globally configured blockrange. + * indexing range by the chain's end block (see {@link blockrangeForChain}). * - * @param {BlockNumberRange} globalBlockrange + * @param chainEndBlocks per-chain end-block overrides, keyed by chain id * @param {number} chainId * @param {ContractConfig} contractConfig * * @returns network configuration based on the contract */ export function chainConfigForContract( - globalBlockrange: BlockNumberRange, + chainEndBlocks: ReadonlyMap, chainId: number, contractConfig: CONTRACT_CONFIG, ) { @@ -139,7 +152,10 @@ export function chainConfigForContract( ); // Ponder will index the contract in perpetuity if endBlock is `undefined` - const { startBlock, endBlock } = constrainBlockrange(globalBlockrange, contractBlockrange); + const { startBlock, endBlock } = constrainBlockrange( + blockrangeForChain(chainEndBlocks, chainId), + contractBlockrange, + ); return { [chainId.toString()]: { @@ -174,11 +190,11 @@ export function pickContracts( * - `startBlock` is the earliest contract `startBlock`. * - `endBlock` is the latest contract `endBlock` if every contract specifies one, otherwise undefined. * - * The result is then constrained against `globalBlockrange` like {@link chainConfigForContract}. + * The result is then constrained against the chain's end block like {@link chainConfigForContract}. * Pass `contracts` as an array; callers can use `.filter(...)` to drop namespace-conditional ones. */ export function mergedChainConfigForContracts( - globalBlockrange: BlockNumberRange, + chainEndBlocks: ReadonlyMap, chainId: number, contracts: readonly ContractConfig[], ) { @@ -201,7 +217,7 @@ export function mergedChainConfigForContracts( : undefined; const { startBlock, endBlock } = constrainBlockrange( - globalBlockrange, + blockrangeForChain(chainEndBlocks, chainId), buildBlockNumberRange(minStartBlock, maxEndBlock), ); diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts index 67291ce4d9..55ff12ebd6 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts @@ -17,6 +17,7 @@ import { import { createPlugin, namespaceContract } from "@/lib/plugin-helpers"; import { + blockrangeForChain, chainConfigForContract, chainsConnectionConfigForDatasources, constrainBlockrange, @@ -90,7 +91,7 @@ export default createPlugin({ (memo, datasource) => ({ ...memo, [datasource.chain.id.toString()]: constrainBlockrange( - config.globalBlockrange, + blockrangeForChain(config.chainEndBlocks, datasource.chain.id), buildBlockNumberRange( datasource.contracts.Resolver.startBlock, datasource.contracts.Resolver.endBlock, @@ -108,7 +109,7 @@ export default createPlugin({ abi: ensroot.contracts.ENSv1RegistryOld.abi, chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.ENSv1RegistryOld, ), @@ -123,21 +124,21 @@ export default createPlugin({ chain: { // ENS Root Chain Registry ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.ENSv1Registry, ), // Basenames (shadow)Registry ...(basenames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.Registry, )), // Lineanames (shadow)Registry ...(lineanames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.Registry, )), @@ -153,7 +154,7 @@ export default createPlugin({ (memo, datasource) => ({ ...memo, ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, datasource.chain.id, datasource.contracts.Registry, ), @@ -170,13 +171,13 @@ export default createPlugin({ chain: { ...(threednsOptimism && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsOptimism.chain.id, threednsOptimism.contracts.ThreeDNSToken, )), ...(threednsBase && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsBase.chain.id, threednsBase.contracts.ThreeDNSToken, )), @@ -192,42 +193,42 @@ export default createPlugin({ // the Root chain's DefaultReverseRegistrar (is StandaloneReverseRegistrar) ...(rrRoot && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrRoot.chain.id, rrRoot.contracts.DefaultReverseRegistrar, )), // Base's L2ReverseRegistrar (is StandaloneReverseRegistrar) ...(rrBase && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrBase.chain.id, rrBase.contracts.L2ReverseRegistrar, )), // Linea's L2ReverseRegistrar (is StandaloneReverseRegistrar) ...(rrLinea && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrLinea.chain.id, rrLinea.contracts.L2ReverseRegistrar, )), // Optimism's L2ReverseRegistrar (is StandaloneReverseRegistrar) ...(rrOptimism && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrOptimism.chain.id, rrOptimism.contracts.L2ReverseRegistrar, )), // Arbitrum's L2ReverseRegistrar (is StandaloneReverseRegistrar) ...(rrArbitrum && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrArbitrum.chain.id, rrArbitrum.contracts.L2ReverseRegistrar, )), // Scroll's L2ReverseRegistrar (is StandaloneReverseRegistrar) ...(rrScroll && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, rrScroll.chain.id, rrScroll.contracts.L2ReverseRegistrar, )), diff --git a/apps/ensindexer/src/plugins/registrars/plugin.ts b/apps/ensindexer/src/plugins/registrars/plugin.ts index dde5173d19..759c1e4388 100644 --- a/apps/ensindexer/src/plugins/registrars/plugin.ts +++ b/apps/ensindexer/src/plugins/registrars/plugin.ts @@ -52,7 +52,7 @@ export default createPlugin({ ////////////////////// [namespaceContract(pluginName, "Ethnames_BaseRegistrar")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ethnames.chain.id, ethnames.contracts.BaseRegistrar, ), @@ -65,7 +65,7 @@ export default createPlugin({ [namespaceContract(pluginName, "Ethnames_RegistrarController")]: { abi: AnyRegistrarControllerABI, chain: mergedChainConfigForContracts( - config.globalBlockrange, + config.chainEndBlocks, ethnames.chain.id, pickContracts(ethnames.contracts, [ "LegacyEthRegistrarController", @@ -81,7 +81,7 @@ export default createPlugin({ /////////////////////// [namespaceContract(pluginName, "Basenames_BaseRegistrar")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.BaseRegistrar, ), @@ -93,7 +93,7 @@ export default createPlugin({ /////////////////////////////////// [namespaceContract(pluginName, "Basenames_EARegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.EARegistrarController, ), @@ -101,7 +101,7 @@ export default createPlugin({ }, [namespaceContract(pluginName, "Basenames_RegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.RegistrarController, ), @@ -109,7 +109,7 @@ export default createPlugin({ }, [namespaceContract(pluginName, "Basenames_UpgradeableRegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.UpgradeableRegistrarController, ), @@ -121,7 +121,7 @@ export default createPlugin({ //////////////////////// [namespaceContract(pluginName, "Lineanames_BaseRegistrar")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.BaseRegistrar, ), @@ -133,7 +133,7 @@ export default createPlugin({ //////////////////////////////////// [namespaceContract(pluginName, "Lineanames_EthRegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.EthRegistrarController, ), diff --git a/apps/ensindexer/src/plugins/subgraph/plugins/basenames/plugin.ts b/apps/ensindexer/src/plugins/subgraph/plugins/basenames/plugin.ts index 8d90744d30..062c47c09b 100644 --- a/apps/ensindexer/src/plugins/subgraph/plugins/basenames/plugin.ts +++ b/apps/ensindexer/src/plugins/subgraph/plugins/basenames/plugin.ts @@ -35,16 +35,16 @@ export default createPlugin({ ), contracts: { [namespaceContract(pluginName, "Registry")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.Registry), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.Registry), abi: contracts.Registry.abi, }, [namespaceContract(pluginName, "BaseRegistrar")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.BaseRegistrar), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.BaseRegistrar), abi: contracts.BaseRegistrar.abi, }, [namespaceContract(pluginName, "EARegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, chain.id, contracts.EARegistrarController, ), @@ -52,7 +52,7 @@ export default createPlugin({ }, [namespaceContract(pluginName, "RegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, chain.id, contracts.RegistrarController, ), @@ -60,7 +60,7 @@ export default createPlugin({ }, [namespaceContract(pluginName, "UpgradeableRegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, chain.id, contracts.UpgradeableRegistrarController, ), @@ -68,7 +68,7 @@ export default createPlugin({ }, // NOTE: shared (non-namespaced) Resolver definition/implementation (see plugins/shared/Resolver.ts) Resolver: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.Resolver), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.Resolver), abi: contracts.Resolver.abi, }, }, diff --git a/apps/ensindexer/src/plugins/subgraph/plugins/lineanames/plugin.ts b/apps/ensindexer/src/plugins/subgraph/plugins/lineanames/plugin.ts index 59f39f93d1..2c9831acae 100644 --- a/apps/ensindexer/src/plugins/subgraph/plugins/lineanames/plugin.ts +++ b/apps/ensindexer/src/plugins/subgraph/plugins/lineanames/plugin.ts @@ -36,28 +36,28 @@ export default createPlugin({ ), contracts: { [namespaceContract(pluginName, "Registry")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.Registry), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.Registry), abi: contracts.Registry.abi, }, [namespaceContract(pluginName, "BaseRegistrar")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.BaseRegistrar), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.BaseRegistrar), abi: contracts.BaseRegistrar.abi, }, [namespaceContract(pluginName, "EthRegistrarController")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, chain.id, contracts.EthRegistrarController, ), abi: contracts.EthRegistrarController.abi, }, [namespaceContract(pluginName, "NameWrapper")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.NameWrapper), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.NameWrapper), abi: contracts.NameWrapper.abi, }, // NOTE: shared (non-namespaced) Resolver definition/implementation (see plugins/shared/Resolver.ts) Resolver: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.Resolver), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.Resolver), abi: contracts.Resolver.abi, }, }, diff --git a/apps/ensindexer/src/plugins/subgraph/plugins/subgraph/plugin.ts b/apps/ensindexer/src/plugins/subgraph/plugins/subgraph/plugin.ts index e0cb6eb84b..5077c827fe 100644 --- a/apps/ensindexer/src/plugins/subgraph/plugins/subgraph/plugin.ts +++ b/apps/ensindexer/src/plugins/subgraph/plugins/subgraph/plugin.ts @@ -39,24 +39,24 @@ export default createPlugin({ contracts: { [namespaceContract(pluginName, "ENSv1RegistryOld")]: { chain: chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, chain.id, contracts.ENSv1RegistryOld, ), abi: contracts.ENSv1RegistryOld.abi, }, [namespaceContract(pluginName, "ENSv1Registry")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.ENSv1Registry), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.ENSv1Registry), abi: contracts.ENSv1Registry.abi, }, [namespaceContract(pluginName, "BaseRegistrar")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.BaseRegistrar), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.BaseRegistrar), abi: contracts.BaseRegistrar.abi, }, [namespaceContract(pluginName, "RegistrarController")]: { abi: AnyRegistrarControllerABI, chain: mergedChainConfigForContracts( - config.globalBlockrange, + config.chainEndBlocks, chain.id, pickContracts(contracts, [ "LegacyEthRegistrarController", @@ -66,12 +66,12 @@ export default createPlugin({ ), }, [namespaceContract(pluginName, "NameWrapper")]: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.NameWrapper), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.NameWrapper), abi: contracts.NameWrapper.abi, }, // NOTE: shared (non-namespaced) Resolver definition/implementation (see plugins/shared/Resolver.ts) Resolver: { - chain: chainConfigForContract(config.globalBlockrange, chain.id, contracts.Resolver), + chain: chainConfigForContract(config.chainEndBlocks, chain.id, contracts.Resolver), abi: contracts.Resolver.abi, }, }, diff --git a/apps/ensindexer/src/plugins/subgraph/plugins/threedns/plugin.ts b/apps/ensindexer/src/plugins/subgraph/plugins/threedns/plugin.ts index 725b3f174a..a284e54e27 100644 --- a/apps/ensindexer/src/plugins/subgraph/plugins/threedns/plugin.ts +++ b/apps/ensindexer/src/plugins/subgraph/plugins/threedns/plugin.ts @@ -39,12 +39,12 @@ export default createPlugin({ [namespaceContract(pluginName, "ThreeDNSToken")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsOptimism.chain.id, threednsOptimism.contracts.ThreeDNSToken, ), ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsBase.chain.id, threednsBase.contracts.ThreeDNSToken, ), @@ -57,12 +57,12 @@ export default createPlugin({ abi: ResolverABI, chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsOptimism.chain.id, threednsOptimism.contracts.Resolver, ), ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsBase.chain.id, threednsBase.contracts.Resolver, ), diff --git a/apps/ensindexer/src/plugins/tokenscope/plugin.ts b/apps/ensindexer/src/plugins/tokenscope/plugin.ts index e844ca10e7..504adee149 100644 --- a/apps/ensindexer/src/plugins/tokenscope/plugin.ts +++ b/apps/ensindexer/src/plugins/tokenscope/plugin.ts @@ -66,7 +66,7 @@ export default createPlugin({ [namespaceContract(pluginName, "Seaport")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, seaport.chain.id, seaport.contracts.Seaport1_5, ), @@ -80,7 +80,7 @@ export default createPlugin({ [namespaceContract(pluginName, "EthBaseRegistrar")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.BaseRegistrar, ), @@ -94,7 +94,7 @@ export default createPlugin({ [namespaceContract(pluginName, "BaseBaseRegistrar")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.BaseRegistrar, ), @@ -108,7 +108,7 @@ export default createPlugin({ [namespaceContract(pluginName, "LineaBaseRegistrar")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.BaseRegistrar, ), @@ -118,7 +118,7 @@ export default createPlugin({ [namespaceContract(pluginName, "NameWrapper")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.NameWrapper, ), @@ -129,12 +129,12 @@ export default createPlugin({ [namespaceContract(pluginName, "ThreeDNSToken")]: { chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsOptimism.chain.id, threednsOptimism.contracts.ThreeDNSToken, ), ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, threednsBase.chain.id, threednsBase.contracts.ThreeDNSToken, ), diff --git a/apps/ensindexer/src/plugins/unigraph/plugin.ts b/apps/ensindexer/src/plugins/unigraph/plugin.ts index 2f66ac865b..a13ba1e29c 100644 --- a/apps/ensindexer/src/plugins/unigraph/plugin.ts +++ b/apps/ensindexer/src/plugins/unigraph/plugin.ts @@ -17,6 +17,7 @@ import { import { createPlugin, namespaceContract } from "@/lib/plugin-helpers"; import { + blockrangeForChain, chainConfigForContract, chainsConnectionConfigForDatasources, constrainBlockrange, @@ -73,7 +74,7 @@ export default createPlugin({ (memo, datasource) => ({ ...memo, ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, datasource.chain.id, datasource.contracts.Registry, ), @@ -91,7 +92,7 @@ export default createPlugin({ (memo, datasource) => ({ ...memo, ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, datasource.chain.id, datasource.contracts.EnhancedAccessControl, ), @@ -108,7 +109,7 @@ export default createPlugin({ chain: { ...(ENSv2Root && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ENSv2Root.chain.id, ENSv2Root.contracts.ETHRegistrar, )), @@ -122,7 +123,7 @@ export default createPlugin({ abi: ensroot.contracts.ENSv1RegistryOld.abi, chain: { ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.ENSv1RegistryOld, ), @@ -140,21 +141,21 @@ export default createPlugin({ chain: { // ENS Root Chain Registry ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.ENSv1Registry, ), // Basenames (shadow)Registry if defined ...(basenames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.Registry, )), // Lineanames (shadow)Registry if defined ...(lineanames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.Registry, )), @@ -171,14 +172,14 @@ export default createPlugin({ chain: { // ENS Root Chain NameWrapper ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.NameWrapper, ), // Lineanames NameWrapper if defined ...(lineanames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.NameWrapper, )), @@ -193,21 +194,21 @@ export default createPlugin({ chain: { // Ethnames BaseRegistrar ...chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, ensroot.contracts.BaseRegistrar, ), // Basenames BaseRegistrar, if defined ...(basenames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, basenames.contracts.BaseRegistrar, )), // Lineanames BaseRegistrar, if defined ...(lineanames && chainConfigForContract( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, lineanames.contracts.BaseRegistrar, )), @@ -224,7 +225,7 @@ export default createPlugin({ // Ethnames Registrar Controllers /////////////////////////////////// ...mergedChainConfigForContracts( - config.globalBlockrange, + config.chainEndBlocks, ensroot.chain.id, pickContracts(ensroot.contracts, [ "LegacyEthRegistrarController", @@ -238,7 +239,7 @@ export default createPlugin({ /////////////////////////////////// ...(basenames && mergedChainConfigForContracts( - config.globalBlockrange, + config.chainEndBlocks, basenames.chain.id, pickContracts(basenames.contracts, [ "EARegistrarController", @@ -252,7 +253,7 @@ export default createPlugin({ //////////////////////////////////// ...(lineanames && mergedChainConfigForContracts( - config.globalBlockrange, + config.chainEndBlocks, lineanames.chain.id, pickContracts(lineanames.contracts, ["EthRegistrarController"]), )), @@ -268,7 +269,7 @@ export default createPlugin({ (memo, datasource) => ({ ...memo, [datasource.chain.id.toString()]: constrainBlockrange( - config.globalBlockrange, + blockrangeForChain(config.chainEndBlocks, datasource.chain.id), buildBlockNumberRange( datasource.contracts.Resolver.startBlock, datasource.contracts.Resolver.endBlock, diff --git a/apps/ensindexer/src/ponder/indexing-behavior-injection-contract.ts b/apps/ensindexer/src/ponder/indexing-behavior-injection-contract.ts index 17c9e95316..3a492ed5af 100644 --- a/apps/ensindexer/src/ponder/indexing-behavior-injection-contract.ts +++ b/apps/ensindexer/src/ponder/indexing-behavior-injection-contract.ts @@ -31,12 +31,13 @@ interface IndexingBehaviorDependencies { plugins: EnsIndexerConfig["plugins"]; /** - * Global Blockrange + * Per-chain end blocks * - * When `globalBlockrange` changes, the blockrange of indexed chains may change, - * which influences the indexing behavior. + * When `chainEndBlocks` changes, the indexing end block of one or more chains may change, + * which influences the indexing behavior. Stored as a key-sorted plain object (not a Map) so it + * serializes into the Ponder build id. */ - globalBlockrange: EnsIndexerConfig["globalBlockrange"]; + chainEndBlocks: Record; /** * Subgraph Compatibility @@ -107,7 +108,11 @@ const indexingBehaviorDependencies = { // 2. Plugin execution order is determined by `ALL_PLUGINS`, not config.plugins // Sorting ensures consistent Build IDs for semantically identical config. plugins: [...config.plugins].sort(), - globalBlockrange: config.globalBlockrange, + // serialize the Map to a plain, key-sorted object so it contributes to the Ponder build id (a Map + // would JSON-serialize to `{}`) and yields a canonical checksum regardless of env order. + chainEndBlocks: Object.fromEntries( + [...config.chainEndBlocks.entries()].sort(([a], [b]) => a - b), + ), // these config properties don't explicitly affect the generated ponderConfig and need to be // injected here to ensure that, if they are configured differently, ponder generates a unique // build id to differentiate between runs with otherwise-identical configs (see above). 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 15717b3483..b8093f3d42 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.test.ts @@ -70,7 +70,7 @@ describe("buildIndexedBlockranges()", () => { // Act const result = buildIndexedBlockranges( ENSNamespaceIds.Mainnet, - undefined, + new Map(), pluginsRequiredDatasourceNames, ); @@ -110,7 +110,7 @@ describe("buildIndexedBlockranges()", () => { // Act const result = buildIndexedBlockranges( ENSNamespaceIds.Mainnet, - undefined, + new Map(), pluginsRequiredDatasourceNames, ); @@ -147,7 +147,7 @@ describe("buildIndexedBlockranges()", () => { // Act const result = buildIndexedBlockranges( ENSNamespaceIds.Mainnet, - undefined, + new Map(), pluginsRequiredDatasourceNames, ); @@ -164,7 +164,7 @@ describe("buildIndexedBlockranges()", () => { // Act const result = buildIndexedBlockranges( ENSNamespaceIds.Mainnet, - undefined, + new Map(), pluginsDatasourceNames, ); @@ -172,21 +172,21 @@ describe("buildIndexedBlockranges()", () => { expect(result).toStrictEqual(new Map()); }); - it("applies global end block to contracts without end block and skips contracts starting after global end block", () => { + it("applies per-chain end block to contracts without end block and skips contracts starting after the chain end block", () => { // Arrange const ensrootDatasourceConfig: unknown = { chain: { id: 1 }, contracts: { - registry: { startBlock: 100 }, // no endBlock, should use global end block (500) + registry: { startBlock: 100 }, // no endBlock, should use chain end block (500) resolver: { startBlock: 80, endBlock: 200 }, // has endBlock, should keep it - registrar: { startBlock: 600 }, // startBlock > global end block, should be skipped + registrar: { startBlock: 600 }, // startBlock > chain end block, should be skipped }, }; const basenamesDatasourceConfig: unknown = { chain: { id: 8453 }, contracts: { - registry: { startBlock: 5 }, // no endBlock, should use global end block (500) + registry: { startBlock: 5 }, // no endBlock, should use chain end block (500) }, }; @@ -206,12 +206,15 @@ describe("buildIndexedBlockranges()", () => { [PluginName.Basenames, [DatasourceNames.Basenames]], ]); - const globalBlockrangeEndBlock = 500; + const chainEndBlocks = new Map([ + [1, 500], + [8453, 500], + ]); // Act const result = buildIndexedBlockranges( ENSNamespaceIds.Mainnet, - globalBlockrangeEndBlock, + chainEndBlocks, pluginsRequiredDatasourceNames, ); @@ -219,7 +222,7 @@ describe("buildIndexedBlockranges()", () => { 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) + // Chain 8453: startBlock = 5, endBlock = 500 (from chain end block) [8453, buildBlockNumberRange(5, 500)], ]); diff --git a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts index 1574fa0ab4..17bc81e26d 100644 --- a/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts +++ b/packages/ensnode-sdk/src/shared/config/indexed-blockranges.ts @@ -9,7 +9,6 @@ import { import type { PluginName } from "../../ensindexer/config/types"; import { - type BlockNumberRange, type BlockNumberRangeWithStartBlock, buildBlockNumberRange, mergeBlockNumberRanges, @@ -23,7 +22,7 @@ import { */ export function buildIndexedBlockranges( namespace: ENSNamespaceId, - globalBlockrangeEndBlock: BlockNumberRange["endBlock"], + chainEndBlocks: ReadonlyMap, pluginsDatasourceNames: Map, ): Map { const indexedBlockranges = new Map(); @@ -37,15 +36,13 @@ export function buildIndexedBlockranges( const datasourceChainId = datasource.chain.id; const datasourceContracts = Object.values(datasource.contracts); + const chainEndBlock = chainEndBlocks.get(datasourceChainId); for (const datasourceContract of datasourceContracts) { const currentChainIndexedBlockrange = indexedBlockranges.get(datasourceChainId); - if ( - typeof globalBlockrangeEndBlock === "number" && - datasourceContract.startBlock > globalBlockrangeEndBlock - ) { - // If the contract's start block is greater than the global end block, + if (typeof chainEndBlock === "number" && datasourceContract.startBlock > chainEndBlock) { + // If the contract's start block is greater than the chain's end block, // then this contract is not indexed at all, so we can skip it from // consideration in the indexed blockrange. continue; @@ -53,7 +50,7 @@ export function buildIndexedBlockranges( const contractIndexedBlockrange = buildBlockNumberRange( datasourceContract.startBlock, - datasourceContract.endBlock ?? globalBlockrangeEndBlock, + datasourceContract.endBlock ?? chainEndBlock, ); const indexedBlockrange = currentChainIndexedBlockrange