From 4b065e5fd55f1d3e31de33deab733c6d10c16352 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:29:56 +0100 Subject: [PATCH 01/21] Extract CrossChainIndexingStatus business-layer from generic types.ts file --- .../cross-chain-indexing-status-snapshot.ts | 91 ++++++++++++++++++ .../src/ensindexer/indexing-status/types.ts | 95 +------------------ 2 files changed, 92 insertions(+), 94 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..05cad8559 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,91 @@ +import type { UnixTimestamp } from "../../shared/types"; +import type { OmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; + +/** + * The strategy used for indexing one or more chains. + * + * @see https://ponder.sh/docs/api-reference/ponder/config#parameters + */ +export const CrossChainIndexingStrategyIds = { + /** + * Represents that the indexing of events across all indexed chains will + * proceed in a deterministic "omnichain" ordering by block timestamp, chain ID, + * and block number. + * + * This strategy is "deterministic" in that the order of processing cross-chain indexed + * events and each resulting indexed data state transition recorded in ENSDb is always + * the same for each ENSIndexer instance operating with an equivalent + * `ENSIndexerConfig` and ENSIndexer version. However it also has the drawbacks of: + * - increased indexing latency that must wait for the slowest indexed chain to + * add new blocks or to discover new blocks through the configured RPCs. + * - if any indexed chain gets "stuck" due to chain or RPC failures, all indexed chains + * will be affected. + */ + Omnichain: "omnichain", +} as const; + +/** + * The derived string union of possible {@link CrossChainIndexingStrategyIds}. + */ +export type CrossChainIndexingStrategyId = + (typeof CrossChainIndexingStrategyIds)[keyof typeof CrossChainIndexingStrategyIds]; + +/** + * Cross-chain indexing status snapshot when the `strategy` is + * {@link CrossChainIndexingStrategyId.Omnichain}. + * + * Invariants: + * - `strategy` is always {@link CrossChainIndexingStrategyId.Omnichain}. + * - `slowestChainIndexingCursor` is always equal to + * `omnichainSnapshot.omnichainIndexingCursor`. + * - `snapshotTime` is always >= the "highest known block timestamp", defined as the max of: + * - the `slowestChainIndexingCursor`. + * - the `config.startBlock.timestamp` for all indexed chains. + * - the `config.endBlock.timestamp` for all indexed chains with a `config.configType` of + * {@link ChainIndexingConfigTypeIds.Definite}. + * - the `backfillEndBlock.timestamp` for all chains with `chainStatus` of + * {@link ChainIndexingStatusIds.Backfill}. + * - the `latestKnownBlock.timestamp` for all chains with `chainStatus` of + * {@link ChainIndexingStatusIds.Following}. + */ +export interface CrossChainIndexingStatusSnapshotOmnichain { + /** + * The strategy used for indexing one or more chains. + */ + strategy: typeof CrossChainIndexingStrategyIds.Omnichain; + + /** + * The timestamp of the "slowest" latest indexed block timestamp + * across all indexed chains. + */ + slowestChainIndexingCursor: UnixTimestamp; + + /** + * The timestamp when the cross-chain indexing status snapshot was generated. + * + * Due to possible clock skew between different systems this value must be set + * to the max of each of the following values to ensure all invariants are followed: + * - the current system time of the system generating this cross-chain indexing + * status snapshot. + * - the "highest known block timestamp" (see invariants above for full definition). + */ + snapshotTime: UnixTimestamp; + + /** + * The omnichain indexing status snapshot for one or more chains. + */ + omnichainSnapshot: OmnichainIndexingStatusSnapshot; +} + +/** + * Cross-chain indexing status snapshot for one or more chains. + * + * Use the `strategy` field to determine the specific type interpretation + * at runtime. + * + * Currently, only omnichain indexing is supported. This type could theoretically + * be extended to support other cross-chain indexing strategies in the future, + * such as Ponder's "multichain" indexing strategy that indexes each chain + * independently without deterministic ordering. + */ +export type CrossChainIndexingStatusSnapshot = CrossChainIndexingStatusSnapshotOmnichain; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts index 8165b0732..ad5cc88db 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts @@ -1,98 +1,5 @@ import type { Duration, UnixTimestamp } from "../../shared/types"; -import type { - ChainIndexingConfigTypeIds, - ChainIndexingStatusIds, -} from "./chain-indexing-status-snapshot"; -import type { OmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; - -/** - * The strategy used for indexing one or more chains. - * - * @see https://ponder.sh/docs/api-reference/ponder/config#parameters - */ -export const CrossChainIndexingStrategyIds = { - /** - * Represents that the indexing of events across all indexed chains will - * proceed in a deterministic "omnichain" ordering by block timestamp, chain ID, - * and block number. - * - * This strategy is "deterministic" in that the order of processing cross-chain indexed - * events and each resulting indexed data state transition recorded in ENSDb is always - * the same for each ENSIndexer instance operating with an equivalent - * `ENSIndexerConfig` and ENSIndexer version. However it also has the drawbacks of: - * - increased indexing latency that must wait for the slowest indexed chain to - * add new blocks or to discover new blocks through the configured RPCs. - * - if any indexed chain gets "stuck" due to chain or RPC failures, all indexed chains - * will be affected. - */ - Omnichain: "omnichain", -} as const; - -/** - * The derived string union of possible {@link CrossChainIndexingStrategyIds}. - */ -export type CrossChainIndexingStrategyId = - (typeof CrossChainIndexingStrategyIds)[keyof typeof CrossChainIndexingStrategyIds]; - -/** - * Cross-chain indexing status snapshot when the `strategy` is - * {@link CrossChainIndexingStrategyId.Omnichain}. - * - * Invariants: - * - `strategy` is always {@link CrossChainIndexingStrategyId.Omnichain}. - * - `slowestChainIndexingCursor` is always equal to - * `omnichainSnapshot.omnichainIndexingCursor`. - * - `snapshotTime` is always >= the "highest known block timestamp", defined as the max of: - * - the `slowestChainIndexingCursor`. - * - the `config.startBlock.timestamp` for all indexed chains. - * - the `config.endBlock.timestamp` for all indexed chains with a `config.configType` of - * {@link ChainIndexingConfigTypeIds.Definite}. - * - the `backfillEndBlock.timestamp` for all chains with `chainStatus` of - * {@link ChainIndexingStatusIds.Backfill}. - * - the `latestKnownBlock.timestamp` for all chains with `chainStatus` of - * {@link ChainIndexingStatusIds.Following}. - */ -export interface CrossChainIndexingStatusSnapshotOmnichain { - /** - * The strategy used for indexing one or more chains. - */ - strategy: typeof CrossChainIndexingStrategyIds.Omnichain; - - /** - * The timestamp of the "slowest" latest indexed block timestamp - * across all indexed chains. - */ - slowestChainIndexingCursor: UnixTimestamp; - - /** - * The timestamp when the cross-chain indexing status snapshot was generated. - * - * Due to possible clock skew between different systems this value must be set - * to the max of each of the following values to ensure all invariants are followed: - * - the current system time of the system generating this cross-chain indexing - * status snapshot. - * - the "highest known block timestamp" (see invariants above for full definition). - */ - snapshotTime: UnixTimestamp; - - /** - * The omnichain indexing status snapshot for one or more chains. - */ - omnichainSnapshot: OmnichainIndexingStatusSnapshot; -} - -/** - * Cross-chain indexing status snapshot for one or more chains. - * - * Use the `strategy` field to determine the specific type interpretation - * at runtime. - * - * Currently, only omnichain indexing is supported. This type could theoretically - * be extended to support other cross-chain indexing strategies in the future, - * such as Ponder's "multichain" indexing strategy that indexes each chain - * independently without deterministic ordering. - */ -export type CrossChainIndexingStatusSnapshot = CrossChainIndexingStatusSnapshotOmnichain; +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; /** * A "realtime" indexing status projection based on worst-case assumptions From 752211c003a4f79e51b3fe0e087a12f23a96dc4b Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:38:06 +0100 Subject: [PATCH 02/21] Update import paths Replace `./types` with `./cross-chain-indexing-status-snapshot` --- .../src/ensindexer/indexing-status/deserialize.ts | 3 ++- .../ensnode-sdk/src/ensindexer/indexing-status/helpers.ts | 2 +- .../ensnode-sdk/src/ensindexer/indexing-status/index.ts | 1 + .../src/ensindexer/indexing-status/projection.test.ts | 3 ++- .../src/ensindexer/indexing-status/projection.ts | 3 ++- .../src/ensindexer/indexing-status/serialize.ts | 3 ++- .../src/ensindexer/indexing-status/serialized-types.ts | 7 ++----- .../src/ensindexer/indexing-status/validations.ts | 6 ++---- .../src/ensindexer/indexing-status/zod-schemas.ts | 5 ++++- 9 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index ea49239ff..01201c660 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -1,10 +1,11 @@ import { prettifyError } from "zod/v4"; +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; import type { SerializedCrossChainIndexingStatusSnapshot, SerializedRealtimeIndexingStatusProjection, } from "./serialized-types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./types"; import { makeCrossChainIndexingStatusSnapshotSchema, makeRealtimeIndexingStatusProjectionSchema, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts index 911676896..f02764ed1 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts @@ -1,6 +1,6 @@ import type { BlockRef, ChainId } from "../../shared/types"; import { ChainIndexingStatusIds } from "./chain-indexing-status-snapshot"; -import type { CrossChainIndexingStatusSnapshot } from "./types"; +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; /** * Gets the latest indexed {@link BlockRef} for the given {@link ChainId}. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index d9ba4f01a..d64c84521 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -1,4 +1,5 @@ export * from "./chain-indexing-status-snapshot"; +export * from "./cross-chain-indexing-status-snapshot"; export * from "./deserialize"; export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts index e8b2c33fd..a1afd9ddb 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts @@ -5,10 +5,11 @@ import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, } from "./chain-indexing-status-snapshot"; +import { CrossChainIndexingStrategyIds } from "./cross-chain-indexing-status-snapshot"; import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize"; import { OmnichainIndexingStatusIds } from "./omnichain-indexing-status-snapshot"; import { createRealtimeIndexingStatusProjection } from "./projection"; -import { CrossChainIndexingStrategyIds, type RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./types"; describe("Realtime Indexing Status Projection", () => { it("can be created from existing omnichain snapshot", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts index 5989d3631..e205b5af1 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts @@ -1,5 +1,6 @@ import type { UnixTimestamp } from "../../shared/types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; +import type { RealtimeIndexingStatusProjection } from "./types"; /** * Create realtime indexing status projection from diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts index fd7b82448..ce2d61a86 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts @@ -1,9 +1,10 @@ +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; import { serializeOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; import type { SerializedCrossChainIndexingStatusSnapshot, SerializedRealtimeIndexingStatusProjection, } from "./serialized-types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./types"; export function serializeCrossChainIndexingStatusSnapshotOmnichain({ strategy, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts index e866d06df..28aa54efd 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts @@ -1,9 +1,6 @@ +import type { CrossChainIndexingStatusSnapshotOmnichain } from "./cross-chain-indexing-status-snapshot"; import type { SerializedOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; -import type { - CrossChainIndexingStatusSnapshot, - CrossChainIndexingStatusSnapshotOmnichain, - RealtimeIndexingStatusProjection, -} from "./types"; +import type { RealtimeIndexingStatusProjection } from "./types"; /** * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts index 94f86b126..23c8d8e29 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts @@ -4,10 +4,8 @@ import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, } from "./chain-indexing-status-snapshot"; -import type { - CrossChainIndexingStatusSnapshotOmnichain, - RealtimeIndexingStatusProjection, -} from "./types"; +import type { CrossChainIndexingStatusSnapshotOmnichain } from "./cross-chain-indexing-status-snapshot"; +import type { RealtimeIndexingStatusProjection } from "./types"; /** * Invariants for {@link OmnichainIndexingSnapshot}. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts index a93463abb..2b7d9f48c 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts @@ -9,7 +9,10 @@ import { z } from "zod/v4"; import { makeDurationSchema, makeUnixTimestampSchema } from "../../shared/zod-schemas"; -import { CrossChainIndexingStatusSnapshotOmnichain, CrossChainIndexingStrategyIds } from "./types"; +import { + CrossChainIndexingStatusSnapshotOmnichain, + CrossChainIndexingStrategyIds, +} from "./cross-chain-indexing-status-snapshot"; import { invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime, invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect, From 8a091e9bc513c2b58aa2e36b460b160fe77213bb Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:39:54 +0100 Subject: [PATCH 03/21] Extract CrossChainIndexingStatus business-layer from generic helpers.ts file --- .../cross-chain-indexing-status-snapshot.ts | 28 ++++++++++++++++++- .../src/ensindexer/indexing-status/helpers.ts | 28 ------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts index 05cad8559..a438783ae 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts @@ -1,4 +1,5 @@ -import type { UnixTimestamp } from "../../shared/types"; +import type { BlockRef, ChainId, UnixTimestamp } from "../../shared/types"; +import { ChainIndexingStatusIds } from "./chain-indexing-status-snapshot"; import type { OmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; /** @@ -89,3 +90,28 @@ export interface CrossChainIndexingStatusSnapshotOmnichain { * independently without deterministic ordering. */ export type CrossChainIndexingStatusSnapshot = CrossChainIndexingStatusSnapshotOmnichain; + +/** + * Gets the latest indexed {@link BlockRef} for the given {@link ChainId}. + * + * @returns the latest indexed {@link BlockRef} for the given {@link ChainId}, or null if the chain + * isn't being indexed at all or is queued and therefore hasn't started indexing yet. + */ +export function getLatestIndexedBlockRef( + indexingStatus: CrossChainIndexingStatusSnapshot, + chainId: ChainId, +): BlockRef | null { + const chainIndexingStatus = indexingStatus.omnichainSnapshot.chains.get(chainId); + + if (chainIndexingStatus === undefined) { + // chain isn't being indexed at all + return null; + } + + if (chainIndexingStatus.chainStatus === ChainIndexingStatusIds.Queued) { + // chain is queued, so no data for the chain has been indexed yet + return null; + } + + return chainIndexingStatus.latestIndexedBlock; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts index f02764ed1..e69de29bb 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts @@ -1,28 +0,0 @@ -import type { BlockRef, ChainId } from "../../shared/types"; -import { ChainIndexingStatusIds } from "./chain-indexing-status-snapshot"; -import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; - -/** - * Gets the latest indexed {@link BlockRef} for the given {@link ChainId}. - * - * @returns the latest indexed {@link BlockRef} for the given {@link ChainId}, or null if the chain - * isn't being indexed at all or is queued and therefore hasn't started indexing yet. - */ -export function getLatestIndexedBlockRef( - indexingStatus: CrossChainIndexingStatusSnapshot, - chainId: ChainId, -): BlockRef | null { - const chainIndexingStatus = indexingStatus.omnichainSnapshot.chains.get(chainId); - - if (chainIndexingStatus === undefined) { - // chain isn't being indexed at all - return null; - } - - if (chainIndexingStatus.chainStatus === ChainIndexingStatusIds.Queued) { - // chain is queued, so no data for the chain has been indexed yet - return null; - } - - return chainIndexingStatus.latestIndexedBlock; -} From 3c30a9763c1d6200b8f8292a79f2be4f5c2f8f9c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:41:28 +0100 Subject: [PATCH 04/21] Remove empty helpers.ts file --- packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts | 0 packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts | 1 - 2 files changed, 1 deletion(-) delete mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index d64c84521..132cb6dea 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -3,7 +3,6 @@ export * from "./cross-chain-indexing-status-snapshot"; export * from "./deserialize"; export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; -export * from "./helpers"; export * from "./omnichain-indexing-status-snapshot"; export * from "./projection"; export * from "./serialize"; From 4babb453db3a0bf2b8f8ec797be18ea5348b595a Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:43:43 +0100 Subject: [PATCH 05/21] Extract CrossChainIndexingStatus serialize-layer from generic serialized-types.ts file --- .../cross-chain-indexing-status-snapshot.ts | 19 +++++++++++++++++++ .../indexing-status/serialized-types.ts | 16 +--------------- 2 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..f2296f578 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,19 @@ +import type { + CrossChainIndexingStatusSnapshot, + CrossChainIndexingStatusSnapshotOmnichain, +} from "../cross-chain-indexing-status-snapshot"; +import type { SerializedOmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; + +/** + * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} + */ +export interface SerializedCrossChainIndexingStatusSnapshotOmnichain + extends Omit { + omnichainSnapshot: SerializedOmnichainIndexingStatusSnapshot; +} + +/** + * Serialized representation of {@link CrossChainIndexingStatusSnapshot} + */ +export type SerializedCrossChainIndexingStatusSnapshot = + SerializedCrossChainIndexingStatusSnapshotOmnichain; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts index 28aa54efd..750ad3a36 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts @@ -1,21 +1,7 @@ -import type { CrossChainIndexingStatusSnapshotOmnichain } from "./cross-chain-indexing-status-snapshot"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; import type { SerializedOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; import type { RealtimeIndexingStatusProjection } from "./types"; -/** - * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} - */ -export interface SerializedCrossChainIndexingStatusSnapshotOmnichain - extends Omit { - omnichainSnapshot: SerializedOmnichainIndexingStatusSnapshot; -} - -/** - * Serialized representation of {@link CrossChainIndexingStatusSnapshot} - */ -export type SerializedCrossChainIndexingStatusSnapshot = - SerializedCrossChainIndexingStatusSnapshotOmnichain; - /** * Serialized representation of {@link RealtimeIndexingStatusProjection} */ From 8e5500d1756e8729a5acc44811327063bff1fb82 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:45:24 +0100 Subject: [PATCH 06/21] Update import paths Replace `./serialized-types` with `./serialize/cross-chain-indexing-status-snapshot` --- .../src/ensindexer/indexing-status/deserialize.ts | 6 ++---- .../ensnode-sdk/src/ensindexer/indexing-status/serialize.ts | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index 01201c660..470bfc692 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -1,10 +1,8 @@ import { prettifyError } from "zod/v4"; import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; -import type { - SerializedCrossChainIndexingStatusSnapshot, - SerializedRealtimeIndexingStatusProjection, -} from "./serialized-types"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; +import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; import type { RealtimeIndexingStatusProjection } from "./types"; import { makeCrossChainIndexingStatusSnapshotSchema, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts index ce2d61a86..6d1d80a4a 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts @@ -1,9 +1,7 @@ import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; import { serializeOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; -import type { - SerializedCrossChainIndexingStatusSnapshot, - SerializedRealtimeIndexingStatusProjection, -} from "./serialized-types"; +import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; import type { RealtimeIndexingStatusProjection } from "./types"; export function serializeCrossChainIndexingStatusSnapshotOmnichain({ From 17d56b45e21c09fb21bf70c9ac3d70ccb3829d6c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:46:50 +0100 Subject: [PATCH 07/21] Extract CrossChainIndexingStatus serialize-layer from generic serialize.ts file --- .../ensindexer/indexing-status/serialize.ts | 18 +----------------- .../cross-chain-indexing-status-snapshot.ts | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts index 6d1d80a4a..e41901c04 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts @@ -1,23 +1,7 @@ -import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; -import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; -import { serializeOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; +import { serializeCrossChainIndexingStatusSnapshotOmnichain } from "./serialize/cross-chain-indexing-status-snapshot"; import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; import type { RealtimeIndexingStatusProjection } from "./types"; -export function serializeCrossChainIndexingStatusSnapshotOmnichain({ - strategy, - slowestChainIndexingCursor, - snapshotTime, - omnichainSnapshot, -}: CrossChainIndexingStatusSnapshot): SerializedCrossChainIndexingStatusSnapshot { - return { - strategy, - slowestChainIndexingCursor, - snapshotTime, - omnichainSnapshot: serializeOmnichainIndexingStatusSnapshot(omnichainSnapshot), - }; -} - export function serializeRealtimeIndexingStatusProjection( indexingProjection: RealtimeIndexingStatusProjection, ): SerializedRealtimeIndexingStatusProjection { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts index f2296f578..1863f359e 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts @@ -2,7 +2,10 @@ import type { CrossChainIndexingStatusSnapshot, CrossChainIndexingStatusSnapshotOmnichain, } from "../cross-chain-indexing-status-snapshot"; -import type { SerializedOmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; +import { + type SerializedOmnichainIndexingStatusSnapshot, + serializeOmnichainIndexingStatusSnapshot, +} from "./omnichain-indexing-status-snapshot"; /** * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} @@ -17,3 +20,17 @@ export interface SerializedCrossChainIndexingStatusSnapshotOmnichain */ export type SerializedCrossChainIndexingStatusSnapshot = SerializedCrossChainIndexingStatusSnapshotOmnichain; + +export function serializeCrossChainIndexingStatusSnapshotOmnichain({ + strategy, + slowestChainIndexingCursor, + snapshotTime, + omnichainSnapshot, +}: CrossChainIndexingStatusSnapshot): SerializedCrossChainIndexingStatusSnapshot { + return { + strategy, + slowestChainIndexingCursor, + snapshotTime, + omnichainSnapshot: serializeOmnichainIndexingStatusSnapshot(omnichainSnapshot), + }; +} From ffd7b9d7fba5c029f7baf9beaf2709a8c9b4e031 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:50:20 +0100 Subject: [PATCH 08/21] Extract CrossChainIndexingStatus zod-schema-layer from generic zod-schemas.ts file --- .../cross-chain-indexing-status-snapshot.ts | 35 +++++++++++++++++++ .../ensindexer/indexing-status/zod-schemas.ts | 34 +----------------- 2 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..e7ea86445 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,35 @@ +import { z } from "zod/v4"; + +import { makeUnixTimestampSchema } from "../../../shared/zod-schemas"; +import { CrossChainIndexingStrategyIds } from "../cross-chain-indexing-status-snapshot"; +import { + invariant_slowestChainEqualsToOmnichainSnapshotTime, + invariant_snapshotTimeIsTheHighestKnownBlockTimestamp, +} from "../validations"; +import { makeOmnichainIndexingStatusSnapshotSchema } from "./omnichain-indexing-status-snapshot"; + +/** + * Makes Zod schema for {@link CrossChainIndexingStatusSnapshotOmnichain} + */ +const makeCrossChainIndexingStatusSnapshotOmnichainSchema = ( + valueLabel: string = "Cross-chain Indexing Status Snapshot Omnichain", +) => + z + .strictObject({ + strategy: z.literal(CrossChainIndexingStrategyIds.Omnichain), + slowestChainIndexingCursor: makeUnixTimestampSchema(valueLabel), + snapshotTime: makeUnixTimestampSchema(valueLabel), + omnichainSnapshot: makeOmnichainIndexingStatusSnapshotSchema(valueLabel), + }) + .check(invariant_slowestChainEqualsToOmnichainSnapshotTime) + .check(invariant_snapshotTimeIsTheHighestKnownBlockTimestamp); + +/** + * Makes Zod schema for {@link CrossChainIndexingStatusSnapshot} + */ +export const makeCrossChainIndexingStatusSnapshotSchema = ( + valueLabel: string = "Cross-chain Indexing Status Snapshot", +) => + z.discriminatedUnion("strategy", [ + makeCrossChainIndexingStatusSnapshotOmnichainSchema(valueLabel), + ]); diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts index 2b7d9f48c..3c9ebcde1 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts @@ -9,43 +9,11 @@ import { z } from "zod/v4"; import { makeDurationSchema, makeUnixTimestampSchema } from "../../shared/zod-schemas"; -import { - CrossChainIndexingStatusSnapshotOmnichain, - CrossChainIndexingStrategyIds, -} from "./cross-chain-indexing-status-snapshot"; import { invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime, invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect, - invariant_slowestChainEqualsToOmnichainSnapshotTime, - invariant_snapshotTimeIsTheHighestKnownBlockTimestamp, } from "./validations"; -import { makeOmnichainIndexingStatusSnapshotSchema } from "./zod-schema/omnichain-indexing-status-snapshot"; - -/** - * Makes Zod schema for {@link CrossChainIndexingStatusSnapshotOmnichain} - */ -const makeCrossChainIndexingStatusSnapshotOmnichainSchema = ( - valueLabel: string = "Cross-chain Indexing Status Snapshot Omnichain", -) => - z - .strictObject({ - strategy: z.literal(CrossChainIndexingStrategyIds.Omnichain), - slowestChainIndexingCursor: makeUnixTimestampSchema(valueLabel), - snapshotTime: makeUnixTimestampSchema(valueLabel), - omnichainSnapshot: makeOmnichainIndexingStatusSnapshotSchema(valueLabel), - }) - .check(invariant_slowestChainEqualsToOmnichainSnapshotTime) - .check(invariant_snapshotTimeIsTheHighestKnownBlockTimestamp); - -/** - * Makes Zod schema for {@link CrossChainIndexingStatusSnapshot} - */ -export const makeCrossChainIndexingStatusSnapshotSchema = ( - valueLabel: string = "Cross-chain Indexing Status Snapshot", -) => - z.discriminatedUnion("strategy", [ - makeCrossChainIndexingStatusSnapshotOmnichainSchema(valueLabel), - ]); +import { makeCrossChainIndexingStatusSnapshotSchema } from "./zod-schema/cross-chain-indexing-status-snapshot"; /** * Makes Zod schema for {@link RealtimeIndexingStatusProjection} From f3621c3234e9bd0f7c08965faabbbbfd9dfaf7a1 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:54:06 +0100 Subject: [PATCH 09/21] Extract CrossChainIndexingStatus deserialize-layer from generic deserialize.ts file --- .../ensindexer/indexing-status/deserialize.ts | 26 +------------------ .../cross-chain-indexing-status-snapshot.ts | 24 +++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index 470bfc692..f7358112a 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -1,32 +1,8 @@ import { prettifyError } from "zod/v4"; -import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; -import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; import type { RealtimeIndexingStatusProjection } from "./types"; -import { - makeCrossChainIndexingStatusSnapshotSchema, - makeRealtimeIndexingStatusProjectionSchema, -} from "./zod-schemas"; - -/** - * Deserialize an {@link CrossChainIndexingStatusSnapshot} object. - */ -export function deserializeCrossChainIndexingStatusSnapshot( - maybeSnapshot: SerializedCrossChainIndexingStatusSnapshot, - valueLabel?: string, -): CrossChainIndexingStatusSnapshot { - const schema = makeCrossChainIndexingStatusSnapshotSchema(valueLabel); - const parsed = schema.safeParse(maybeSnapshot); - - if (parsed.error) { - throw new Error( - `Cannot deserialize into CrossChainIndexingStatusSnapshot:\n${prettifyError(parsed.error)}\n`, - ); - } - - return parsed.data; -} +import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schemas"; /** * Deserialize into a {@link RealtimeIndexingStatusProjection} object. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..c346c88cf --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,24 @@ +import { prettifyError } from "zod/v4"; + +import type { CrossChainIndexingStatusSnapshot } from "../cross-chain-indexing-status-snapshot"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "../serialize/cross-chain-indexing-status-snapshot"; +import { makeCrossChainIndexingStatusSnapshotSchema } from "../zod-schema/cross-chain-indexing-status-snapshot"; + +/** + * Deserialize an {@link CrossChainIndexingStatusSnapshot} object. + */ +export function deserializeCrossChainIndexingStatusSnapshot( + maybeSnapshot: SerializedCrossChainIndexingStatusSnapshot, + valueLabel?: string, +): CrossChainIndexingStatusSnapshot { + const schema = makeCrossChainIndexingStatusSnapshotSchema(valueLabel); + const parsed = schema.safeParse(maybeSnapshot); + + if (parsed.error) { + throw new Error( + `Cannot deserialize into CrossChainIndexingStatusSnapshot:\n${prettifyError(parsed.error)}\n`, + ); + } + + return parsed.data; +} From a18df443b5bf37ca38c959932aeba770188c9264 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:55:21 +0100 Subject: [PATCH 10/21] Update import paths Replace `./deserialize` with `./deserialize/cross-chain-indexing-status-snapshot` --- .../src/ensindexer/indexing-status/projection.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts index a1afd9ddb..a0860fee1 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts @@ -6,7 +6,7 @@ import { ChainIndexingStatusIds, } from "./chain-indexing-status-snapshot"; import { CrossChainIndexingStrategyIds } from "./cross-chain-indexing-status-snapshot"; -import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize"; +import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize/cross-chain-indexing-status-snapshot"; import { OmnichainIndexingStatusIds } from "./omnichain-indexing-status-snapshot"; import { createRealtimeIndexingStatusProjection } from "./projection"; import type { RealtimeIndexingStatusProjection } from "./types"; From ac95ad71435bf70fb9b3b93618ef3f7d98728ab7 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 17:57:09 +0100 Subject: [PATCH 11/21] Extract CrossChainIndexingStatus zod-schema-layer from generic validations.ts file --- .../ensindexer/indexing-status/validations.ts | 81 ------------------- .../cross-chain-indexing-status-snapshot.ts | 76 ++++++++++++++++- 2 files changed, 72 insertions(+), 85 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts index 23c8d8e29..382cc2ab8 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts @@ -1,88 +1,7 @@ import type { ParsePayload } from "zod/v4/core"; -import { - ChainIndexingConfigTypeIds, - ChainIndexingStatusIds, -} from "./chain-indexing-status-snapshot"; -import type { CrossChainIndexingStatusSnapshotOmnichain } from "./cross-chain-indexing-status-snapshot"; import type { RealtimeIndexingStatusProjection } from "./types"; -/** - * Invariants for {@link OmnichainIndexingSnapshot}. - */ - -/** - * Invariants for {@link CrossChainIndexingStatusSnapshotOmnichain}. - */ - -/** - * Invariant: for cross-chain indexing status snapshot omnichain, - * slowestChainIndexingCursor equals to omnichainSnapshot.omnichainIndexingCursor - */ -export function invariant_slowestChainEqualsToOmnichainSnapshotTime( - ctx: ParsePayload, -) { - const { slowestChainIndexingCursor, omnichainSnapshot } = ctx.value; - const { omnichainIndexingCursor } = omnichainSnapshot; - - if (slowestChainIndexingCursor !== omnichainIndexingCursor) { - console.log("invariant_slowestChainEqualsToOmnichainSnapshotTime", { - slowestChainIndexingCursor, - omnichainIndexingCursor, - }); - ctx.issues.push({ - code: "custom", - input: ctx.value, - message: `'slowestChainIndexingCursor' must be equal to 'omnichainSnapshot.omnichainIndexingCursor'`, - }); - } -} - -/** - * Invariant: for cross-chain indexing status snapshot omnichain, - * snapshotTime is greater than or equal to the "highest known block" timestamp. - */ -export function invariant_snapshotTimeIsTheHighestKnownBlockTimestamp( - ctx: ParsePayload, -) { - const { snapshotTime, omnichainSnapshot } = ctx.value; - const chains = Array.from(omnichainSnapshot.chains.values()); - - const startBlockTimestamps = chains.map((chain) => chain.config.startBlock.timestamp); - - const endBlockTimestamps = chains - .map((chain) => chain.config) - .filter((chainConfig) => chainConfig.configType === ChainIndexingConfigTypeIds.Definite) - .map((chainConfig) => chainConfig.endBlock.timestamp); - - const backfillEndBlockTimestamps = chains - .filter((chain) => chain.chainStatus === ChainIndexingStatusIds.Backfill) - .map((chain) => chain.backfillEndBlock.timestamp); - - const latestKnownBlockTimestamps = chains - .filter((chain) => chain.chainStatus === ChainIndexingStatusIds.Following) - .map((chain) => chain.latestKnownBlock.timestamp); - - const highestKnownBlockTimestamp = Math.max( - ...startBlockTimestamps, - ...endBlockTimestamps, - ...backfillEndBlockTimestamps, - ...latestKnownBlockTimestamps, - ); - - if (snapshotTime < highestKnownBlockTimestamp) { - ctx.issues.push({ - code: "custom", - input: ctx.value, - message: `'snapshotTime' (${snapshotTime}) must be greater than or equal to the "highest known block timestamp" (${highestKnownBlockTimestamp})`, - }); - } -} - -/** - * Invariants for {@link RealtimeIndexingStatusProjection}. - */ - /** * Invariant: For realtime indexing status projection, * `projectedAt` is after or same as `snapshot.snapshotTime`. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts index e7ea86445..872a42601 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts @@ -1,13 +1,81 @@ import { z } from "zod/v4"; +import type { ParsePayload } from "zod/v4/core"; import { makeUnixTimestampSchema } from "../../../shared/zod-schemas"; -import { CrossChainIndexingStrategyIds } from "../cross-chain-indexing-status-snapshot"; import { - invariant_slowestChainEqualsToOmnichainSnapshotTime, - invariant_snapshotTimeIsTheHighestKnownBlockTimestamp, -} from "../validations"; + ChainIndexingConfigTypeIds, + ChainIndexingStatusIds, +} from "../chain-indexing-status-snapshot"; +import { + type CrossChainIndexingStatusSnapshotOmnichain, + CrossChainIndexingStrategyIds, +} from "../cross-chain-indexing-status-snapshot"; import { makeOmnichainIndexingStatusSnapshotSchema } from "./omnichain-indexing-status-snapshot"; +/** + * Invariant: for cross-chain indexing status snapshot omnichain, + * slowestChainIndexingCursor equals to omnichainSnapshot.omnichainIndexingCursor + */ +export function invariant_slowestChainEqualsToOmnichainSnapshotTime( + ctx: ParsePayload, +) { + const { slowestChainIndexingCursor, omnichainSnapshot } = ctx.value; + const { omnichainIndexingCursor } = omnichainSnapshot; + + if (slowestChainIndexingCursor !== omnichainIndexingCursor) { + console.log("invariant_slowestChainEqualsToOmnichainSnapshotTime", { + slowestChainIndexingCursor, + omnichainIndexingCursor, + }); + ctx.issues.push({ + code: "custom", + input: ctx.value, + message: `'slowestChainIndexingCursor' must be equal to 'omnichainSnapshot.omnichainIndexingCursor'`, + }); + } +} + +/** + * Invariant: for cross-chain indexing status snapshot omnichain, + * snapshotTime is greater than or equal to the "highest known block" timestamp. + */ +export function invariant_snapshotTimeIsTheHighestKnownBlockTimestamp( + ctx: ParsePayload, +) { + const { snapshotTime, omnichainSnapshot } = ctx.value; + const chains = Array.from(omnichainSnapshot.chains.values()); + + const startBlockTimestamps = chains.map((chain) => chain.config.startBlock.timestamp); + + const endBlockTimestamps = chains + .map((chain) => chain.config) + .filter((chainConfig) => chainConfig.configType === ChainIndexingConfigTypeIds.Definite) + .map((chainConfig) => chainConfig.endBlock.timestamp); + + const backfillEndBlockTimestamps = chains + .filter((chain) => chain.chainStatus === ChainIndexingStatusIds.Backfill) + .map((chain) => chain.backfillEndBlock.timestamp); + + const latestKnownBlockTimestamps = chains + .filter((chain) => chain.chainStatus === ChainIndexingStatusIds.Following) + .map((chain) => chain.latestKnownBlock.timestamp); + + const highestKnownBlockTimestamp = Math.max( + ...startBlockTimestamps, + ...endBlockTimestamps, + ...backfillEndBlockTimestamps, + ...latestKnownBlockTimestamps, + ); + + if (snapshotTime < highestKnownBlockTimestamp) { + ctx.issues.push({ + code: "custom", + input: ctx.value, + message: `'snapshotTime' (${snapshotTime}) must be greater than or equal to the "highest known block timestamp" (${highestKnownBlockTimestamp})`, + }); + } +} + /** * Makes Zod schema for {@link CrossChainIndexingStatusSnapshotOmnichain} */ From 79442e242c4d6bee4a539b653a4cd6a1765435de Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:02:43 +0100 Subject: [PATCH 12/21] Extract RealtimeIndexingStatusProjection business-layer from generic types.ts file Rename `types.ts` to `realtime-indexing-status-projection.ts`. --- .../ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts | 2 +- packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts | 2 +- .../src/ensindexer/indexing-status/projection.test.ts | 2 +- .../ensnode-sdk/src/ensindexer/indexing-status/projection.ts | 2 +- .../{types.ts => realtime-indexing-status-projection.ts} | 0 .../ensnode-sdk/src/ensindexer/indexing-status/serialize.ts | 2 +- .../src/ensindexer/indexing-status/serialized-types.ts | 2 +- .../ensnode-sdk/src/ensindexer/indexing-status/validations.ts | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename packages/ensnode-sdk/src/ensindexer/indexing-status/{types.ts => realtime-indexing-status-projection.ts} (100%) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index f7358112a..0b1d52f3b 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -1,7 +1,7 @@ import { prettifyError } from "zod/v4"; +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; -import type { RealtimeIndexingStatusProjection } from "./types"; import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schemas"; /** diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index 132cb6dea..9d989d0cb 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -5,9 +5,9 @@ export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; export * from "./omnichain-indexing-status-snapshot"; export * from "./projection"; +export * from "./realtime-indexing-status-projection"; export * from "./serialize"; export * from "./serialize/chain-indexing-status-snapshot"; export * from "./serialize/omnichain-indexing-status-snapshot"; export * from "./serialized-types"; -export * from "./types"; export * from "./validate/chain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts index a0860fee1..29e0eb209 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts @@ -9,7 +9,7 @@ import { CrossChainIndexingStrategyIds } from "./cross-chain-indexing-status-sna import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize/cross-chain-indexing-status-snapshot"; import { OmnichainIndexingStatusIds } from "./omnichain-indexing-status-snapshot"; import { createRealtimeIndexingStatusProjection } from "./projection"; -import type { RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; describe("Realtime Indexing Status Projection", () => { it("can be created from existing omnichain snapshot", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts index e205b5af1..0af557a6c 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts @@ -1,6 +1,6 @@ import type { UnixTimestamp } from "../../shared/types"; import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; -import type { RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; /** * Create realtime indexing status projection from diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts similarity index 100% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts index e41901c04..e1c3ef132 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts @@ -1,6 +1,6 @@ +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; import { serializeCrossChainIndexingStatusSnapshotOmnichain } from "./serialize/cross-chain-indexing-status-snapshot"; import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; -import type { RealtimeIndexingStatusProjection } from "./types"; export function serializeRealtimeIndexingStatusProjection( indexingProjection: RealtimeIndexingStatusProjection, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts index 750ad3a36..e9caa5da3 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts @@ -1,6 +1,6 @@ +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; import type { SerializedOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; -import type { RealtimeIndexingStatusProjection } from "./types"; /** * Serialized representation of {@link RealtimeIndexingStatusProjection} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts index 382cc2ab8..0734f71b3 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts @@ -1,6 +1,6 @@ import type { ParsePayload } from "zod/v4/core"; -import type { RealtimeIndexingStatusProjection } from "./types"; +import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; /** * Invariant: For realtime indexing status projection, From 4a2550253dd3a670616daef51318bece2afd0ef2 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:04:12 +0100 Subject: [PATCH 13/21] Extract RealtimeIndexingStatusProjection business-layer from generic projection.ts file Merge `projection.ts` into `realtime-indexing-status-projection.ts`. --- .../src/ensindexer/indexing-status/index.ts | 1 - .../ensindexer/indexing-status/projection.ts | 29 ------------------- ...altime-indexing-status-projection.test.ts} | 6 ++-- .../realtime-indexing-status-projection.ts | 26 +++++++++++++++++ 4 files changed, 30 insertions(+), 32 deletions(-) delete mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts rename packages/ensnode-sdk/src/ensindexer/indexing-status/{projection.test.ts => realtime-indexing-status-projection.test.ts} (91%) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index 9d989d0cb..cb37918b1 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -4,7 +4,6 @@ export * from "./deserialize"; export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; export * from "./omnichain-indexing-status-snapshot"; -export * from "./projection"; export * from "./realtime-indexing-status-projection"; export * from "./serialize"; export * from "./serialize/chain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts deleted file mode 100644 index 0af557a6c..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { UnixTimestamp } from "../../shared/types"; -import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; - -/** - * Create realtime indexing status projection from - * a {@link CrossChainIndexingStatusSnapshot}. - */ -export function createRealtimeIndexingStatusProjection( - snapshot: CrossChainIndexingStatusSnapshot, - now: UnixTimestamp, -): RealtimeIndexingStatusProjection { - /** - * The timestamp when the realtime indexing status was projected. - * - * Due to possible clock skew between different systems, - * if the "now" timestamp on the system generating this indexing status - * projection is less than the snapshot time, then this value must be set to - * equal to the whichever is higher between the `now` and - * the snapshot time to ensure all invariants are followed. - */ - const projectedAt = Math.max(now, snapshot.snapshotTime); - - return { - projectedAt, - worstCaseDistance: projectedAt - snapshot.slowestChainIndexingCursor, - snapshot, - }; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts similarity index 91% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts index 29e0eb209..91515fd83 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts @@ -8,8 +8,10 @@ import { import { CrossChainIndexingStrategyIds } from "./cross-chain-indexing-status-snapshot"; import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize/cross-chain-indexing-status-snapshot"; import { OmnichainIndexingStatusIds } from "./omnichain-indexing-status-snapshot"; -import { createRealtimeIndexingStatusProjection } from "./projection"; -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; +import { + createRealtimeIndexingStatusProjection, + type RealtimeIndexingStatusProjection, +} from "./realtime-indexing-status-projection"; describe("Realtime Indexing Status Projection", () => { it("can be created from existing omnichain snapshot", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts index ad5cc88db..39d2c3389 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts @@ -31,3 +31,29 @@ export type RealtimeIndexingStatusProjection = { */ snapshot: CrossChainIndexingStatusSnapshot; }; + +/** + * Create realtime indexing status projection from + * a {@link CrossChainIndexingStatusSnapshot}. + */ +export function createRealtimeIndexingStatusProjection( + snapshot: CrossChainIndexingStatusSnapshot, + now: UnixTimestamp, +): RealtimeIndexingStatusProjection { + /** + * The timestamp when the realtime indexing status was projected. + * + * Due to possible clock skew between different systems, + * if the "now" timestamp on the system generating this indexing status + * projection is less than the snapshot time, then this value must be set to + * equal to the whichever is higher between the `now` and + * the snapshot time to ensure all invariants are followed. + */ + const projectedAt = Math.max(now, snapshot.snapshotTime); + + return { + projectedAt, + worstCaseDistance: projectedAt - snapshot.slowestChainIndexingCursor, + snapshot, + }; +} From b55519154085e4adf94c4fe6c5ef8fdef3549af7 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:05:56 +0100 Subject: [PATCH 14/21] Extract RealtimeIndexingStatusProjection serialize-layer from generic serialized-types.ts file Rename `serialize-types.ts` to `serialize/realtime-indexing-status-projection.ts`. --- .../src/ensindexer/indexing-status/deserialize.ts | 2 +- .../ensnode-sdk/src/ensindexer/indexing-status/index.ts | 2 +- .../ensnode-sdk/src/ensindexer/indexing-status/serialize.ts | 2 +- .../realtime-indexing-status-projection.ts} | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename packages/ensnode-sdk/src/ensindexer/indexing-status/{serialized-types.ts => serialize/realtime-indexing-status-projection.ts} (76%) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index 0b1d52f3b..08661b778 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -1,7 +1,7 @@ import { prettifyError } from "zod/v4"; import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; -import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; +import type { SerializedRealtimeIndexingStatusProjection } from "./serialize/realtime-indexing-status-projection"; import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schemas"; /** diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index cb37918b1..f6bd5daed 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -8,5 +8,5 @@ export * from "./realtime-indexing-status-projection"; export * from "./serialize"; export * from "./serialize/chain-indexing-status-snapshot"; export * from "./serialize/omnichain-indexing-status-snapshot"; -export * from "./serialized-types"; +export * from "./serialize/realtime-indexing-status-projection"; export * from "./validate/chain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts index e1c3ef132..07ec2c6f0 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts @@ -1,6 +1,6 @@ import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; import { serializeCrossChainIndexingStatusSnapshotOmnichain } from "./serialize/cross-chain-indexing-status-snapshot"; -import type { SerializedRealtimeIndexingStatusProjection } from "./serialized-types"; +import type { SerializedRealtimeIndexingStatusProjection } from "./serialize/realtime-indexing-status-projection"; export function serializeRealtimeIndexingStatusProjection( indexingProjection: RealtimeIndexingStatusProjection, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts similarity index 76% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts index e9caa5da3..cd5ebdff6 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts @@ -1,6 +1,6 @@ -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; -import type { SerializedCrossChainIndexingStatusSnapshot } from "./serialize/cross-chain-indexing-status-snapshot"; -import type { SerializedOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; +import type { SerializedOmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; /** * Serialized representation of {@link RealtimeIndexingStatusProjection} From e05ac9cf7dcf77f8048d74c7b59e06d70dafd490 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:06:59 +0100 Subject: [PATCH 15/21] Extract RealtimeIndexingStatusProjection serialize-layer from generic serialize.ts file Merge `serialize.ts` into `serialize/realtime-indexing-status-projection.ts`. --- .../src/ensindexer/indexing-status/index.ts | 2 +- .../src/ensindexer/indexing-status/serialize.ts | 13 ------------- .../realtime-indexing-status-projection.ts | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index f6bd5daed..1803b969a 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -5,8 +5,8 @@ export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; export * from "./omnichain-indexing-status-snapshot"; export * from "./realtime-indexing-status-projection"; -export * from "./serialize"; export * from "./serialize/chain-indexing-status-snapshot"; export * from "./serialize/omnichain-indexing-status-snapshot"; export * from "./serialize/realtime-indexing-status-projection"; +export * from "./serialize/realtime-indexing-status-projection"; export * from "./validate/chain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts deleted file mode 100644 index 07ec2c6f0..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; -import { serializeCrossChainIndexingStatusSnapshotOmnichain } from "./serialize/cross-chain-indexing-status-snapshot"; -import type { SerializedRealtimeIndexingStatusProjection } from "./serialize/realtime-indexing-status-projection"; - -export function serializeRealtimeIndexingStatusProjection( - indexingProjection: RealtimeIndexingStatusProjection, -): SerializedRealtimeIndexingStatusProjection { - return { - projectedAt: indexingProjection.projectedAt, - worstCaseDistance: indexingProjection.worstCaseDistance, - snapshot: serializeCrossChainIndexingStatusSnapshotOmnichain(indexingProjection.snapshot), - } satisfies SerializedRealtimeIndexingStatusProjection; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts index cd5ebdff6..0ae32d1d9 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts @@ -1,5 +1,8 @@ import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; -import type { SerializedCrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; +import { + type SerializedCrossChainIndexingStatusSnapshot, + serializeCrossChainIndexingStatusSnapshotOmnichain, +} from "./cross-chain-indexing-status-snapshot"; import type { SerializedOmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; /** @@ -17,3 +20,13 @@ export interface SerializedRealtimeIndexingStatusProjection extends Omit { snapshot: SerializedCrossChainIndexingStatusSnapshot; } + +export function serializeRealtimeIndexingStatusProjection( + indexingProjection: RealtimeIndexingStatusProjection, +): SerializedRealtimeIndexingStatusProjection { + return { + projectedAt: indexingProjection.projectedAt, + worstCaseDistance: indexingProjection.worstCaseDistance, + snapshot: serializeCrossChainIndexingStatusSnapshotOmnichain(indexingProjection.snapshot), + } satisfies SerializedRealtimeIndexingStatusProjection; +} From 8cc84fdd2c99d6b6297b9304048e5522a1651364 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:09:50 +0100 Subject: [PATCH 16/21] Extract RealtimeIndexingStatusProjection zod-schema-layer from generic zod-schemas.ts file Rename `zod-schemas.ts` into `zod-schema/realtime-indexing-status-projection.ts`. --- packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts | 2 +- .../src/ensindexer/indexing-status/deserialize.ts | 2 +- .../chain-indexing-status-snapshot.test.ts} | 6 +++--- .../realtime-indexing-status-projection.ts} | 6 +++--- packages/ensnode-sdk/src/internal.ts | 1 - 5 files changed, 8 insertions(+), 9 deletions(-) rename packages/ensnode-sdk/src/ensindexer/indexing-status/{zod-schemas.test.ts => zod-schema/chain-indexing-status-snapshot.test.ts} (98%) rename packages/ensnode-sdk/src/ensindexer/indexing-status/{zod-schemas.ts => zod-schema/realtime-indexing-status-projection.ts} (87%) diff --git a/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts index 407ae50ce..9475b6cf1 100644 --- a/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts @@ -1,6 +1,6 @@ import { z } from "zod/v4"; -import { makeRealtimeIndexingStatusProjectionSchema } from "../../ensindexer/indexing-status/zod-schemas"; +import { makeRealtimeIndexingStatusProjectionSchema } from "../../ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection"; import { type IndexingStatusResponse, IndexingStatusResponseCodes, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts index 08661b778..229122104 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts @@ -2,7 +2,7 @@ import { prettifyError } from "zod/v4"; import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; import type { SerializedRealtimeIndexingStatusProjection } from "./serialize/realtime-indexing-status-projection"; -import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schemas"; +import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schema/realtime-indexing-status-projection"; /** * Deserialize into a {@link RealtimeIndexingStatusProjection} object. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts similarity index 98% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts index e58b6dd31..084138ceb 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts @@ -6,7 +6,7 @@ import { earliestBlockRef, laterBlockRef, latestBlockRef, -} from "./block-refs.mock"; +} from "../block-refs.mock"; import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, @@ -15,8 +15,8 @@ import { type ChainIndexingStatusSnapshotCompleted, type ChainIndexingStatusSnapshotFollowing, type ChainIndexingStatusSnapshotQueued, -} from "./chain-indexing-status-snapshot"; -import { makeChainIndexingStatusSnapshotSchema } from "./zod-schema/chain-indexing-status-snapshot"; +} from "../chain-indexing-status-snapshot"; +import { makeChainIndexingStatusSnapshotSchema } from "./chain-indexing-status-snapshot"; describe("ENSIndexer: Indexing Status", () => { describe("Zod Schemas", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts similarity index 87% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts index 3c9ebcde1..4e6b23179 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts @@ -8,12 +8,12 @@ */ import { z } from "zod/v4"; -import { makeDurationSchema, makeUnixTimestampSchema } from "../../shared/zod-schemas"; +import { makeDurationSchema, makeUnixTimestampSchema } from "../../../shared/zod-schemas"; import { invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime, invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect, -} from "./validations"; -import { makeCrossChainIndexingStatusSnapshotSchema } from "./zod-schema/cross-chain-indexing-status-snapshot"; +} from "../validations"; +import { makeCrossChainIndexingStatusSnapshotSchema } from "./cross-chain-indexing-status-snapshot"; /** * Makes Zod schema for {@link RealtimeIndexingStatusProjection} diff --git a/packages/ensnode-sdk/src/internal.ts b/packages/ensnode-sdk/src/internal.ts index 7352f9a05..da2b4a166 100644 --- a/packages/ensnode-sdk/src/internal.ts +++ b/packages/ensnode-sdk/src/internal.ts @@ -20,7 +20,6 @@ export * from "./api/shared/errors/zod-schemas"; export * from "./api/shared/pagination/zod-schemas"; export * from "./ensapi/config/zod-schemas"; export * from "./ensindexer/config/zod-schemas"; -export * from "./ensindexer/indexing-status/zod-schemas"; export * from "./registrars/zod-schemas"; export * from "./rpc"; export * from "./shared/config/build-rpc-urls"; From 62010f76c5feb5304be374d490b969b7da30f918 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:12:26 +0100 Subject: [PATCH 17/21] Extract RealtimeIndexingStatusProjection zod-schema-layer from generic validations.ts file Merge `validations.ts` into `zod-schema/realtime-indexing-status-projection.ts`. --- .../ensindexer/indexing-status/validations.ts | 46 ----------------- .../realtime-indexing-status-projection.ts | 49 +++++++++++++++++-- 2 files changed, 45 insertions(+), 50 deletions(-) delete mode 100644 packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts deleted file mode 100644 index 0734f71b3..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { ParsePayload } from "zod/v4/core"; - -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; - -/** - * Invariant: For realtime indexing status projection, - * `projectedAt` is after or same as `snapshot.snapshotTime`. - */ -export function invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime( - ctx: ParsePayload, -) { - const projection = ctx.value; - - const { snapshot, projectedAt } = projection; - - if (snapshot.snapshotTime > projectedAt) { - ctx.issues.push({ - code: "custom", - input: projection, - message: "`projectedAt` must be after or same as `snapshot.snapshotTime`.", - }); - } -} - -/** - * Invariant: For realtime indexing status projection, - * `worstCaseDistance` is the difference between `projectedAt` - * and `omnichainIndexingCursor`. - */ -export function invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect( - ctx: ParsePayload, -) { - const projection = ctx.value; - const { projectedAt, snapshot, worstCaseDistance } = projection; - const { omnichainSnapshot } = snapshot; - const expectedWorstCaseDistance = projectedAt - omnichainSnapshot.omnichainIndexingCursor; - - if (worstCaseDistance !== expectedWorstCaseDistance) { - ctx.issues.push({ - code: "custom", - input: projection, - message: - "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.omnichainIndexingCursor`.", - }); - } -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts index 4e6b23179..dc9caab9c 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts @@ -7,14 +7,55 @@ * `./src/internal.ts` file. */ import { z } from "zod/v4"; +import type { ParsePayload } from "zod/v4/core"; import { makeDurationSchema, makeUnixTimestampSchema } from "../../../shared/zod-schemas"; -import { - invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime, - invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect, -} from "../validations"; +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; import { makeCrossChainIndexingStatusSnapshotSchema } from "./cross-chain-indexing-status-snapshot"; +/** + * Invariant: For realtime indexing status projection, + * `projectedAt` is after or same as `snapshot.snapshotTime`. + */ +export function invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime( + ctx: ParsePayload, +) { + const projection = ctx.value; + + const { snapshot, projectedAt } = projection; + + if (snapshot.snapshotTime > projectedAt) { + ctx.issues.push({ + code: "custom", + input: projection, + message: "`projectedAt` must be after or same as `snapshot.snapshotTime`.", + }); + } +} + +/** + * Invariant: For realtime indexing status projection, + * `worstCaseDistance` is the difference between `projectedAt` + * and `omnichainIndexingCursor`. + */ +export function invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect( + ctx: ParsePayload, +) { + const projection = ctx.value; + const { projectedAt, snapshot, worstCaseDistance } = projection; + const { omnichainSnapshot } = snapshot; + const expectedWorstCaseDistance = projectedAt - omnichainSnapshot.omnichainIndexingCursor; + + if (worstCaseDistance !== expectedWorstCaseDistance) { + ctx.issues.push({ + code: "custom", + input: projection, + message: + "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.omnichainIndexingCursor`.", + }); + } +} + /** * Makes Zod schema for {@link RealtimeIndexingStatusProjection} */ From 84665521ccf2e0892809d5a2c68038c9a2f4b296 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:14:31 +0100 Subject: [PATCH 18/21] Extract RealtimeIndexingStatusProjection deserialize-layer from generic deserialize.ts file Rename `deserialize.ts` to `deserialize/realtime-indexing-status-projection.ts`. --- .../realtime-indexing-status-snapshot.ts} | 6 +++--- .../ensnode-sdk/src/ensindexer/indexing-status/index.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) rename packages/ensnode-sdk/src/ensindexer/indexing-status/{deserialize.ts => deserialize/realtime-indexing-status-snapshot.ts} (72%) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-snapshot.ts similarity index 72% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-snapshot.ts index 229122104..90a79470e 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-snapshot.ts @@ -1,8 +1,8 @@ import { prettifyError } from "zod/v4"; -import type { RealtimeIndexingStatusProjection } from "./realtime-indexing-status-projection"; -import type { SerializedRealtimeIndexingStatusProjection } from "./serialize/realtime-indexing-status-projection"; -import { makeRealtimeIndexingStatusProjectionSchema } from "./zod-schema/realtime-indexing-status-projection"; +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; +import type { SerializedRealtimeIndexingStatusProjection } from "../serialize/realtime-indexing-status-projection"; +import { makeRealtimeIndexingStatusProjectionSchema } from "../zod-schema/realtime-indexing-status-projection"; /** * Deserialize into a {@link RealtimeIndexingStatusProjection} object. diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index 1803b969a..4834ee924 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -1,8 +1,9 @@ export * from "./chain-indexing-status-snapshot"; export * from "./cross-chain-indexing-status-snapshot"; -export * from "./deserialize"; export * from "./deserialize/chain-indexing-status-snapshot"; +export * from "./deserialize/cross-chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; +export * from "./deserialize/realtime-indexing-status-snapshot"; export * from "./omnichain-indexing-status-snapshot"; export * from "./realtime-indexing-status-projection"; export * from "./serialize/chain-indexing-status-snapshot"; From 8c40bfa260ec7660b63e3aa839fa6b1cfec61745 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:35:45 +0100 Subject: [PATCH 19/21] Apply AI agent feedback Import paths updates --- ...atus-snapshot.ts => realtime-indexing-status-projection.ts} | 0 packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) rename packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/{realtime-indexing-status-snapshot.ts => realtime-indexing-status-projection.ts} (100%) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts similarity index 100% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-snapshot.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index 4834ee924..7d642d64a 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -3,11 +3,10 @@ export * from "./cross-chain-indexing-status-snapshot"; export * from "./deserialize/chain-indexing-status-snapshot"; export * from "./deserialize/cross-chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; -export * from "./deserialize/realtime-indexing-status-snapshot"; +export * from "./deserialize/realtime-indexing-status-projection"; export * from "./omnichain-indexing-status-snapshot"; export * from "./realtime-indexing-status-projection"; export * from "./serialize/chain-indexing-status-snapshot"; export * from "./serialize/omnichain-indexing-status-snapshot"; export * from "./serialize/realtime-indexing-status-projection"; -export * from "./serialize/realtime-indexing-status-projection"; export * from "./validate/chain-indexing-status-snapshot"; From bf5bf94121e5c40c351111ccd6c32431a6281a33 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:36:19 +0100 Subject: [PATCH 20/21] Apply AI agent feedback General serialization method for `CrossChainIndexingStatusSnapshot`. --- .../cross-chain-indexing-status-snapshot.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts index 1863f359e..82c1c309f 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts @@ -1,6 +1,7 @@ -import type { - CrossChainIndexingStatusSnapshot, - CrossChainIndexingStatusSnapshotOmnichain, +import { + type CrossChainIndexingStatusSnapshot, + type CrossChainIndexingStatusSnapshotOmnichain, + CrossChainIndexingStrategyIds, } from "../cross-chain-indexing-status-snapshot"; import { type SerializedOmnichainIndexingStatusSnapshot, @@ -26,7 +27,7 @@ export function serializeCrossChainIndexingStatusSnapshotOmnichain({ slowestChainIndexingCursor, snapshotTime, omnichainSnapshot, -}: CrossChainIndexingStatusSnapshot): SerializedCrossChainIndexingStatusSnapshot { +}: CrossChainIndexingStatusSnapshotOmnichain): SerializedCrossChainIndexingStatusSnapshotOmnichain { return { strategy, slowestChainIndexingCursor, @@ -34,3 +35,12 @@ export function serializeCrossChainIndexingStatusSnapshotOmnichain({ omnichainSnapshot: serializeOmnichainIndexingStatusSnapshot(omnichainSnapshot), }; } + +export function serializeCrossChainIndexingStatusSnapshot( + snapshot: CrossChainIndexingStatusSnapshot, +): SerializedCrossChainIndexingStatusSnapshot { + switch (snapshot.strategy) { + case CrossChainIndexingStrategyIds.Omnichain: + return serializeCrossChainIndexingStatusSnapshotOmnichain(snapshot); + } +} From 66ae12daade15a1ebb4bf24eeaea0eb477d33d5c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Wed, 11 Feb 2026 18:37:30 +0100 Subject: [PATCH 21/21] Apply AI agent feedback Use equivalent `slowestChainIndexingCursor` field to replace `omnichainSnapshot.omnichainIndexingCursor` in invariant logic for `RealtimeIndexingStatusProjection`. --- .../zod-schema/realtime-indexing-status-projection.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts index dc9caab9c..0558caeb2 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts @@ -36,22 +36,21 @@ export function invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEq /** * Invariant: For realtime indexing status projection, * `worstCaseDistance` is the difference between `projectedAt` - * and `omnichainIndexingCursor`. + * and `snapshot.slowestChainIndexingCursor`. */ export function invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect( ctx: ParsePayload, ) { const projection = ctx.value; const { projectedAt, snapshot, worstCaseDistance } = projection; - const { omnichainSnapshot } = snapshot; - const expectedWorstCaseDistance = projectedAt - omnichainSnapshot.omnichainIndexingCursor; + const expectedWorstCaseDistance = projectedAt - snapshot.slowestChainIndexingCursor; if (worstCaseDistance !== expectedWorstCaseDistance) { ctx.issues.push({ code: "custom", input: projection, message: - "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.omnichainIndexingCursor`.", + "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.slowestChainIndexingCursor`.", }); } }