From 6c8d1c8fba36e64dc5b1ad25cb17a4abd26855b2 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:23:19 +0400 Subject: [PATCH 1/6] Add getDefaultEnsNodeUrl --- packages/ensnode-sdk/src/client.test.ts | 35 +++++++++++++++---------- packages/ensnode-sdk/src/client.ts | 25 +++++++++++++----- packages/ensnode-sdk/src/deployments.ts | 29 ++++++++++++++++++++ packages/ensnode-sdk/src/index.ts | 1 + 4 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 packages/ensnode-sdk/src/deployments.ts diff --git a/packages/ensnode-sdk/src/client.test.ts b/packages/ensnode-sdk/src/client.test.ts index 0c7619d5db..f951a97b33 100644 --- a/packages/ensnode-sdk/src/client.test.ts +++ b/packages/ensnode-sdk/src/client.test.ts @@ -11,9 +11,10 @@ import { type SerializedIndexingStatusResponseOk, serializeIndexingStatusResponse, } from "./api"; -import { DEFAULT_ENSNODE_API_URL, ENSNodeClient } from "./client"; +import { ENSNodeClient } from "./client"; import { ClientError } from "./client-error"; -import type { Name } from "./ens"; +import { DEFAULT_ENSNODE_API_URL_MAINNET, getDefaultEnsNodeUrl } from "./deployments"; +import { ENSNamespaceIds, type Name } from "./ens"; import { deserializeENSApiPublicConfig, type SerializedENSApiPublicConfig } from "./ensapi"; import { ChainIndexingConfigTypeIds, @@ -210,7 +211,7 @@ describe("ENSNodeClient", () => { const client = new ENSNodeClient(); const options = client.getOptions(); - expect(options).toEqual({ url: new URL(DEFAULT_ENSNODE_API_URL) }); + expect(options).toEqual({ url: getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet) }); }); it("should merge provided options with defaults", () => { @@ -238,7 +239,10 @@ describe("ENSNodeClient", () => { const client = new ENSNodeClient(); const response = await client.resolveRecords(EXAMPLE_NAME, EXAMPLE_SELECTION); - const expectedUrl = new URL(`/api/resolve/records/${EXAMPLE_NAME}`, DEFAULT_ENSNODE_API_URL); + const expectedUrl = new URL( + `/api/resolve/records/${EXAMPLE_NAME}`, + DEFAULT_ENSNODE_API_URL_MAINNET, + ); expectedUrl.searchParams.set("addresses", EXAMPLE_SELECTION.addresses.join(",")); expectedUrl.searchParams.set("texts", EXAMPLE_SELECTION.texts.join(",")); @@ -255,7 +259,10 @@ describe("ENSNodeClient", () => { trace: true, }); - const expectedUrl = new URL(`/api/resolve/records/${EXAMPLE_NAME}`, DEFAULT_ENSNODE_API_URL); + const expectedUrl = new URL( + `/api/resolve/records/${EXAMPLE_NAME}`, + DEFAULT_ENSNODE_API_URL_MAINNET, + ); expectedUrl.searchParams.set("addresses", EXAMPLE_SELECTION.addresses.join(",")); expectedUrl.searchParams.set("texts", EXAMPLE_SELECTION.texts.join(",")); expectedUrl.searchParams.set("trace", "true"); @@ -286,7 +293,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-name/${EXAMPLE_ADDRESS}/1`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expect(mockFetch).toHaveBeenCalledWith(expectedUrl); @@ -302,7 +309,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-name/${EXAMPLE_ADDRESS}/1`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expectedUrl.searchParams.set("trace", "true"); @@ -321,7 +328,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-name/${EXAMPLE_ADDRESS}/1`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expectedUrl.searchParams.set("accelerate", "true"); @@ -348,7 +355,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-names/${EXAMPLE_ADDRESS}`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expect(mockFetch).toHaveBeenCalledWith(expectedUrl); @@ -366,7 +373,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-names/${EXAMPLE_ADDRESS}`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expectedUrl.searchParams.set("chainIds", "1,10"); @@ -382,7 +389,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-names/${EXAMPLE_ADDRESS}`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expectedUrl.searchParams.set("trace", "true"); @@ -401,7 +408,7 @@ describe("ENSNodeClient", () => { const expectedUrl = new URL( `/api/resolve/primary-names/${EXAMPLE_ADDRESS}`, - DEFAULT_ENSNODE_API_URL, + DEFAULT_ENSNODE_API_URL_MAINNET, ); expectedUrl.searchParams.set("accelerate", "true"); @@ -419,7 +426,7 @@ describe("ENSNodeClient", () => { describe("Config API", () => { it("can fetch config object successfully", async () => { // arrange - const requestUrl = new URL(`/api/config`, DEFAULT_ENSNODE_API_URL); + const requestUrl = new URL(`/api/config`, DEFAULT_ENSNODE_API_URL_MAINNET); const serializedMockedResponse = EXAMPLE_CONFIG_RESPONSE; const mockedResponse = deserializeENSApiPublicConfig(serializedMockedResponse); const client = new ENSNodeClient(); @@ -446,7 +453,7 @@ describe("ENSNodeClient", () => { describe("Indexing Status API", () => { it("can fetch overall indexing 'backfill' status object successfully", async () => { // arrange - const requestUrl = new URL(`/api/indexing-status`, DEFAULT_ENSNODE_API_URL); + const requestUrl = new URL(`/api/indexing-status`, DEFAULT_ENSNODE_API_URL_MAINNET); const mockedResponse = EXAMPLE_INDEXING_STATUS_BACKFILL_RESPONSE; const client = new ENSNodeClient(); diff --git a/packages/ensnode-sdk/src/client.ts b/packages/ensnode-sdk/src/client.ts index a6d4cd5039..591e7c6bdd 100644 --- a/packages/ensnode-sdk/src/client.ts +++ b/packages/ensnode-sdk/src/client.ts @@ -27,13 +27,10 @@ import { type SerializedRegistrarActionsResponse, } from "./api"; import { ClientError } from "./client-error"; +import { getDefaultEnsNodeUrl } from "./deployments"; +import { ENSNamespaceIds } from "./ens"; import type { ResolverRecordsSelection } from "./resolution"; -/** - * Default ENSNode API endpoint URL - */ -export const DEFAULT_ENSNODE_API_URL = "https://api.alpha.ensnode.io" as const; - /** * Configuration options for ENSNode API client */ @@ -64,6 +61,22 @@ export interface ClientOptions { * * @example * ```typescript + * // Use default ENSNode API URL for Mainnet + * const client = new ENSNodeClient({ + * url: getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet), + * }); + * ``` + * + * @example + * ```typescript + * // Use default ENSNode API URL for Sepolia + * const client = new ENSNodeClient({ + * url: getDefaultEnsNodeUrl(ENSNamespaceIds.Sepolia), + * }); + * ``` + * + * @example + * ```typescript * // Custom configuration * const client = new ENSNodeClient({ * url: new URL("https://my-ensnode-instance.com"), @@ -75,7 +88,7 @@ export class ENSNodeClient { static defaultOptions(): ClientOptions { return { - url: new URL(DEFAULT_ENSNODE_API_URL), + url: getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet), }; } diff --git a/packages/ensnode-sdk/src/deployments.ts b/packages/ensnode-sdk/src/deployments.ts new file mode 100644 index 0000000000..01e2eaa75f --- /dev/null +++ b/packages/ensnode-sdk/src/deployments.ts @@ -0,0 +1,29 @@ +import { type ENSNamespaceId, ENSNamespaceIds } from "@ensnode/datasources"; + +/** + * Default ENSNode API endpoint URL for Mainnet + */ +export const DEFAULT_ENSNODE_API_URL_MAINNET = "https://api.alpha.ensnode.io" as const; + +/** + * Default ENSNode API endpoint URL for Sepolia + */ +export const DEFAULT_ENSNODE_API_URL_SEPOLIA = "https://api.alpha-sepolia.ensnode.io" as const; + +/** + * Gets the default ENSNode URL for the provided ENSNamespaceId. + * + * @param namespace - The ENSNamespaceId to get the default ENSNode URL for + * @returns The default ENSNode URL for the provided ENSNamespaceId + * @throws If the provided ENSNamespaceId does not have a default ENSNode URL defined + */ +export const getDefaultEnsNodeUrl = (namespace: ENSNamespaceId): URL => { + switch (namespace) { + case ENSNamespaceIds.Mainnet: + return new URL(DEFAULT_ENSNODE_API_URL_MAINNET); + case ENSNamespaceIds.Sepolia: + return new URL(DEFAULT_ENSNODE_API_URL_SEPOLIA); + default: + throw new Error(`ENSNamespaceId ${namespace} does not have a default ENSNode URL defined`); + } +}; diff --git a/packages/ensnode-sdk/src/index.ts b/packages/ensnode-sdk/src/index.ts index 61d67de256..abaca51bc8 100644 --- a/packages/ensnode-sdk/src/index.ts +++ b/packages/ensnode-sdk/src/index.ts @@ -1,6 +1,7 @@ export * from "./api"; export { type ClientOptions, ENSNodeClient } from "./client"; export * from "./client-error"; +export * from "./deployments"; export * from "./ens"; export * from "./ensapi"; export * from "./ensindexer"; From e6e74e154aca6767d62be865623485d6dbe86598 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:26:25 +0400 Subject: [PATCH 2/6] docs(changeset): Added getDefaultEnsNodeUrl utility to get the URL for the default ENSNode deployment for a given ENS namespace --- .changeset/brown-pears-help.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brown-pears-help.md diff --git a/.changeset/brown-pears-help.md b/.changeset/brown-pears-help.md new file mode 100644 index 0000000000..efda7bd7ae --- /dev/null +++ b/.changeset/brown-pears-help.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-sdk": patch +--- + +Added getDefaultEnsNodeUrl utility to get the URL for the default ENSNode deployment for a given ENS namespace From 5d139ccd1eae27dd70cbb5664901dd8034d63fe2 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:34:44 +0400 Subject: [PATCH 3/6] add unit tests --- packages/ensnode-sdk/src/deployments.test.ts | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/ensnode-sdk/src/deployments.test.ts diff --git a/packages/ensnode-sdk/src/deployments.test.ts b/packages/ensnode-sdk/src/deployments.test.ts new file mode 100644 index 0000000000..5ffdf2f63b --- /dev/null +++ b/packages/ensnode-sdk/src/deployments.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; + +import { + DEFAULT_ENSNODE_API_URL_MAINNET, + DEFAULT_ENSNODE_API_URL_SEPOLIA, + getDefaultEnsNodeUrl, +} from "./deployments"; +import { ENSNamespaceIds } from "./ens"; + +describe("getDefaultEnsNodeUrl", () => { + it("returns the mainnet default URL", () => { + const url = getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet); + + expect(url.href).toBe(`${DEFAULT_ENSNODE_API_URL_MAINNET}/`); + }); + + it("returns the sepolia default URL", () => { + const url = getDefaultEnsNodeUrl(ENSNamespaceIds.Sepolia); + + expect(url.href).toBe(`${DEFAULT_ENSNODE_API_URL_SEPOLIA}/`); + }); + + it("throws for unsupported namespaces", () => { + const unsupportedNamespace = ENSNamespaceIds.EnsTestEnv; + + expect(() => getDefaultEnsNodeUrl(unsupportedNamespace)).toThrow( + `ENSNamespaceId ${unsupportedNamespace} does not have a default ENSNode URL defined`, + ); + }); +}); From 302039510ae9ae57d0eb33a5ee7210fc1f95bdd3 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:38:24 +0400 Subject: [PATCH 4/6] Add support for no explicit namespace param --- packages/ensnode-sdk/src/deployments.test.ts | 6 ++++++ packages/ensnode-sdk/src/deployments.ts | 11 +++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/ensnode-sdk/src/deployments.test.ts b/packages/ensnode-sdk/src/deployments.test.ts index 5ffdf2f63b..62f8bad3b5 100644 --- a/packages/ensnode-sdk/src/deployments.test.ts +++ b/packages/ensnode-sdk/src/deployments.test.ts @@ -8,6 +8,12 @@ import { import { ENSNamespaceIds } from "./ens"; describe("getDefaultEnsNodeUrl", () => { + it("returns the mainnet default URL when no namespace is provided", () => { + const url = getDefaultEnsNodeUrl(); + + expect(url.href).toBe(`${DEFAULT_ENSNODE_API_URL_MAINNET}/`); + }); + it("returns the mainnet default URL", () => { const url = getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet); diff --git a/packages/ensnode-sdk/src/deployments.ts b/packages/ensnode-sdk/src/deployments.ts index 01e2eaa75f..c289436f69 100644 --- a/packages/ensnode-sdk/src/deployments.ts +++ b/packages/ensnode-sdk/src/deployments.ts @@ -13,12 +13,15 @@ export const DEFAULT_ENSNODE_API_URL_SEPOLIA = "https://api.alpha-sepolia.ensnod /** * Gets the default ENSNode URL for the provided ENSNamespaceId. * - * @param namespace - The ENSNamespaceId to get the default ENSNode URL for - * @returns The default ENSNode URL for the provided ENSNamespaceId + * @param namespace - Optional. The ENSNamespaceId to get the default ENSNode URL for. If not + * provided, defaults to Mainnet. + * @returns The default ENSNode URL for the provided ENSNamespaceId, or for Mainnet if no + * namespace is provided. * @throws If the provided ENSNamespaceId does not have a default ENSNode URL defined */ -export const getDefaultEnsNodeUrl = (namespace: ENSNamespaceId): URL => { - switch (namespace) { +export const getDefaultEnsNodeUrl = (namespace?: ENSNamespaceId): URL => { + const effectiveNamespace = namespace ?? ENSNamespaceIds.Mainnet; + switch (effectiveNamespace) { case ENSNamespaceIds.Mainnet: return new URL(DEFAULT_ENSNODE_API_URL_MAINNET); case ENSNamespaceIds.Sepolia: From 4fe5a0f62c9700e0142e697ad6b2428fc1a61923 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:42:11 +0400 Subject: [PATCH 5/6] make use of default param --- packages/ensnode-sdk/src/client.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ensnode-sdk/src/client.ts b/packages/ensnode-sdk/src/client.ts index 591e7c6bdd..7d7572a824 100644 --- a/packages/ensnode-sdk/src/client.ts +++ b/packages/ensnode-sdk/src/client.ts @@ -28,7 +28,6 @@ import { } from "./api"; import { ClientError } from "./client-error"; import { getDefaultEnsNodeUrl } from "./deployments"; -import { ENSNamespaceIds } from "./ens"; import type { ResolverRecordsSelection } from "./resolution"; /** @@ -88,7 +87,7 @@ export class ENSNodeClient { static defaultOptions(): ClientOptions { return { - url: getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet), + url: getDefaultEnsNodeUrl(), }; } From eb7aa05e0ffaaf4fdbfa53688422d54da8713a40 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:55:11 +0400 Subject: [PATCH 6/6] Apply suggestions from Copilot --- packages/ensnode-sdk/src/client.ts | 8 ++++++++ packages/ensnode-sdk/src/deployments.ts | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/ensnode-sdk/src/client.ts b/packages/ensnode-sdk/src/client.ts index 7d7572a824..7c85cd097a 100644 --- a/packages/ensnode-sdk/src/client.ts +++ b/packages/ensnode-sdk/src/client.ts @@ -48,6 +48,8 @@ export interface ClientOptions { * * @example * ```typescript + * import { ENSNodeClient } from "@ensnode/ensnode-sdk"; + * * // Create client with default options * const client = new ENSNodeClient(); * @@ -60,6 +62,8 @@ export interface ClientOptions { * * @example * ```typescript + * import { ENSNamespaceIds, ENSNodeClient, getDefaultEnsNodeUrl } from "@ensnode/ensnode-sdk"; + * * // Use default ENSNode API URL for Mainnet * const client = new ENSNodeClient({ * url: getDefaultEnsNodeUrl(ENSNamespaceIds.Mainnet), @@ -68,6 +72,8 @@ export interface ClientOptions { * * @example * ```typescript + * import { ENSNamespaceIds, ENSNodeClient, getDefaultEnsNodeUrl } from "@ensnode/ensnode-sdk"; + * * // Use default ENSNode API URL for Sepolia * const client = new ENSNodeClient({ * url: getDefaultEnsNodeUrl(ENSNamespaceIds.Sepolia), @@ -76,6 +82,8 @@ export interface ClientOptions { * * @example * ```typescript + * import { ENSNodeClient } from "@ensnode/ensnode-sdk"; + * * // Custom configuration * const client = new ENSNodeClient({ * url: new URL("https://my-ensnode-instance.com"), diff --git a/packages/ensnode-sdk/src/deployments.ts b/packages/ensnode-sdk/src/deployments.ts index c289436f69..74a0138021 100644 --- a/packages/ensnode-sdk/src/deployments.ts +++ b/packages/ensnode-sdk/src/deployments.ts @@ -27,6 +27,8 @@ export const getDefaultEnsNodeUrl = (namespace?: ENSNamespaceId): URL => { case ENSNamespaceIds.Sepolia: return new URL(DEFAULT_ENSNODE_API_URL_SEPOLIA); default: - throw new Error(`ENSNamespaceId ${namespace} does not have a default ENSNode URL defined`); + throw new Error( + `ENSNamespaceId ${effectiveNamespace} does not have a default ENSNode URL defined`, + ); } };