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 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..7c85cd097a 100644 --- a/packages/ensnode-sdk/src/client.ts +++ b/packages/ensnode-sdk/src/client.ts @@ -27,13 +27,9 @@ import { type SerializedRegistrarActionsResponse, } from "./api"; import { ClientError } from "./client-error"; +import { getDefaultEnsNodeUrl } from "./deployments"; 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 */ @@ -52,6 +48,8 @@ export interface ClientOptions { * * @example * ```typescript + * import { ENSNodeClient } from "@ensnode/ensnode-sdk"; + * * // Create client with default options * const client = new ENSNodeClient(); * @@ -64,6 +62,28 @@ 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), + * }); + * ``` + * + * @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), + * }); + * ``` + * + * @example + * ```typescript + * import { ENSNodeClient } from "@ensnode/ensnode-sdk"; + * * // Custom configuration * const client = new ENSNodeClient({ * url: new URL("https://my-ensnode-instance.com"), @@ -75,7 +95,7 @@ export class ENSNodeClient { static defaultOptions(): ClientOptions { return { - url: new URL(DEFAULT_ENSNODE_API_URL), + url: getDefaultEnsNodeUrl(), }; } diff --git a/packages/ensnode-sdk/src/deployments.test.ts b/packages/ensnode-sdk/src/deployments.test.ts new file mode 100644 index 0000000000..62f8bad3b5 --- /dev/null +++ b/packages/ensnode-sdk/src/deployments.test.ts @@ -0,0 +1,36 @@ +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 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); + + 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`, + ); + }); +}); diff --git a/packages/ensnode-sdk/src/deployments.ts b/packages/ensnode-sdk/src/deployments.ts new file mode 100644 index 0000000000..74a0138021 --- /dev/null +++ b/packages/ensnode-sdk/src/deployments.ts @@ -0,0 +1,34 @@ +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 - 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 => { + const effectiveNamespace = namespace ?? ENSNamespaceIds.Mainnet; + switch (effectiveNamespace) { + 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 ${effectiveNamespace} 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";