diff --git a/apps/ensadmin/src/app/mock/config-api.mock.ts b/apps/ensadmin/src/app/mock/config-api.mock.ts index 8edbde34c..7958e2dcc 100644 --- a/apps/ensadmin/src/app/mock/config-api.mock.ts +++ b/apps/ensadmin/src/app/mock/config-api.mock.ts @@ -1,6 +1,10 @@ -import { deserializeENSIndexerPublicConfig } from "@ensnode/ensnode-sdk"; +import { + deserializeENSIndexerPublicConfig, + SerializedEnsApiConfigResponse, + SerializedEnsIndexerConfigResponse, +} from "@ensnode/ensnode-sdk"; -export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig({ +const serializedEnsIndexerPublicConfig = { labelSet: { labelSetId: "subgraph", labelSetVersion: 0, @@ -27,4 +31,17 @@ export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig({ ensRainbowSchema: 3, ensNormalize: "1.11.1", }, -}); +} satisfies SerializedEnsIndexerConfigResponse; + +export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig( + serializedEnsIndexerPublicConfig, +); + +export const serializedEnsApiPublicConfig = { + ensIndexerPublicConfig: serializedEnsIndexerPublicConfig, + theGraphFallback: { + canFallback: true, + url: "https://api.thegraph.com/subgraphs/name/ensdomains/ens", + }, + version: "0.35.0", +} satisfies SerializedEnsApiConfigResponse; diff --git a/apps/ensadmin/src/app/mock/indexing-stats/page.tsx b/apps/ensadmin/src/app/mock/indexing-stats/page.tsx index 1ceffccf8..218b79dcb 100644 --- a/apps/ensadmin/src/app/mock/indexing-stats/page.tsx +++ b/apps/ensadmin/src/app/mock/indexing-stats/page.tsx @@ -7,6 +7,7 @@ import { useEffect, useState } from "react"; import { CrossChainIndexingStatusSnapshot, createRealtimeIndexingStatusProjection, + EnsApiIndexingStatusResponseOk, IndexingStatusResponseCodes, IndexingStatusResponseOk, OmnichainIndexingStatusIds, @@ -37,7 +38,7 @@ let loadingTimeoutId: number; async function fetchMockedIndexingStatus( selectedVariant: Variant, -): Promise { +): Promise { // always try clearing loading timeout when performing a mocked fetch // this way we get a fresh and very long request to observe the loading state if (loadingTimeoutId) { @@ -53,14 +54,14 @@ async function fetchMockedIndexingStatus( selectedVariant ] as IndexingStatusResponseOk; - return response.realtimeProjection.snapshot; + return response; } case "Error ResponseCode": throw new Error( "Received Indexing Status response with responseCode other than 'ok' which will not be cached.", ); case "Loading": - return new Promise((_resolve, reject) => { + return new Promise((_resolve, reject) => { loadingTimeoutId = +setTimeout(reject, 5 * 60 * 1_000); }); case "Loading Error": @@ -77,10 +78,14 @@ export default function MockIndexingStatusPage() { const mockedIndexingStatus = useQuery({ queryKey: ["mock", "useIndexingStatus", selectedVariant], queryFn: () => fetchMockedIndexingStatus(selectedVariant), - select: (cachedSnapshot) => { + select: (response) => { return { responseCode: IndexingStatusResponseCodes.Ok, - realtimeProjection: createRealtimeIndexingStatusProjection(cachedSnapshot, now), + realtimeProjection: createRealtimeIndexingStatusProjection( + response.realtimeProjection.snapshot, + now, + ), + config: response.config, } satisfies IndexingStatusResponseOk; }, retry: false, // allows loading error to be observed immediately diff --git a/apps/ensadmin/src/app/mock/indexing-status-api.mock.ts b/apps/ensadmin/src/app/mock/indexing-status-api.mock.ts index 914e5c329..661c81933 100644 --- a/apps/ensadmin/src/app/mock/indexing-status-api.mock.ts +++ b/apps/ensadmin/src/app/mock/indexing-status-api.mock.ts @@ -18,6 +18,8 @@ import { type SerializedOmnichainIndexingStatusSnapshotUnstarted, } from "@ensnode/ensnode-sdk"; +import { serializedEnsApiPublicConfig } from "@/app/mock/config-api.mock"; + export const indexingStatusResponseError: IndexingStatusResponseError = { responseCode: IndexingStatusResponseCodes.Error, }; @@ -85,6 +87,7 @@ export const indexingStatusResponseOkOmnichain: Record< } satisfies SerializedOmnichainIndexingStatusSnapshotUnstarted, }, }, + config: serializedEnsApiPublicConfig, }), [OmnichainIndexingStatusIds.Backfill]: deserializeIndexingStatusResponse({ @@ -163,6 +166,7 @@ export const indexingStatusResponseOkOmnichain: Record< } satisfies SerializedOmnichainIndexingStatusSnapshotBackfill, }, }, + config: serializedEnsApiPublicConfig, }), [OmnichainIndexingStatusIds.Following]: deserializeIndexingStatusResponse({ @@ -256,6 +260,7 @@ export const indexingStatusResponseOkOmnichain: Record< } satisfies SerializedOmnichainIndexingStatusSnapshotFollowing, }, }, + config: serializedEnsApiPublicConfig, }), [OmnichainIndexingStatusIds.Completed]: deserializeIndexingStatusResponse({ @@ -293,5 +298,6 @@ export const indexingStatusResponseOkOmnichain: Record< } satisfies SerializedOmnichainIndexingStatusSnapshotCompleted, }, }, + config: serializedEnsApiPublicConfig, }), }; diff --git a/apps/ensadmin/src/components/config/useEnsApiConfig.ts b/apps/ensadmin/src/components/config/useEnsApiConfig.ts new file mode 100644 index 000000000..de5696ed9 --- /dev/null +++ b/apps/ensadmin/src/components/config/useEnsApiConfig.ts @@ -0,0 +1,16 @@ +import { useQuery } from "@tanstack/react-query"; + +import { useEnsApiProviderOptions } from "@ensnode/ensnode-react"; + +import { useIndexingStatusWithSwr } from "@/components/indexing-status"; + +export function useEnsApiConfig() { + const ensApiProviderOptions = useEnsApiProviderOptions(); + const indexingStatus = useIndexingStatusWithSwr(); + + return useQuery({ + enabled: indexingStatus.isSuccess, + queryKey: ["swr", ensApiProviderOptions.client.url.href, "config"], + queryFn: async () => indexingStatus.data?.config, // enabled flag ensures this is only called when indexingStatus.data is available + }); +} diff --git a/apps/ensadmin/src/components/connection/cards/ensnode-info.tsx b/apps/ensadmin/src/components/connection/cards/ensnode-info.tsx index 29210bb11..b803baa93 100644 --- a/apps/ensadmin/src/components/connection/cards/ensnode-info.tsx +++ b/apps/ensadmin/src/components/connection/cards/ensnode-info.tsx @@ -9,9 +9,9 @@ import { ChainIcon, getChainName } from "@namehash/namehash-ui"; import { History, Replace } from "lucide-react"; import { Fragment, ReactNode } from "react"; -import { useENSNodeConfig } from "@ensnode/ensnode-react"; -import { type ENSApiPublicConfig, getENSRootChainId } from "@ensnode/ensnode-sdk"; +import { type EnsApiPublicConfig, getENSRootChainId } from "@ensnode/ensnode-sdk"; +import { useEnsApiConfig } from "@/components/config/useEnsApiConfig"; import { ErrorInfo, type ErrorInfoProps } from "@/components/error-info"; import { ENSApiIcon } from "@/components/icons/ensnode-apps/ensapi-icon"; import { ENSDbIcon } from "@/components/icons/ensnode-apps/ensdb-icon"; @@ -96,7 +96,7 @@ function ENSNodeCardLoadingSkeleton() { * Props for ENSNodeConfigCardDisplay - display component that accepts props for testing/mocking */ export interface ENSNodeConfigCardDisplayProps { - ensApiPublicConfig: ENSApiPublicConfig; + ensApiPublicConfig: EnsApiPublicConfig; } /** @@ -114,7 +114,7 @@ export function ENSNodeConfigCardDisplay({ ensApiPublicConfig }: ENSNodeConfigCa * Props for ENSNodeConfigInfoView - internal component that accepts props for testing/mocking */ export interface ENSNodeConfigInfoViewProps { - ensApiPublicConfig?: ENSApiPublicConfig; + ensApiPublicConfig?: EnsApiPublicConfig; error?: ErrorInfoProps; isLoading?: boolean; } @@ -147,28 +147,30 @@ export function ENSNodeConfigInfoView({ * ENSNodeConfigInfo component - fetches and displays ENSNode configuration data */ export function ENSNodeConfigInfo() { - const ensNodeConfigQuery = useENSNodeConfig(); + const ensApiConfig = useEnsApiConfig(); - return ( - - ); + if (ensApiConfig.isPending) { + return ; + } + + if (ensApiConfig.isError) { + return ( + + ); + } + + return ; } function ENSNodeConfigCardContent({ ensApiPublicConfig, }: { - ensApiPublicConfig: ENSApiPublicConfig; + ensApiPublicConfig: EnsApiPublicConfig; }) { const cardItemValueStyles = "text-sm leading-6 font-normal text-black"; diff --git a/apps/ensadmin/src/components/connections/require-active-connection.tsx b/apps/ensadmin/src/components/connections/require-active-connection.tsx index e3d921cc9..d56c7709b 100644 --- a/apps/ensadmin/src/components/connections/require-active-connection.tsx +++ b/apps/ensadmin/src/components/connections/require-active-connection.tsx @@ -2,8 +2,7 @@ import type { PropsWithChildren } from "react"; -import { useENSNodeConfig } from "@ensnode/ensnode-react"; - +import { useEnsApiConfig } from "@/components/config/useEnsApiConfig"; import { ErrorInfo } from "@/components/error-info"; import { LoadingSpinner } from "@/components/loading-spinner"; @@ -11,14 +10,14 @@ import { LoadingSpinner } from "@/components/loading-spinner"; * Allows consumers to use `useActiveConnection` by blocking rendering until it is available. */ export function RequireActiveConnection({ children }: PropsWithChildren) { - const { status, error } = useENSNodeConfig(); + const ensApiConfig = useEnsApiConfig(); - if (status === "pending") return ; + if (ensApiConfig.status === "pending") return ; - if (status === "error") { + if (ensApiConfig.status === "error") { return (
- +
); } diff --git a/apps/ensadmin/src/components/indexing-status/use-indexing-status-with-swr.ts b/apps/ensadmin/src/components/indexing-status/use-indexing-status-with-swr.ts index ba9d80cc2..449138818 100644 --- a/apps/ensadmin/src/components/indexing-status/use-indexing-status-with-swr.ts +++ b/apps/ensadmin/src/components/indexing-status/use-indexing-status-with-swr.ts @@ -7,40 +7,49 @@ import { useCallback, useMemo } from "react"; import { createIndexingStatusQueryOptions, QueryParameter, - useENSNodeSDKConfig, + useEnsApiProviderOptions, type useIndexingStatus, useSwrQuery, - WithSDKConfigParameter, + WithEnsApiProviderOptions, } from "@ensnode/ensnode-react"; import { CrossChainIndexingStatusSnapshotOmnichain, createRealtimeIndexingStatusProjection, Duration, - type IndexingStatusRequest, - IndexingStatusResponseCodes, - IndexingStatusResponseOk, + EnsApiIndexingStatusRequest, + EnsApiIndexingStatusResponseCodes, + EnsApiIndexingStatusResponseOk, + EnsApiPublicConfig, } from "@ensnode/ensnode-sdk"; const DEFAULT_REFETCH_INTERVAL = secondsToMilliseconds(10); const REALTIME_PROJECTION_REFRESH_RATE: Duration = 1; +interface CachableIndexingStatus { + crossChainIndexingStatusSnapshot: CrossChainIndexingStatusSnapshotOmnichain; + config: EnsApiPublicConfig; +} + interface UseIndexingStatusParameters - extends IndexingStatusRequest, - QueryParameter {} + extends EnsApiIndexingStatusRequest, + QueryParameter {} /** * A proxy hook for {@link useIndexingStatus} which applies * stale-while-revalidate cache for successful responses. */ export function useIndexingStatusWithSwr( - parameters: WithSDKConfigParameter & UseIndexingStatusParameters = {}, + parameters: WithEnsApiProviderOptions & UseIndexingStatusParameters = {}, ) { - const { config, query = {} } = parameters; - const _config = useENSNodeSDKConfig(config); + const { options, query = {} } = parameters; + const providerOptions = useEnsApiProviderOptions(options); const now = useNow({ timeToRefresh: REALTIME_PROJECTION_REFRESH_RATE }); - const queryOptions = useMemo(() => createIndexingStatusQueryOptions(_config), [_config]); + const queryOptions = useMemo( + () => createIndexingStatusQueryOptions(providerOptions), + [providerOptions], + ); const queryKey = useMemo(() => ["swr", ...queryOptions.queryKey], [queryOptions.queryKey]); const queryFn = useCallback( async () => @@ -48,17 +57,21 @@ export function useIndexingStatusWithSwr( // An indexing status response was successfully fetched, // but the response code contained within the response was not 'ok'. // Therefore, throw an error to avoid caching this response. - if (response.responseCode !== IndexingStatusResponseCodes.Ok) { + if (response.responseCode !== EnsApiIndexingStatusResponseCodes.Ok) { throw new Error( "Received Indexing Status response with responseCode other than 'ok' which will not be cached.", ); } - // The indexing status snapshot has been fetched and successfully validated for caching. + // The indexing status snapshot, including ENSApi public config, + // has been fetched and successfully validated for caching. // Therefore, return it so that query cache for `queryOptions.queryKey` will: // - Replace the currently cached value (if any) with this new value. // - Return this non-null value. - return response.realtimeProjection.snapshot; + return { + crossChainIndexingStatusSnapshot: response.realtimeProjection.snapshot, + config: response.config, + } satisfies CachableIndexingStatus; }), [queryOptions.queryFn], ); @@ -66,16 +79,20 @@ export function useIndexingStatusWithSwr( // Call select function to `createRealtimeIndexingStatusProjection` each time // `now` is updated. const select = useCallback( - (cachedSnapshot: CrossChainIndexingStatusSnapshotOmnichain): IndexingStatusResponseOk => { - const realtimeProjection = createRealtimeIndexingStatusProjection(cachedSnapshot, now); + (cachedResult: CachableIndexingStatus): EnsApiIndexingStatusResponseOk => { + const realtimeProjection = createRealtimeIndexingStatusProjection( + cachedResult.crossChainIndexingStatusSnapshot, + now, + ); - // Maintain the original response shape of `IndexingStatusResponse` + // Maintain the original response shape of `EnsApiIndexingStatusResponseOk` // for the consumers. Creating a new projection from the cached snapshot // each time `now` is updated should be implementation detail. return { - responseCode: IndexingStatusResponseCodes.Ok, + responseCode: EnsApiIndexingStatusResponseCodes.Ok, realtimeProjection, - } satisfies IndexingStatusResponseOk; + config: cachedResult.config, + } satisfies EnsApiIndexingStatusResponseOk; }, [now], ); diff --git a/apps/ensadmin/src/components/layout-wrapper.tsx b/apps/ensadmin/src/components/layout-wrapper.tsx index d9c910753..da1d3a10c 100644 --- a/apps/ensadmin/src/components/layout-wrapper.tsx +++ b/apps/ensadmin/src/components/layout-wrapper.tsx @@ -7,7 +7,7 @@ import { AppSidebar } from "@/components/app-sidebar"; import { RequireActiveConnection } from "@/components/connections/require-active-connection"; import { RequireSelectedConnection } from "@/components/connections/require-selected-connection"; import { Header, HeaderActions, HeaderBreadcrumbs, HeaderNav } from "@/components/header"; -import { SelectedENSNodeProvider } from "@/components/providers/selected-ensnode-provider"; +import { SelectedEnsApiProvider } from "@/components/providers/selected-ensnode-provider"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { Skeleton } from "@/components/ui/skeleton"; @@ -54,7 +54,7 @@ export function LayoutWrapper({ - +
{breadcrumbs} @@ -62,7 +62,7 @@ export function LayoutWrapper({ {actions}
{children} -
+
diff --git a/apps/ensadmin/src/components/providers/selected-ensnode-provider.tsx b/apps/ensadmin/src/components/providers/selected-ensnode-provider.tsx index dff46ac85..f0284b5ee 100644 --- a/apps/ensadmin/src/components/providers/selected-ensnode-provider.tsx +++ b/apps/ensadmin/src/components/providers/selected-ensnode-provider.tsx @@ -1,33 +1,36 @@ "use client"; -import type { PropsWithChildren } from "react"; +import { type PropsWithChildren, useMemo } from "react"; -import { ENSNodeProvider } from "@ensnode/ensnode-react"; +import { createEnsApiOptions, EnsApiProvider } from "@ensnode/ensnode-react"; import { useSelectedConnection } from "@/hooks/active/use-selected-connection"; /** - * Provider component that configures ENSNodeProvider with the currently + * Provider component that configures EnsApiProvider with the currently * selected ENSNode connection. * - * This component wraps the ENSNodeProvider from @ensnode/ensnode-react and + * This component wraps the EnsApiProvider from @ensnode/ensnode-react and * automatically configures it with the URL from the currently selected ENSNode * connection URL. It serves as a bridge between the connection management * system and the ENSNode React hooks. * * @param children - React children to render within the provider context */ -export function SelectedENSNodeProvider({ children }: PropsWithChildren) { +export function SelectedEnsApiProvider({ children }: PropsWithChildren) { const selectedConnection = useSelectedConnection(); if (selectedConnection.validatedSelectedConnection.isValid) { - return ( - - {children} - + const selectedConenctionUrl = selectedConnection.validatedSelectedConnection.url; + const options = useMemo( + () => + createEnsApiOptions({ + url: selectedConenctionUrl, + }), + [selectedConenctionUrl], ); + + return {children}; } else { // TODO: Logic here needs a deeper refactor to recognize the difference // between the selected connection being in a valid format or not. diff --git a/apps/ensadmin/src/components/registrar-actions/use-stateful-fetch-registrar-actions.ts b/apps/ensadmin/src/components/registrar-actions/use-stateful-fetch-registrar-actions.ts index 8ed5a1eee..c7a2741b4 100644 --- a/apps/ensadmin/src/components/registrar-actions/use-stateful-fetch-registrar-actions.ts +++ b/apps/ensadmin/src/components/registrar-actions/use-stateful-fetch-registrar-actions.ts @@ -1,4 +1,4 @@ -import { useENSNodeConfig, useRegistrarActions } from "@ensnode/ensnode-react"; +import { useRegistrarActions } from "@ensnode/ensnode-react"; import { RegistrarActionsOrders, RegistrarActionsResponseCodes, @@ -38,18 +38,16 @@ const { export function useStatefulRegistrarActions({ itemsPerPage, }: UseStatefulRegistrarActionsProps): StatefulFetchRegistrarActions { - const ensNodeConfigQuery = useENSNodeConfig(); const indexingStatusQuery = useIndexingStatusWithSwr(); let isRegistrarActionsApiSupported = false; - if (ensNodeConfigQuery.isSuccess && indexingStatusQuery.isSuccess) { - const { ensIndexerPublicConfig } = ensNodeConfigQuery.data; - const { realtimeProjection } = indexingStatusQuery.data; + if (indexingStatusQuery.isSuccess) { + const { realtimeProjection, config } = indexingStatusQuery.data; const { omnichainSnapshot } = realtimeProjection.snapshot; isRegistrarActionsApiSupported = - hasEnsIndexerConfigSupport(ensIndexerPublicConfig) && + hasEnsIndexerConfigSupport(config.ensIndexerPublicConfig) && hasIndexingStatusSupport(omnichainSnapshot.omnichainStatus); } @@ -63,21 +61,13 @@ export function useStatefulRegistrarActions({ }, }); - // ENSNode config is not fetched yet, so wait in the initial status - if (ensNodeConfigQuery.isPending || indexingStatusQuery.isPending) { + // ENSApi config is not fetched yet, so wait in the initial status + if (indexingStatusQuery.isPending) { return { fetchStatus: StatefulFetchStatusIds.Connecting, } satisfies StatefulFetchRegistrarActionsConnecting; } - // ENSNode config fetched as error - if (!ensNodeConfigQuery.isSuccess) { - return { - fetchStatus: StatefulFetchStatusIds.Error, - reason: "ENSNode config could not be fetched successfully", - } satisfies StatefulFetchRegistrarActionsError; - } - // Indexing Status fetched as error if (!indexingStatusQuery.isSuccess) { return { @@ -86,7 +76,7 @@ export function useStatefulRegistrarActions({ } satisfies StatefulFetchRegistrarActionsError; } - const { ensIndexerPublicConfig } = ensNodeConfigQuery.data; + const { ensIndexerPublicConfig } = indexingStatusQuery.data.config; // fetching is indefinitely not possible due to unsupported ENSNode config if (!hasEnsIndexerConfigSupport(ensIndexerPublicConfig)) { diff --git a/apps/ensadmin/src/hooks/active/use-active-connection.tsx b/apps/ensadmin/src/hooks/active/use-active-connection.tsx index e1fa783ca..0e0f3affc 100644 --- a/apps/ensadmin/src/hooks/active/use-active-connection.tsx +++ b/apps/ensadmin/src/hooks/active/use-active-connection.tsx @@ -1,6 +1,6 @@ "use client"; -import { useENSNodeConfig } from "@ensnode/ensnode-react"; +import { useEnsApiConfig } from "@/components/config/useEnsApiConfig"; /** * Hook to get the currently active ENSNode connection synchronously. @@ -16,7 +16,7 @@ import { useENSNodeConfig } from "@ensnode/ensnode-react"; * @throws Error if no active ENSNode connection is available */ export function useActiveConnection() { - const { data } = useENSNodeConfig(); + const { data } = useEnsApiConfig(); if (data === undefined) { throw new Error(`Invariant(useActiveConnection): Expected an active ENSNode Config`); diff --git a/apps/ensadmin/src/hooks/async/use-namespace.ts b/apps/ensadmin/src/hooks/async/use-namespace.ts index be3a0925e..a48acbd02 100644 --- a/apps/ensadmin/src/hooks/async/use-namespace.ts +++ b/apps/ensadmin/src/hooks/async/use-namespace.ts @@ -1,4 +1,4 @@ -import { useENSNodeConfig } from "@ensnode/ensnode-react"; +import { useEnsApiConfig } from "@/components/config/useEnsApiConfig"; /** * Hook to get the namespace ID from the active ENSNode connection. @@ -22,7 +22,7 @@ import { useENSNodeConfig } from "@ensnode/ensnode-react"; * ``` */ export function useNamespace() { - const query = useENSNodeConfig(); + const query = useEnsApiConfig(); return { ...query, diff --git a/apps/ensapi/src/cache/indexing-status.cache.ts b/apps/ensapi/src/cache/indexing-status.cache.ts index 4a4364e11..8e23c2513 100644 --- a/apps/ensapi/src/cache/indexing-status.cache.ts +++ b/apps/ensapi/src/cache/indexing-status.cache.ts @@ -2,22 +2,22 @@ import config from "@/config"; import { type CrossChainIndexingStatusSnapshot, - ENSNodeClient, - IndexingStatusResponseCodes, + EnsIndexerClient, + EnsIndexerIndexingStatusResponseCodes, SWRCache, } from "@ensnode/ensnode-sdk"; import { makeLogger } from "@/lib/logger"; const logger = makeLogger("indexing-status.cache"); -const client = new ENSNodeClient({ url: config.ensIndexerUrl }); +const client = new EnsIndexerClient({ url: config.ensIndexerUrl }); export const indexingStatusCache = new SWRCache({ fn: async (_cachedResult) => client .indexingStatus() // fetch a new indexing status snapshot .then((response) => { - if (response.responseCode !== IndexingStatusResponseCodes.Ok) { + if (response.responseCode !== EnsIndexerIndexingStatusResponseCodes.Ok) { // An indexing status response was successfully fetched, but the response code contained within the response was not 'ok'. // Therefore, throw an error to trigger the subsequent `.catch` handler. throw new Error("Received Indexing Status response with responseCode other than 'ok'."); diff --git a/apps/ensapi/src/handlers/ensnode-api.routes.ts b/apps/ensapi/src/handlers/ensnode-api.routes.ts index a8487d385..0b7b9970d 100644 --- a/apps/ensapi/src/handlers/ensnode-api.routes.ts +++ b/apps/ensapi/src/handlers/ensnode-api.routes.ts @@ -3,30 +3,10 @@ import { createRoute } from "@hono/zod-openapi"; import { makeEnsApiIndexingStatusResponseErrorSchema, makeSerializedEnsApiIndexingStatusResponseOkSchema, - makeSerializedEnsApiPublicConfigSchema, } from "@ensnode/ensnode-sdk/internal"; export const basePath = "/api"; -export const getConfigRoute = createRoute({ - method: "get", - path: "/config", - operationId: "getConfig", - tags: ["Meta"], - summary: "Get ENSApi Public Config", - description: "Gets the public config of the ENSApi instance", - responses: { - 200: { - description: "Successfully retrieved ENSApi public config", - content: { - "application/json": { - schema: makeSerializedEnsApiPublicConfigSchema(), - }, - }, - }, - }, -}); - export const getIndexingStatusRoute = createRoute({ method: "get", path: "/indexing-status", @@ -54,4 +34,4 @@ export const getIndexingStatusRoute = createRoute({ }, }); -export const routes = [getConfigRoute, getIndexingStatusRoute]; +export const routes = [getIndexingStatusRoute]; diff --git a/apps/ensapi/src/handlers/ensnode-api.ts b/apps/ensapi/src/handlers/ensnode-api.ts index 96e65d0c9..02ca26a9f 100644 --- a/apps/ensapi/src/handlers/ensnode-api.ts +++ b/apps/ensapi/src/handlers/ensnode-api.ts @@ -4,14 +4,13 @@ import { EnsApiIndexingStatusResponseCodes, type EnsApiIndexingStatusResponseError, type EnsApiIndexingStatusResponseOk, - serializeENSApiPublicConfig, serializeEnsApiIndexingStatusResponse, } from "@ensnode/ensnode-sdk"; import { buildEnsApiPublicConfig } from "@/config/config.schema"; import { createApp } from "@/lib/hono-factory"; -import { getConfigRoute, getIndexingStatusRoute } from "./ensnode-api.routes"; +import { getIndexingStatusRoute } from "./ensnode-api.routes"; import ensnodeGraphQLApi from "./ensnode-graphql-api"; import nameTokensApi from "./name-tokens-api"; import registrarActionsApi from "./registrar-actions-api"; @@ -19,11 +18,6 @@ import resolutionApi from "./resolution-api"; const app = createApp(); -app.openapi(getConfigRoute, async (c) => { - const ensApiPublicConfig = buildEnsApiPublicConfig(config); - return c.json(serializeENSApiPublicConfig(ensApiPublicConfig)); -}); - app.openapi(getIndexingStatusRoute, async (c) => { // context must be set by the required middleware if (c.var.indexingStatus === undefined) { @@ -39,11 +33,14 @@ app.openapi(getIndexingStatusRoute, async (c) => { ); } + const ensApiPublicConfig = buildEnsApiPublicConfig(config); + // return successful response using the indexing status projection from the middleware context return c.json( serializeEnsApiIndexingStatusResponse({ responseCode: EnsApiIndexingStatusResponseCodes.Ok, realtimeProjection: c.var.indexingStatus, + config: ensApiPublicConfig, } satisfies EnsApiIndexingStatusResponseOk), 200, ); diff --git a/apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts b/apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts index 0cb27f996..8dc3e84e7 100644 --- a/apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts +++ b/apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts @@ -6,11 +6,11 @@ import { Hono } from "hono"; import { buildCrossChainIndexingStatusSnapshotOmnichain, createRealtimeIndexingStatusProjection, - IndexingStatusResponseCodes, - type IndexingStatusResponseError, - type IndexingStatusResponseOk, - serializeENSIndexerPublicConfig, - serializeIndexingStatusResponse, + EnsIndexerIndexingStatusResponseCodes, + type EnsIndexerIndexingStatusResponseError, + type EnsIndexerIndexingStatusResponseOk, + serializeEnsIndexerIndexingStatusResponse, + serializeEnsIndexerPublicConfig, } from "@ensnode/ensnode-sdk"; import { buildENSIndexerPublicConfig } from "@/config/public"; @@ -26,7 +26,7 @@ app.get("/config", async (c) => { const publicConfig = await buildENSIndexerPublicConfig(config); // respond with the serialized public config object - return c.json(serializeENSIndexerPublicConfig(publicConfig)); + return c.json(serializeEnsIndexerPublicConfig(publicConfig)); }); app.get("/indexing-status", async (c) => { @@ -48,19 +48,19 @@ app.get("/indexing-status", async (c) => { ); return c.json( - serializeIndexingStatusResponse({ - responseCode: IndexingStatusResponseCodes.Ok, + serializeEnsIndexerIndexingStatusResponse({ + responseCode: EnsIndexerIndexingStatusResponseCodes.Ok, realtimeProjection, - } satisfies IndexingStatusResponseOk), + } satisfies EnsIndexerIndexingStatusResponseOk), ); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; console.error(`Omnichain snapshot is currently not available: ${errorMessage}`); return c.json( - serializeIndexingStatusResponse({ - responseCode: IndexingStatusResponseCodes.Error, - } satisfies IndexingStatusResponseError), + serializeEnsIndexerIndexingStatusResponse({ + responseCode: EnsIndexerIndexingStatusResponseCodes.Error, + } satisfies EnsIndexerIndexingStatusResponseError), 500, ); } diff --git a/packages/ensnode-react/README.md b/packages/ensnode-react/README.md index 63b48c595..ec01024b7 100644 --- a/packages/ensnode-react/README.md +++ b/packages/ensnode-react/README.md @@ -16,18 +16,18 @@ Note: `@tanstack/react-query` is a peer dependency but you don't need to interac ### 1. Setup the Provider -Wrap your app with the `ENSNodeProvider`: +Wrap your app with the `EnsApiProvider`: ```tsx -import { ENSNodeProvider, createConfig } from "@ensnode/ensnode-react"; +import { EnsApiProvider, createEnsApiOptions } from "@ensnode/ensnode-react"; -const config = createConfig({ url: "https://api.alpha.ensnode.io" }); +const options = createEnsApiOptions({ url: "https://api.alpha.ensnode.io" }); function App() { return ( - + - + ); } ``` @@ -124,13 +124,13 @@ function DisplayPrimaryNames() { ## API Reference -### ENSNodeProvider +### EnsApiProvider -The provider component that supplies ENSNode configuration to all child components. +The provider component that supplies ENSApi options to all child components. ```tsx -interface ENSNodeProviderProps { - config: ENSNodeConfig; +interface EnsApiProviderProps { + options: EnsApiProviderOptions; queryClient?: QueryClient; queryClientOptions?: QueryClientOptions; } @@ -138,16 +138,16 @@ interface ENSNodeProviderProps { #### Props -- `config`: ENSNode configuration object +- `options`: ENSApi options object - `queryClient`: Optional TanStack Query client instance (requires manual QueryClientProvider setup) - `queryClientOptions`: Optional Custom options for auto-created QueryClient (only used when queryClient is not provided) -### createConfig +### createEnsApiOptions -Helper function to create ENSNode configuration with defaults. +Helper function to create ENSApi options with defaults. ```tsx -const config = createConfig({ +const options = createEnsApiOptions({ url: "https://api.alpha.ensnode.io", }); ``` @@ -230,12 +230,12 @@ const { data, isLoading, error, refetch } = usePrimaryNames({ ### Custom Query Configuration -The `ENSNodeProvider` automatically creates and manages a QueryClient for you. Cache keys include the ENSNode endpoint URL, so different endpoints (mainnet vs testnet) maintain separate caches. You can customize the QueryClient without importing TanStack Query: +The `EnsApiProvider` automatically creates and manages a QueryClient for you. Cache keys include the ENSNode endpoint URL, so different endpoints (mainnet vs testnet) maintain separate caches. You can customize the QueryClient without importing TanStack Query: ```tsx // Simple setup - no TanStack Query knowledge needed - - + ``` ### Advanced: Bring Your Own QueryClient @@ -268,9 +268,9 @@ const queryClient = new QueryClient({ }); - + - + ; ``` diff --git a/packages/ensnode-react/src/context.ts b/packages/ensnode-react/src/context.ts index 42474d243..12a3b23f2 100644 --- a/packages/ensnode-react/src/context.ts +++ b/packages/ensnode-react/src/context.ts @@ -1,13 +1,13 @@ import { createContext } from "react"; -import type { ENSNodeSDKConfig } from "./types"; +import type { EnsApiProviderOptions } from "./types"; /** - * React context for ENSNode configuration + * React context for ENSApi configuration */ -export const ENSNodeContext = createContext(undefined); +export const EnsApiContext = createContext(undefined); /** * Display name for debugging */ -ENSNodeContext.displayName = "ENSNodeContext"; +EnsApiContext.displayName = "EnsApiContext"; diff --git a/packages/ensnode-react/src/hooks/index.ts b/packages/ensnode-react/src/hooks/index.ts index 1a3e1c3cd..5fbf4ebf6 100644 --- a/packages/ensnode-react/src/hooks/index.ts +++ b/packages/ensnode-react/src/hooks/index.ts @@ -1,5 +1,4 @@ -export * from "./useENSNodeConfig"; -export * from "./useENSNodeSDKConfig"; +export * from "./useEnsApiProviderOptions"; export * from "./useIndexingStatus"; export * from "./useNameTokens"; export * from "./usePrimaryName"; diff --git a/packages/ensnode-react/src/hooks/useENSNodeConfig.ts b/packages/ensnode-react/src/hooks/useENSNodeConfig.ts deleted file mode 100644 index c246d61af..000000000 --- a/packages/ensnode-react/src/hooks/useENSNodeConfig.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -import type { ConfigResponse } from "@ensnode/ensnode-sdk"; - -import type { QueryParameter, WithSDKConfigParameter } from "../types"; -import { ASSUME_IMMUTABLE_QUERY, createConfigQueryOptions } from "../utils/query"; -import { useENSNodeSDKConfig } from "./useENSNodeSDKConfig"; - -type UseENSNodeConfigParameters = QueryParameter; - -export function useENSNodeConfig( - parameters: WithSDKConfigParameter & UseENSNodeConfigParameters = {}, -) { - const { config, query = {} } = parameters; - const _config = useENSNodeSDKConfig(config); - - const queryOptions = createConfigQueryOptions(_config); - - const options = { - ...queryOptions, - ...ASSUME_IMMUTABLE_QUERY, - ...query, - enabled: query.enabled ?? queryOptions.enabled, - }; - - return useQuery(options); -} diff --git a/packages/ensnode-react/src/hooks/useENSNodeSDKConfig.ts b/packages/ensnode-react/src/hooks/useENSNodeSDKConfig.ts deleted file mode 100644 index 2f7b1016a..000000000 --- a/packages/ensnode-react/src/hooks/useENSNodeSDKConfig.ts +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; - -import { useContext } from "react"; - -import { ENSNodeContext } from "../context"; -import type { ENSNodeSDKConfig } from "../types"; - -/** - * Hook to access the ENSNodeSDKConfig from context or parameters. - * - * @param parameters - Optional config parameter that overrides context - * @returns The ENSNode configuration - * @throws Error if no config is available in context or parameters - */ -export function useENSNodeSDKConfig( - config: TConfig | undefined, -): TConfig { - const contextConfig = useContext(ENSNodeContext); - - // Use provided config or fall back to context - const resolvedConfig = config ?? contextConfig; - - if (!resolvedConfig) { - throw new Error( - "useENSNodeSDKConfig must be used within an ENSNodeProvider or you must pass a config parameter", - ); - } - - return resolvedConfig as TConfig; -} diff --git a/packages/ensnode-react/src/hooks/useEnsApiProviderOptions.ts b/packages/ensnode-react/src/hooks/useEnsApiProviderOptions.ts new file mode 100644 index 000000000..90f420842 --- /dev/null +++ b/packages/ensnode-react/src/hooks/useEnsApiProviderOptions.ts @@ -0,0 +1,29 @@ +"use client"; + +import { useContext } from "react"; + +import { EnsApiContext } from "../context"; +import type { EnsApiProviderOptions } from "../types"; + +/** + * Hook to access the EnsApiProviderOptions from context or parameters. + * + * @param options - Options parameter that overrides context + * @throws Error if no config is available in context or parameters + */ +export function useEnsApiProviderOptions< + ProviderOptionsType extends EnsApiProviderOptions = EnsApiProviderOptions, +>(options?: ProviderOptionsType): ProviderOptionsType { + const contextOptions = useContext(EnsApiContext); + + // Use provided options or fall back to context + const resolvedOptions = options ?? contextOptions; + + if (!resolvedOptions) { + throw new Error( + "useEnsApiProviderOptions must be used within an EnsApiProvider or you must pass the options parameter", + ); + } + + return resolvedOptions as ProviderOptionsType; +} diff --git a/packages/ensnode-react/src/hooks/useIndexingStatus.ts b/packages/ensnode-react/src/hooks/useIndexingStatus.ts index c56f3d692..0f7977983 100644 --- a/packages/ensnode-react/src/hooks/useIndexingStatus.ts +++ b/packages/ensnode-react/src/hooks/useIndexingStatus.ts @@ -1,29 +1,29 @@ import { useQuery } from "@tanstack/react-query"; -import type { IndexingStatusRequest, IndexingStatusResponse } from "@ensnode/ensnode-sdk"; +import type { + EnsApiIndexingStatusRequest, + EnsApiIndexingStatusResponse, +} from "@ensnode/ensnode-sdk"; -import type { QueryParameter, WithSDKConfigParameter } from "../types"; +import type { QueryParameter, WithEnsApiProviderOptions } from "../types"; import { createIndexingStatusQueryOptions } from "../utils/query"; -import { useENSNodeSDKConfig } from "./useENSNodeSDKConfig"; +import { useEnsApiProviderOptions } from "./useEnsApiProviderOptions"; interface UseIndexingStatusParameters - extends IndexingStatusRequest, - QueryParameter {} + extends EnsApiIndexingStatusRequest, + QueryParameter {} export function useIndexingStatus( - parameters: WithSDKConfigParameter & UseIndexingStatusParameters = {}, + parameters: WithEnsApiProviderOptions & UseIndexingStatusParameters = {}, ) { - const { config, query = {} } = parameters; - const _config = useENSNodeSDKConfig(config); + const { options, query = {} } = parameters; + const providerOptions = useEnsApiProviderOptions(options); + const queryOptions = createIndexingStatusQueryOptions(providerOptions); - const queryOptions = createIndexingStatusQueryOptions(_config); - - const options = { + return useQuery({ ...queryOptions, refetchInterval: 10 * 1000, // 10 seconds - indexing status changes frequently ...query, enabled: query.enabled ?? queryOptions.enabled, - }; - - return useQuery(options); + }); } diff --git a/packages/ensnode-react/src/hooks/useNameTokens.ts b/packages/ensnode-react/src/hooks/useNameTokens.ts index 7eefdcbbc..249efad4d 100644 --- a/packages/ensnode-react/src/hooks/useNameTokens.ts +++ b/packages/ensnode-react/src/hooks/useNameTokens.ts @@ -2,9 +2,9 @@ import { useQuery } from "@tanstack/react-query"; import type { NameTokensRequest, NameTokensResponse } from "@ensnode/ensnode-sdk"; -import type { QueryParameter, WithSDKConfigParameter } from "../types"; +import type { QueryParameter, WithEnsApiProviderOptions } from "../types"; import { createNameTokensQueryOptions } from "../utils/query"; -import { useENSNodeSDKConfig } from "./useENSNodeSDKConfig"; +import { useEnsApiProviderOptions } from "./useEnsApiProviderOptions"; type UseNameTokensParameters = NameTokensRequest & QueryParameter; @@ -13,11 +13,11 @@ type UseNameTokensParameters = NameTokensRequest & QueryParameter( - parameters: UseRecordsParameters & WithSDKConfigParameter, + parameters: UseRecordsParameters & WithEnsApiProviderOptions, ) { - const { config, query = {}, name, ...args } = parameters; - const _config = useENSNodeSDKConfig(config); + const { options, query = {}, name, ...args } = parameters; + const _config = useEnsApiProviderOptions(options); const canEnable = name !== null; @@ -62,11 +62,9 @@ export function useRecords( ? createRecordsQueryOptions(_config, { ...args, name }) : { enabled: false, queryKey: ["disabled"] as const }; - const options = { + return useQuery({ ...queryOptions, ...query, enabled: canEnable && (query.enabled ?? queryOptions.enabled), - }; - - return useQuery(options); + }); } diff --git a/packages/ensnode-react/src/hooks/useRegistrarActions.ts b/packages/ensnode-react/src/hooks/useRegistrarActions.ts index ed5a6ca0d..e8ba548ed 100644 --- a/packages/ensnode-react/src/hooks/useRegistrarActions.ts +++ b/packages/ensnode-react/src/hooks/useRegistrarActions.ts @@ -2,9 +2,9 @@ import { useQuery } from "@tanstack/react-query"; import type { RegistrarActionsRequest, RegistrarActionsResponse } from "@ensnode/ensnode-sdk"; -import type { QueryParameter, WithSDKConfigParameter } from "../types"; +import type { QueryParameter, WithEnsApiProviderOptions } from "../types"; import { createRegistrarActionsQueryOptions } from "../utils/query"; -import { useENSNodeSDKConfig } from "./useENSNodeSDKConfig"; +import { useEnsApiProviderOptions } from "./useEnsApiProviderOptions"; interface UseRegistrarActionsParameters extends RegistrarActionsRequest, @@ -16,19 +16,17 @@ interface UseRegistrarActionsParameters * Query ENSNode Registrar Actions API. */ export function useRegistrarActions( - parameters: WithSDKConfigParameter & UseRegistrarActionsParameters = {}, + parameters: WithEnsApiProviderOptions & UseRegistrarActionsParameters = {}, ) { - const { config, query = {} } = parameters; - const _config = useENSNodeSDKConfig(config); + const { options, query = {} } = parameters; + const providerOptions = useEnsApiProviderOptions(options); - const queryOptions = createRegistrarActionsQueryOptions(_config, parameters); + const queryOptions = createRegistrarActionsQueryOptions(providerOptions, parameters); - const options = { + return useQuery({ ...queryOptions, refetchInterval: 10 * 1000, // 10 seconds - latest registrar actions change frequently ...query, enabled: query.enabled ?? queryOptions.enabled, - }; - - return useQuery(options); + }); } diff --git a/packages/ensnode-react/src/hooks/useResolvedIdentity.ts b/packages/ensnode-react/src/hooks/useResolvedIdentity.ts index 460ecaca6..e590f5aeb 100644 --- a/packages/ensnode-react/src/hooks/useResolvedIdentity.ts +++ b/packages/ensnode-react/src/hooks/useResolvedIdentity.ts @@ -14,7 +14,6 @@ import { } from "@ensnode/ensnode-sdk"; import type { UseResolvedIdentityParameters } from "../types"; -import { useENSNodeConfig } from "./useENSNodeConfig"; import { usePrimaryName } from "./usePrimaryName"; /** @@ -24,7 +23,7 @@ import { usePrimaryName } from "./usePrimaryName"; * @param parameters - Configuration object for the hook * @param parameters.identity - An {@link UnresolvedIdentity} containing the {@link DefaultableChainId} * and {@link Address} to resolve. - * @param parameters.namespaceId - The {@link ENSNamespaceId} that `identity.chainId` should be interpreted + * @param parameters.namespace - The {@link ENSNamespaceId} that `identity.chainId` should be interpreted * through (via {@link getResolvePrimaryNameChainIdParam}) to determine the literal * chainId that should be used for ENSIP-19 primary name resolution. * @param parameters.accelerate - Whether to attempt Protocol Acceleration (default: false) @@ -39,10 +38,7 @@ import { usePrimaryName } from "./usePrimaryName"; * - All other properties from the underlying {@link usePrimaryName} query (e.g., `isLoading`, `error`, `refetch`, etc.) */ export function useResolvedIdentity(parameters: UseResolvedIdentityParameters) { - const { identity, accelerate, query: _query = {} } = parameters; - - const { data } = useENSNodeConfig(); - const namespace = data?.ensIndexerPublicConfig.namespace; + const { identity, namespace, accelerate, query: _query = {} } = parameters; const { data: primaryNameData, diff --git a/packages/ensnode-react/src/provider.tsx b/packages/ensnode-react/src/provider.tsx index 457db1319..f1f27fc9b 100644 --- a/packages/ensnode-react/src/provider.tsx +++ b/packages/ensnode-react/src/provider.tsx @@ -4,18 +4,18 @@ import { QueryClient, QueryClientProvider, useQueryClient } from "@tanstack/react-query"; import { createElement, useMemo } from "react"; -import { ENSNodeClient } from "@ensnode/ensnode-sdk"; +import { EnsApiClient } from "@ensnode/ensnode-sdk"; -import { ENSNodeContext } from "./context"; -import type { ENSNodeSDKConfig } from "./types"; +import { EnsApiContext } from "./context"; +import type { EnsApiProviderOptions } from "./types"; -export interface ENSNodeProviderProps { - /** ENSNode configuration */ - config: ENSNodeSDKConfig; +export interface EnsApiProviderProps { + /** ENSApi options */ + options: EnsApiProviderOptions; /** * Optional QueryClient instance. If provided, you must wrap your app with QueryClientProvider yourself. - * If not provided, ENSNodeProvider will create and manage its own QueryClient internally. + * If not provided, EnsApiProvider will create and manage its own QueryClient internally. */ queryClient?: QueryClient; @@ -26,21 +26,18 @@ export interface ENSNodeProviderProps { queryClientOptions?: ConstructorParameters[0]; } -function ENSNodeInternalProvider({ +function EnsApiInternalProvider({ children, - config, + options, }: { children?: React.ReactNode; - config: ENSNodeSDKConfig; + options: EnsApiProviderOptions; }) { - // Memoize the config to prevent unnecessary re-renders - const memoizedConfig = useMemo(() => config, [config]); - - return createElement(ENSNodeContext.Provider, { value: memoizedConfig }, children); + return createElement(EnsApiContext.Provider, { value: options }, children); } -export function ENSNodeProvider(parameters: React.PropsWithChildren) { - const { children, config, queryClient, queryClientOptions } = parameters; +export function EnsApiProvider(parameters: React.PropsWithChildren) { + const { children, options, queryClient, queryClientOptions } = parameters; // Check if we're already inside a QueryClientProvider let hasExistingQueryClient = false; @@ -59,12 +56,12 @@ export function ENSNodeProvider(parameters: React.PropsWithChildren { /** * Configuration parameter for hooks that need access to config */ -export interface WithSDKConfigParameter { - config?: TConfig | undefined; +export interface WithEnsApiProviderOptions< + TOptions extends EnsApiProviderOptions = EnsApiProviderOptions, +> { + options?: TOptions | undefined; } /** @@ -75,4 +78,5 @@ export interface UseResolvedIdentityParameters extends QueryParameter, AcceleratableRequest { identity: UnresolvedIdentity; + namespace?: ENSNamespaceId; } diff --git a/packages/ensnode-react/src/utils/query.ts b/packages/ensnode-react/src/utils/query.ts index fc9a5cb00..8a8696d4c 100644 --- a/packages/ensnode-react/src/utils/query.ts +++ b/packages/ensnode-react/src/utils/query.ts @@ -3,7 +3,7 @@ import type { UndefinedInitialDataOptions } from "@tanstack/react-query"; import { - ENSNodeClient, + EnsApiClient, type NameTokensRequest, type RegistrarActionsRequest, type ResolvePrimaryNameRequest, @@ -12,7 +12,7 @@ import { type ResolverRecordsSelection, } from "@ensnode/ensnode-sdk"; -import type { ENSNodeSDKConfig } from "../types"; +import type { EnsApiProviderOptions } from "../types"; /** * Immutable query options for data that is assumed to be immutable and should only be fetched once per full page refresh per unique key. @@ -54,8 +54,6 @@ export const queryKeys = { primaryNames: (url: string, args: ResolvePrimaryNamesRequest) => [...queryKeys.resolve(url), "primary-names", args] as const, - config: (url: string) => [...queryKeys.base(url), "config"] as const, - indexingStatus: (url: string) => [...queryKeys.base(url), "indexing-status"] as const, registrarActions: (url: string, args: RegistrarActionsRequest) => @@ -69,14 +67,14 @@ export const queryKeys = { * Creates query options for Records Resolution */ export function createRecordsQueryOptions( - config: ENSNodeSDKConfig, + config: EnsApiProviderOptions, args: ResolveRecordsRequest, ) { return { enabled: true, queryKey: queryKeys.records(config.client.url.href, args), queryFn: async () => { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.resolveRecords(args.name, args.selection, args); }, }; @@ -86,14 +84,14 @@ export function createRecordsQueryOptions { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.resolvePrimaryName(args.address, args.chainId, args); }, }; @@ -103,42 +101,28 @@ export function createPrimaryNameQueryOptions( * Creates query options for Primary Name Resolution */ export function createPrimaryNamesQueryOptions( - config: ENSNodeSDKConfig, + config: EnsApiProviderOptions, args: ResolvePrimaryNamesRequest, ) { return { enabled: true, queryKey: queryKeys.primaryNames(config.client.url.href, args), queryFn: async () => { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.resolvePrimaryNames(args.address, args); }, }; } -/** - * Creates query options for ENSNode Config API - */ -export function createConfigQueryOptions(config: ENSNodeSDKConfig) { - return { - enabled: true, - queryKey: queryKeys.config(config.client.url.href), - queryFn: async () => { - const client = new ENSNodeClient(config.client); - return client.config(); - }, - }; -} - /** * Creates query options for ENSNode Indexing Status API */ -export function createIndexingStatusQueryOptions(config: ENSNodeSDKConfig) { +export function createIndexingStatusQueryOptions(config: EnsApiProviderOptions) { return { enabled: true, queryKey: queryKeys.indexingStatus(config.client.url.href), queryFn: async () => { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.indexingStatus(); }, }; @@ -148,14 +132,14 @@ export function createIndexingStatusQueryOptions(config: ENSNodeSDKConfig) { * Creates query options for ENSNode Registrar Actions API */ export function createRegistrarActionsQueryOptions( - config: ENSNodeSDKConfig, + config: EnsApiProviderOptions, args: RegistrarActionsRequest, ) { return { enabled: true, queryKey: queryKeys.registrarActions(config.client.url.href, args), queryFn: async () => { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.registrarActions(args); }, @@ -165,12 +149,15 @@ export function createRegistrarActionsQueryOptions( /** * Creates query options for Name Tokens API */ -export function createNameTokensQueryOptions(config: ENSNodeSDKConfig, args: NameTokensRequest) { +export function createNameTokensQueryOptions( + config: EnsApiProviderOptions, + args: NameTokensRequest, +) { return { enabled: true, queryKey: queryKeys.nameTokens(config.client.url.href, args), queryFn: async () => { - const client = new ENSNodeClient(config.client); + const client = new EnsApiClient(config.client); return client.nameTokens(args); }, diff --git a/packages/ensnode-sdk/src/ensapi/api/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensapi/api/indexing-status/deserialize.ts index 08e927795..9e58d0606 100644 --- a/packages/ensnode-sdk/src/ensapi/api/indexing-status/deserialize.ts +++ b/packages/ensnode-sdk/src/ensapi/api/indexing-status/deserialize.ts @@ -2,6 +2,7 @@ import { prettifyError } from "zod/v4"; import { buildUnvalidatedRealtimeIndexingStatusProjection } from "../../../indexing-status/deserialize/realtime-indexing-status-projection"; import type { Unvalidated } from "../../../shared/types"; +import { buildUnvalidatedEnsApiPublicConfig } from "../../config/deserialize"; import { type EnsApiIndexingStatusResponse, EnsApiIndexingStatusResponseCodes } from "./response"; import type { SerializedEnsApiIndexingStatusResponse } from "./serialized-response"; import { @@ -28,6 +29,7 @@ function buildUnvalidatedEnsApiIndexingStatusResponse( realtimeProjection: buildUnvalidatedRealtimeIndexingStatusProjection( serializedResponse.realtimeProjection, ), + config: buildUnvalidatedEnsApiPublicConfig(serializedResponse.config), }; } diff --git a/packages/ensnode-sdk/src/ensapi/api/indexing-status/response.ts b/packages/ensnode-sdk/src/ensapi/api/indexing-status/response.ts index 14cd8b9ba..2cb679a65 100644 --- a/packages/ensnode-sdk/src/ensapi/api/indexing-status/response.ts +++ b/packages/ensnode-sdk/src/ensapi/api/indexing-status/response.ts @@ -1,4 +1,5 @@ import type { RealtimeIndexingStatusProjection } from "../../../indexing-status/realtime-indexing-status-projection"; +import type { EnsApiPublicConfig } from "../../config"; /** * A status code for indexing status responses. @@ -41,6 +42,7 @@ export type IndexingStatusResponseCode = EnsApiIndexingStatusResponseCode; export type EnsApiIndexingStatusResponseOk = { responseCode: typeof EnsApiIndexingStatusResponseCodes.Ok; realtimeProjection: RealtimeIndexingStatusProjection; + config: EnsApiPublicConfig; }; /** diff --git a/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialize.ts index 5dcf85b87..ac7fbfd9e 100644 --- a/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialize.ts +++ b/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialize.ts @@ -1,4 +1,5 @@ import { serializeRealtimeIndexingStatusProjection } from "../../../indexing-status/serialize/realtime-indexing-status-projection"; +import { serializeEnsApiPublicConfig } from "../../config/serialize"; import { type EnsApiIndexingStatusResponse, EnsApiIndexingStatusResponseCodes, @@ -31,6 +32,7 @@ export function serializeEnsApiIndexingStatusResponse( return { responseCode: response.responseCode, realtimeProjection: serializeRealtimeIndexingStatusProjection(response.realtimeProjection), + config: serializeEnsApiPublicConfig(response.config), } satisfies SerializedEnsApiIndexingStatusResponseOk; case EnsApiIndexingStatusResponseCodes.Error: diff --git a/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialized-response.ts b/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialized-response.ts index 6db56e758..cfd47ddab 100644 --- a/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialized-response.ts +++ b/packages/ensnode-sdk/src/ensapi/api/indexing-status/serialized-response.ts @@ -1,4 +1,5 @@ import type { SerializedRealtimeIndexingStatusProjection } from "../../../indexing-status/serialize/realtime-indexing-status-projection"; +import type { SerializedEnsApiPublicConfig } from "../../config/serialized-types"; import type { EnsApiIndexingStatusResponse, EnsApiIndexingStatusResponseError, @@ -21,8 +22,9 @@ export type SerializedIndexingStatusResponseError = SerializedEnsApiIndexingStat * Serialized representation of {@link EnsApiIndexingStatusResponseOk}. */ export interface SerializedEnsApiIndexingStatusResponseOk - extends Omit { + extends Omit { realtimeProjection: SerializedRealtimeIndexingStatusProjection; + config: SerializedEnsApiPublicConfig; } /** diff --git a/packages/ensnode-sdk/src/ensapi/api/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/indexing-status/zod-schemas.ts index 0123a6aed..83df37e6b 100644 --- a/packages/ensnode-sdk/src/ensapi/api/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/indexing-status/zod-schemas.ts @@ -4,6 +4,7 @@ import { makeRealtimeIndexingStatusProjectionSchema, makeSerializedRealtimeIndexingStatusProjectionSchema, } from "../../../indexing-status/zod-schema/realtime-indexing-status-projection"; +import { makeEnsApiPublicConfigSchema, makeSerializedEnsApiPublicConfigSchema } from "../../config"; import { type EnsApiIndexingStatusResponse, EnsApiIndexingStatusResponseCodes, @@ -24,6 +25,7 @@ export const makeEnsApiIndexingStatusResponseOkSchema = ( z.strictObject({ responseCode: z.literal(EnsApiIndexingStatusResponseCodes.Ok), realtimeProjection: makeRealtimeIndexingStatusProjectionSchema(valueLabel), + config: makeEnsApiPublicConfigSchema(valueLabel), }); /** @@ -59,9 +61,10 @@ export const makeIndexingStatusResponseSchema = makeEnsApiIndexingStatusResponse export const makeSerializedEnsApiIndexingStatusResponseOkSchema = ( valueLabel: string = "Serialized Indexing Status Response OK", ) => - z.strictObject({ + z.object({ responseCode: z.literal(EnsApiIndexingStatusResponseCodes.Ok), realtimeProjection: makeSerializedRealtimeIndexingStatusProjectionSchema(valueLabel), + config: makeSerializedEnsApiPublicConfigSchema(valueLabel), }); /** diff --git a/packages/ensnode-sdk/src/ensapi/client.test.ts b/packages/ensnode-sdk/src/ensapi/client.test.ts index 1c3c6dedc..5f1508ad8 100644 --- a/packages/ensnode-sdk/src/ensapi/client.test.ts +++ b/packages/ensnode-sdk/src/ensapi/client.test.ts @@ -136,7 +136,7 @@ const EXAMPLE_INDEXING_STATUS_BACKFILL_RESPONSE = deserializeEnsApiIndexingStatu }, }, }, - + config: EXAMPLE_CONFIG_RESPONSE, responseCode: EnsApiIndexingStatusResponseCodes.Ok, } satisfies SerializedEnsApiIndexingStatusResponseOk); @@ -194,7 +194,7 @@ const _EXAMPLE_INDEXING_STATUS_FOLLOWING_RESPONSE: EnsApiIndexingStatusResponse } satisfies SerializedOmnichainIndexingStatusSnapshotFollowing, }, }, - + config: EXAMPLE_CONFIG_RESPONSE, responseCode: EnsApiIndexingStatusResponseCodes.Ok, }); diff --git a/packages/ensnode-sdk/src/ensapi/config/deserialize.ts b/packages/ensnode-sdk/src/ensapi/config/deserialize.ts index 78aa79412..5c22c2180 100644 --- a/packages/ensnode-sdk/src/ensapi/config/deserialize.ts +++ b/packages/ensnode-sdk/src/ensapi/config/deserialize.ts @@ -16,7 +16,7 @@ import { * @param serializedPublicConfig - The serialized public config to build from. * @return An unvalidated {@link EnsApiPublicConfig} object. */ -function buildUnvalidatedEnsApiPublicConfig( +export function buildUnvalidatedEnsApiPublicConfig( serializedPublicConfig: SerializedEnsApiPublicConfig, ): Unvalidated { return { diff --git a/packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx b/packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx index b9d112f6c..7df1498cd 100644 --- a/packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx +++ b/packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx @@ -62,6 +62,7 @@ export function ResolveAndDisplayIdentity({ const { identity: identityResult } = useResolvedIdentity({ identity, accelerate, + namespace: namespaceId, }); return (