Skip to content

feat: Migrate domain indexing to ENSNode (Omnigraph + subgraph-compat) for ENSv2 - #4

Open
Quantumlyy wants to merge 2 commits into
mainfrom
ens-v2
Open

feat: Migrate domain indexing to ENSNode (Omnigraph + subgraph-compat) for ENSv2#4
Quantumlyy wants to merge 2 commits into
mainfrom
ens-v2

Conversation

@Quantumlyy

Copy link
Copy Markdown
Collaborator

Summary

  • Replace The Graph subgraph client with a new src/services/ensnode.ts module that talks to ENSNode hosted instances.
  • Hybrid API usage:
    • NameWrapper token metadata and /queryNFT name lookups use the Omnigraph GraphQL API (/api/omnigraph), including ENSv1Domain and ENSv2Domain fragments.
    • Base Registrar v1 lookups by labelhash continue to use ENSNode’s subgraph-compatible endpoint (/subgraph) with the existing domains(where: …) query shape.
  • Configuration: remove SUBGRAPH_URL_* and THE_GRAPH_API_KEY. Add ENSNODE_URL_MAINNET, ENSNODE_URL_SEPOLIA, and ENSNODE_URL_HOLESKY in wrangler.toml [vars] (defaults: https://api.alpha.ensnode.io, https://api.alpha-sepolia.ensnode.io, empty for Holesky). Network config in src/lib/networks.ts now exposes ensnodeUrl per chain.
  • Holesky: no hosted ENSNode instance today. Indexed routes (GET /{network}/… metadata, /queryNFT) on holesky return 503 with holesky_unsupported and a link to hosted instances docs. Avatar/header resolution via RPC still works when HOLESKY_RPC_URL is set.
  • Metadata version: 1 = Base Registrar, 2 = NameWrapper (ENSv1), 3 = native ENSv2 (protocolVersion === "ENSv2" from Omnigraph).
  • /queryNFT: resolves domains by normalized name via Omnigraph (queryDomainByName) instead of namehash-only subgraph lookup.
  • Remove src/services/subgraph.ts. Add unit coverage in test/unit/ensnode.test.ts; update route cache tests to mock Omnigraph + subgraph-compat responses.
  • README documents the indexing backend, env vars, Holesky limitation, and version semantics.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ens-metadata-flarecloud e798440 Commit Preview URL Jun 08 2026, 04:07 PM

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces The Graph subgraph client with a new src/services/ensnode.ts module that talks to ENSNode via two distinct APIs: Omnigraph (/api/omnigraph) for NameWrapper and name lookups, and a subgraph-compatible endpoint (/subgraph) for Base Registrar v1 labelhash lookups. It introduces ENSv2 support (protocolVersion === "ENSv2", metadata version: 3) and handles Holesky's lack of a hosted instance with a 503 guard.

  • New ENSNode service (src/services/ensnode.ts): queryDomainByNamehash iterates over configured registries (ENSv1, optional ENSv2) via Omnigraph; queryDomainByName resolves by normalized name; queryDomainByLabelhash uses the subgraph-compat endpoint. mapOmnigraphDomain converts Omnigraph's union type to DomainRecord and resolveNftIdentifiers picks the correct contract/tokenId for ENSv1 vs ENSv2.
  • Config migration: SUBGRAPH_URL_* / THE_GRAPH_API_KEY removed; ENSNODE_URL_MAINNET, ENSNODE_URL_SEPOLIA, ENSNODE_URL_HOLESKY added to wrangler.toml and Env; NetworkConfig gains ensnodeUrl and optional ethRegistryV2.
  • ENSv2 metadata gap: /queryNFT now returns the correct ENSv2 registry contract address for ENSv2 domains, but contractKind in domain.ts only accepts BASE_REGISTRAR_V1 and NAME_WRAPPER_V2, so the metadata endpoint rejects that contract — breaking the standard queryNFTmetadata lookup flow for ENSv2 names.

Confidence Score: 3/5

The core ENSv1 lookup paths work correctly, but two defects in the ENSv2 paths could silently return wrong data or break downstream consumers.

Two issues affect live ENSv2 behaviour: resolveNftIdentifiers silently returns NAME_WRAPPER_V2 as the contract when an ENSv2Domain from Omnigraph is missing tokenId or registry.contract.address, giving callers an incorrect NFT identity with no error signal. Separately, /queryNFT now advertises the ENSv2 registry contract address in its response, but the metadata endpoint's contractKind will reject that address with a 400 — breaking any consumer that uses the queryNFT response to construct a metadata request.

src/services/ensnode.ts (resolveNftIdentifiers fallback) and src/services/domain.ts (contractKind missing ENSv2 registry support)

Important Files Changed

Filename Overview
src/services/ensnode.ts New ENSNode service replacing subgraph; has a silent fallback in resolveNftIdentifiers that returns NAME_WRAPPER_V2 for ENSv2 domains with missing nftContract/nftTokenId fields
src/services/domain.ts contractKind only recognises BASE_REGISTRAR_V1 and NAME_WRAPPER_V2; ENSv2 registry contracts returned by queryNFT are not accepted by the metadata route, breaking the two-endpoint lookup flow
src/routes/queryNFT.ts Now uses resolveNftIdentifiers for correct ENSv2 contract/tokenId resolution; missing 503 response in OpenAPI schema
src/routes/metadata.ts version:3 correctly set for ENSv2; token_hash uses namehash which aligns with the NAME_WRAPPER_V2 access pattern; missing 503 in OpenAPI schema
src/lib/networks.ts Replaced subgraphUrl with ensnodeUrl and added optional ethRegistryV2 per network; clean
test/unit/ensnode.test.ts Good coverage of happy paths and holesky fallback; no test for the resolveNftIdentifiers fallback when ENSv2 domain is missing nftContract/nftTokenId
wrangler.toml Replaced SUBGRAPH_URL_* vars with ENSNODE_URL_*; ENSNODE_URL_HOLESKY intentionally empty; clean

Sequence Diagram

sequenceDiagram
    participant Client
    participant MetadataRoute as /network/contract/tokenId
    participant QueryNFTRoute as /queryNFT?name=
    participant DomainSvc as domain.ts
    participant ENSNode as ensnode.ts
    participant Omnigraph as ENSNode /api/omnigraph
    participant Subgraph as ENSNode /subgraph

    Client->>QueryNFTRoute: "GET /queryNFT?name=foo.eth"
    QueryNFTRoute->>ENSNode: queryDomainByName(name)
    ENSNode->>Omnigraph: DomainByName query
    Omnigraph-->>ENSNode: "ENSv1Domain | ENSv2Domain"
    ENSNode-->>QueryNFTRoute: DomainRecord (protocolVersion, nftContract?)
    QueryNFTRoute-->>Client: "{contract: ENSv2_registry, tokenId: nftTokenId}"

    Client->>MetadataRoute: "GET /mainnet/{ENSv2_registry}/{nftTokenId}"
    MetadataRoute->>DomainSvc: resolveDomain(contract)
    DomainSvc->>DomainSvc: contractKind(ENSv2_registry) → null
    DomainSvc-->>MetadataRoute: 400 unsupported contract

    Client->>MetadataRoute: "GET /mainnet/NAME_WRAPPER_V2/{namehash}"
    MetadataRoute->>DomainSvc: resolveDomain(NAME_WRAPPER_V2)
    DomainSvc->>ENSNode: queryDomainByNamehash(namehash)
    ENSNode->>Omnigraph: DomainById (ETH_REGISTRY_V1)
    Omnigraph-->>ENSNode: null (miss)
    ENSNode->>Omnigraph: DomainById (ETH_REGISTRY_V2)
    Omnigraph-->>ENSNode: ENSv2Domain
    ENSNode-->>DomainSvc: "DomainRecord{protocolVersion:ENSv2}"
    DomainSvc-->>MetadataRoute: "{kind:v2, record}"
    MetadataRoute-->>Client: "{version:3}"

    Client->>MetadataRoute: "GET /mainnet/BASE_REGISTRAR_V1/{labelhash}"
    MetadataRoute->>DomainSvc: resolveDomain(BASE_REGISTRAR_V1)
    DomainSvc->>ENSNode: queryDomainByLabelhash(labelhash)
    ENSNode->>Subgraph: DomainByLabelhash query
    Subgraph-->>ENSNode: domains[]
    ENSNode-->>DomainSvc: "DomainRecord{protocolVersion:ENSv1}"
    DomainSvc-->>MetadataRoute: "{kind:v1, record}"
    MetadataRoute-->>Client: "{version:1}"
Loading

Fix All in Conductor Fix All in Cursor Fix All in Codex Fix All in Claude Code

Reviews (2): Last reviewed commit: "fix: address requested changes" | Re-trigger Greptile

Comment thread src/services/ensnode.ts
Comment thread src/services/ensnode.ts
Comment thread src/services/ensnode.ts Outdated
Comment thread src/services/ensnode.ts
Comment on lines +42 to +44
fragment OmnigraphDomainFields on Domain {
__typename
... on ENSv1Domain {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The ENSv1Domain and ENSv2Domain inline fragments in OMNIGRAPH_DOMAIN_FIELDS are byte-for-byte identical. Duplicate fragment bodies increase query size and make future field additions error-prone (easy to update one and forget the other). A comment noting the intentional duplication would at least document the constraint.

Suggested change
fragment OmnigraphDomainFields on Domain {
__typename
... on ENSv1Domain {
# ENSv1Domain and ENSv2Domain expose identical fields today; both inline
# fragments must remain in sync if the schema diverges in the future.
fragment OmnigraphDomainFields on Domain {
__typename
... on ENSv1Domain {

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

Comment thread src/services/ensnode.ts
@Quantumlyy
Quantumlyy marked this pull request as ready for review June 8, 2026 13:58
Copilot AI review requested due to automatic review settings June 8, 2026 13:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates ENS domain indexing from The Graph subgraph to ENSNode, using ENSNode’s Omnigraph GraphQL API for NameWrapper/name-based lookups (including ENSv2 support) while retaining a subgraph-compatible query shape for Base Registrar v1 labelhash lookups. It also updates configuration, documentation, and unit tests to reflect the new indexing backend and Holesky limitations.

Changes:

  • Replace subgraph-based lookups with src/services/ensnode.ts (Omnigraph + subgraph-compat hybrid), and remove src/services/subgraph.ts.
  • Update env/config wiring (Env, NetworkConfig, wrangler.toml, test envs) from SUBGRAPH_URL_* + THE_GRAPH_API_KEY to ENSNODE_URL_*.
  • Extend metadata version semantics to emit 3 for ENSv2 domains, and add/adjust unit tests and docs accordingly.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated no comments.

Show a summary per file
File Description
wrangler.toml Replaces SUBGRAPH_URL_* vars with ENSNODE_URL_* vars (including empty Holesky default).
vitest.config.ts Updates test env vars to use ENSNODE_URL_* instead of subgraph URLs.
test/unit/routeCache.test.ts Updates route cache tests to mock ENSNode Omnigraph + subgraph-compat endpoints via fetch.
test/unit/ensnode.test.ts Adds unit tests for ENSNode query helpers, Omnigraph mapping, and Holesky fast-fail behavior.
src/storage/r2Cache.ts Updates internal comment to reference ENSNode instead of subgraph roundtrips.
src/services/subgraph.ts Removes The Graph subgraph client implementation.
src/services/ensnode.ts Adds ENSNode client (Omnigraph + subgraph-compat), Omnigraph mapping, and Holesky availability guard.
src/services/domain.ts Repoints domain resolution to ENSNode-based query functions.
src/routes/queryNFT.ts Changes /queryNFT to resolve domains by normalized name via Omnigraph instead of namehash-only subgraph lookup.
src/routes/metadata.ts Emits metadata version: 3 for ENSv2 domains based on protocolVersion.
src/lib/networks.ts Changes network config field from subgraphUrl to ensnodeUrl and wires it from env.
src/env.ts Replaces SUBGRAPH_URL_*/THE_GRAPH_API_KEY with ENSNODE_URL_* env vars.
src/constants.ts Adds ETH_REGISTRY_V1 constant used for Omnigraph DomainId construction.
README.md Documents ENSNode indexing backend, Holesky behavior, and metadata version semantics.
package.json Updates deploy-time variable descriptions to reflect ENSNODE_URL_* configuration.
.dev.vars.example Removes The Graph API key example and clarifies local dev secrets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants