diff --git a/.changeset/shy-rabbits-drum.md b/.changeset/shy-rabbits-drum.md new file mode 100644 index 000000000..0f8b192d9 --- /dev/null +++ b/.changeset/shy-rabbits-drum.md @@ -0,0 +1,6 @@ +--- +"@ensnode/ensnode-sdk": minor +"ensapi": minor +--- + +Made `accurateAsOf` a required field in the Registrar Actions API response (`RegistrarActionsResponseOk`). diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts index ae9eb3461..5b0a48892 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts @@ -101,6 +101,16 @@ async function fetchRegistrarActions(parentNode: Node | undefined, query: Regist */ app.openapi(getRegistrarActionsRoute, async (c) => { try { + // Defensive: `registrarActionsApiMiddleware` already short-circuits with a + // serialized 503 when indexingStatus is an Error, so this branch is + // unreachable at runtime — kept only for TypeScript type narrowing. + if (c.var.indexingStatus instanceof Error) { + throw new Error("Indexing status has not been loaded yet"); + } + + // Get the accurateAsOf timestamp from the omnichain indexing cursor + const accurateAsOf = c.var.indexingStatus.snapshot.omnichainSnapshot.omnichainIndexingCursor; + const query = c.req.valid("query"); const { registrarActions, pageContext } = await fetchRegistrarActions(undefined, query); @@ -110,6 +120,7 @@ app.openapi(getRegistrarActionsRoute, async (c) => { responseCode: RegistrarActionsResponseCodes.Ok, registrarActions, pageContext, + accurateAsOf, } satisfies RegistrarActionsResponseOk), ); } catch (error) { @@ -163,17 +174,20 @@ app.openapi(getRegistrarActionsRoute, async (c) => { */ app.openapi(getRegistrarActionsByParentNodeRoute, async (c) => { try { + // Defensive: `registrarActionsApiMiddleware` already short-circuits with a + // serialized 503 when indexingStatus is an Error, so this branch is + // unreachable at runtime — kept only for TypeScript type narrowing. if (c.var.indexingStatus instanceof Error) { throw new Error("Indexing status has not been loaded yet"); } + // Get the accurateAsOf timestamp from the omnichain indexing cursor + const accurateAsOf = c.var.indexingStatus.snapshot.omnichainSnapshot.omnichainIndexingCursor; + const { parentNode } = c.req.valid("param"); const query = c.req.valid("query"); const { registrarActions, pageContext } = await fetchRegistrarActions(parentNode, query); - // Get the accurateAsOf timestamp from the slowest chain indexing cursor - const accurateAsOf = c.var.indexingStatus.snapshot.slowestChainIndexingCursor; - // respond with success response return c.json( serializeRegistrarActionsResponse({ diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/response.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/response.ts index f87c87c42..1064fed7d 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/response.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/response.ts @@ -56,15 +56,11 @@ export type RegistrarActionsResponseOk = { * The {@link UnixTimestamp} of when the data used to build the list of {@link NamedRegistrarAction} was accurate as of. * * @remarks - * **Note:** This value represents the `slowestChainIndexingCursor` from the latest omnichain indexing status + * **Note:** This value represents the `omnichainIndexingCursor` from the latest omnichain indexing status * snapshot captured by ENSApi. The state returned in the response is guaranteed to be accurate as of this * timestamp but may be from a timestamp higher than this value. - * - * **Temporary:** This field is currently optional to maintain backward compatibility with ENS Awards - * using older snapshot NPM packages. This will be changed to required in a future release. - * See: https://github.com/namehash/ensnode/issues/1497 */ - accurateAsOf?: UnixTimestamp; + accurateAsOf: UnixTimestamp; }; /** diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts index eb7fe5320..33c6b868f 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts @@ -125,6 +125,12 @@ describe("ENSNode API Schema", () => { expect(() => makeRegistrarActionsResponseSchema().parse(validResponseOk)).not.toThrowError(); }); + it("rejects ResponseOk object missing required accurateAsOf", () => { + const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = validResponseOk; + + expect(() => makeRegistrarActionsResponseSchema().parse(invalidResponseOk)).toThrowError(); + }); + it("can parse valid ResponseError object", () => { const parsed = makeRegistrarActionsResponseSchema().parse(validResponseError); diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts index 9057ddf4d..0e4b45c82 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts @@ -43,7 +43,7 @@ export const makeRegistrarActionsResponseOkSchema = ( responseCode: z.literal(RegistrarActionsResponseCodes.Ok), registrarActions: z.array(makeNamedRegistrarActionSchema(valueLabel)), pageContext: makeResponsePageContextSchema(`${valueLabel}.pageContext`), - accurateAsOf: makeUnixTimestampSchema(`${valueLabel}.accurateAsOf`).optional(), + accurateAsOf: makeUnixTimestampSchema(`${valueLabel}.accurateAsOf`), }); /**