Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/ensnode-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./internal": {
"import": {
"types": "./dist/internal.d.ts",
"default": "./dist/internal.js"
},
"require": {
"types": "./dist/internal.d.cts",
"default": "./dist/internal.cjs"
}
}
},
Comment on lines 20 to 48
"main": "./dist/index.cjs",
Expand Down
54 changes: 45 additions & 9 deletions packages/ensnode-sdk/src/omnigraph-api/example-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,38 @@ const ENS_TEST_ENV_V2_ETH_REGISTRAR = maybeGetDatasourceContract(
const VITALIK_ADDRESS = toNormalizedAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");

// owns sfmonicdeb*.eth (mix of v1 + v2) on sepolia-v2 and holds v2 ETHRegistry permissions
const SEPOLIA_V2_USER_ADDRESS = toNormalizedAddress("0x2f8e8b1126e75fde0b7f731e7cb5847eba2d2574");
const _SEPOLIA_V2_USER_ADDRESS = toNormalizedAddress("0x2f8e8b1126e75fde0b7f731e7cb5847eba2d2574");

Comment on lines 35 to +37
Copy link
Copy Markdown
Contributor

@vercel vercel Bot May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable _SEPOLIA_V2_USER_ADDRESS declared but never referenced anywhere in the codebase

Fix on Vercel

const SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES = toNormalizedAddress(
"0x205d2686da3bf33f64c17f21462c51b5ead462cf",
);
Comment on lines +36 to +40
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the Sepolia V2 fixtures that actually demonstrate permissions.

Line 35 introduces _SEPOLIA_V2_USER_ADDRESS as an address with v2 ETHRegistry permissions, but Lines 303-304 and 328-329 still point the exported Sepolia V2 examples at fixtures that are explicitly documented as returning empty results. That means the published permissions-by-contract and permissions-by-user examples stay blank in the namespace this PR is trying to make consumable.

Suggested patch
     variables: {
       // TODO: same as above
       default: { contract: ENS_TEST_ENV_V2_ETH_REGISTRAR },
-      // TODO: example response is empty for this address on Sepolia V2
-      [ENSNamespaceIds.SepoliaV2]: { contract: SEPOLIA_V2_V2_ETH_REGISTRAR },
+      [ENSNamespaceIds.SepoliaV2]: { contract: SEPOLIA_V2_V2_ETH_REGISTRY },
     },
   },
@@
     variables: {
       default: { address: accounts.deployer.address },
-      // TODO: example response is empty for this address on Sepolia V2
-      [ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES },
+      [ENSNamespaceIds.SepoliaV2]: { address: _SEPOLIA_V2_USER_ADDRESS },
     },
   },

Also applies to: 300-304, 326-329

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ensnode-sdk/src/omnigraph-api/example-queries.ts` around lines 36 -
40, The Sepolia V2 example exports are pointing at fixtures that return empty
results; update the exported examples for "permissions-by-contract" and
"permissions-by-user" to use the actual Sepolia V2 fixture addresses that
demonstrate permissions: replace the empty-result addresses with the constants
_SEPOLIA_V2_USER_ADDRESS and SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES (the variables
defined at top of the file) so the examples show non-empty permission data;
locate the export objects/entries named "permissions-by-contract" and
"permissions-by-user" and swap their address values to these constants.


const DEVNET_NAME_WITH_OWNED_RESOLVER = asInterpretedName("example.eth");

const SEPOLIA_V2_NAME_WITH_OWNED_RESOLVER = asInterpretedName("sfmonicdebmig.eth");

export const GRAPHQL_API_EXAMPLE_QUERIES: Array<{
const SEPOLIA_V2_TEST_NAME = asInterpretedName("test-name.eth");

export type GraphqlApiExampleQuery = {
id: string;
query: string;
variables: NamespaceSpecificValue<Record<string, unknown>>;
}> = [
};
Comment on lines +48 to +52
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Enforce the query-ID invariant explicitly and fail fast on duplicates.

Line 422 currently allows duplicate id values to overwrite silently in the lookup map. Since id is now a public lookup contract, encode it as a dedicated alias and throw on duplicates during map construction.

Suggested patch
+export type GraphqlApiExampleQueryId = string;
+
 export type GraphqlApiExampleQuery = {
-  id: string;
+  id: GraphqlApiExampleQueryId;
   query: string;
   variables: NamespaceSpecificValue<Record<string, unknown>>;
 };
 
-export function getGraphqlApiExampleQueryById(id: string): GraphqlApiExampleQuery {
+export function getGraphqlApiExampleQueryById(id: GraphqlApiExampleQueryId): GraphqlApiExampleQuery {
   const found = graphqlApiExampleQueryById.get(id);
   if (!found) {
     throw new Error(`Unknown GraphQL API example query id: ${id}`);
   }
   return found;
 }
@@
-const graphqlApiExampleQueryById = new Map(
-  GRAPHQL_API_EXAMPLE_QUERIES.map((entry) => [entry.id, entry]),
-);
+const graphqlApiExampleQueryById = (() => {
+  const byId = new Map<GraphqlApiExampleQueryId, GraphqlApiExampleQuery>();
+  for (const entry of GRAPHQL_API_EXAMPLE_QUERIES) {
+    if (byId.has(entry.id)) {
+      throw new Error(`Duplicate GraphQL API example query id: ${entry.id}`);
+    }
+    byId.set(entry.id, entry);
+  }
+  return byId;
+})();
As per coding guidelines, "Use type aliases to document invariants; each invariant MUST be documented exactly once on its type alias".

Also applies to: 421-423

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ensnode-sdk/src/omnigraph-api/example-queries.ts` around lines 48 -
52, Introduce a dedicated type alias (e.g., QueryId) that documents the
invariant that GraphqlApiExampleQuery.id is a unique public lookup key, replace
usages of raw string for ids with this alias on GraphqlApiExampleQuery, and
change the example-queries map construction code (the routine that builds the
lookup by id) to check for existing keys and throw an error immediately on
duplicate ids instead of silently overwriting; update any call sites that assume
string ids to use QueryId where appropriate.


export function getGraphqlApiExampleQueryById(id: string): GraphqlApiExampleQuery {
const found = graphqlApiExampleQueryById.get(id);
if (!found) {
throw new Error(`Unknown GraphQL API example query id: ${id}`);
}
return found;
}

export const GRAPHQL_API_EXAMPLE_QUERIES: GraphqlApiExampleQuery[] = [
////////////////
// Hello World
////////////////
{
id: "hello-world",
query: `#
# Welcome to this interactive playground for
# ENSNode's GraphQL API!
Expand All @@ -65,6 +83,7 @@ query HelloWorld {
// Find Domains
/////////////////
{
id: "find-domains",
query: `
query FindDomains(
$name: String!
Expand All @@ -90,14 +109,15 @@ query FindDomains(
variables: {
default: { name: "vitalik", order: { by: "NAME", dir: "DESC" } },
[ENSNamespaceIds.EnsTestEnv]: { name: "c", order: { by: "NAME", dir: "DESC" } },
[ENSNamespaceIds.SepoliaV2]: { name: "sfmonic", order: { by: "NAME", dir: "DESC" } },
[ENSNamespaceIds.SepoliaV2]: { name: "test-na", order: { by: "NAME", dir: "DESC" } },
},
},

///////////////////
// Domain By Name
///////////////////
{
id: "domain-by-name",
query: `
query DomainByName($name: InterpretedName!) {
domain(by: {name: $name}) {
Expand All @@ -120,14 +140,15 @@ query DomainByName($name: InterpretedName!) {
}`,
variables: {
default: { name: "eth" },
[ENSNamespaceIds.SepoliaV2]: { name: "sfmonicdebmig.eth" },
[ENSNamespaceIds.SepoliaV2]: { name: SEPOLIA_V2_TEST_NAME },
},
},

//////////////////////
// Domain Subdomains
//////////////////////
{
id: "domain-subdomains",
query: `
query DomainSubdomains($name: InterpretedName!) {
domain(by: {name: $name}) {
Expand All @@ -148,6 +169,7 @@ query DomainSubdomains($name: InterpretedName!) {
// Domain Events
/////////////////
{
id: "domain-events",
query: `
query DomainEvents($name: InterpretedName!) {
domain(by: {name: $name}) {
Expand Down Expand Up @@ -176,6 +198,7 @@ query DomainEvents($name: InterpretedName!) {
// Account Domains
////////////////////
{
id: "domains-by-address",
query: `
query AccountDomains(
$address: Address!
Expand All @@ -194,14 +217,15 @@ query AccountDomains(
variables: {
default: { address: VITALIK_ADDRESS },
[ENSNamespaceIds.EnsTestEnv]: { address: accounts.owner.address },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_USER_ADDRESS },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES },
},
},

////////////////////
// Account Events
////////////////////
{
id: "account-events",
query: `
query AccountEvents(
$address: Address!
Expand All @@ -213,14 +237,15 @@ query AccountEvents(
variables: {
default: { address: VITALIK_ADDRESS },
[ENSNamespaceIds.EnsTestEnv]: { address: accounts.deployer.address },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_USER_ADDRESS },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES },
},
},

/////////////////////
// Registry Domains
/////////////////////
{
id: "registry-domains",
query: `
query RegistryDomains(
$registry: AccountIdInput!
Expand All @@ -247,6 +272,7 @@ query RegistryDomains(
// Permissions By Contract
////////////////////////////
{
id: "permissions-by-contract",
query: `
query PermissionsByContract(
$contract: AccountIdInput!
Expand Down Expand Up @@ -274,6 +300,7 @@ query PermissionsByContract(
variables: {
// TODO: same as above
default: { contract: ENS_TEST_ENV_V2_ETH_REGISTRAR },
// TODO: example response is empty for this address on Sepolia V2
[ENSNamespaceIds.SepoliaV2]: { contract: SEPOLIA_V2_V2_ETH_REGISTRAR },
},
},
Expand All @@ -282,6 +309,7 @@ query PermissionsByContract(
// Permissions By User
////////////////////////
{
id: "permissions-by-user",
query: `
query PermissionsByUser($address: Address!) {
account(by: { address: $address }) {
Expand All @@ -297,14 +325,16 @@ query PermissionsByUser($address: Address!) {
}`,
variables: {
default: { address: accounts.deployer.address },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_USER_ADDRESS },
// TODO: example response is empty for this address on Sepolia V2
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES },
},
},

//////////////////////////////////
// Account Resolver Permissions
//////////////////////////////////
{
id: "account-resolver-permissions",
query: `
query AccountResolverPermissions($address: Address!) {
account(by: { address: $address }) {
Expand All @@ -323,14 +353,15 @@ query AccountResolverPermissions($address: Address!) {
}`,
variables: {
default: { address: accounts.deployer.address },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_USER_ADDRESS },
[ENSNamespaceIds.SepoliaV2]: { address: SEPOLIA_V2_ADDRESS_WITH_LOT_OF_NAMES },
},
},

//////////////////////////////
// Domain's Assigned Resolver
//////////////////////////////
{
id: "domain-resolver",
query: `
query DomainResolver($name: InterpretedName!) {
domain(by: { name: $name }) {
Expand All @@ -352,6 +383,7 @@ query DomainResolver($name: InterpretedName!) {
// Namegraph
//////////////
{
id: "namegraph",
query: `
query Namegraph {
root {
Expand Down Expand Up @@ -384,3 +416,7 @@ query Namegraph {
variables: { default: {} },
},
];

const graphqlApiExampleQueryById = new Map(
GRAPHQL_API_EXAMPLE_QUERIES.map((entry) => [entry.id, entry]),
);
3 changes: 2 additions & 1 deletion packages/ensnode-sdk/src/shared/datasource-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
type ENSNamespaceId,
maybeGetDatasource,
} from "@ensnode/datasources";
import { accountIdEqual } from "@ensnode/ensnode-sdk";

import { accountIdEqual } from "./account-id";

/**
* Gets the AccountId for the contract in the specified namespace, datasource, and
Expand Down
2 changes: 1 addition & 1 deletion packages/ensnode-sdk/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
entry: ["src/index.ts", "src/internal.ts"],
platform: "neutral",
format: ["esm", "cjs"],
target: "es2022",
Expand Down
Loading