This is the full developer documentation for ENSNode # Hosted ENSNode Instances ## Get Started Quickly [Section titled “Get Started Quickly”](#get-started-quickly) NameHash Labs provides hosted instances of ENSNode for developers building on ENS and looking to get started quickly. These instances are currently provided free of charge with no API key required, have no rate limiting, and are maintained and monitored by the NameHash Labs team. Version compatibility with hosted instances [Our hosted ENSNode instances](/docs/hosted-instances) currently run ENSNode `1.15.1`. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the `gql.tada` TypeScript plugin to type your queries, so pin an **exact** version (no `^` or `~`) of `enssdk@1.15.1` (and `enskit@1.15.1` when using React) to keep your generated types matched to the deployed schema. Use these exact install commands: ``` npm install enssdk@1.15.1 # or, for React apps: npm install enskit@1.15.1 enssdk@1.15.1 ``` ### ENS Namespaces [Section titled “ENS Namespaces”](#ens-namespaces) Each ENSNode instance is configured for a specific **ENS namespace**. An ENS namespace identifies which ENS protocol deployment ENSNode will provide data for (ex: mainnet or sepolia). Each ENS namespace is associated with a particular ENS Root Registry deployment, which may or may not have completed the transition from ENSv1 (only) to ENSv1 + ENSv2 (both v1 and v2 concurrently activated together). ENSNode supports all of the following ENS namespaces: * `mainnet` * Currently ENSv1 only and not also ENSv2 yet. * `sepolia` * Currently ENSv1 only and not also ENSv2 yet. * `sepolia-v2` * Now ENSv1 + ENSv2 — An *all new* deployment of ENS to Sepolia that is already upgraded to ENSv1 + ENSv2 and is independent of the traditional `sepolia` ENS namespace listed above which currently remains ENSv1 only. * `ens-test-env` * Now ENSv1 + ENSv2 — A deployment of ENSv1 + ENSv2 to a local Anvil chain for development and testing. About the ens-test-env For details see [ens-test-env](https://github.com/ensdomains/ens-test-env) and [contracts-v2](https://github.com/ensdomains/contracts-v2). Teams working on the core ENS protocol and core ENS infrastructure such as ENS Labs and NameHash Labs are using this ENSNode configuration to support ENSv2 development. More details about ENS namespaces can be found inside [ENSNode’s datasources package](https://github.com/namehash/ensnode/tree/main/packages/datasources). ### ENSNode Plugins [Section titled “ENSNode Plugins”](#ensnode-plugins) Each ENSNode instance is also configured for a specific set of activated **ENSNode plugins**. The activated plugins determine the specific indexed data model and data records ENSIndexer will produce in ENSDb and therefore which APIs and data records ENSApi will make available to query. ### ENSv1 + ENSv2 Instances [Section titled “ENSv1 + ENSv2 Instances”](#ensv1--ensv2-instances) These instances are associated with an ENS namespace that has upgraded to ENSv1 + ENSv2 and demonstrate the latest support in ENSNode for ENSv1 and ENSv2 being concurrently activated together. #### ENSNode ‘v2 Sepolia’ [Section titled “ENSNode ‘v2 Sepolia’”](#ensnode-v2-sepolia) v2 Sepolia The `sepolia-v2` namespace is undergoing active development by the ENS Labs team who is continuing to release updated ENSv2 contracts. It should be considered experimental. | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Hosted at | [ Connect with ENSAdmin](https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io) | | ENSNode Version | v1.15.1 | | ENS Namespace | sepolia-v2 | | ENS Versions | ENSv1 + ENSv2 (concurrently activated together) | | ENSNode Plugins | *unigraph*, *protocol-acceleration*, *subgraph* | | ENSApi Services | * ENS Omnigraph API ✅ *Activated.* Powered by a polymorphic GraphQL API with native support for both ENSv1 and ENSv2. When ENSv2 launches in Summer 2026, data from two protocol versions coexist — and the ENS Omnigraph API will keep your app working against both, at the same time, with no code changes. Domains from ENSv1 and ENSv2 are indexed concurrently and exposed through a unified schema. * ENS Protocol Acceleration ✅ *Available.* Accelerate most of your ENS protocol resolution requests while maintaining full compliance with all ENS protocol standards and best practices. * ENS Subgraph API 🚨 *API-level Subgraph Compatibility.* This ENSNode instance has a fully backwards compatible ENS Subgraph GraphQL API. However, additional plugins have been activated which index a superset of data into the subgraph data model in ENSDb. This superset of indexed data means that the data returned for some ENS Subgraph API queries may be different. ENSv2 launch completed. Subgraph API now unreliable. ENSv2 has launched on the `sepolia-v2` ENS namespace. This means the legacy ENS Subgraph no longer returns reliable data. Transition your apps now to the new [ENS Omnigraph API](/docs/integrate/omnigraph) to be ENSv2 ready. | ## ENSv1 Only Instances (not also ENSv2 yet) [Section titled “ENSv1 Only Instances (not also ENSv2 yet)”](#ensv1-only-instances-not-also-ensv2-yet) These instances are associated with an ENS namespace that has *NOT* upgraded to support ENSv1 and ENSv2 concurrently yet and are still operating exclusively under ENSv1. These demonstrate how [*the ENS Omnigraph API*](/docs/integrate/omnigraph), [*enssdk*](/docs/integrate/integration-options/enssdk), [*enskit*](/docs/integrate/integration-options/enskit), and [the full stack of ENSNode services](/docs/services) provide support to developers to become ENSv2 ready even before ENSv2 launches. ### Alpha-style Deployments [Section titled “Alpha-style Deployments”](#alpha-style-deployments) These ENSNode instances do not constrain themselves to exclusively activating the `subgraph` plugin. They activate additional plugins such as the `unigraph` plugin to demonstrate the latest capabilities of ENSNode including [*the new ENS Omnigraph API*](/docs/integrate/omnigraph). #### ENSNode ‘Alpha’ [Section titled “ENSNode ‘Alpha’”](#ensnode-alpha) | | | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Hosted at | [ Connect with ENSAdmin](https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.alpha.ensnode.io) | | ENSNode Version | v1.15.1 | | ENS Namespace | mainnet | | ENS Versions | ENSv1 only (not also ENSv2 yet) | | ENSNode Plugins | *unigraph*, *protocol-acceleration*, *subgraph*, basenames, lineanames, threedns, registrars, tokenscope | | ENSApi Services | * ENS Omnigraph API ✅ *Activated.* Demonstrates the ENS Omnigraph's ability to support ENSv1 only before ENSv2 launches and both ENSv1 and ENSv2 are concurrently active. Until ENSv2 launches on this namespace, only ENSv1 domains will be available. As soon as ENSv2 launches on this namespace both ENSv1 and ENSv2 domains will be available. * ENS Protocol Acceleration ✅ *Available.* Accelerate most of your ENS protocol resolution requests while maintaining full compliance with all ENS protocol standards and best practices. * ENS Subgraph API ✅ *API-level Subgraph Compatibility.* This ENSNode instance has a fully backwards compatible ENS Subgraph GraphQL API. However, additional plugins have been activated which index a superset of data into the subgraph data model in ENSDb. This superset of indexed data means that the data returned for some ENS Subgraph API queries may be different. ENSv2 launches soon. Subgraph API soon unreliable. ENSv2 launches soon! As soon as ENSv2 launches on the `mainnet` ENS namespace, the legacy ENS Subgraph will no longer return reliable data. Transition your apps now to the new [ENS Omnigraph API](/docs/integrate/omnigraph) to be ENSv2 ready. | #### ENSNode ‘Alpha-Sepolia’ [Section titled “ENSNode ‘Alpha-Sepolia’”](#ensnode-alpha-sepolia) | | | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Hosted at | [ Connect with ENSAdmin](https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.alpha-sepolia.ensnode.io) | | ENSNode Version | v1.15.1 | | ENS Namespace | sepolia | | ENS Versions | ENSv1 only (not also ENSv2 yet) | | ENSNode Plugins | *unigraph*, *protocol-acceleration*, *subgraph*, basenames, lineanames, registrars | | ENSApi Services | * ENS Omnigraph API ✅ *Activated.* Demonstrates the ENS Omnigraph's ability to support ENSv1 only before ENSv2 launches and both ENSv1 and ENSv2 are concurrently active. Until ENSv2 launches on this namespace, only ENSv1 domains will be available. As soon as ENSv2 launches on this namespace both ENSv1 and ENSv2 domains will be available. * ENS Protocol Acceleration ✅ *Available.* Accelerate most of your ENS protocol resolution requests while maintaining full compliance with all ENS protocol standards and best practices. * ENS Subgraph API ✅ *API-level Subgraph Compatibility.* This ENSNode instance has a fully backwards compatible ENS Subgraph GraphQL API. However, additional plugins have been activated which index a superset of data into the subgraph data model in ENSDb. This superset of indexed data means that the data returned for some ENS Subgraph API queries may be different. ENSv2 launches soon. Subgraph API soon unreliable. ENSv2 launches soon! As soon as ENSv2 launches on the `sepolia` ENS namespace, the legacy ENS Subgraph will no longer return reliable data. Transition your apps now to the new [ENS Omnigraph API](/docs/integrate/omnigraph) to be ENSv2 ready. | ### Subgraph-style Deployments [Section titled “Subgraph-style Deployments”](#subgraph-style-deployments) These ENSNode instances focus on maximizing backwards compatibility with the ENS Subgraph. Therefore they exclusively activate the `subgraph` plugin and no others. #### ENSNode ‘Mainnet’ [Section titled “ENSNode ‘Mainnet’”](#ensnode-mainnet) | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Hosted at | [ Connect with ENSAdmin](https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.mainnet.ensnode.io) | | ENSNode Version | v1.15.1 | | ENS Namespace | mainnet | | ENS Versions | ENSv1 only (not also ENSv2 yet) | | ENSNode Plugins | *subgraph* | | ENSApi Services | * ENS Omnigraph API ❌ *Not activated.* This ENSNode instance optimizes for maximum compatibility with the ENS Subgraph. Therefore it exclusively activates the *subgraph* plugin and does not activate the *unigraph* plugin that the ENS Omnigraph API requires. * ENS Protocol Acceleration ❌ *Unavailable.* Your ENS protocol resolution requests will be serviced in full compliance with all ENS protocol standards and best practices. However, this ENSNode instance did not activate the *protocol-acceleration* plugin and therefore ENS resolution requests will be serviced accurately but without acceleration. * ENS Subgraph API ✅ *Full Subgraph Compatibility.* This ENSNode instance has a fully backwards compatible ENS Subgraph GraphQL API at both the API-level and the data-level. We have exhaustively tested and verified that every data record returned by this ENSNode instance's ENS Subgraph API is byte-for-byte identical with The Graph's ENS Subgraph API. ENSv2 launches soon. Subgraph API soon unreliable. ENSv2 launches soon! As soon as ENSv2 launches on the `mainnet` ENS namespace, the legacy ENS Subgraph will no longer return reliable data. Transition your apps now to the new [ENS Omnigraph API](/docs/integrate/omnigraph) to be ENSv2 ready. | #### ENSNode ‘Sepolia’ [Section titled “ENSNode ‘Sepolia’”](#ensnode-sepolia) | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Hosted at | [ Connect with ENSAdmin](https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.sepolia.ensnode.io) | | ENSNode Version | v1.15.1 | | ENS Namespace | sepolia | | ENS Versions | ENSv1 only (not also ENSv2 yet) | | ENSNode Plugins | *subgraph* | | ENSApi Services | * ENS Omnigraph API ❌ *Not activated.* This ENSNode instance optimizes for maximum compatibility with the ENS Subgraph. Therefore it exclusively activates the *subgraph* plugin and does not activate the *unigraph* plugin that the ENS Omnigraph API requires. * ENS Protocol Acceleration ❌ *Unavailable.* Your ENS protocol resolution requests will be serviced in full compliance with all ENS protocol standards and best practices. However, this ENSNode instance did not activate the *protocol-acceleration* plugin and therefore ENS resolution requests will be serviced accurately but without acceleration. * ENS Subgraph API ✅ *Full Subgraph Compatibility.* This ENSNode instance has a fully backwards compatible ENS Subgraph GraphQL API at both the API-level and the data-level. We have exhaustively tested and verified that every data record returned by this ENSNode instance's ENS Subgraph API is byte-for-byte identical with The Graph's ENS Subgraph API. ENSv2 launches soon. Subgraph API soon unreliable. ENSv2 launches soon! As soon as ENSv2 launches on the `sepolia` ENS namespace, the legacy ENS Subgraph will no longer return reliable data. Transition your apps now to the new [ENS Omnigraph API](/docs/integrate/omnigraph) to be ENSv2 ready. | ## Start building [Section titled “Start building”](#start-building) Pick an instance above, then jump into the Quickstart for integration options to start making use of it. [ENSNode Quickstart ](/docs/integrate)Wire enskit, enssdk, or the ENS Omnigraph API up to a hosted instance in a few minutes. # ENSv2 Quickstart > Get started building full-stack ENSv2 apps. ## What is ENSv2? [Section titled “What is ENSv2?”](#what-is-ensv2) [ENSv2](https://ens.domains/ensv2) is the next generation of the [Ethereum Name Service](https://ens.domains) — a protocol upgrade that fundamentally changes how the ENS protocol works. Prepare for ENSv2 The ENSv2 upgrade to the ENS protocol is coming **Summer 2026**! Your app, regardless of how it interacts with names, needs to be updated to avoid being left behind.\ [Learn more about ENSv2 Readiness](/docs/integrate/why-ensnode/ensv2-readiness) ## What is the ENS Omnigraph? [Section titled “What is the ENS Omnigraph?”](#what-is-the-ens-omnigraph) ENSNode fully supports ENSv2 via the [ENS Omnigraph API](/docs/integrate/omnigraph), the world’s first and only *unified* API over the full state of **both ENSv1 and ENSv2**. When ENSv2 launches in **Summer 2026**, ENSv1 continues to exist, and apps *must* be updated to use the new protocol version. ENSNode takes the guesswork out of building on ENS, whether you need to resolve up-to-date records, search all Domains, or see which Domains a user owns (and much, much more). ![ENS Omnigraph diagram](/ens-omnigraph-diagram.png) ENS Omnigraph supports both ENSv1 and ENSv2 **concurrently** within the **same unified data model**. This means you can integrate today (before ENSv2 launches) and continue with full ENSv2 support when it goes live, with zero downtime! ## ENSNode’s Integration Options [Section titled “ENSNode’s Integration Options”](#ensnodes-integration-options) ENSNode supports a full range of different integration options across the stack, whether you’re using React, any JavaScript runtime, raw GraphQL, or looking to go deep and build a fully custom service using indexed ENS data. [Catalog of Integration Options ](/docs/integrate/integration-options) Here’s a summary of some popular integration strategies: ### 1. enssdk + Omnigraph [Section titled “1. enssdk + Omnigraph”](#1-enssdk--omnigraph) With `enssdk`, leverage ENSNode and the Omnigraph from any JavaScript runtime to power your frontend or backend apps. `enssdk` comes with built-in type-safety and editor autocomplete for Omnigraph queries. example.ts ```ts // create and extend an EnsNodeClient with Omnigraph API support const client = createEnsNodeClient({ url: process.env.ENSNODE_URL! }).extend(omnigraph); // this is fully typechecked and supports editor autocomplete! const HelloWorldQuery = graphql(` query HelloWorld { domain(by: { name: "eth" }) { canonical { name { beautified } } owner { address } } } `); // `result` is fully typed! const result = await client.omnigraph.query({ query: HelloWorldQuery }); ``` BeautifiedName `beautified` is the display-ready form of the Canonical Name — its normalized labels rendered per [ENSIP-15](https://docs.ens.domains/ensip/15) (e.g. `♾.eth` → `♾️.eth`) — so you can render it directly with no normalization or emoji logic of your own. It’s **display-only**: use `interpreted` (or the Domain `id`) as a lookup key or navigation target, never `beautified`. See [Beautified Name](/docs/reference/terminology#beautified-name). [Full enssdk Integration Documentation ](/docs/integrate/integration-options/enssdk) [enssdk-example app ](https://github.com/namehash/ensnode/tree/main/examples/enssdk-example)Check out our enssdk-example for a full example app. [Interactive enssdk example ⚡ ](/docs/integrate/integration-options/enssdk/example)Edit and run the enssdk-example script in your browser. ### 2. enskit + Omnigraph [Section titled “2. enskit + Omnigraph”](#2-enskit--omnigraph) With `enskit`, leverage ENSNode and the Omnigraph to power your React components using `useOmnigraphQuery`. `enskit` comes with built-in type-safety, Omnigraph-specific cache directives, easy infinite pagination, and much much more. example.tsx ```tsx // this query is fully typechecked and supports editor autocomplete! const DomainByNameQuery = graphql(` query DomainByNameQuery($name: InterpretedName!) { domain(by: { name: $name }) { canonical { name { beautified } } owner { address } } } `); export function DomainCard({ name }: { name: InterpretedName }) { // `result` is fully typed! const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name } }); const { data, fetching, error } = result; if (fetching) return

Loading...

; if (error) return

Error: {error.message}

; if (!data?.domain) return

No domain found for '{name}'.

; const { domain } = data; return (

Name: {domain.canonical?.name.beautified ?? "Unnamed Domain"}

Owner: {domain.owner?.address ?? "Unowned"}

); } ``` [Full enskit Integration Documentation ](/docs/integrate/integration-options/enskit) [enskit-react-example app ](https://github.com/namehash/ensnode/tree/main/examples/enskit-react-example)Check out our enskit-react-example for a full example app. [Interactive enskit example ⚡ ](/docs/integrate/integration-options/enskit/example)Edit and run the enskit-react-example app in your browser with a live preview. ### 3. ENS Omnigraph GraphQL API [Section titled “3. ENS Omnigraph GraphQL API”](#3-ens-omnigraph-graphql-api) The ENS Omnigraph API is a GraphQL API following the Relay specification, so you get built-in support for efficient infinite pagination and idiomatic access to all of the ENS protocol within a *unified* ENSv1 + ENSv2 datamodel. omnigraphcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+AccountDomains%28%0A++%24address%3A+Address%21%0A%29+%7B%0A++account%28by%3A+%7B+address%3A+%24address+%7D%29+%7B%0A++++domains+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++label+%7B+interpreted+%7D%0A++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22address%22%3A+%220x801d2e48d378f161dba7ad7ad002ad557714c191%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } ``` Variables ```json { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } ``` Output ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } }", "variables": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } EOF ``` Response ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Full ENS Omnigraph GraphQL API Documentation ](/docs/integrate/integration-options/omnigraph-graphql-api) [omnigraph-graphql-example app ](https://github.com/namehash/ensnode/tree/main/examples/omnigraph-graphql-example)Check out our omnigraph-graphql-example for a full example app. ### 4. Further Integration Options [Section titled “4. Further Integration Options”](#4-further-integration-options) Beyond [`enssdk`](/docs/integrate/integration-options/enssdk), [`enskit`](/docs/integrate/integration-options/enskit), and the [Omnigraph GraphQL API](/docs/integrate/integration-options/omnigraph-graphql-api), ENSNode exposes a deeper set of integration surfaces for advanced use cases: * **[ENSDb (SQL)](/docs/integrate/integration-options/ensdb)** — query the indexed ENSv1 and ENSv2 datasets directly via SQL for custom analytics or your own service layer, from any language with a Postgres driver. * **[enscli (CLI)](/docs/integrate/integration-options/enscli)** — resolve names, look up records, and run ad-hoc Omnigraph queries from the terminal — built for humans and AI agents alike. * **[ensskills (AI agents)](/docs/integrate/integration-options/ensskills)** — a curated set of skills that gives AI coding agents a well-defined contract for working with ENS. * **[ensdb-cli (ENSDb Snapshots)](/docs/integrate/integration-options/ensdb-cli)** — bootstrap a fresh ENSDb in minutes from portable, versioned snapshots instead of waiting days on a full historical backfill. * **[ENSEngine (Webhooks)](/docs/integrate/integration-options/ensengine)** — subscribe to ENS-aware webhooks driven by changes in ENSDb, so your apps can stop polling and start reacting. [See all Integration Options ](/docs/integrate/integration-options)Includes more advanced integration options not introduced in this quickstart guide # AI / LLM Tooling > AI and LLM tooling for building on ENSv2. AI and LLM tooling is a key priority for ENSNode, and we’re building the infrastructure to make ENS a first-class citizen in the world of AI coding assistants and chat-based interfaces. Next on the roadmap for this space are [`enscli`](/docs/integrate/integration-options/enscli) and [`ensskills`](/docs/integrate/integration-options/ensskills) — the foundation for how developers and their AI agents will reach for ENS. [enscli ](/docs/integrate/integration-options/enscli)An agent- and human-friendly CLI for the ENS Omnigraph API. [ensskills ](/docs/integrate/integration-options/ensskills)Skill bundles that give AI agents an opinionated contract for ENS. Check back soon for more detail on our AI / LLM tooling roadmap. # ENS Subgraph > The ENS Subgraph quietly became critical infrastructure for ENS and the broader web3 ecosystem — and it cannot carry that ecosystem into ENSv2. Here's who depends on it, and what comes next. 🚨 The ENS Subgraph is not ENSv2 compatible The ENS Subgraph **fundamentally fails as a source of ENS data as soon as ENSv2 launches.** [Keep ENS apps working 🚨 ](/docs/integrate/why-ensnode/keep-ens-working)See apps currently set to break when ENSv2 launches unless they upgrade to the new ENS Omnigraph API. [Key Limitations 🚨 ](/docs/integrate/ens-subgraph/key-limitations)See the full list of Key Subgraph Limitations and how the Omnigraph addresses them. Start here instead: the ENS Omnigraph API The [ENS Omnigraph API](/docs/integrate/omnigraph) is the ENSv2-ready replacement: one unified, typed GraphQL API over **both ENSv1 and ENSv2**, multichain by default, with protocol-correct [ENS Protocol Acceleration](/docs/integrate/omnigraph/protocol-acceleration) resolution built in. [ENS Omnigraph API ](/docs/integrate/omnigraph)ENSv2-ready unified GraphQL API over both ENSv1 and ENSv2 — start here for new integrations. [ENS Unigraph SQL ](/docs/integrate/unigraph)Direct SQL access to the unified, multichain ENS data model. ## ENSNode Subgraph compatibility [Section titled “ENSNode Subgraph compatibility”](#ensnode-subgraph-compatibility) ENSNode maintains a verified Subgraph-compatible API for migrating existing integrations from The Graph, but it is **not** the path forward for ENSv2. [ENSNode’s Backwards Compatibility with the ENS Subgraph ](/docs/integrate/ens-subgraph/backwards-compatibility#ensnodes-backwards-compatibility-with-the-ens-subgraph)A verified Subgraph-compatible GraphQL endpoint for migrating existing integrations to ENSNode. # Backwards Compatibility > How ENSNode provides a verified Subgraph-compatible API for migrating existing integrations, the ecosystem it grew out of, and how to query it correctly. This page is background on the ENS Subgraph and the ecosystem ENSNode grew out of. ENSNode maintains a verified Subgraph-compatible API for migrating existing integrations, but it is **not** the path forward for ENSv2 — see [Key Limitations](/docs/integrate/ens-subgraph/key-limitations) and the [ENS Omnigraph API](/docs/integrate/omnigraph) for what is. ## The Graph & Graph Node [Section titled “The Graph & Graph Node”](#the-graph--graph-node) [The Graph](https://thegraph.com/) leads development of [Graph Node](https://thegraph.com/docs/en/indexing/tooling/graph-node/), an [open source software application](https://github.com/graphprotocol/graph-node) for indexing blockchain data. ## Subgraphs [Section titled “Subgraphs”](#subgraphs) Each Graph Node server can run any number of “subgraphs”. Each subgraph is essentially a plugin describing: 1. A strategy for how the Graph Node should index blockchain data. 2. A schema for a GraphQL API providing access to the indexed data. ## ENS Subgraph [Section titled “ENS Subgraph”](#ens-subgraph) [ENS Labs](https://www.enslabs.org/) has led development of the [ENS Subgraph](https://github.com/ensdomains/ens-subgraph). In the past, this was the “official” strategy for indexing ENS data. Additional background info is available in [official ENS docs](https://docs.ens.domains/web/subgraph). ## Graph Network [Section titled “Graph Network”](#graph-network) Operating your own Graph Node server instance can be complex, expensive, and time consuming. An alternative is to use The Graph’s semi-decentralized network of indexers operating Graph Node instances. This network provides access to a [semi-decentralized ENS Subgraph](https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH?view=Query\&chain=arbitrum-one). Developers are welcome to use this rate limited API endpoint above for testing, but are highly encouraged to sign up for an account with The Graph to get their own (paid) API key. ## ENSNode’s Backwards Compatibility with the ENS Subgraph [Section titled “ENSNode’s Backwards Compatibility with the ENS Subgraph”](#ensnodes-backwards-compatibility-with-the-ens-subgraph) To support the ENS ecosystem’s transition away from legacy ENS indexing strategies to ENSNode, ENSNode provides a verified backwards compatible ENS Subgraph GraphQL endpoint. This therefore also provides backwards compatibility with `ensjs`. 1. For those that wish to host their own ENS indexer, it is faster and easier to deploy ENSNode than to run an ENS Subgraph instance. 2. For those building an app that simply want to query the legacy ENS Subgraph API in the easiest way possible, we make this freely available through [our hosted ENSNode instances](/docs/hosted-instances). ## Self-hosted ENSNode instance configuration for ENS Subgraph compatibility [Section titled “Self-hosted ENSNode instance configuration for ENS Subgraph compatibility”](#self-hosted-ensnode-instance-configuration-for-ens-subgraph-compatibility) To enable full ENS Subgraph compatibility on a self-hosted ENSNode instance, configure ENSIndexer with `SUBGRAPH_COMPAT=true`. This single flag: 1. **Applies Subgraph Indexing Behavior**: Uses Subgraph Interpreted Labels and Names, allowing unnormalized labels to be returned as they appear in the original ENS Subgraph 2. **Sets Default Plugins & Label Set**: Defaults to `PLUGINS=subgraph`, `LABEL_SET_ID=subgraph` and `LABEL_SET_VERSION=0` to match subgraph indexing logic and label healing behavior When `SUBGRAPH_COMPAT=false` (default), ENSIndexer operates in enhanced mode with: * **Enhanced Indexing Behavior**: Uses Interpreted Labels and Names with improved security by encoding unnormalized labels as labelhashes * **Expanded Plugin Support**: Defaults to `PLUGINS=subgraph,basenames,lineanames,threedns,protocol-acceleration,registrars,tokenscope` for multichain ENS indexing * **Reverse Address Healing**: Attempts to heal subnames of addr.reverse for enhanced reverse resolution support ## Compatibility Tooling [Section titled “Compatibility Tooling”](#compatibility-tooling) ENSNode has developed tooling to verify subgraph compatibility and ease migration from the ENS Subgraph. The tools in the [ens-subgraph-transition-tools](https://github.com/namehash/ens-subgraph-transition-tools) repository help users verify ENSNode’s subgraph-compatibility. 1. `snapshot-eq` — verify subgraph-equivalent data via snapshots at specific blockheights 2. 🚧 `proxy-eq` — verify live query compatibility & easing migrations from the Subgraph to ENSNode by identifying any response discrepancies while using an app in real-time See the [ens-subgraph-transition-tools](https://github.com/namehash/ens-subgraph-transition-tools) README for additional context and usage instructions. [ens-subgraph-transition-tools ](https://github.com/namehash/ens-subgraph-transition-tools)Tools for verifying ENSNode's subgraph compatibility ## Querying the Subgraph-Compatible API correctly [Section titled “Querying the Subgraph-Compatible API correctly”](#querying-the-subgraph-compatible-api-correctly) The care required to query the ENS Subgraph correctly is itself one of its [Key Limitations](/docs/integrate/ens-subgraph/key-limitations) — the guidance below exists because the Subgraph data model exposes raw protocol internals that every client has to handle carefully. If you are migrating an existing integration onto ENSNode’s Subgraph-compatible API, the following patterns apply. Terminology It may be helpful to refer to the [Terminology](/docs/reference/terminology) guide when reading this section. ### Use the node as the stable identifier [Section titled “Use the node as the stable identifier”](#use-the-node-as-the-stable-identifier) When querying for specific names or sets of names, it’s crucial to understand that the representation of labels (both known and unknown) should not generally be assumed to be immutable identifiers. Here’s why: **Label Mutability** * ENSNode indexes all onchain events where a subname is created in the ENS Registry. When these events are indexed, the labelhash of the subname is always known, however sometimes the label of the subname is unknown (strictly from indexed onchain data). When this happens ENSNode attempts to look up the label for the labelhash through an attached ENSRainbow server. If this lookup succeeds, ENSNode will represent the subname using its true label. If this lookup fails, some label to represent the subname is still required. Therefore, ENSNode will represent the “unknown label” using its labelhash in the format `[labelhash]`. * Changes in the set of healable labels maintained by an ENSRainbow instance can modify the resulting indexed state in attached ENSNode instances. For example, if at “time 1” ENSRainbow does not have knowledge to heal label X, but at “time 2” it does (from the perspective of an ENSNode client) a label represented as “unknown” at “time 1” could transition to become known at “time 2”. Each ENSNode instance should ensure it is attached to an ENSRainbow instance that only grows its set of healable labels across time, such that from the perspective of an ENSNode client a “known label” should never transition back to its “unknown” representation. However, if an ENSNode instance is improperly operated, such a situation could occur. **ENS Normalization Standard Changes** The [ENSIP-15: ENS Name Normalization Standard](https://docs.ens.domains/ensip/15) may change across time such that the set of normalizable names grows (thankfully it should never shrink). For example, consider a new Unicode release that standardizes new emoji. The ENS Normalize standard may subsequently change to expand support for those new emoji. Therefore, always use the node of a name (calculated by the namehash of the name) as the stable identifier when querying. The node of a name is immutable across time and works for all names, even if they are unknown, unnormalized, or subgraph-unindexable. #### Pattern 1: Names from User Input / Offchain Data [Section titled “Pattern 1: Names from User Input / Offchain Data”](#pattern-1-names-from-user-input--offchain-data) When querying for names that originate from user input (e.g., search fields, user-entered addresses) or offchain data (e.g. traditional data sources), always apply the following procedure within your app: 1. Normalize the name according to ENSIP-15. 2. Calculate the `node` for the normalized name using the `namehash` function. 3. Query the `id` field of domains using the `node` calculated in the previous step, rather than the name itself (for backwards compatibility with the ENS Subgraph, the field for the `node` of the name is actually the `id` field). Example: First, let’s prepare the name for querying by normalizing it and calculating its node: prep-example.ts ```typescript import { namehashInterpretedName, normalizeName, asInterpretedName } from "enssdk"; // 1. Normalize the user input according to ENSIP-15 const userInput = "Vitalik.eth"; const normalizedName = normalizeName(userInput); // 2. Calculate the node from the normalized name const node = namehashInterpretedName(asInterpretedName(normalizedName)); ``` Now use this node to query the domain id: query.graphql ```graphql { domain(id: "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835") { id name labelName labelhash createdAt } } ``` The query will return the domain information: response.json ```json { "data": { "domain": { "createdAt": "1497775154", "id": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "labelName": "vitalik", "labelhash": "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", "name": "vitalik.eth" } } } ``` #### Pattern 2: Names from Onchain Data [Section titled “Pattern 2: Names from Onchain Data”](#pattern-2-names-from-onchain-data) When querying for name values sourced directly from onchain data (e.g., ENS NFTs, contract events), you must: 1. Skip any normalization step — the name value passed to namehash must be exactly as it appears onchain, even if unnormalized. 2. Calculate the node by taking the namehash of the onchain name (without any normalization). Be warned however that unnormalized labels may contain ”.” characters within the label value which can confuse namehash if special precautions are not taken. 3. Query the domain id using the node of the name. This pattern is crucial when dealing with unnormalized names that exist onchain. For example, if while examining onchain data you see a registration for “EXAMPLE.eth” (note the uppercase unnormalized characters), attempting to normalize this name in the process of querying for additional information about it would result in looking up details for a different node in the ENS Registry (in this case the node for “example.eth” rather than “EXAMPLE.eth”). The query structure in Pattern 2 remains the same as Pattern 1, except the normalization step is skipped to ensure the node that you query data about is the intended node. ### Never normalize labels returned by ENSNode [Section titled “Never normalize labels returned by ENSNode”](#never-normalize-labels-returned-by-ensnode) Configuration Note ENSNode’s handling of unnormalized labels is controlled by the `SUBGRAPH_COMPAT` configuration option: * `SUBGRAPH_COMPAT=true` allows unnormalized labels to be returned as Subgraph Interpreted Labels (required for full ENS Subgraph compatibility) * `SUBGRAPH_COMPAT=false` (default) encodes unnormalized labels as Interpreted Labels, improving security When `SUBGRAPH_COMPAT=true`, ENSNode may return unnormalized labels as [Subgraph Interpreted Labels](/docs/reference/terminology#subgraph-interpreted-label) associated with indexed names. **ENSNode clients should never attempt to normalize labels returned by ENSNode.** This is because when ENSNode returns an unnormalized label, that label is associated with a specific node that has been indexed. Normalizing an unnormalized label in this context would represent a different node. An ENSNode client is permitted to validate that all labels returned by ENSNode are in normalized form, and to reject any names with unnormalized labels from further processing. However, the key principle is that an ENSNode client should never normalize returned labels, as normalization transforms the label and therefore also the node associated with the name the label is contained within. When `SUBGRAPH_COMPAT=false` (default), ENSNode uses [Interpreted Labels](/docs/reference/terminology#interpreted-label) instead of [Subgraph Interpreted Labels](/docs/reference/terminology#subgraph-interpreted-label), which helps avoid edge cases related to null bytes, full-stop characters (periods), or exotic unicode characters. When names are returned from any of the ENSNode APIs, including the Subgraph-compatible GraphQL API, names will be [Interpreted Names](/docs/reference/terminology#interpreted-name). ### Calculating the node for names that contain Encoded LabelHashes [Section titled “Calculating the node for names that contain Encoded LabelHashes”](#calculating-the-node-for-names-that-contain-encoded-labelhashes) According to [ENSIP-1](https://docs.ens.domains/ensip/1#namehash-algorithm), the namehash algorithm makes no special consideration for Encoded LabelHashes, and therefore interprets Encoded-LabelHash-looking strings as Literal Label values. Due to this behavior, we recommend using an “Encoded-LabelHash-aware” namehash algorithm implementation such as the [viem namehash implementation](https://github.com/wevm/viem/blob/fe558fdef7e2e9cd5f3f57d8bdeae0c7ff67a1b0/src/utils/ens/namehash.ts#L36-L51). SUBGRAPH\_COMPAT The following is relevant when `SUBGRAPH_COMPAT=false` (default) and ENSNode is using Interpreted Labels for handling unknown labels. When an **Unknown** or `subgraph-unindexable` label is encountered, ENSNode represents it as an **Encoded LabelHash** in the format `[{labelhash}]`, where `{labelhash}` is the labelhash of the label in question. This representation creates an interesting edge case that must be handled carefully: Consider an unnormalized label that literally looks like `[24695ee963d29f0f52edfdea1e830d2fcfc9052d5ba70b194bddd0afbbc89765]`. Because this label contains square brackets (`subgraph-unindexable` characters), it will be represented as the unknown label: `[80968d00b78a91f47b233eaa213576293d16dadcbbdceb257bca94b08451ba7f]` Therefore, this represents the `subgraph-unindexable` label as an **Encoded LabelHash**, encoding the labelhash of the original unnormalized label (including its square brackets) in square brackets. This demonstrates why square brackets are considered `subgraph-unindexable` — they create ambiguity between literal labels and the representation of **Encoded LabelHashes**. When ENSNode encounters a `subgraph-unindexable` label, it will represent it as an **Encoded LabelHash** even if the actual label data is available. For more detailed information about `subgraph-unindexable` labels and their handling, please refer to the [ENSNode SDK implementation](https://github.com/namehash/ensnode/blob/main/apps/ensindexer/src/lib/is-label-subgraph-indexable.ts). ## Unplanned Features [Section titled “Unplanned Features”](#unplanned-features) The following features of the subgraph GraphQL API are explicitly unsupported and are not planned. * [1-level-nested Entity `_orderBy` param](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#nested-entity-sorting-example) * [time travel queries](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#time-travel-queries-example) * [\_change\_block filtering](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#block-based-filtering-example) * [fulltext search queries](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#full-text-search-example) # ENS Subgraph Examples > Examples of integrating ENSNode's Subgraph-compatible API with popular ENS libraries. Examples of integrating ENSNode’s Subgraph-compatible GraphQL API with popular ENS libraries. [With ENSjs ](/docs/integrate/ens-subgraph/examples/with-ensjs)Point @ensdomains/ensjs at an ENSNode Subgraph-compatible endpoint. [With Viem ](/docs/integrate/ens-subgraph/examples/with-viem)Configure a viem Chain's subgraph URL to use ENSNode. # Using ENSNode with ENSjs To use ENSNode with `@ensdomains/ensjs`, follow the [ENSjs documentation for custom subgraph URIs](https://github.com/ensdomains/ensjs/blob/17ab314/docs/basics/custom-subgraph-uris.md), replacing the subgraph URI with your ENSNode’s subgraph-compatible api endpoint. No backend required You don't need to run your own ENSNode to follow this guide — the steps below default to a NameHash-hosted instance. Browse the available deployments below. Version compatibility with hosted instances [Our hosted ENSNode instances](/docs/hosted-instances) currently run ENSNode `1.15.1`. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the `gql.tada` TypeScript plugin to type your queries, so pin an **exact** version (no `^` or `~`) of `enssdk@1.15.1` (and `enskit@1.15.1` when using React) to keep your generated types matched to the deployed schema. Use these exact install commands: ``` npm install enssdk@1.15.1 # or, for React apps: npm install enskit@1.15.1 enssdk@1.15.1 ``` [Hosted ENSNode Instances ](/docs/hosted-instances) example.ts ```ts import { http, createClient } from "viem"; import { mainnet } from "viem/chains"; import { addEnsContracts } from "@ensdomains/ensjs"; import { getNamesForAddress } from "@ensdomains/ensjs/subgraph"; const mainnetWithEns = addEnsContracts(mainnet); const chain = { ...mainnetWithEns, subgraphs: { ens: { // use the NameHash-hosted 'alpha' instance subgraph-compatible responses with (mainnet, Base, and Linea) names url: "https://api.alpha.ensnode.io/subgraph", // or use your own local instance // url: 'http://localhost:42069/subgraph', }, }, }; const client = createClient({ chain, transport: http(), }); const names = await getNamesForAddress(client, { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth }); ``` ## Well-Known Subgraph Queries [Section titled “Well-Known Subgraph Queries”](#well-known-subgraph-queries) Once ENSjs is pointed at an ENSNode Subgraph-compatible endpoint, its Subgraph functions work unchanged. ENSNode’s Subgraph-compatible GraphQL API provides full compatibility with these use cases (and all other possible queries, with the only exception of the [unplanned features](/docs/integrate/ens-subgraph/backwards-compatibility#unplanned-features)). The functions below are the patterns we see most often in the wild. Contributions If you’d like to highlight additional query patterns of the ENS Subgraph GraphQL, please [contribute to this documentation](https://github.com/namehash/ensnode/issues). ### ENSjs Subgraph functions [Section titled “ENSjs Subgraph functions”](#ensjs-subgraph-functions) * [`getDecodedName`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getDecodedName.ts) — gets the full name for a name with unknown labels from the subgraph (heals encoded labels, splits the name into labels, finds domains by id, and queries the domain by namehash). * [`getNameHistory`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getNameHistory.ts) — retrieves all events associated with a name. * [`getNamesForAddress`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts) — gets all names related to an address via registrant, owner, wrappedOwner, and resolvedAddress; supports `searchString`, filtering (by expiry, reverse records, empty domains), ordering (by expiry date, name, labelName, createdAt), and pagination. * [`getSubgraphRecords`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts) — gets the records for a name from the subgraph; allows querying by a specific resolver id. * [`getSubgraphRegistrant`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts) — gets the name registrant from the subgraph (`.eth` second-level domains only). * [`getSubnames`](https://github.com/ensdomains/ensjs/blob/17ab314/packages/ensjs/src/functions/subgraph/getSubnames.ts) — gets the subnames for a name; supports `searchString`, filtering (by expiry, empty domains), ordering, and pagination. ### ENSv1 Manager App queries [Section titled “ENSv1 Manager App queries”](#ensv1-manager-app-queries) These query patterns come from the ENSv1 Manager App (`ens-app-v3`). They may not go through ENSjs directly, but they’re useful references for the kinds of Subgraph queries real apps depend on: * [`useResolverExists`](https://github.com/ensdomains/ens-app-v3/blob/328692ae832618f8143916c143b7e4cb9e520811/src/hooks/useResolverExists.ts#L27) — checks if a resolver exists. * [`useRegistrationData`](https://github.com/ensdomains/ens-app-v3/blob/328692ae832618f8143916c143b7e4cb9e520811/src/hooks/useRegistrationData.ts#L31) — gets registration by id and `nameRegistered` events. ## ENSjs Documentation [Section titled “ENSjs Documentation”](#ensjs-documentation) Refer to the ENSjs documentation for further usage. [ENSjs Documentation ](https://github.com/ensdomains/ensjs/) # Using ENSNode with `viem/chain` Some libraries (for example, [`ENSjs`](/docs/integrate/subgraph/examples/with-ensjs)) use a `viem/chain` object to identify the ENS Subgraph url. If you’re integrating with a library that expects a url in the `subgraph` key for your chain, you can update the `Chain` spec to use ENSNode like so: example.ts ```ts import { mainnet } from "viem/chains"; const mainnetWithENSNode = { ...mainnet, subgraphs: { ens: { url: "https://api.alpha.ensnode.io/subgraph" } }, }; ``` # Key Limitations > The ENS Subgraph was never designed to be a complete view of ENS. These are the limitations that break apps today — and that get worse the moment ENSv2 launches. The ENS Subgraph was never designed to be a complete view of ENS. It indexes a single chain’s events and exposes them largely as-is — leaving every app that builds on it to work around a long list of gaps. Many apps don’t work around them correctly, and the result is shipping real bugs in some of the most-used software in the ecosystem. Each of the following limitations is a place where the burden of getting ENS right is pushed onto app developers. There is a path forward The [ENS Omnigraph API](/docs/integrate/omnigraph) is built to close every gap on this page: one unified, typed API over ENSv1 and ENSv2, multichain by default, with [ENS Protocol Acceleration](/docs/integrate/omnigraph/protocol-acceleration) for resolution. ## Two systems, neither complete [Section titled “Two systems, neither complete”](#two-systems-neither-complete) DIY ENS Integrations are Hard Historically, full access to ENS data required two separate data-fetching strategies working in parallel: 1. **ENS resolution** — RPC calls with CCIP-Read support for offchain data (e.g. via `viem` or `wagmi`) to perform forward or reverse resolution. 2. **Indexed ENS data** — the ENS Subgraph, for discovering names owned by an address and all other ENS state outside of resolution. Neither system alone is complete. Resolution gives you resolver records but no access to the rest of ENS state, and it is painfully “close to the metal.” The Subgraph gives you queryable indexed data but cannot resolve names and carries the limitations below. Apps have had to live with the split, its limitations, and its downstream complexity — and **with ENSv2, the complexity of ENS’s onchain state meaningfully increases.** One unified API The [Omnigraph API](/docs/integrate/omnigraph) bundles access to ENS onchain resources (like Domains and Registrations) with Protocol Accelerated Resolution; a single unified API for all of your ENS needs. ## No ENS resolution — and apps that fake it are broken [Section titled “No ENS resolution — and apps that fake it are broken”](#no-ens-resolution--and-apps-that-fake-it-are-broken) Faking resolution ships real bugs The Subgraph does not perform ENS resolution. It has no concept of the ENS Universal Resolver, CCIP-Read, or ENSIP-10 wildcard resolution. Despite this, developers routinely reach for the Subgraph to resolve names — because it’s the indexed data source already in front of them — which produces incorrect results because it **doesn’t follow the ENS Forward Resolution protocol**. This isn’t hypothetical. It happens in widely-used software: * **[Stamp](https://github.com/snapshot-labs/stamp) by [Snapshot Labs](https://snapshot.box/)** powers the avatars across [Snapshot](https://snapshot.box/), among the most-used DAO infrastructure in the ecosystem. It resolves addresses [directly against the Subgraph](https://github.com/snapshot-labs/stamp/blob/a6d341a65159a1e76d1dc889156c4676e54eea14/src/addressResolvers/ens.ts#L87-L94). * **[ethVM](https://www.ethvm.com/) by [MyEtherWallet](https://www.myetherwallet.com/)** resolves names [via a generated Subgraph query](https://github.com/EthVM/EthVM/blob/2ad42adc544074ed8cd6c2cba6a7fa0ff4ffc48b/v2/src/core/composables/ResolveName/ensResolveName.generated.ts#L21-L30). * **[Ethereum Comments Protocol](https://github.com/ecp-eth/comments-monorepo)** resolves names [against an indexer query](https://github.com/ecp-eth/comments-monorepo/blob/c301d76fa56b6b807f135c98273ac9eb5ddebe95/apps/indexer/src/services/resolvers/ens-by-query-resolver.ts) rather than the resolution protocol. Each of these approaches produces results that diverge from what the ENS protocol *actually* says. Protocol-correct resolution The [Omnigraph API](/docs/integrate/omnigraph) performs protocol-correct resolution for you — including the CCIP-Read offchain lookups — so the correct result is the default. ## It forces you to stitch together multiple APIs [Section titled “It forces you to stitch together multiple APIs”](#it-forces-you-to-stitch-together-multiple-apis) You become the integration glue Because the Subgraph can’t resolve names, any app that needs both indexed data *and* resolution has to run two integrations side by side: the Subgraph for indexed state, and a resolution library for records. You reconcile their differences, their failure modes, and their data models yourself. Developers shouldn’t have to care about these implementation details of the ENS protocol — getting “all the ENS data I need,” whether ENSv1, ENSv2, indexed, or resolved, should come from a single unified API. Write your query once The [Omnigraph API](/docs/integrate/omnigraph) provides a unified datamodel across ENSv1 and ENSv2: write your query once and your platform automatically understands both protocol versions without any extra work on your end. ## ENSv1 only — blind to ENSv2 [Section titled “ENSv1 only — blind to ENSv2”](#ensv1-only--blind-to-ensv2) Stale the moment ENSv2 launches The Subgraph’s data model has no concept of ENSv2. The moment ENSv2 launches (Summer 2026), apps still reading the Subgraph are looking at a stale, partial view of ENS — missing the new Namegraph data model entirely. There is no upgrade path: the schema was never designed for it. The Omnigraph transparently upgrades to ENSv2 The [Omnigraph API](/docs/integrate/omnigraph) provides a unified datamodel across ENSv1 and ENSv2: your app works before, during, and after the ENSv2 release without any changes. ## Single-chain only — misses most names [Section titled “Single-chain only — misses most names”](#single-chain-only--misses-most-names) Most ENS names are invisible The Subgraph indexes a single chain, so it never sees Basenames (`.base.eth`), Lineanames (`.linea.eth`), or 3DNS names (`.box`). A large and growing majority of ENS names already live off of mainnet and are simply invisible to it. Every chain in one schema The [Omnigraph API](/docs/integrate/omnigraph) indexes the full suite of onchain ENS names, including Basenames (`.base.eth`), Lineanames (`.linea.eth`) and 3DNS names (`.box`). ## No multichain primary names (ENSIP-19) [Section titled “No multichain primary names (ENSIP-19)”](#no-multichain-primary-names-ensip-19) No cross-chain primary names Beyond being single-chain, the Subgraph has no concept of [ENSIP-19](https://docs.ens.domains/ensip/19) multichain primary names. Even an app willing to query several per-chain Subgraphs cannot reconstruct a name’s primary-name configuration across chains from Subgraph data. Full ENSIP-19 support The [Omnigraph API](/docs/integrate/omnigraph) fully implements [ENSIP-19](https://docs.ens.domains/ensip/19) and accurately returns an account’s multichain primary names in milliseconds. ## No concept of the effective resolver (ENSIP-10) [Section titled “No concept of the effective resolver (ENSIP-10)”](#no-concept-of-the-effective-resolver-ensip-10) Wrong resolver, wrong records The Subgraph records the resolver *assigned* to a domain but has no understanding of [ENSIP-10](https://docs.ens.domains/ensip/10) wildcard resolution, and therefore no concept of the *effective* resolver — the resolver that actually answers for a name via a parent’s wildcard resolver. Apps that read the assigned resolver from the Subgraph and assume it’s the effective one get the wrong answer for any name that relies on wildcard resolution. Assigned and effective resolvers The [Omnigraph API](/docs/integrate/omnigraph) supports both a Domain’s *assigned* resolver as well as its [ENSIP-10](https://docs.ens.domains/ensip/10) *effective* resolver, so you can write applications that understand the difference, whether you’re resolving up-to-date records or letting users edit records onchain. ## Unnormalized names (ENSIP-15 not applied) [Section titled “Unnormalized names (ENSIP-15 not applied)”](#unnormalized-names-ensip-15-not-applied) Normalization is left to you The Subgraph does not apply [ENSIP-15](https://docs.ens.domains/ensip/15) name normalization. It returns unnormalized labels and names, putting the burden on every consuming app to implement normalization correctly and consistently — and the ones that don’t display or match names incorrectly. ENSNode [replaces unnormalized labels](/docs/integrate/ens-subgraph/backwards-compatibility#never-normalize-labels-returned-by-ensnode) for you, so you automatically enjoy safer handling. Interpreted Names by default The [Omnigraph API](/docs/integrate/omnigraph) stores and operates over [Interpreted Names](/docs/reference/terminology#interpreted-name), a consistent name format that ensures that names are composed of labels that are either normalized or [Encoded LabelHashes](/docs/reference/terminology#encoded-labelhash). This means consistent handling and fewer application bugs. ## Unstable domain identification [Section titled “Unstable domain identification”](#unstable-domain-identification) Identifiers shift underneath you Labels in the Subgraph are not stable identifiers. A label that is “unknown” today can become “known” later (as label-healing coverage grows), and the set of normalizable names can change over time. Apps that key on label or name strings will see identifiers shift underneath them. The only stable identifier is the `node` (the namehash of the name) — but the Subgraph schema surfaces it as the `id` field, and getting this right requires careful, documented handling. See [Use the node as the stable identifier](/docs/integrate/ens-subgraph/backwards-compatibility#use-the-node-as-the-stable-identifier). Stable IDs that never move The [Omnigraph API](/docs/integrate/omnigraph) provides stable identification via a Domain’s `id`, a multichain-aware globally unique identifier that works for both ENSv1 and ENSv2 Domains. Domains are also addressable by [InterpretedName](/docs/reference/terminology#interpreted-name), and a Domain’s Canonical Name (`Domain.canonical.name`) is always maximally healed at request time, thanks to [ENSRainbow](/docs/services/ensrainbow). ## Effective ownership is hard to determine [Section titled “Effective ownership is hard to determine”](#effective-ownership-is-hard-to-determine) Effective ownership is ambiguous The Subgraph schema spreads ownership across multiple fields — `owner`, `registrant`, `wrappedOwner` — reflecting raw protocol state (the Registry, the `.eth` Registrar, and the Name Wrapper). Determining the *effective* owner of a domain requires understanding the interplay of all of them. This is exactly the kind of protocol-implementation detail app developers are forced to learn and re-implement, with plenty of room to get it wrong. One effective owner field In the [Omnigraph API](/docs/integrate/omnigraph) `Domain.owner` is *always* the effective owner’s address—no weird edge-cases! For ENSv2 Domains, `Domain.owner` is Smart-Account-aware and represents the true owner of the Domain at a given time. ## Missing all offchain ENS names [Section titled “Missing all offchain ENS names”](#missing-all-offchain-ens-names) Offchain names are missing entirely The Subgraph indexes onchain events only, so it has no knowledge of offchain ENS names. A meaningful and growing slice of ENS lives offchain and is entirely absent from Subgraph data. Automatic offchain name resolution While the [Omnigraph API](/docs/integrate/omnigraph) doesn’t (*yet!*) index offchain names, it does provide protocol-correct Accelerated Forward Resolution, including support for offchain CCIP-Read-based names. ## Raw “bare-metal” values push the decoding burden onto you [Section titled “Raw “bare-metal” values push the decoding burden onto you”](#raw-bare-metal-values-push-the-decoding-burden-onto-you) Decoding is your problem The Subgraph exposes raw values straight from the ENS protocol, with none of the interpretation that apps actually need: * **Address records** may be for non-EVM chains and need chain-specific decoding before they’re usable. * **Contenthash** values are encoded and need decoding to become a usable URL. * **Text records** are represented by users in many inconsistent ways. Consider the many variations in how someone might set a Twitter/X handle — both the record key and the value vary widely. Every bit of this interpretation, decoding, and normalization is left to the app developer. The result is more bugs in ENS integrations across the ecosystem, which damages the network effects and growth of ENS. Interpreted records — coming soon 🚧 The [Omnigraph API](/docs/integrate/omnigraph) will support two major record resolution use-cases: 1. Protocol-accurate ‘raw’ requests, without post processing, and 2. Consumer-friendly semantic interpretation of records for, e.g. profile display. ## It can’t cleanly power NFT-reference → avatar use cases [Section titled “It can’t cleanly power NFT-reference → avatar use cases”](#it-cant-cleanly-power-nft-reference--avatar-use-cases) Avatar resolution is painful A common and important flow is taking an NFT reference as input and mapping it: **NFT Ref → Domain → Name → Avatar text record → Avatar image.** This powers services like the ENS Metadata Service, which provides NFT metadata for ENS names using standardized protocols adopted by platforms such as OpenSea, Rarible, Grails, and ENS Vision. The Subgraph’s raw, resolution-free data model makes this flow far harder than it should be. Automatic avatars — coming soon 🚧 The [Omnigraph API](/docs/integrate/omnigraph) will support automatic avatar url derivation; all your app will need to do is render an `` tag! ## Unhealed names degrade developer and user experience [Section titled “Unhealed names degrade developer and user experience”](#unhealed-names-degrade-developer-and-user-experience) Unhealed labelhashes everywhere The Subgraph (and any data-level-compatible indexer) contains a large volume of unhealed names — names whose labels are only known as labelhashes (e.g. `[abcd…].eth`). These complicate both the developer experience and the UX of every app built on top, which must decide how to display and handle them. \~94% healed, and climbing The [Omnigraph API](/docs/integrate/omnigraph) already heals **\~94%** of these labels, compared to roughly **10%** with the label set bundled in the traditional ENS Subgraph — and climbing toward a **99%** target. ## Thick-client lock-in [Section titled “Thick-client lock-in”](#thick-client-lock-in) Locked into a thick TypeScript client Achieving common ENS query operations correctly against the Subgraph requires meaningful additional logic. In practice that logic lives in “thick client” libraries — most notably `ensjs`, which embeds special-case handling for working with the Subgraph (e.g. decoding names, reconstructing histories, assembling the queries for names owned by an address). A thin client that simply wraps the GraphQL API can’t reproduce these behaviors. And because such thick clients are written for a single language (`ensjs` is TypeScript only), developers building on ENS in any other language don’t get them at all. Any client, any language The [Omnigraph API](/docs/integrate/omnigraph) is a Relay-compatible GraphQL API; bring your own GraphQL client library, or simply make GraphQL requests over HTTP and receive exactly the data you need! ## No type-safe API client [Section titled “No type-safe API client”](#no-type-safe-api-client) No type safety, no autocomplete The Subgraph’s auto-generated GraphQL API ships without a first-class, type-safe client. Developers are left hand-writing queries and types, with no compile-time guarantees that a query matches the schema or that responses are shaped as expected. Typed enssdk and enskit The [Omnigraph API](/docs/integrate/omnigraph) comes with [`enssdk` (TypeScript)](/docs/integrate/integration-options/enssdk) and [`enskit` (React)](/docs/integrate/integration-options/enskit) fully typed client libraries with built-in editor intellisense and autocomplete. ## Operational gaps [Section titled “Operational gaps”](#operational-gaps) Operations left half-solved Beyond the data-model limitations, the Subgraph’s auto-generated GraphQL API leaves common operational needs underserved: * **Pagination** is awkward and easy to implement incorrectly at scale. * **Caching** has no first-class story for ENS-shaped data. * **Indexing status** is hard to reason about — there’s no clear, app-friendly signal for how far behind realtime the data is (e.g. worst-case distance from the chain head). * **Joins** in the auto-generated API perform certain multi-entity operations in ways that don’t match what apps actually need, forcing client-side stitching. Relay pagination and caching The [Omnigraph API](/docs/integrate/omnigraph) supports [Relay Connections](https://relay.dev/graphql/connections.htm) for paginated resources; your app gets lightning-quick infinite scroll with minimal work! If using [`enskit` (React)](/docs/integrate/integration-options/enskit), the `useOmnigraphQuery` hook comes with Omnigraph-specific local cache directives for instant resolution of cacheable values. The Omnigraph’s idiomatic GraphQL API also aims to match consumer query patterns as closely as possible to negate complex and bespoke client-side logic; if your use-case isn’t yet supported, [open an issue on GitHub](https://github.com/namehash/ensnode/issues/new). ENSNode’s Indexing Status API ENSNode supports the [Indexing Status API](/docs/services/ensapi/reference/api-reference#tag/meta/GET/api/indexing-status) for up-to-date knowledge of ENSNode’s indexing status, including realtime lag, if any. ## Next Steps [Section titled “Next Steps”](#next-steps) Indexed ENS data is vital for the upcoming launch of ENSv2. The legacy ENS Subgraph is fundamentally unsuitable for ENSv2, and a replacement is critically required for many of ENS’s most important apps — including the official ENS Manager App. Build on the Omnigraph API or the Unigraph datamodel to take the guesswork out of your ENS integration. [Integrate with ENSNode ](/docs/integrate/integration-options)Integrate the Omnigraph API or the Unigraph datamodel # Integration Options > Integration options for building with ENSNode. ENSNode takes the guesswork out of ENS integrations, whether you need to resolve up-to-date records, search all Domains, or see which Domains a user owns (and much, much more). There are a few different ways to integrate with ENSNode, depending on your app, runtime, and needs. ## 1. enssdk [Section titled “1. enssdk”](#1-enssdk) With `enssdk`, leverage ENSNode and the Omnigraph from any JavaScript runtime to power your frontend or backend apps. `enssdk` comes with built-in type-safety and editor autocomplete for Omnigraph queries. [enssdk Integration Documentation ](/docs/integrate/integration-options/enssdk) ## 2. enskit [Section titled “2. enskit”](#2-enskit) With `enskit`, leverage ENSNode and the Omnigraph to power your React components using `useOmnigraphQuery`. `enskit` comes with built-in type-safety, Omnigraph-specific cache directives, easy infinite pagination, and much much more. [enskit Integration Documentation ](/docs/integrate/integration-options/enskit) ## 3. Omnigraph GraphQL API [Section titled “3. Omnigraph GraphQL API”](#3-omnigraph-graphql-api) The Omnigraph API is a GraphQL API following the Relay specification, so you get built-in support for efficient infinite pagination and idiomatic access to all of the ENS protocol within a *unified* ENSv1 + ENSv2 datamodel. [Omnigraph GraphQL API Documentation ](/docs/integrate/integration-options/omnigraph-graphql-api) ## 4. ENSDb [Section titled “4. ENSDb”](#4-ensdb) For special use cases that go beyond what the ENS Omnigraph exposes, query the live state of ENSv2 directly via SQL. `ENSDb` stores ENS state in a PostgreSQL database — usable from any language with a Postgres driver. [ENSDb Integration Documentation ](/docs/integrate/integration-options/ensdb) ## 5. enscli [Section titled “5. enscli”](#5-enscli) `enscli` is a CLI that wraps `enssdk` to bring the ENS Omnigraph to the terminal. Designed for developers exploring or validating integrations, operators wiring ENS lookups into shell pipelines, and AI coding agents driving `ensskills`. [enscli Integration Documentation ](/docs/integrate/integration-options/enscli) ## 6. ensskills [Section titled “6. ensskills”](#6-ensskills) `ensskills` is a collection of curated skill bundles that give AI coding agents a well-defined contract for working with ENS — powering conversational ENS lookups and streamlining integration code written with `enskit`, `enssdk`, or the raw Omnigraph API. [ensskills Integration Documentation ](/docs/integrate/integration-options/ensskills) ## 7. ensdb-cli & ENSDb snapshots [Section titled “7. ensdb-cli & ENSDb snapshots”](#7-ensdb-cli--ensdb-snapshots) `ensdb-cli` is the operator-facing tool for ENSDb snapshots — portable, versioned packages of an ENSDb instance. Pull one down, restore it into Postgres, and start querying ENS in minutes instead of waiting days to complete a full historical indexing backfill from scratch. [ensdb-cli & ENSDb snapshots Documentation ](/docs/integrate/integration-options/ensdb-cli) ## 8. ENSEngine [Section titled “8. ENSEngine”](#8-ensengine) ENSEngine watches your ENSDb for changes in real time and delivers ENS-aware events — including webhooks — to any sink you configure. Stop polling and start reacting to ENS state changes. [ENSEngine Integration Documentation ](/docs/integrate/integration-options/ensengine) # enscli (CLI) > Coming soon — an agent- and human-friendly CLI for the ENS Omnigraph API, wrapping enssdk. Coming soon **`enscli`** is a planned ENS CLI that wraps [`enssdk`](/docs/integrate/integration-options/enssdk) to bring the ENS Omnigraph to the terminal. The npm name is reserved; we’re still shaping the design. `enscli` will ship soon. `enscli` will be the terminal-shaped entry point to the [ENS Omnigraph](/docs/integrate/omnigraph) — a single CLI for resolving names, looking up records, searching domains, and running ad-hoc queries against any ENSNode instance. Designed to feel natural whether you’re driving it yourself or letting an AI agent drive. From a terminal it’s one `npx` away with sensible defaults against the public Omnigraph; for agents it’s predictable arguments, structured output, and machine-readable help. ## Built for [Section titled “Built for”](#built-for) * Developers exploring or validating an ENS integration from a terminal, without writing a script first. * Operators wiring ENS lookups into shell pipelines, cron, or CI. * AI coding agents driving [`ensskills`](/docs/integrate/integration-options/ensskills), which reach into the protocol through `enscli`. ## Related [Section titled “Related”](#related) [enssdk ](/docs/integrate/integration-options/enssdk)The TypeScript SDK that enscli wraps. [ensskills ](/docs/integrate/integration-options/ensskills)The agent-skill bundles that drive enscli on a developer's behalf. [ENS Omnigraph API ](/docs/integrate/omnigraph)The underlying GraphQL API enscli speaks to. # ENSDb (SQL) > Direct database access for self-hosters needing custom queries, analytics, or building custom services beyond what existing APIs expose. For special use cases that go beyond what the ENS Omnigraph exposes — you can query the live onchain state of both **ENSv1 and ENSv2** directly via `SQL`. **ENSDb** is a bi-directional integration standard for any EnsDbWriter and EnsDbReader implementations to coordinate around the live unified onchain state of ENSv1 **and ENSv2** in a carefully-crafted standardized data model within a PostgreSQL database. Because ENSDb builds on Postgres, you can use *any* language with a Postgres driver — **TypeScript**, **Python**, **Rust**, **Go**, and more. Coming soon: ensdb-cli & ENSDb snapshots We’re building [**`ensdb-cli` & ENSDb snapshots**](/docs/integrate/integration-options/ensdb-cli) so you can pull down a fresh ENSDb in minutes instead of paying for a full historical RPC backfill among many other benefits. Self-hosting required ENSDb is available when you [self-host ENSNode](/docs/self-host). Each ENSNode instance writes its data into a PostgreSQL database that you own and control — that database **is** your ENSDb instance. The hosted ENSNode instances do not expose direct database access. ### Inspirations for what you can build with ENSDb [Section titled “Inspirations for what you can build with ENSDb”](#inspirations-for-what-you-can-build-with-ensdb) Custom APIs Build specialized GraphQL or REST APIs tailored to your use case. Query exactly the data you need with full SQL power. Analytics & Dashboards Create real-time dashboards and analytics pipelines. Better than Dune — you have the live ENS state locally with sub-second query latency. CLIs & Developer Tools Build command-line tools for ENS operations. Query domains, and any specialized lookup — all from your terminal. Event-Based Engines Build reactive systems that respond to ENS state changes. Respond to registration lifecycle updates, ownership transfers, resolver updates. Data Pipelines Feed ENS data into your existing data infrastructure. Sync to data warehouses, trigger webhooks, populate search indexes. AI Agents Build AI agents that can query ENS state and perform actions based on that data. ### Example: fetch a Domain by canonical name [Section titled “Example: fetch a Domain by canonical name”](#example-fetch-a-domain-by-canonical-name) Canonical fields (`canonical_name`, `canonical_path`, `canonical_node`, `canonical_depth`) are populated on every Domain reachable from the canonical root, across both ENSv1 and ENSv2 — query them uniformly without branching by `type`. example.ts ```typescript import { EnsDbReader } from "@ensnode/ensdb-sdk"; import { eq } from "drizzle-orm"; const ensDbReader = new EnsDbReader(ensDbConnectionString, ensIndexerSchemaName); const { ensDb, ensIndexerSchema } = ensDbReader; const [vitalik] = await ensDb .select() .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.canonicalName, "vitalik.eth")); ``` example.sql ```sql SELECT * FROM ensindexer_0.domains WHERE canonical_name = 'vitalik.eth'; ``` ### Example: count an address’s Domains by type (ENSv1 vs ENSv2) [Section titled “Example: count an address’s Domains by type (ENSv1 vs ENSv2)”](#example-count-an-addresss-domains-by-type-ensv1-vs-ensv2) example.ts ```typescript import { count, eq } from "drizzle-orm"; const counts = await ensDb .select({ type: ensIndexerSchema.domain.type, count: count() }) .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.ownerId, "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")) .groupBy(ensIndexerSchema.domain.type); ``` example.sql ```sql SELECT type, count(*) FROM ensindexer_0.domains WHERE owner_id = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' GROUP BY type; ``` ### Learn more [Section titled “Learn more”](#learn-more) [ENSDb overview ](/docs/services/ensdb)Architecture, schemas, what you can build, and quick-start guides. [ENSDb SDK (TypeScript) ](/docs/services/ensdb/usage/sdk)Type-safe database access and utilities for working with ENSDb. [ENSDb SQL interface ](/docs/services/ensdb/usage/sql)Connect with any PostgreSQL client and run queries directly. [Self-host ENSNode ](/docs/self-host)Run your own ENSNode instance to get full ENSDb access. # ensdb-cli (ENSDb Snapshots) > Coming soon - publish, download, and diff snapshots of the live ENS state with the planned ensdb-cli. Coming soon **`ensdb-cli`** and **ENSDb snapshots** are planned capabilities that are coming soon. ## The problem [Section titled “The problem”](#the-problem) Completing a full historical indexing backfill of ENS from zero is **expensive on two axes: time and money**. A full historical indexing backfill of ENS across mainnet, Basenames, Lineanames, 3DNS, ENSv1, ENSv2, etc generally means days of indexing *and* a heavy RPC bill - every request to your RPC provider, multiplied by every chain, multiplied by every block of 9+ years of ENS history you have to index. That tax is painful for anyone building, prototyping, demoing, or testing. [ENSDb](/docs/services/ensdb) already gives you the live, onchain state of ENS in a plain PostgreSQL database that any language with a Postgres driver can read. The next step is making it **trivial - and cheap - to bootstrap and operate** that database. That’s what ENSDb snapshots and `ensdb-cli` are for. ## What we’re building [Section titled “What we’re building”](#what-were-building) ENSDb snapshots are **portable, versioned packages of an ENSDb instance** that anyone can publish or consume. Pull one down, restore it into Postgres, and start querying ENS in minutes instead of waiting days to complete 9-years+ of historical indexing backfill from scratch - and **without** being required to send hundreds of thousands of pricy RPC-calls yourself. When you start from a fresh ENSDb snapshot, it’s cheap and fast to catch up to realtime indexing. `ensdb-cli` will be the operator-facing tool around them - the surface where you list indexer instances and their indexing status, generate fresh snapshots, inspect what configuration a snapshot was built with, and clean up schemas you no longer need. Cut your RPC bill Skip the historical RPC fanout entirely. Pull down a published ENSDb snapshot instead of replaying 9+ years of ENS history through your own RPC provider - saving real money on every bootstrap, every CI run, every fresh environment. Spin up in minutes, not days Skip the long historical backfill too. A nearly up-to-date ENS index is ready as fast as you can download it - no specially configured Postgres required, no days of waiting. Perfect for hackathons, demos, prototypes, and any other time you need a fresh ENSDb fast. Repeatable CI environments Use snapshots as deterministic fixtures for tests and CI pipelines. Every run starts from the same well-known ENS state, with zero RPC dependency. Confident release reviews Diff snapshots across ENSNode versions to catch unexpected changes in indexed data - a powerful way to validate indexing-logic refactors before a release ships. Easier self-hosting Bootstrap your own ENSNode instance from a recent snapshot, then catch up to realtime from there. Less time waiting, more time building. ensdb-cli: purpose-built ops tooling \`ensdb-cli\` surfaces the operations you actually need day-to-day: inspect indexer instances, manage snapshots, clean up unused schemas, and more. ## Who this is for [Section titled “Who this is for”](#who-this-is-for) * **Hackathon teams and prototypers** who want a full ENSDb right now and don’t have time or money to wait days on 9+ years of an ENS historical indexing backfill. * **Self-hosters** standing up new ENSNode instances who’d rather start from a recent snapshot than backfill from genesis. * **CI and testing pipelines** that need deterministic ENS state to run repeatable assertions against. * **Maintainers** running release reviews who want to diff indexed data across versions to catch unintended regressions before they ship. * **Anyone building specialized ENS services on top of ENSDb** who needs faster iteration loops and lower operational cost. ## How this complements ENSDb [Section titled “How this complements ENSDb”](#how-this-complements-ensdb) Snapshots and the CLI sit *on top of* the [ENSDb open standard](/docs/services/ensdb). Because ENSDb is just Postgres governed by a well-defined schema, snapshots are portable: any [ENSDb Writer](/docs/services/ensdb/concepts/glossary#ensdb-writer) implementation can produce one, and any [ENSDb Reader](/docs/services/ensdb/concepts/glossary#ensdb-reader) (or your own code) can consume one. This is the same foundation that powers ENSNode’s other planned ENSDb capabilities - including [ENSEngine](/docs/integrate/integration-options/ensengine), enabling you to react to changes in ENSDb through webhooks and other event-driven strategies without need for polling. ## Related [Section titled “Related”](#related) [ENSDb Integration Quickstart ](/docs/integrate/integration-options/ensdb)The integration point these snapshots build on - the live ENS state, ready for SQL or the ENSDb SDK. [ENSDb overview ](/docs/services/ensdb)Vision, philosophy, and how an ENSDb instance works. [ENSEngine Integration Quickstart ](/docs/integrate/integration-options/ensengine)React to ENS state changes with webhooks & events rather than polling. [ENSEngine overview ](/docs/services/ensengine)React to changes in ENSDb through webhooks and other reactive / event-based development strategies. [Self-host ENSNode ](/docs/self-host)Operate your own ENSNode instance to build and publish your own ENSDb snapshots. # ENSEngine (Webhooks) > Coming soon - ENS-native webhooks and push-based events that fire when ENS state changes, so apps can stop polling and start reacting. Coming soon ENS-native event-driven reactive development (such as webhooks) is coming soon with [ENSEngine](/docs/services/ensengine) that builds on top of [ENSDb](/docs/services/ensdb). ## The problem [Section titled “The problem”](#the-problem) Today, if you want your app to react to ENS state changes, you have two unappealing options: * **Poll constantly.** Hammer API endpoints or RPCs every few seconds and discard most responses. Wasteful, inefficient, and scales poorly. * **Build your own complex indexer and data pipeline.** Stand up infrastructure to listen to chain events, decode them, and turn them into the ENS-level signals you actually care about. Way too much work for what should be a primitive. ## What we’re building [Section titled “What we’re building”](#what-were-building) ENSEngine will be an **ENS-native event-driven reactive development service** (for webhooks and more) so your app can subscribe to ENS state changes in ENSDb the same way you’d subscribe to a Stripe or GitHub webhook - except the events speak the language of discrete meaningful ENS actions, not the language of bare-metal raw onchain event transaction logs. example-webhook.http ```http POST https://your-app.example.com/webhooks/ens Content-Type: application/json { "type": "domain.transferred", "name": "vitalik.eth", ... } ``` The shape above is illustrative only. Full details coming soon. ## Example events [Section titled “Example events”](#example-events) Events will represent meaningful ENS events, such as: * `domain.registered`, `domain.renewed`, `domain.transferred` * `resolver.address_changed`, `resolver.text_changed`, `resolver.contenthash_changed` * `subname.created` * … and many more. ## What this unlocks [Section titled “What this unlocks”](#what-this-unlocks) ### Cache invalidation [Section titled “Cache invalidation”](#cache-invalidation) Build aggressive, edge-friendly caches for ENS data. The goal is to make it practical to cache profiles, avatars, text records, and resolver responses for much longer than polling-based integrations allow, then invalidate them when relevant onchain state changes. That’s the foundation for web2-grade UX on a web3 protocol - fast page loads, low RPC fanout, globally distributed reads - without giving up freshness. Of course, ENSNode will also include metadata letting you know key attributes about ENS data you fetch so that you can wisely implement the best possible caching strategies. For example: If a particular data record is coming from purely onchain data (that will receive a guaranteed ENSEngine event notification as soon as it changes) or if a particular data record is coming from offchain data (such as a resolver record sourced through CCIP-read) and that therefore still requires polling to identify if it has changed. Where this matters: * Frontend apps caching ENS profiles * CDNs serving ENS-driven content * API gateways with ENS lookups * Any service that caches resolver records or other ENS data. ### Additional key use cases [Section titled “Additional key use cases”](#additional-key-use-cases) Once a reliable change feed exists, a lot of follow-on use cases get dramatically easier to build: * **Notification services** - expiry reminders, watchlists, security alerts on unexpected transfers, activity bots in Discord/Telegram/Twitter. * **Sync to alternative databases** - want to store alternative representations of ENSDb in databases that provide specialized benefits that Postgres doesn’t offer, such as Elasticsearch, Typesense, Meilisearch, Redis, Kafka, Google BigQuery, and more? ENSEngine will ensure 100% delivery of database changes to sinks with strict ordering and exactly-once processing. * **Realtime dashboards** - surface ENS analytics and activity data as it happens. ## How it works (high level) [Section titled “How it works (high level)”](#how-it-works-high-level) ENSEngine builds on solid foundations: 1. **[ENSIndexer](/docs/services/ensindexer)** keeps **[ENSDb](/docs/services/ensdb)** up to date with the live, onchain state of ENS - including automatic chain reorg healing. 2. **[ENSEngine](/docs/services/ensengine)** watches that database for changes and turns them into enriched, filtered, ENS-aware events. 3. Those events get delivered to **your sinks** - webhooks, cache invalidation hooks, database sync targets, notification systems, and more. For the architecture overview, see [ENSEngine Overview](/docs/services/ensengine). ## Related [Section titled “Related”](#related) [ENSEngine Overview ](/docs/services/ensengine)Learn more about ENSEngine. # enskit (React) > React toolkit for ENSv2 development, includes fully typed providers for the ENS Omnigraph API. `enskit` is the React toolkit for ENSv2 development. It provides a fully typed Omnigraph API client (powered by [`urql`](https://nearform.com/open-source/urql/) and [`gql.tada`](https://gql-tada.0no.co/)), the `OmnigraphProvider`, and the `useOmnigraphQuery` hook for writing type-safe ENS queries with editor autocomplete, Relay-style pagination, and Omnigraph-specific cache directives. This guide walks you from an empty directory to a working React component that renders an [ENS Domain](/docs/concepts/the-ens-protocol) and a paginated list of its subdomains — the same flow as the [`DomainView`](https://github.com/namehash/ensnode/blob/main/examples/enskit-react-example/src/DomainView.tsx) in our example app. No backend required You don't need to run your own ENSNode to follow this guide — the steps below default to a NameHash-hosted instance. Browse the available deployments below. Version compatibility with hosted instances [Our hosted ENSNode instances](/docs/hosted-instances) currently run ENSNode `1.15.1`. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the `gql.tada` TypeScript plugin to type your queries, so pin an **exact** version (no `^` or `~`) of `enssdk@1.15.1` (and `enskit@1.15.1` when using React) to keep your generated types matched to the deployed schema. Use these exact install commands: ``` npm install enssdk@1.15.1 # or, for React apps: npm install enskit@1.15.1 enssdk@1.15.1 ``` [Hosted ENSNode Instances ](/docs/hosted-instances) ## 1. Scaffold a React app [Section titled “1. Scaffold a React app”](#1-scaffold-a-react-app) If you already have a React + TypeScript app, skip ahead to [Install `enskit` and `enssdk`](#2-install-enskit-and-enssdk). Otherwise, the fastest way to get going is [Vite](https://vite.dev): ```sh npm create vite@latest my-ens-app -- --template react-ts cd my-ens-app npm install ``` ## 2. Install enskit and enssdk [Section titled “2. Install enskit and enssdk”](#2-install-enskit-and-enssdk) ```sh npm install enskit@1.15.1 enssdk@1.15.1 ``` Pin exact versions Always pin **exact** versions (no `^` or `~`) of `enskit` and `enssdk`, and keep them on the same version. The Omnigraph GraphQL schema is bundled inside `enssdk` and consumed by the `gql.tada` TypeScript plugin to type your queries — a minor or patch bump can change the schema and silently drift your generated types away from your queries. Locking exact versions keeps types and runtime in sync, and matched to the ENSNode version your hosted instance runs. ## 3. Configure the gql.tada TypeScript plugin [Section titled “3. Configure the gql.tada TypeScript plugin”](#3-configure-the-gqltada-typescript-plugin) `gql.tada` is what gives your `graphql(...)` query strings end-to-end type safety. It reads the Omnigraph schema from `enssdk` at typecheck time. Add the plugin to `tsconfig.json`: tsconfig.json ```diff { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", +"plugins": [ +{ +"name": "gql.tada/ts-plugin", +"schema": "node_modules/enssdk/src/omnigraph/generated/schema.graphql", +"tadaOutputLocation": "./src/generated/graphql-env.d.ts" +} +] }, "include": ["src"] } ``` If you’re using VS Code, make sure your workspace is using the workspace TypeScript version so the plugin loads. Add this to `.vscode/settings.json`: .vscode/settings.json ```json { "js/ts.tsdk.path": "node_modules/typescript/lib", "js/ts.tsdk.promptToUseWorkspaceVersion": true } ``` ## 4. Mount the OmnigraphProvider [Section titled “4. Mount the OmnigraphProvider”](#4-mount-the-omnigraphprovider) `OmnigraphProvider` is what `useOmnigraphQuery` reads from. Construct an `EnsNodeClient`, extend it with the `omnigraph` module, and wrap your app: src/App.tsx ```tsx import { OmnigraphProvider } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; import { StrictMode } from "react"; import { DomainView } from "./DomainView"; // you may use a NameHash Hosted ENSNode instance // learn more at https://ensnode.io/docs/hosted-instances const ENSNODE_URL = import.meta.env.VITE_ENSNODE_URL ?? "https://api.v2-sepolia.ensnode.io"; // create and extend an EnsNodeClient with Omnigraph support const client = createEnsNodeClient({ url: ENSNODE_URL }).extend(omnigraph); export function App() { return (

My ENS App

); } ``` ## 5. Hello world [Section titled “5. Hello world”](#5-hello-world) Create `src/DomainView.tsx`. We’ll start with the simplest possible query — look up the `eth` Domain and render its owner and protocol version. InterpretedName An **InterpretedName** is a Name whose labels are each either normalized or represented as an [encoded labelhash](/docs/reference/terminology#encoded-labelhash) (e.g. `[abcd...].eth`) — the canonical, lossless form ENSNode uses to identify a Name. `asInterpretedName("eth")` brands a known-safe string as one; for user input, validate first. See [Interpreted Name](/docs/reference/terminology#interpreted-name) in the terminology reference. src/DomainView\.tsx ```tsx import { graphql, useOmnigraphQuery } from "enskit/react/omnigraph"; import { asInterpretedName } from "enssdk"; const DomainByNameQuery = graphql(` query DomainByName($name: InterpretedName!) { domain(by: { name: $name }) { __typename canonical { name { beautified } } owner { address } } } `); export function DomainView() { const name = asInterpretedName("eth"); const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading...

; if (error) return

Error: {error.message}

; if (!data?.domain) return

No domain found.

; const { domain } = data; return (

{domain.canonical ? domain.canonical.name.beautified : "Unnamed Domain"}

Version: {domain.__typename}

Owner: {domain.owner?.address ?? "0x0"}

); } ``` BeautifiedName `beautified` is the display-ready form of the Canonical Name — its normalized labels rendered per [ENSIP-15](https://docs.ens.domains/ensip/15) (e.g. `♾.eth` → `♾️.eth`) — so you can render it directly with no normalization or emoji logic of your own. It’s **display-only**: use `interpreted` (or the Domain `id`) as a lookup key or navigation target, never `beautified`. See [Beautified Name](/docs/reference/terminology#beautified-name). A few things to notice: * `graphql(...)` parses your query at typecheck time. Hover over `result.data` and you’ll see it’s typed exactly to your selection set — try removing `owner { address }` from the query and watch the access below become a type error. * `domain` is a union of `ENSv1Domain | ENSv2Domain` (both implement the `Domain` interface). The Omnigraph unifies ENSv1 and ENSv2 behind the same query — `__typename` tells you which one you got. * `canonical` may be `null` for non-canonical names (e.g. Domains whose name cannot be inferred). **Always** guard the access; TypeScript will help you. ## 6. List subdomains [Section titled “6. List subdomains”](#6-list-subdomains) Expand the query to also fetch the Domain’s subdomains. `subdomains` is a [Relay Connection](https://relay.dev/graphql/connections.htm), so the shape is `{ edges: [{ node }] }`. src/DomainView\.tsx ```diff const DomainByNameQuery = graphql(` query DomainByName($name: InterpretedName!) { domain(by: { name: $name }) { __typename canonical { name { beautified } } owner { address } subdomains { edges { node { canonical { name { beautified } } owner { address } } } } } } `); export function DomainView() { const name = asInterpretedName("eth"); const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading...

; if (error) return

Error: {error.message}

; if (!data?.domain) return

No domain found.

; const { domain } = data; return (

{domain.canonical ? domain.canonical.name.beautified : "Unnamed Domain"}

Version: {domain.__typename}

Owner: {domain.owner?.address ?? "0x0"}

+

Subdomains

+
    +{domain.subdomains?.edges.map(({ node }, i) => ( +
  • +{node.canonical ? node.canonical.name.beautified : unnamed} — Owner{" "} +{node.owner?.address ?? "0x0"} +
  • ))} +
); } ``` ## 7. Extract a typed fragment [Section titled “7. Extract a typed fragment”](#7-extract-a-typed-fragment) Notice we’re selecting the same fields (`canonical { name { beautified } }`, `owner { address }`) on the parent Domain *and* on each subdomain. Extract a `DomainFragment` to deduplicate the selection — and get a reusable, fully-typed shape for components that render a Domain. src/DomainView\.tsx ```diff +import { type FragmentOf, graphql, readFragment, useOmnigraphQuery } from "enskit/react/omnigraph"; import { asInterpretedName } from "enssdk"; +const DomainFragment = graphql(` fragment DomainFragment on Domain { __typename canonical { name { beautified } } owner { address } } +`); const DomainByNameQuery = graphql( ` query DomainByName($name: InterpretedName!) { domain(by: { name: $name }) { ...DomainFragment subdomains { edges { node { ...DomainFragment } } } } } `, [DomainFragment], ); +function RenderDomain({ data }: { data: FragmentOf }) { +// type-safe access to fragment data! +const domain = readFragment(DomainFragment, data); +return ( +<> +{domain.canonical ? domain.canonical.name.beautified : "Unnamed Domain"}{" "} +({domain.__typename}){" "} + — Owner {domain.owner?.address ?? "0x0"} + + ); +} export function DomainView() { const name = asInterpretedName("eth"); const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading...

; if (error) return

Error: {error.message}

; if (!data?.domain) return

No domain found.

; return (
+

+ +

Subdomains

    {data.domain.subdomains?.edges.map(({ node }, i) => (
  • +
  • ))}
); } ``` `FragmentOf` is the opaque type for any selection that includes `...DomainFragment` — `RenderDomain` accepts any of them. `readFragment(DomainFragment, data)` unwraps that opaque type to the typed fields you declared. ## 8. Paginate with “Load more” [Section titled “8. Paginate with “Load more””](#8-paginate-with-load-more) `subdomains` is a Relay Connection — page through it with the `first` and `after` arguments. Add `pageInfo { hasNextPage endCursor }` to the query, track the cursor in component state, and wire up a “Next page” button. src/DomainView\.tsx ```diff +import { useState } from "react"; // ...other imports const DomainByNameQuery = graphql( ` query DomainByName($name: InterpretedName!, $first: Int!, $after: String) { domain(by: { name: $name }) { ...DomainFragment subdomains(first: $first, after: $after) { edges { node { ...DomainFragment } } pageInfo { hasNextPage endCursor } } } } `, [DomainFragment], ); +const PAGE_SIZE = 20; export function DomainView() { const name = asInterpretedName("eth"); +const [after, setAfter] = useState(null); const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name, first: PAGE_SIZE, after }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading...

; if (error) return

Error: {error.message}

; if (!data?.domain) return

No domain found.

; const { subdomains } = data.domain; return (

Subdomains

    {subdomains?.edges.map(({ node }, i) => (
  • ))}
+{subdomains?.pageInfo.hasNextPage && ( + )}
); } ``` ## 9. Run it [Section titled “9. Run it”](#9-run-it) ```sh VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` Open the printed URL and you should see the `eth` Domain, its owner, and the first page of its subdomains. Clicking **Next page** advances the cursor. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) * Try the [Interactive Example](/docs/integrate/integration-options/enskit/example): edit and run the full `enskit-react-example` app in your browser with a live preview. * Swap the hardcoded `"eth"` for a name from props or a router — see [`EnsureInterpretedName`](https://github.com/namehash/ensnode/blob/main/examples/enskit-react-example/src/DomainView.tsx) in the example app for safe handling of user-provided names. * See [Omnigraph examples](/docs/integrate/omnigraph/examples) for ready-to-copy queries: account-owned domains, events, registrar permissions, full-text search, and more. * See the [Omnigraph Schema Reference](/docs/integrate/omnigraph/schema-reference) for the full set of types, fields, and arguments you can query. * Need data outside React? Use [`enssdk`](/docs/integrate/integration-options/enssdk) directly with the same `graphql(...)` helper. [Interactive Example ⚡ ](/docs/integrate/integration-options/enskit/example)Edit and run the enskit-react-example app in your browser with a live preview. [enskit-react-example (GitHub) ](https://github.com/namehash/ensnode/tree/main/examples/enskit-react-example)A full example app with routing, account views, registry cache demo, and search. [gql.tada documentation ](https://gql-tada.0no.co/)The TypeScript-first GraphQL client that powers enskit's typed queries. [urql documentation ](https://nearform.com/open-source/urql/)The GraphQL client enskit builds on — useful for understanding cache exchanges and customizing the client. # enssdk (TypeScript) > SDK for ENSv2 development in TypeScript/JavaScript. `enssdk` is the foundational TypeScript/JavaScript SDK for ENS development. It’s a fully modular and tree-shakeable modern TypeScript/JavaScript package that provides ENS-specific types and helpers — usable from any JS runtime, browser or server. `enssdk` also directly integrates with ENSNode, providing an `EnsNodeClient` (via `createEnsNodeClient`) that can be extended with a fully-typed Omnigraph API client (powered by [`gql.tada`](https://gql-tada.0no.co/)). This guide walks you from an empty directory to a working TypeScript script that queries the `eth` Domain and queries its subdomains — the same flow as our [enssdk-example](https://github.com/namehash/ensnode/tree/main/examples/enssdk-example). No backend required You don't need to run your own ENSNode to follow this guide — the steps below default to a NameHash-hosted instance. Browse the available deployments below. Version compatibility with hosted instances [Our hosted ENSNode instances](/docs/hosted-instances) currently run ENSNode `1.15.1`. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the `gql.tada` TypeScript plugin to type your queries, so pin an **exact** version (no `^` or `~`) of `enssdk@1.15.1` (and `enskit@1.15.1` when using React) to keep your generated types matched to the deployed schema. Use these exact install commands: ``` npm install enssdk@1.15.1 # or, for React apps: npm install enskit@1.15.1 enssdk@1.15.1 ``` [Hosted ENSNode Instances ](/docs/hosted-instances) ## 1. Scaffold a TypeScript project [Section titled “1. Scaffold a TypeScript project”](#1-scaffold-a-typescript-project) If you already have a TypeScript project, skip ahead to [Install `enssdk`](#2-install-enssdk). Otherwise: ```sh mkdir my-ens-script && cd my-ens-script npm init -y mkdir src ``` ## 2. Install enssdk [Section titled “2. Install enssdk”](#2-install-enssdk) We’ll use [`tsx`](https://tsx.is/) to run TypeScript directly without a bundler. ```sh npm install enssdk@1.15.1 npm install -D tsx typescript @types/node ``` Pin exact versions Always pin an **exact** version (no `^` or `~`) of `enssdk`. The Omnigraph GraphQL schema is bundled inside `enssdk` and consumed by the `gql.tada` TypeScript plugin to type your queries — a minor or patch bump can change the schema and silently drift your generated types away from your queries. Locking the exact version keeps types and runtime in sync, and matched to the ENSNode version your hosted instance runs. ## 3. Configure the gql.tada TypeScript plugin [Section titled “3. Configure the gql.tada TypeScript plugin”](#3-configure-the-gqltada-typescript-plugin) `gql.tada` is what gives your `graphql(...)` query strings end-to-end type safety. It reads the Omnigraph schema from `enssdk` at typecheck time. Create `tsconfig.json`: tsconfig.json ```diff { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "lib": ["ESNext"], "types": ["node"], +"plugins": [ +{ +"name": "gql.tada/ts-plugin", +"schema": "node_modules/enssdk/src/omnigraph/generated/schema.graphql", +"tadaOutputLocation": "./src/generated/graphql-env.d.ts" +} +] }, "include": ["src"] } ``` If you’re using VS Code, make sure your workspace is using the workspace TypeScript version so the plugin loads. Add this to `.vscode/settings.json`: .vscode/settings.json ```json { "js/ts.tsdk.path": "node_modules/typescript/lib", "js/ts.tsdk.promptToUseWorkspaceVersion": true } ``` Also add a `start` script to `package.json`: package.json ```diff { "type": "module", +"scripts": { +"start": "tsx src/index.ts" +} } ``` ## 4. Construct the client [Section titled “4. Construct the client”](#4-construct-the-client) The `EnsNodeClient` is the entry point. Extend it with the `omnigraph` module to get the `client.omnigraph.query(...)` method. src/index.ts ```ts import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; // you may use a NameHash Hosted ENSNode instance // learn more at https://ensnode.io/docs/hosted-instances const ENSNODE_URL = process.env.ENSNODE_URL ?? "https://api.v2-sepolia.ensnode.io"; // create and extend an EnsNodeClient with Omnigraph support const client = createEnsNodeClient({ url: ENSNODE_URL }).extend(omnigraph); ``` ## 5. Hello world [Section titled “5. Hello world”](#5-hello-world) Add your first query — look up the `eth` Domain and print its owner and protocol version. InterpretedName An **InterpretedName** is a Name whose labels are each either normalized or represented as an [encoded labelhash](/docs/reference/terminology#encoded-labelhash) (e.g. `[abcd...].eth`) — the canonical, lossless form ENSNode uses to identify a Name. `asInterpretedName("eth")` brands a known-safe string as one; for user input, validate first. See [Interpreted Name](/docs/reference/terminology#interpreted-name) in the terminology reference. src/index.ts ```ts // existing imports... import { asInterpretedName } from "enssdk"; import { graphql, omnigraph } from "enssdk/omnigraph"; // existing client... // this is typechecked and editor autocompleted with built-in docs! const HelloWorldQuery = graphql(` query HelloWorld($name: InterpretedName!) { domain(by: { name: $name }) { __typename canonical { name { beautified } } owner { address } } } `); async function main() { const name = asInterpretedName("eth"); const result = await client.omnigraph.query({ query: HelloWorldQuery, variables: { name }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); if (!result.data?.domain) throw new Error(`Domain '${name}' not found`); const { domain } = result.data; console.log(`Name: ${domain.canonical ? domain.canonical.name.beautified : ""}`); console.log(`Version: ${domain.__typename}`); console.log(`Owner: ${domain.owner?.address ?? "0x0"}`); } main().catch((err) => { console.error(err); process.exit(1); }); ``` BeautifiedName `beautified` is the display-ready form of the Canonical Name — its normalized labels rendered per [ENSIP-15](https://docs.ens.domains/ensip/15) (e.g. `♾.eth` → `♾️.eth`) — so you can render it directly with no normalization or emoji logic of your own. It’s **display-only**: use `interpreted` (or the Domain `id`) as a lookup key or navigation target, never `beautified`. See [Beautified Name](/docs/reference/terminology#beautified-name). A few things to notice: * `graphql(...)` parses your query at typecheck time. Hover over `result.data` and you’ll see it’s typed exactly to your selection set — try removing `owner { address }` from the query and watch the access below become a type error. * `domain` is a union of `ENSv1Domain | ENSv2Domain` (both implement the `Domain` interface). The Omnigraph unifies ENSv1 and ENSv2 behind the same query — `__typename` tells you which one you got. * `canonical` is `null` for non-canonical Domains (e.g. Domains whose name cannot be inferred). When non-null, `canonical.name.beautified` is the Domain’s Canonical Name, beautified for display. **Always** guard the access; TypeScript will help you. ## 6. List subdomains [Section titled “6. List subdomains”](#6-list-subdomains) Expand the query to also fetch the Domain’s subdomains. `subdomains` is a [Relay Connection](https://relay.dev/graphql/connections.htm) — pass `first: 20` to cap the page, and select `totalCount` to learn how many subdomains exist in total. src/index.ts ```diff const HelloWorldQuery = graphql(` query HelloWorld($name: InterpretedName!) { domain(by: { name: $name }) { __typename canonical { name { beautified } } owner { address } subdomains(first: 20) { totalCount edges { node { canonical { name { beautified } } owner { address } } } } } } `); async function main() { const name = asInterpretedName("eth"); const result = await client.omnigraph.query({ query: HelloWorldQuery, variables: { name }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); if (!result.data?.domain) throw new Error(`Domain '${name}' not found`); const { domain } = result.data; console.log(`Name: ${domain.canonical ? domain.canonical.name.beautified : ""}`); console.log(`Version: ${domain.__typename}`); console.log(`Owner: ${domain.owner?.address ?? "0x0"}`); console.log(`\nSubdomains (showing 20 of ${domain.subdomains?.totalCount ?? 0}):`); +for (const { node } of domain.subdomains?.edges ?? []) { +const subName = node.canonical ? node.canonical.name.beautified : ""; console.log(` - ${subName} — Owner ${node.owner?.address ?? "0x0"}`); +} } ``` Notice we’re now writing the same canonical/owner rendering twice — once for the parent Domain and once inside the subdomain loop. We’ll fix that next. To page beyond the first 20, the connection also exposes `pageInfo { hasNextPage endCursor }` and accepts an `after: String` cursor — see [Relay’s connection spec](https://relay.dev/graphql/connections.htm). ## 7. Extract a typed fragment [Section titled “7. Extract a typed fragment”](#7-extract-a-typed-fragment) Notice we’re selecting the same fields (`canonical { name { beautified } }`, `owner { address }`) on the parent Domain *and* on each subdomain, and rendering them the same way. Extract a `DomainFragment` to deduplicate the selection — and get a reusable, fully-typed function that formats a Domain. src/index.ts ```diff import { asInterpretedName } from "enssdk"; import { createEnsNodeClient } from "enssdk/core"; +import { type FragmentOf, graphql, omnigraph, readFragment } from "enssdk/omnigraph"; // you may use a NameHash Hosted ENSNode instance // learn more at https://ensnode.io/docs/hosted-instances const ENSNODE_URL = process.env.ENSNODE_URL ?? "https://api.v2-sepolia.ensnode.io"; const client = createEnsNodeClient({ url: ENSNODE_URL }).extend(omnigraph); +const DomainFragment = graphql(` fragment DomainFragment on Domain { __typename canonical { name { beautified } } owner { address } } +`); const HelloWorldQuery = graphql( ` query HelloWorld($name: InterpretedName!) { domain(by: { name: $name }) { ...DomainFragment subdomains(first: 20) { totalCount edges { node { ...DomainFragment } } } } } `, [DomainFragment], ); +function formatDomain(data: FragmentOf): string { +// type-safe access to fragment data! +const domain = readFragment(DomainFragment, data); +const name = domain.canonical ? domain.canonical.name.beautified : ""; +const owner = domain.owner?.address ?? "0x0 (means reserved for ENSv2)"; +return `${name} (${domain.__typename}) — Owner ${owner}`; +} async function main() { const name = asInterpretedName("eth"); const result = await client.omnigraph.query({ query: HelloWorldQuery, variables: { name }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); if (!result.data?.domain) throw new Error(`Domain '${name}' not found`); const { domain } = result.data; const totalCount = domain.subdomains?.totalCount ?? 0; console.log(formatDomain(domain)); console.log(`\nSubdomains (showing 20 of ${totalCount}):`); for (const { node } of domain.subdomains?.edges ?? []) { console.log(` - ${formatDomain(node)}`); } } ``` `FragmentOf` is the opaque type for any selection that includes `...DomainFragment` — `formatDomain` accepts any of them, including each `node` in the subdomain edges. `readFragment(DomainFragment, data)` unwraps that opaque type to the typed fields you declared. ## 8. Run it [Section titled “8. Run it”](#8-run-it) Point at a hosted ENSNode and go: ```sh ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` You should see the `eth` Domain, followed by its first 20 subdomains and the total subdomain count. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) * See [Omnigraph examples](/docs/integrate/omnigraph/examples) for ready-to-copy GraphQL queries: account-owned domains, events, registrar permissions, full-text search, and more. * See the [Omnigraph Schema Reference](/docs/integrate/omnigraph/schema-reference) for the full set of types, fields, and arguments you can query. * Building a React app? Use [`enskit`](/docs/integrate/integration-options/enskit) — same `graphql(...)` helper, with `useOmnigraphQuery` and a graphcache. [Interactive Example ⚡ ](/docs/integrate/integration-options/enssdk/example)Edit and run the enssdk-example script in your browser. [enssdk-example (GitHub) ](https://github.com/namehash/ensnode/tree/main/examples/enssdk-example)The full example script from this guide. [gql.tada documentation ](https://gql-tada.0no.co/)The TypeScript-first GraphQL client that powers enssdk's typed queries. # ensskills (AI agents) > Coming soon — skill bundles that give AI agents an opinionated contract for ENS. Coming soon **`ensskills`** is a planned collection of agent skills for ENS development and ENS lookups. The npm name is reserved; we’re still shaping the design. `ensskills` will ship soon. `ensskills` will be a small, curated set of skills that give AI coding agents — Claude Code, Cursor, Codex, and the rest — a well-defined contract for working with ENS. We want to support users who want a conversational interaction with ENS through their AI assistant, handled by skills that drive [`enscli`](/docs/integrate/integration-options/enscli) behind the scenes, and to streamline the developer experience writing integration code with [`enssdk`](/docs/integrate/integration-options/enssdk), [`enskit`](/docs/integrate/integration-options/enskit), or the raw [Omnigraph API](/docs/integrate/omnigraph). Left to their own devices, agents love to reinvent the wheel from scratch every prompt — burning through your token budget rediscovering what ENS even is before they get around to the actual task. `ensskills` will be blueprints: focused, versioned bundles that hand the agent the right context for ENS work — no more, no less. The result is agents that produce ENS code that actually works, without the developer having to brief them from scratch every time. ## Built for [Section titled “Built for”](#built-for) * Developers building ENS apps with AI agents in the loop. * End-users asking AI assistants ENS-shaped questions and expecting answers grounded in the protocol. * Teams moving from the legacy ENS Subgraph onto the Omnigraph. ## Related [Section titled “Related”](#related) [enscli ](/docs/integrate/integration-options/enscli)The CLI ensskills drives at runtime for ENS state lookups. [enssdk ](/docs/integrate/integration-options/enssdk)The TypeScript SDK ensskills teaches agents to author code against. [enskit ](/docs/integrate/integration-options/enskit)The React toolkit ensskills steers agents toward for UI work. [ENS Omnigraph API ](/docs/integrate/omnigraph)The GraphQL API behind all of the above. # Omnigraph API (GraphQL) > Query the ENS Omnigraph API directly via HTTP from any language. The Omnigraph is **a GraphQL API** following the [Relay specification](https://relay.dev/graphql/connections.htm). There’s no proprietary protocol or transport — any GraphQL client in any language works, from `curl` and `fetch` to [`urql`](https://nearform.com/open-source/urql/), [`Apollo`](https://www.apollographql.com/), [`graphql-request`](https://github.com/jasonkuhrt/graphql-request), and beyond. This guide walks you through the minimum: a single `fetch` call against `/api/omnigraph` — the same flow as our [omnigraph-graphql-example](https://github.com/namehash/ensnode/tree/main/examples/omnigraph-graphql-example). If you want end-to-end typed queries (via [`gql.tada`](https://gql-tada.0no.co/)) with editor autocomplete and a built-in client, use [`enssdk`](/docs/integrate/integration-options/enssdk) instead — but if you need to integrate from a language without first-class GraphQL tooling, or you’re already in a stack with its own GraphQL client, this is the path. No backend required You don't need to run your own ENSNode to follow this guide — the steps below default to a NameHash-hosted instance. Browse the available deployments below. Version compatibility with hosted instances [Our hosted ENSNode instances](/docs/hosted-instances) currently run ENSNode `1.15.1`. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the `gql.tada` TypeScript plugin to type your queries, so pin an **exact** version (no `^` or `~`) of `enssdk@1.15.1` (and `enskit@1.15.1` when using React) to keep your generated types matched to the deployed schema. Use these exact install commands: ``` npm install enssdk@1.15.1 # or, for React apps: npm install enskit@1.15.1 enssdk@1.15.1 ``` [Hosted ENSNode Instances ](/docs/hosted-instances) ## 1. The endpoint [Section titled “1. The endpoint”](#1-the-endpoint) The Omnigraph lives at: ```plaintext POST {ENSNODE_URL}/api/omnigraph Content-Type: application/json { "query": "...", "variables": { ... } } ``` It returns `{ "data": ..., "errors": [...] }` per the standard GraphQL response shape. A minimum-viable hello world over `curl`: ```sh curl -sS -X POST \ -H 'Content-Type: application/json' \ -d '{"query":"{ domain(by: { name: \"eth\" }) { canonical { name { beautified } } owner { address } } }"}' \ https://api.v2-sepolia.ensnode.io/api/omnigraph ``` BeautifiedName `beautified` is the display-ready form of the Canonical Name — its normalized labels rendered per [ENSIP-15](https://docs.ens.domains/ensip/15) (e.g. `♾.eth` → `♾️.eth`) — so you can render it directly with no normalization or emoji logic of your own. It’s **display-only**: use `interpreted` (or the Domain `id`) as a lookup key or navigation target, never `beautified`. See [Beautified Name](/docs/reference/terminology#beautified-name). The rest of this guide builds the same thing in TypeScript using `fetch`, so you have something to extend. ## 2. Scaffold a TypeScript project [Section titled “2. Scaffold a TypeScript project”](#2-scaffold-a-typescript-project) If you already have one, skip ahead to [Write the query](#3-write-the-query). ```sh mkdir my-ens-script && cd my-ens-script npm init -y mkdir src ``` Install [`tsx`](https://tsx.is/) so you can run TypeScript directly: ```sh npm install -D tsx typescript @types/node ``` Add a `start` script to `package.json`: package.json ```diff { "type": "module", +"scripts": { +"start": "tsx src/index.ts" +} } ``` ## 3. Write the query [Section titled “3. Write the query”](#3-write-the-query) Create `src/index.ts`. The whole script is a single `fetch` against `/api/omnigraph`. src/index.ts ```ts // you may use a NameHash Hosted ENSNode instance // learn more at https://ensnode.io/docs/hosted-instances const ENSNODE_URL = process.env.ENSNODE_URL ?? "https://api.v2-sepolia.ensnode.io"; const HELLO_WORLD_QUERY = /* GraphQL */ ` query HelloWorld($name: InterpretedName!) { domain(by: { name: $name }) { __typename canonical { name { beautified } } owner { address } subdomains(first: 20) { totalCount edges { node { __typename canonical { name { beautified } } owner { address } } } } } } `; interface Domain { __typename: "ENSv1Domain" | "ENSv2Domain"; canonical: { name: { beautified: string } } | null; owner: { address: string } | null; } interface QueryResult { data?: { domain: | (Domain & { subdomains: { totalCount: number; edges: { node: Domain }[]; } | null; }) | null; } | null; errors?: { message: string }[]; } function formatDomain(domain: Domain): string { const name = domain.canonical?.name.beautified ?? ""; const owner = domain.owner?.address ?? "0x0 (means reserved for ENSv2)"; return `${name} (${domain.__typename}) — Owner ${owner}`; } async function main() { const response = await fetch(new URL("/api/omnigraph", ENSNODE_URL), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: HELLO_WORLD_QUERY, variables: { name: "eth" }, }), }); if (!response.ok) { throw new Error(`Request failed: ${response.status} ${response.statusText}`); } const { data, errors } = (await response.json()) as QueryResult; if (errors) throw new Error(JSON.stringify(errors)); if (!data?.domain) throw new Error("Domain 'eth' not found"); const { domain } = data; const totalCount = domain.subdomains?.totalCount ?? 0; console.log(formatDomain(domain)); console.log(`\nSubdomains (showing 20 of ${totalCount}):`); for (const { node } of domain.subdomains?.edges ?? []) { console.log(` - ${formatDomain(node)}`); } } main().catch((err) => { console.error(err); process.exit(1); }); ``` A few things to notice: * **`InterpretedName` is a scalar.** From the wire’s perspective it’s just a string — the server validates the format. Pass `"eth"` as a plain string in `variables`. * **`subdomains` is a [Relay Connection](https://relay.dev/graphql/connections.htm).** Cursor through with `first`, `after`, `pageInfo { hasNextPage endCursor }` — same shape as any Relay-style API. * **Hand-written types.** We’re maintaining `interface Domain` and `interface QueryResult` ourselves here. If you want these generated and kept in sync with your queries automatically, use [`enssdk`](/docs/integrate/integration-options/enssdk). ## 4. Run it [Section titled “4. Run it”](#4-run-it) ```sh ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` You should see the `eth` Domain, its owner, and the first 20 of its subdomains. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) * Want typed queries with editor autocomplete and a real GraphQL client? Use [`enssdk`](/docs/integrate/integration-options/enssdk) — same API, with `gql.tada` types and an `EnsNodeClient`. * Building a React app? Use [`enskit`](/docs/integrate/integration-options/enskit) — same `graphql(...)` helper plus `useOmnigraphQuery` and a graphcache. * See [Omnigraph examples](/docs/integrate/omnigraph/examples) for ready-to-copy queries: account-owned domains, events, registrar permissions, full-text search, and more. * See the [Omnigraph Schema Reference](/docs/integrate/omnigraph/schema-reference) for the full set of types, fields, and arguments you can query. [omnigraph-graphql-example ](https://github.com/namehash/ensnode/tree/main/examples/omnigraph-graphql-example)The full example script from this guide. [GraphQL specification ](https://graphql.org/learn/)Variables, fragments, directives — the underlying spec the Omnigraph follows. [Relay Cursor Connections Specification ](https://relay.dev/graphql/connections.htm)The pagination convention the Omnigraph uses for all list fields. # ENS Omnigraph API > The world's first and only API providing unified access to the full state of ENSv1 and ENSv2. The **ENS Omnigraph API** is the world’s first and only API providing unified access to the full state of ENSv1 and ENSv2. It’s a single, polymorphic GraphQL API over **both ENSv1 and ENSv2** — write your query once and get correct, typed results regardless of which protocol version a given Domain lives in. ENSv1 and ENSv2 will coexist Unlike traditional software, **ENSv2 does not replace ENSv1**. When ENSv2 launches, ENSv1 doesn’t stop existing — both protocols coexist onchain, at the same time, even though ENSv2 has a substantially different onchain data model from ENSv1. That’s exactly why developers building on ENS need ENSNode and the ENS Omnigraph API: it’s the world’s first and only service delivering the **unified ENSv1 + ENSv2 data access** that building on ENS requires. ![ENS Omnigraph diagram](/ens-omnigraph-diagram.png) The Omnigraph is delivered by [ENSApi](/docs/services/ensapi) on top of the indexed data in [ENSDb](/docs/services/ensdb). It follows the [Relay specification](https://relay.dev/graphql/connections.htm), abstracts away the most common ENS-protocol footguns, and exposes enough of the underlying protocol for builders who need to go deep. ## One unified API over ENSv1 + ENSv2 [Section titled “One unified API over ENSv1 + ENSv2”](#one-unified-api-over-ensv1--ensv2) When ENSv2 launches in **Summer 2026**, the two protocol versions coexist — and the Omnigraph keeps your app working against both, at the same time, with no code changes. Both ENSv1 and ENSv2 Domains are indexed concurrently and exposed through a unified schema. Ask for a Domain by name (`domain(by: { name: "vitalik.eth" })`) and you get a typed result whether that name lives in ENSv1 or ENSv2 — your code doesn’t have to know which at query time. ## What you get [Section titled “What you get”](#what-you-get) * **Multichain in one query** — mainnet `.eth`, Basenames (`.base.eth`), Lineanames (`.linea.eth`), and 3DNS names (`.box`) all in a single unified schema. * **Typed, no footguns** — names and labels are normalized for you, so rendering them in a UI is trivial. * **The full ENS picture** — resolve records, search Domains, list what an address owns, read a complete history of onchain Events, inspect Permissions, and more. * **Built-in pagination** — Relay-spec connections mean infinite scroll and stable pagination work out of the box. ## Your first query [Section titled “Your first query”](#your-first-query) Look up a Domain by name and read its owner: example.gql ```graphql query GetDomain { domain(by: { name: "vitalik.eth" }) { canonical { name { interpreted } } owner { address } } } ``` That’s a simple shape to get started. When you’re ready to understand the data model underneath, see [Core Concepts](/docs/integrate/omnigraph/concepts). ## Next steps [Section titled “Next steps”](#next-steps) [Core Concepts ](/docs/integrate/omnigraph/concepts)The data model behind the Omnigraph — Namegraph, canonicality, polymorphism, Events, and Permissions. [Examples ](/docs/integrate/omnigraph/examples)Worked queries for the most common ENS access patterns — Domain by name, subdomains, events, permissions, and more. [Schema Reference ](/docs/integrate/omnigraph/schema-reference)The full Omnigraph GraphQL schema, every type and field documented. [Integration Options ](/docs/integrate/integration-options)enskit (React), enssdk (TypeScript), or raw GraphQL — pick the integration shape that fits your stack. # ENS Omnigraph Core Concepts > The core data-model concepts behind the ENS Omnigraph API — Namegraph, Unigraph, canonicality, polymorphism, Interpreted Names, Events, and Permissions. While the [ENS Omnigraph API](/docs/integrate/omnigraph) abstracts away most of the complexity of the ENS protocol, there are still some core concepts that are useful to learn. These concepts will help you understand *how* the Omnigraph presents ENSv1 and ENSv2 through a single unified schema. You don’t need to master these concepts to build your first Omnigraph query, but as you level-up to building more advanced ENS features in your app, these concepts will help you to understand how the Omnigraph represents the state of the ENS protocol. ENSv1 and ENSv2 will coexist Unlike traditional software, **ENSv2 does not replace ENSv1**. When ENSv2 launches, ENSv1 doesn’t stop existing — both protocols coexist onchain, at the same time, even though ENSv2 has a substantially different onchain data model from ENSv1. That’s exactly why developers building on ENS need ENSNode and the ENS Omnigraph API: it’s the world’s first and only service delivering the **unified ENSv1 + ENSv2 data access** that building on ENS requires. The concepts below explain how the Omnigraph presents that unified state. ## What is a Namegraph? [Section titled “What is a Namegraph?”](#what-is-a-namegraph) A **Namegraph** is the native onchain data model of ENSv2: it represents names not as a flat mapping of namehashes in a Nametable (as in ENSv1), but as a graph of `Registry → Domain → Registry → Domain → …`. This graph may be cyclic, and within the ENSv2 protocol an unbounded number of *disjoint* (not connected) Namegraphs will exist. Within the unified ENS protocol (v1+v2) there will exist many Namegraphs, at the very least those headed by the ENSv1 Root Registry, Basenames, Lineanames, 3DNS, and the ENSv2 Root Registry. Because ENSv1 names do not actually have on-chain Subregistries, the Unigraph represents this relationship with an `ENSv1VirtualRegistry`. ``` flowchart TD root([ENSv1 Root Registry]) eth["eth"] ethReg([".eth (virtual) Subregistry"]) vitalik["vitalik.eth"] exampleName["example.eth"] vitalikReg(["vitalik.eth's (virtual) Subregistry"]) blog["blog.vitalik.eth"] exampleReg(["example.eth's (virtual) Subregistry"]) sub["sub.example.eth"] root --> eth eth --> ethReg ethReg --> vitalik ethReg --> exampleName vitalik --> vitalikReg vitalikReg --> blog exampleName --> exampleReg exampleReg --> sub ``` You navigate the graph by following a Domain to its Subregistry to its child Domains, and so on: example.gql ```graphql query Namegraph { # reference a Domain by name domain(by: { name: "eth" }) { # this is the Registry that "eth" exists within registry { id contract { chainId address } } # "eth"'s parent Domain (if any) parent { id } # the Subregistry that "eth" declares subregistry { domains { edges { node { # get each domain's (beautified) name canonical { name { beautified } } } } } } # Domain.subdomains is short form of Domain.subregistry.domains subdomains { edges { node { canonical { name { beautified } } } } } } } ``` ## What is the Unigraph? [Section titled “What is the Unigraph?”](#what-is-the-unigraph) The **Unigraph** is the entire collection of these disjoint ENSv2 Namegraphs and multiple ENSv1 Nametables, combined together into a single unified data model using ENS Resolution semantics. Navigating the Unigraph from `"eth"` down to `"vitalik.eth"` and beyond looks identical regardless of whether the underlying entities are ENSv1 or ENSv2. The [`unigraph` plugin](/docs/services/ensindexer) in ENSIndexer is what builds this unified model. The Unigraph constructs two Namegraphs, one rooted at the ENSv1 Root Registry and another rooted at the ENSv2 Root Registry. It’s also where multichain coverage lives: Basenames (`.base.eth`), Lineanames (`.linea.eth`), and 3DNS names (`.box`) are all stitched into the ENSv1 Namegraph. ``` flowchart TD root([ENSv1 Root Registry]) eth["eth"] ethReg([.eth Subregistry]) vitalik["vitalik.eth"] baseName["base.eth"] vitalikReg([vitalik.eth Subregistry on Ethereum]) blog["blog.vitalik.eth"] baseReg([Basenames Subregistry on Base Chain]) jesse["jesse.base.eth"] root --> eth eth --> ethReg ethReg --> vitalik ethReg --> baseName vitalik --> vitalikReg vitalikReg --> blog baseName --> baseReg baseReg --> jesse ``` The same query shape works for any indexed name regardless of chain or protocol version — here, a Basename on Base: example.gql ```graphql query Basenames { domain(by: { name: "jesse.base.eth" }) { canonical { name { interpreted } } } } ``` ## Unigraph with ENSv2 [Section titled “Unigraph with ENSv2”](#unigraph-with-ensv2) Once ENSv2 launches, the ENSv2 Namegraph will exist in parallel with the ENSv1 Namegraph. When referencing a Domain by `name`, the Omnigraph will start at the **ENSv2 Root Registry**, and traverse the Namegraph to find the appropriate Domain. Once `.eth` names are reserved in the ENSv2 `EthRegistry`, then the **ENSv2 Domain** will be returned, since that’s the Domain that would be referenced during resolution. This is part of why it’s important to reference specific Domains by `id`; once `vitalik` has been reserved in the ENSv2 EthRegistry, the ENS protocol considers the ENSv2 Domain (*not* the ENSv1 Domain) to be the ‘real’ one. That said, after the `.eth` names are reserved (but before they’re individually migrated), their resolver will be the `ENSv1Resolver`, forwarding resolution to the ENSv1 Namegraph. The end result is that there are **two** Domains considered to be “vitalik.eth”, one in the ENSv1 Namegraph and one in the ENSv2 Namegraph. example.gql ```graphql query ByProtocolVersion { # before ENSv2 launches: returns ENSv1 vitalik.eth # after ENSv2 launched: returns ENSv2 vitalik.eth domain(by: { name: "vitalik.eth" }) { id } # always returns the protocol-specific version of vitalik.eth domains(where: { name: { eq: "vitalik.eth" }, version: ENSv1 }) { edges { node { id } } } } ``` Domain `id` vs `name` This is part of why there’s a distinction between a Domain’s `id` (the stable, unique reference that always refers to the same onchain entity) and a Domain’s `name` (which may change over time). See below for further discussion. ## Canonicality [Section titled “Canonicality”](#canonicality) Given that a Domain entity (say, the `sub` in `sub.example.eth`) can be reached by infinitely many aliases (for example, `sub.other.eth`), it becomes important to determine a *canonical* reference to the Domain — this is the **Canonical Name**. Canonicality is also connected to nameability within the Unigraph; if an ENSv2 Domain exists on-chain but isn’t eventually connected to the ENSv1 or ENSv2 Root Registry via a series of canonical names, **it doesn’t have a Canonical Name!** Within the Omnigraph API the complexity of the Namegraph is reduced, and all Canonical Domains are queryable, searchable, and addressable by said Canonical Name. Domains that are not canonical are *still* referenceable by `id` (eg. `domain(by: { id: DomainId! })`). Canonical Domains have a `Domain.canonical` field hosting the canonicality-derived fields such as `name`, `node`, `depth` (i.e. 2 for `vitalik.eth`), and `path` from the ENS root. example.gql ```graphql query Canonicality { domain(by: { name: "vitalik.eth" }) { canonical { name { interpreted # the InterpretedName beautified # the ENSIP-15 BeautifiedName } node # namehash(name) depth # i.e. 2 for "vitalik.eth" path { id } # [Domain("eth"), Domain("vitalik.eth")] } } } ``` ## Stable IDs vs. Namegraph addressing [Section titled “Stable IDs vs. Namegraph addressing”](#stable-ids-vs-namegraph-addressing) Every entity in the Omnigraph has an `id` — a nominally-typed, stable reference to a specific on-chain entity (`DomainId`, `RegistryId`, `RegistrationId`, etc.). For Domains, when you already have an `id` and want to reference the *exact same* on-chain entity, query it by `id`: `domain(by: { id: "..." })` which is stable across time, even if its Canonical Name could change. Addressing a Domain by **name** is a different operation. It’s **forward traversal of the unified Namegraph**: `domain(by: { name: "vitalik.eth" })` walks from the Root Registry (ENSv1 or ENSv2 if defined) → `"eth"` in that Registry → the Registry that `"eth"` points at (the EthRegistry) → `"vitalik"` in that Registry. The Domain returned is whichever on-chain entity (if any) would be identified during Forward Resolution. These two views are not interchangeable: * The `id` you receive from a name lookup *is* the stable reference to the on-chain entity that the Namegraph currently resolves `"vitalik.eth"` to. * But `"vitalik.eth"` is not a stable reference to that entity. The Namegraph can be re-parented or re-aliased — and tomorrow, `"vitalik.eth"` may resolve to an entirely different on-chain Domain, which may have a different resolver with different records. Address the exact on-chain entity by `id` — stable across time: example.gql ```graphql query ById { domain(by: { id: "..." }) { canonical { name { interpreted } } } } ``` Address by `name` to perform forward traversal — you get whichever entity the name resolves to right now: example.gql ```graphql query ByName { domain(by: { name: "vitalik.eth" }) { __typename # could be ENSv1Domain or ENSv2Domain id # vitalik.eth could refer to a different Domain over time, but id is always stable canonical { name { interpreted } } } } ``` Rule of Thumb Address by `name` when you’re answering “which Domain would records come from if resolved right now”; address by `id` when you’re answering “what’s the latest state of this specific on-chain entity?”. So if you’re writing an application that shows a profile for `vitalik.eth`, `by: { name: "vitalik.eth" }`. If you’re writing an application where users are managing their on-chain Registry contracts and the Domains they own, reference each `by: { id: "..." }`. ## Polymorphism via GraphQL interfaces [Section titled “Polymorphism via GraphQL interfaces”](#polymorphism-via-graphql-interfaces) `Domain`, `Registry`, and `Registration` are GraphQL **interfaces**, with concrete types implementing each: * `Domain` → `ENSv1Domain`, `ENSv2Domain` * `Registry` → `ENSv1Registry`, `ENSv1VirtualRegistry`, `ENSv2Registry` * `Registration` → `BaseRegistrarRegistration`, `NameWrapperRegistration`, `ThreeDNSRegistration`, `ENSv2RegistryRegistration`, `ENSv2RegistryReservation` Shared fields are available unconditionally on the interface. Protocol- or implementation-specific fields are reached via typed inline fragments — `... on ENSv1Domain { rootRegistryOwner }`, `... on ENSv2Domain { tokenId }`, `... on BaseRegistrarRegistration { wrapped { fuses } }`, `... on NameWrapperRegistration { fuses }`. The result is a single query that compiles, type-checks, and returns the right fields for whichever concrete type each record turns out to be. example.gql ```graphql query Polymorphism { domain(by: { name: "vitalik.eth" }) { __typename ... on ENSv1Domain { # the owner of the Domain in the ENSv1 Root Registry rootRegistryOwner { address } } ... on ENSv2Domain { # ENSv2 Domains are identified by a `tokenId` within their Registry tokenId } } } ``` ## InterpretedNames and InterpretedLabels everywhere [Section titled “InterpretedNames and InterpretedLabels everywhere”](#interpretednames-and-interpretedlabels-everywhere) Every name and label crossing the Omnigraph surface is an **Interpreted Name** (or **Interpreted Label**). Each label in an Interpreted Name is either a normalized literal label or an Encoded LabelHash (`[abc123…]`) when the literal isn’t known or is unnormalized. This eliminates one of the most common ENS UI footguns — unnormalized labels, unhealed hashes, and rendering surprises — at the schema layer, making UI rendering trivial. See [terminology](/docs/reference/terminology#interpreted-label) for the full definition. In addition, both provide a `beautified` variant, where the [ENSIP-15](https://docs.ens.domains/ensip/15) beautified form is rendered. This allows your UI to trivially render the best form of a name or label, without further logic. example.gql ```graphql query InterpretedNames { domain(by: { name: "vitalik.eth" }) { label { interpreted # "vitalik" beautified # "vitalik" hash # 0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc } canonical { name { interpreted # "vitalik.eth" beautified # "vitalik.eth" } } } } ``` Example.tsx ```tsx

the Label for this domain is {domain.label.beautified}

``` [Interpreted Name ](/docs/reference/terminology#interpreted-name)The full definition in terminology. [Interpreted Label ](/docs/reference/terminology#interpreted-label)The full definition in terminology. [Beautified Name ](/docs/reference/terminology#beautified-name)The full definition in terminology. [Beautified Label ](/docs/reference/terminology#beautified-label)The full definition in terminology. ### InterpretedName with Encoded LabelHash Example [Section titled “InterpretedName with Encoded LabelHash Example”](#interpretedname-with-encoded-labelhash-example) As noted above, an InterpretedName may contain Labels that are [Encoded LabelHashes](/docs/reference/terminology#encoded-labelhash), meaning that the human-readable Label isn’t known or isn’t normalized. The Omnigraph still supports referencing Domains by these InterpretedNames, like so: example.gql ```graphql query ByInterpretedName { domain(by: { name: "[af2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc].eth" }) { id # this is the same Domain as "vitalik.eth" above! canonical { name { # in this case, the Omnigraph knows the fully healed Canonical Name of "vitalik.eth" interpreted # "vitalik.eth" } } } } ``` Not all InterpretedNames are Resolvable ENS Forward Resolution does *not* support Encoded LabelHashes in names, so while an InterpretedName can be used to traverse the Namegraph (i.e. with `domain(by: { name: "" })`), if the Canonical Name is not a [ResolvableName](/docs/reference/terminology#resolvable-name), then the records for that name *cannot* be determined. ### BeautifiedNames and BeautifiedLabels [Section titled “BeautifiedNames and BeautifiedLabels”](#beautifiednames-and-beautifiedlabels) For some labels and names, there exists an [ENSIP-15](https://docs.ens.domains/ensip/15) beautified form, where certain normalized characters are ‘beautified’ into their *un-normalized* but *normalizable* prettier forms. For example, the InterpretedName `♾.eth` (normalized) would be beautified to `♾️.eth` (un-normalized, but is normalizable back to `♾.eth`). All name and label fields in the Omnigraph provide a `beautified` variant which can be used for display. example.gql ```graphql query Beautified { domain(by: { name: "♾.eth" }) { canonical { name { interpreted # ♾.eth beautified # ♾️.eth } } } } ``` Example.tsx ```tsx

the Domain's name is {domain.canonical?.name.beautified ?? 'unknown'}

``` Only use BeautifiedNames for Display [BeautifiedNames](/docs/reference/terminology#beautified-name) are only suitable for display, not for identification of a Domain. Always use a Domain’s `id` or [InterpretedName](/docs/reference/terminology#interpreted-name) as an identifier (for url paths, for example). ## Relay-spec connections [Section titled “Relay-spec connections”](#relay-spec-connections) Collection and paginated relationship fields in the schema follow the [Relay-spec Connection](https://relay.dev/graphql/connections.htm) pattern with `edges`, `pageInfo`, and `totalCount`. Cursor-based pagination is idiomatic in urql, Apollo, Relay, and most modern GraphQL clients — infinite scroll and stable pagination work out of the box, with no per-endpoint plumbing. example.gql ```graphql query RelayConnections { domain(by: { name: "eth" }) { subdomains(first: 10, order: { by: NAME }) { totalCount pageInfo { hasNextPage endCursor } edges { node { canonical { name { interpreted } } } } } } } ``` ## A complete audit log of ENS Events [Section titled “A complete audit log of ENS Events”](#a-complete-audit-log-of-ens-events) The Omnigraph indexes every onchain Event relevant to ENS and exposes it from the entities each Event relates to: * `Domain.events` — every Event for a specific Domain * `Resolver.events` — every Event emitted by a specific Resolver * `Account.events` — every Event for which an Account is the HCA-aware `sender` * `Permissions.events`, `PermissionsUser.events` — Permission grant and revocation history Each `Event` carries chain, block, transaction, and log metadata, plus an HCA-aware `sender` field distinct from the raw `tx.from` for HCA-mediated transactions. example.gql ```graphql query DomainEvents { domain(by: { name: "vitalik.eth" }) { events(first: 5) { totalCount edges { node { timestamp transactionHash } } } } } ``` ## First-class Permissions [Section titled “First-class Permissions”](#first-class-permissions) In ENSv2, many contracts (like `Registry` and `PermissionedResolver`) have **Permissions** indicating who can do what on a given resource within the contract. It’s a very flexible system, and the Omnigraph gives developers the power to write the necessary queries to drive UI. Permissions are modeled as top-level entities. `Permissions` represents a contract that manages role grants; `PermissionsResource` is an addressable resource within that contract; `PermissionsUser` is a specific user’s role bitmap on a specific resource. Registries, Resolvers, and ENSv2 Domains all expose their Permissions directly (`Registry.permissions`, `Resolver.permissions`, `ENSv2Domain.permissions`), and an `Account` can be queried for every Permission it’s been granted (`Account.permissions`, `Account.registryPermissions`, `Account.resolverPermissions`). Access-aware UIs — “which Domains can this address manage?”, “who can update this Registry?” — become a single query. ### Permissions a user holds [Section titled “Permissions a user holds”](#permissions-a-user-holds) Query an `Account` for every Permission it’s been granted: example.gql ```graphql query PermissionsByUser($address: Address!) { account(by: { address: $address }) { permissions { edges { node { resource roles } } } } } ``` ### Permissions on a contract [Section titled “Permissions on a contract”](#permissions-on-a-contract) Address a `Permissions` entity by the contract that manages it, then walk its resources and the users granted roles on each: example.gql ```graphql query PermissionsByContract($contract: AccountIdInput!) { permissions(by: { contract: $contract }) { resources { edges { node { resource users { edges { node { user { address } roles } } } } } } } } ``` ### Permissions on a Domain [Section titled “Permissions on a Domain”](#permissions-on-a-domain) Start from a Domain by name and read the roles users hold on that Domain’s token (ENSv2 Domains manage Permissions per-token): example.gql ```graphql query DomainPermissions { domain(by: { name: "vitalik.eth" }) { ... on ENSv2Domain { permissions { edges { node { user { address } roles } } } } } } ``` ## Learn more about ENSv2 [Section titled “Learn more about ENSv2”](#learn-more-about-ensv2) New to the ENSv2 protocol? These resources from the ENS team explain the architecture and onchain data model that the Omnigraph unifies with ENSv1: [ENSv2 ](https://ens.domains/ensv2)The ENS team's overview of ENSv2. [ENSv2 Architecture ](https://ens.domains/blog/post/ensv2-architecture)How ENSv2 is architected onchain. [Names Are No Longer Single Objects ](https://ens.domains/blog/post/names-are-no-longer-single-objects)The shift from a flat namehash mapping to a graph of Registries and Domains. [ENS Explorer Deep Dive ](https://ens.domains/blog/post/ens-explorer-deep-dive)A deep dive into exploring ENSv2 onchain state. [ENSv2 Contracts Overview ](https://docs.ens.domains/contracts/ensv2/overview/)The ENSv2 contract documentation. # ENS Omnigraph Example Queries > Ready-to-run Omnigraph examples with `GraphQL`, `enssdk`, and `enskit` snippets, plus links to ENSAdmin and `curl` samples. The **Omnigraph examples** are GraphQL queries with input variables, response examples, `curl` samples, and links to the ENSAdmin playground. Each example also includes ready-to-run **enssdk** (TypeScript) and **enskit** (React) code snippets. [Find Domains ](/docs/integrate/omnigraph/examples/find-domains)List domains matching a name prefix with ordering and registration metadata. [Domain By Name ](/docs/integrate/omnigraph/examples/domain-by-name)Load a domain by interpreted name, including v1/v2 discriminated fields and subregistry on ENSv2. [Domain Subdomains ](/docs/integrate/omnigraph/examples/domain-subdomains)Paginate direct child names under a parent domain. [Domain Events ](/docs/integrate/omnigraph/examples/domain-events)Raw contract events associated with a domain’s registry records. [Account Domains ](/docs/integrate/omnigraph/examples/domains-by-address)Load domains owned by an address via the Omnigraph \`account\` root field. [Account Events ](/docs/integrate/omnigraph/examples/account-events)Events touching an account across indexed ENS contracts. [Registry Domains ](/docs/integrate/omnigraph/examples/registry-domains)Enumerate domains under a specific v2 ETH registry contract. [Permissions By Contract ](/docs/integrate/omnigraph/examples/permissions-by-contract)Roles and users granted on resources for a registrar or registry contract. [Permissions By User ](/docs/integrate/omnigraph/examples/permissions-by-user)Resources and roles for an address in the permissions graph. [Account Resolver Permissions ](/docs/integrate/omnigraph/examples/account-resolver-permissions)Resolver contracts where an account has been granted resolver ACLs. [Domain Resolver ](/docs/integrate/omnigraph/examples/domain-resolver)Assigned resolver, stored records, resolver permissions, and events. [Namegraph ](/docs/integrate/omnigraph/examples/namegraph)Walk the root tree: root → domains → nested subdomains (depth-limited). [Account Migration Counts ](/docs/integrate/omnigraph/examples/account-migrated-names)Count an account's ENSv1 vs ENSv2 domains to gauge its migration progress. [ETH TLD By Version ](/docs/integrate/omnigraph/examples/eth-by-version)Load the .eth TLD across protocol versions: one Domain per version, discriminated by \`\_\_typename\` (ENSv1Domain / ENSv2Domain). # Account Events > Indexed events involving an address across ENS contracts. Events touching an account across indexed ENS contracts. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+AccountEvents%28%0A++%24address%3A+Address%21%0A%29+%7B%0A++account%28by%3A+%7B+address%3A+%24address+%7D%29+%7B%0A++++events+%7B+totalCount+edges+%7B+node+%7B+topics+data+timestamp+%7D+%7D+%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22address%22%3A+%220x801d2e48d378f161dba7ad7ad002ad557714c191%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query AccountEvents( $address: Address! ) { account(by: { address: $address }) { events { totalCount edges { node { topics data timestamp } } } } } ``` Variables ```json { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } ``` Output ```json { "data": { "account": { "events": { "totalCount": 176, "edges": [ { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771853244" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008666f6f6c2e657468000000000000000000000000000000000000000000000000", "timestamp": "1771859376" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771859400" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860816" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860876" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771860912" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771862064" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000001f5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000205227808cb3b93188b047390e4c61db74a9e1e79ad676556576f293b40fc4f35f", "timestamp": "1771864008" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864056" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864152" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000008900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771932120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1772106972" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772537040" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772638812" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772644404" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644440" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644596" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772651544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772654448" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654652" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772656200" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772702844" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772703744" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078720" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078768" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773138828" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140040" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140652" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142524" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142872" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773149148" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158676" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000001000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000001000", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000010000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158904" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158916" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773333000" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773333024" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007776f6f2e65746800000000000000000000000000000000000000000000000000", "timestamp": "1773414312" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773416856" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a7661727468692e65746800000000000000000000000000000000000000000000", "timestamp": "1773418980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706173742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773419052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b706f73747572652e657468000000000000000000000000000000000000000000", "timestamp": "1773420576" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773421128" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a706f7065726c2e65746800000000000000000000000000000000000000000000", "timestamp": "1773421236" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773425064" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773425568" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a626f6f6f6f6d2e65746800000000000000000000000000000000000000000000", "timestamp": "1773658476" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773659052" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773659052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659112" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773659148" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659184" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706f726b2e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659484" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664884" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664884" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773664980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669444" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b70726f6d6973652e657468000000000000000000000000000000000000000000", "timestamp": "1773669600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000977686f6f702e6574680000000000000000000000000000000000000000000000", "timestamp": "1773669924" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773670152" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773672552" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c62616279796f64612e6574680000000000000000000000000000000000000000", "timestamp": "1773672600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773673020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773741168" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x669cb3f55b61c4a2ac7bd5033220fa7daae0f19aa1a5f6fc9f668f6b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773741228" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773834696" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848472" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848796" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773849192" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xfaecfd7df2f4eef2c3253c4b118dc40601a636106c7e2b3407695b0700000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773849252" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851124" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851424" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x56a44589b00955ef58fa4f2cddd98d86e66f76aeb70c5a08714e153900000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773855840" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856224" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856236" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856392" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x30807e1cd3d1c05931f313a61745daea0d818f542283600dcd62a42100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773856452" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773924660" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x1d68e3643b313dde8edc65f9a5fc196ffeb02abf89722ef45daea97000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773924756" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928332" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x42652d1e55e18fb7dfbc9fcb0990ce08babe7d62a931db9afea4d94a00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928464" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928704" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xdeb392cde9ef6cf5ae4f81dfbfb42045d989164cff227cc7978f075b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928764" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773991800" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773991920" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountEventsQuery = graphql(` query AccountEvents( $address: Address! ) { account(by: { address: $address }) { events { totalCount edges { node { topics data timestamp } } } } } `); const result = await client.omnigraph.query({ query: AccountEventsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "account": { "events": { "totalCount": 176, "edges": [ { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771853244" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008666f6f6c2e657468000000000000000000000000000000000000000000000000", "timestamp": "1771859376" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771859400" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860816" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860876" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771860912" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771862064" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000001f5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000205227808cb3b93188b047390e4c61db74a9e1e79ad676556576f293b40fc4f35f", "timestamp": "1771864008" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864056" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864152" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000008900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771932120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1772106972" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772537040" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772638812" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772644404" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644440" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644596" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772651544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772654448" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654652" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772656200" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772702844" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772703744" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078720" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078768" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773138828" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140040" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140652" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142524" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142872" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773149148" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158676" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000001000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000001000", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000010000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158904" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158916" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773333000" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773333024" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007776f6f2e65746800000000000000000000000000000000000000000000000000", "timestamp": "1773414312" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773416856" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a7661727468692e65746800000000000000000000000000000000000000000000", "timestamp": "1773418980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706173742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773419052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b706f73747572652e657468000000000000000000000000000000000000000000", "timestamp": "1773420576" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773421128" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a706f7065726c2e65746800000000000000000000000000000000000000000000", "timestamp": "1773421236" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773425064" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773425568" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a626f6f6f6f6d2e65746800000000000000000000000000000000000000000000", "timestamp": "1773658476" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773659052" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773659052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659112" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773659148" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659184" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706f726b2e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659484" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664884" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664884" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773664980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669444" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b70726f6d6973652e657468000000000000000000000000000000000000000000", "timestamp": "1773669600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000977686f6f702e6574680000000000000000000000000000000000000000000000", "timestamp": "1773669924" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773670152" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773672552" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c62616279796f64612e6574680000000000000000000000000000000000000000", "timestamp": "1773672600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773673020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773741168" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x669cb3f55b61c4a2ac7bd5033220fa7daae0f19aa1a5f6fc9f668f6b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773741228" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773834696" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848472" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848796" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773849192" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xfaecfd7df2f4eef2c3253c4b118dc40601a636106c7e2b3407695b0700000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773849252" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851124" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851424" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x56a44589b00955ef58fa4f2cddd98d86e66f76aeb70c5a08714e153900000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773855840" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856224" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856236" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856392" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x30807e1cd3d1c05931f313a61745daea0d818f542283600dcd62a42100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773856452" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773924660" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x1d68e3643b313dde8edc65f9a5fc196ffeb02abf89722ef45daea97000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773924756" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928332" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x42652d1e55e18fb7dfbc9fcb0990ce08babe7d62a931db9afea4d94a00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928464" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928704" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xdeb392cde9ef6cf5ae4f81dfbfb42045d989164cff227cc7978f075b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928764" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773991800" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773991920" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountEventsQuery = graphql(` query AccountEvents( $address: Address! ) { account(by: { address: $address }) { events { totalCount edges { node { topics data timestamp } } } } } `); function AccountEventsResult() { const [result] = useOmnigraphQuery({ query: AccountEventsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "account": { "events": { "totalCount": 176, "edges": [ { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771853244" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008666f6f6c2e657468000000000000000000000000000000000000000000000000", "timestamp": "1771859376" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771859400" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860816" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860876" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771860912" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771862064" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000001f5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000205227808cb3b93188b047390e4c61db74a9e1e79ad676556576f293b40fc4f35f", "timestamp": "1771864008" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864056" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864152" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000008900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771932120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1772106972" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772537040" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772638812" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772644404" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644440" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644596" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772651544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772654448" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654652" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772656200" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772702844" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772703744" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078720" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078768" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773138828" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140040" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140652" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142524" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142872" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773149148" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158676" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000001000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000001000", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000010000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158904" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158916" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773333000" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773333024" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007776f6f2e65746800000000000000000000000000000000000000000000000000", "timestamp": "1773414312" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773416856" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a7661727468692e65746800000000000000000000000000000000000000000000", "timestamp": "1773418980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706173742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773419052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b706f73747572652e657468000000000000000000000000000000000000000000", "timestamp": "1773420576" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773421128" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a706f7065726c2e65746800000000000000000000000000000000000000000000", "timestamp": "1773421236" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773425064" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773425568" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a626f6f6f6f6d2e65746800000000000000000000000000000000000000000000", "timestamp": "1773658476" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773659052" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773659052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659112" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773659148" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659184" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706f726b2e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659484" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664884" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664884" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773664980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669444" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b70726f6d6973652e657468000000000000000000000000000000000000000000", "timestamp": "1773669600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000977686f6f702e6574680000000000000000000000000000000000000000000000", "timestamp": "1773669924" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773670152" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773672552" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c62616279796f64612e6574680000000000000000000000000000000000000000", "timestamp": "1773672600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773673020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773741168" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x669cb3f55b61c4a2ac7bd5033220fa7daae0f19aa1a5f6fc9f668f6b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773741228" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773834696" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848472" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848796" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773849192" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xfaecfd7df2f4eef2c3253c4b118dc40601a636106c7e2b3407695b0700000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773849252" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851124" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851424" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x56a44589b00955ef58fa4f2cddd98d86e66f76aeb70c5a08714e153900000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773855840" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856224" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856236" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856392" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x30807e1cd3d1c05931f313a61745daea0d818f542283600dcd62a42100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773856452" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773924660" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x1d68e3643b313dde8edc65f9a5fc196ffeb02abf89722ef45daea97000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773924756" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928332" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x42652d1e55e18fb7dfbc9fcb0990ce08babe7d62a931db9afea4d94a00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928464" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928704" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xdeb392cde9ef6cf5ae4f81dfbfb42045d989164cff227cc7978f075b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928764" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773991800" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773991920" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query AccountEvents( $address: Address! ) { account(by: { address: $address }) { events { totalCount edges { node { topics data timestamp } } } } }", "variables": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } EOF ``` Response ```json { "data": { "account": { "events": { "totalCount": 176, "edges": [ { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771853244" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008666f6f6c2e657468000000000000000000000000000000000000000000000000", "timestamp": "1771859376" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771859400" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860816" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771860876" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1771860912" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b686173687469632e657468000000000000000000000000000000000000000000", "timestamp": "1771862064" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1771863960" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000001f5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000205227808cb3b93188b047390e4c61db74a9e1e79ad676556576f293b40fc4f35f", "timestamp": "1771864008" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864056" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001600143024278442c1aa4b3bfa66796b4d21d06cd23f5800000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864104" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000210500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771864152" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000008000008900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1771932120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1772106972" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772537040" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772638812" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772644404" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644440" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772644596" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772651544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772654448" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654544" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772654652" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001248657979212049276d204368616b726921210000000000000000000000000000", "timestamp": "1772656200" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001348657979212049276d204368616b726921212100000000000000000000000000", "timestamp": "1772702844" } }, { "node": { "topics": [ "0x448bc014f1536726cf8d54ff3d6481ed3cbc683c2591ca204274009afa09b1a1", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x1596dc38e2ac5a6ddc5e019af4adcc1e017a04f510d57e69d6879d5d2996bb8e" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b6465736372697074696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001148657979212049276d204368616b726921000000000000000000000000000000", "timestamp": "1772703744" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1772806272" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773073440" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078720" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773078768" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773138828" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140040" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773140652" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142524" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773142872" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773149148" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158676" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000001000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000001000", "timestamp": "1773158736" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000010000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773158904" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x76e06793ada1dfbe9d6636003ddd4d5b747e7e34a842a0a3ee7622a800000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000101100000000000000000000000000000011000000000000000000000000000010110000000000000000000000000000000100", "timestamp": "1773158916" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773333000" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773333024" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007776f6f2e65746800000000000000000000000000000000000000000000000000", "timestamp": "1773414312" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773416856" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a7661727468692e65746800000000000000000000000000000000000000000000", "timestamp": "1773418980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706173742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773419052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b706f73747572652e657468000000000000000000000000000000000000000000", "timestamp": "1773420576" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773421128" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a706f7065726c2e65746800000000000000000000000000000000000000000000", "timestamp": "1773421236" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773425064" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773425568" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a626f6f6f6f6d2e65746800000000000000000000000000000000000000000000", "timestamp": "1773658476" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773659052" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773659052" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659112" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773659148" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008776861742e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659184" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008706f726b2e657468000000000000000000000000000000000000000000000000", "timestamp": "1773659484" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664836" } }, { "node": { "topics": [ "0x65412581168e88a1e60c6459d7f44ae83ad0832e670826c05a4e2476b57af752", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000", "timestamp": "1773664884" } }, { "node": { "topics": [ "0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191", "timestamp": "1773664884" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773664980" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669120" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30784368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773669444" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b70726f6d6973652e657468000000000000000000000000000000000000000000", "timestamp": "1773669600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000977686f6f702e6574680000000000000000000000000000000000000000000000", "timestamp": "1773669924" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773670152" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773672552" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c62616279796f64612e6574680000000000000000000000000000000000000000", "timestamp": "1773672600" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c30786368616b72692e6574680000000000000000000000000000000000000000", "timestamp": "1773673020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773741168" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x669cb3f55b61c4a2ac7bd5033220fa7daae0f19aa1a5f6fc9f668f6b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773741228" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773834696" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848472" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773848796" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773849192" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xfaecfd7df2f4eef2c3253c4b118dc40601a636106c7e2b3407695b0700000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773849252" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851124" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773851424" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773855780" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x56a44589b00955ef58fa4f2cddd98d86e66f76aeb70c5a08714e153900000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773855840" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856224" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856236" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773856392" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x30807e1cd3d1c05931f313a61745daea0d818f542283600dcd62a42100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773856452" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773924660" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x1d68e3643b313dde8edc65f9a5fc196ffeb02abf89722ef45daea97000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773924756" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928332" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x42652d1e55e18fb7dfbc9fcb0990ce08babe7d62a931db9afea4d94a00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928464" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773928704" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xdeb392cde9ef6cf5ae4f81dfbfb42045d989164cff227cc7978f075b00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110000000000000000000000000000001100", "timestamp": "1773928764" } }, { "node": { "topics": [ "0xb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7", "0xe56f85cb9093de6704a8f18de3f7b14f38f7eba195e283cb873121d8262097d1" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a6368616b72692e65746800000000000000000000000000000000000000000000", "timestamp": "1773991800" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111", "timestamp": "1773991920" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Account Resolver Permissions > Resolver contracts where an account holds resolver-scoped permissions. Resolver contracts where an account has been granted resolver ACLs. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+AccountResolverPermissions%28%24address%3A+Address%21%29+%7B%0A++account%28by%3A+%7B+address%3A+%24address+%7D%29+%7B%0A++++resolverPermissions+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++resolver+%7B%0A++++++++++++contract+%7B%0A++++++++++++++address%0A++++++++++++%7D%0A++++++++++%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22address%22%3A+%220x801d2e48d378f161dba7ad7ad002ad557714c191%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query AccountResolverPermissions($address: Address!) { account(by: { address: $address }) { resolverPermissions { edges { node { resolver { contract { address } } } } } } } ``` Variables ```json { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } ``` Output ```json { "data": { "account": { "resolverPermissions": { "edges": [ { "node": { "resolver": { "contract": { "address": "0x9399dec5d023c18661bf6317696b2721931c5988" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountResolverPermissionsQuery = graphql(` query AccountResolverPermissions($address: Address!) { account(by: { address: $address }) { resolverPermissions { edges { node { resolver { contract { address } } } } } } } `); const result = await client.omnigraph.query({ query: AccountResolverPermissionsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "account": { "resolverPermissions": { "edges": [ { "node": { "resolver": { "contract": { "address": "0x9399dec5d023c18661bf6317696b2721931c5988" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountResolverPermissionsQuery = graphql(` query AccountResolverPermissions($address: Address!) { account(by: { address: $address }) { resolverPermissions { edges { node { resolver { contract { address } } } } } } } `); function AccountResolverPermissionsResult() { const [result] = useOmnigraphQuery({ query: AccountResolverPermissionsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "account": { "resolverPermissions": { "edges": [ { "node": { "resolver": { "contract": { "address": "0x9399dec5d023c18661bf6317696b2721931c5988" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query AccountResolverPermissions($address: Address!) { account(by: { address: $address }) { resolverPermissions { edges { node { resolver { contract { address } } } } } } }", "variables": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } EOF ``` Response ```json { "data": { "account": { "resolverPermissions": { "edges": [ { "node": { "resolver": { "contract": { "address": "0x9399dec5d023c18661bf6317696b2721931c5988" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Domain By Name > Omnigraph query for a single domain with v1/v2 fields. Load a domain by interpreted name, including v1/v2 discriminated fields and subregistry on ENSv2. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+DomainByName%28%24name%3A+InterpretedName%21%29+%7B%0A++domain%28by%3A+%7Bname%3A+%24name%7D%29+%7B%0A++++__typename%0A++++id%0A++++label+%7B+interpreted+hash+%7D%0A++++canonical+%7B+name+%7B+interpreted+%7D+node+path+%7B+id+%7D+%7D%0A++++owner+%7B+address+%7D%0A++++subregistry+%7B+contract+%7B+chainId+address+%7D+%7D%0A%0A++++...+on+ENSv1Domain+%7B%0A++++++rootRegistryOwner+%7B+address+%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22name%22%3A+%22roppp.eth%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query DomainByName($name: InterpretedName!) { domain(by: {name: $name}) { __typename id label { interpreted hash } canonical { name { interpreted } node path { id } } owner { address } subregistry { contract { chainId address } } ... on ENSv1Domain { rootRegistryOwner { address } } } } ``` Variables ```json { "name": "roppp.eth" } ``` Output ```json { "data": { "domain": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432", "label": { "interpreted": "roppp", "hash": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b" }, "subregistry": null, "owner": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" }, "canonical": { "name": { "interpreted": "roppp.eth" }, "node": "0x39095c3dfb872d6441c95547f88591e7fb97014eef30cabe3df12a9b2a64dbe8", "path": [ { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e-35894389512221139346028120028875095598761990588366713962827482865183915769856" }, { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432" } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainByNameQuery = graphql(` query DomainByName($name: InterpretedName!) { domain(by: {name: $name}) { __typename id label { interpreted hash } canonical { name { interpreted } node path { id } } owner { address } subregistry { contract { chainId address } } ... on ENSv1Domain { rootRegistryOwner { address } } } } `); const result = await client.omnigraph.query({ query: DomainByNameQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "domain": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432", "label": { "interpreted": "roppp", "hash": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b" }, "subregistry": null, "owner": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" }, "canonical": { "name": { "interpreted": "roppp.eth" }, "node": "0x39095c3dfb872d6441c95547f88591e7fb97014eef30cabe3df12a9b2a64dbe8", "path": [ { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e-35894389512221139346028120028875095598761990588366713962827482865183915769856" }, { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432" } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainByNameQuery = graphql(` query DomainByName($name: InterpretedName!) { domain(by: {name: $name}) { __typename id label { interpreted hash } canonical { name { interpreted } node path { id } } owner { address } subregistry { contract { chainId address } } ... on ENSv1Domain { rootRegistryOwner { address } } } } `); function DomainByNameResult() { const [result] = useOmnigraphQuery({ query: DomainByNameQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "domain": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432", "label": { "interpreted": "roppp", "hash": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b" }, "subregistry": null, "owner": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" }, "canonical": { "name": { "interpreted": "roppp.eth" }, "node": "0x39095c3dfb872d6441c95547f88591e7fb97014eef30cabe3df12a9b2a64dbe8", "path": [ { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e-35894389512221139346028120028875095598761990588366713962827482865183915769856" }, { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432" } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query DomainByName($name: InterpretedName!) { domain(by: {name: $name}) { __typename id label { interpreted hash } canonical { name { interpreted } node path { id } } owner { address } subregistry { contract { chainId address } } ... on ENSv1Domain { rootRegistryOwner { address } } } }", "variables": { "name": "roppp.eth" } } EOF ``` Response ```json { "data": { "domain": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432", "label": { "interpreted": "roppp", "hash": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b" }, "subregistry": null, "owner": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" }, "canonical": { "name": { "interpreted": "roppp.eth" }, "node": "0x39095c3dfb872d6441c95547f88591e7fb97014eef30cabe3df12a9b2a64dbe8", "path": [ { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e-35894389512221139346028120028875095598761990588366713962827482865183915769856" }, { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432" } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Domain Events > Contract events linked to a domain’s on-chain records. Raw contract events associated with a domain’s registry records. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+DomainEvents%28%24name%3A+InterpretedName%21%29+%7B%0A++domain%28by%3A+%7Bname%3A+%24name%7D%29+%7B%0A++++events+%7B%0A++++++totalCount%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++from%0A++++++++++to%0A++++++++++topics%0A++++++++++data%0A++++++++++timestamp%0A++++++++++transactionHash%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22name%22%3A+%22roppp.eth%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query DomainEvents($name: InterpretedName!) { domain(by: {name: $name}) { events { totalCount edges { node { from to topics data timestamp transactionHash } } } } } ``` Variables ```json { "name": "roppp.eth" } ``` Output ```json { "data": { "domain": { "events": { "edges": [ { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0x2fe093918572373e9f1f0368f414dffd0043a74ae8c9fd7b0e390b26a0d20b6e", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000006bf187840000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1000000000000000000000000000000000000000000000000000000000000000000000001", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xebd3982eafd13b820e3edb2a4abd57a82ce3b8802e0cd45637a5de51383f9fac", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050e8854658f7b92297fd3c8241e249f615dca0150000000000000000000000000000000000000000000000000000000001e133800000000000000000000000006fdfd2a902ae83a1617abc47eec6d9d2cbe7d38e000000000000000000000000000000000000000000000000000000000079fca600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } } ], "totalCount": 3 } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainEventsQuery = graphql(` query DomainEvents($name: InterpretedName!) { domain(by: {name: $name}) { events { totalCount edges { node { from to topics data timestamp transactionHash } } } } } `); const result = await client.omnigraph.query({ query: DomainEventsQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "domain": { "events": { "edges": [ { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0x2fe093918572373e9f1f0368f414dffd0043a74ae8c9fd7b0e390b26a0d20b6e", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000006bf187840000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1000000000000000000000000000000000000000000000000000000000000000000000001", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xebd3982eafd13b820e3edb2a4abd57a82ce3b8802e0cd45637a5de51383f9fac", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050e8854658f7b92297fd3c8241e249f615dca0150000000000000000000000000000000000000000000000000000000001e133800000000000000000000000006fdfd2a902ae83a1617abc47eec6d9d2cbe7d38e000000000000000000000000000000000000000000000000000000000079fca600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } } ], "totalCount": 3 } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainEventsQuery = graphql(` query DomainEvents($name: InterpretedName!) { domain(by: {name: $name}) { events { totalCount edges { node { from to topics data timestamp transactionHash } } } } } `); function DomainEventsResult() { const [result] = useOmnigraphQuery({ query: DomainEventsQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "domain": { "events": { "edges": [ { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0x2fe093918572373e9f1f0368f414dffd0043a74ae8c9fd7b0e390b26a0d20b6e", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000006bf187840000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1000000000000000000000000000000000000000000000000000000000000000000000001", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xebd3982eafd13b820e3edb2a4abd57a82ce3b8802e0cd45637a5de51383f9fac", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050e8854658f7b92297fd3c8241e249f615dca0150000000000000000000000000000000000000000000000000000000001e133800000000000000000000000006fdfd2a902ae83a1617abc47eec6d9d2cbe7d38e000000000000000000000000000000000000000000000000000000000079fca600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } } ], "totalCount": 3 } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query DomainEvents($name: InterpretedName!) { domain(by: {name: $name}) { events { totalCount edges { node { from to topics data timestamp transactionHash } } } } }", "variables": { "name": "roppp.eth" } } EOF ``` Response ```json { "data": { "domain": { "events": { "edges": [ { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0x2fe093918572373e9f1f0368f414dffd0043a74ae8c9fd7b0e390b26a0d20b6e", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1a4cc166b", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000006bf187840000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c1000000000000000000000000000000000000000000000000000000000000000000000001", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } }, { "node": { "from": "0x801d2e48d378f161dba7ad7ad002ad557714c191", "to": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "topics": [ "0xebd3982eafd13b820e3edb2a4abd57a82ce3b8802e0cd45637a5de51383f9fac", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050e8854658f7b92297fd3c8241e249f615dca0150000000000000000000000000000000000000000000000000000000001e133800000000000000000000000006fdfd2a902ae83a1617abc47eec6d9d2cbe7d38e000000000000000000000000000000000000000000000000000000000079fca600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005726f707070000000000000000000000000000000000000000000000000000000", "timestamp": "1779454980", "transactionHash": "0xed0c37a9fa3e9757a1946fd099089d7c7ce12166c227451f76c207d2c639afd7" } } ], "totalCount": 3 } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Domain Resolver > Assigned resolver, records, permissions, and resolver events for a name. Assigned resolver, stored records, resolver permissions, and events. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+DomainResolver%28%24name%3A+InterpretedName%21%29+%7B%0A++domain%28by%3A+%7B+name%3A+%24name+%7D%29+%7B%0A++++resolver+%7B%0A++++++assigned+%7B%0A++++++++records+%7B+edges+%7B+node+%7B+node+keys+coinTypes+%7D+%7D+%7D%0A++++++++permissions+%7B+resources+%7B+edges+%7B+node+%7B+resource+users+%7B+edges+%7B+node+%7B+user+%7B+address+%7D+roles+%7D+%7D+%7D+%7D+%7D+%7D+%7D%0A++++++++events+%7B+totalCount+edges+%7B+node+%7B+topics+data+timestamp+%7D+%7D+%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22name%22%3A+%22roppp.eth%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query DomainResolver($name: InterpretedName!) { domain(by: { name: $name }) { resolver { assigned { records { edges { node { node keys coinTypes } } } permissions { resources { edges { node { resource users { edges { node { user { address } roles } } } } } } } events { totalCount edges { node { topics data timestamp } } } } } } } ``` Variables ```json { "name": "roppp.eth" } ``` Output ```json { "data": { "domain": { "resolver": { "assigned": null } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainResolverQuery = graphql(` query DomainResolver($name: InterpretedName!) { domain(by: { name: $name }) { resolver { assigned { records { edges { node { node keys coinTypes } } } permissions { resources { edges { node { resource users { edges { node { user { address } roles } } } } } } } events { totalCount edges { node { topics data timestamp } } } } } } } `); const result = await client.omnigraph.query({ query: DomainResolverQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "domain": { "resolver": { "assigned": null } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainResolverQuery = graphql(` query DomainResolver($name: InterpretedName!) { domain(by: { name: $name }) { resolver { assigned { records { edges { node { node keys coinTypes } } } permissions { resources { edges { node { resource users { edges { node { user { address } roles } } } } } } } events { totalCount edges { node { topics data timestamp } } } } } } } `); function DomainResolverResult() { const [result] = useOmnigraphQuery({ query: DomainResolverQuery, variables: { name: asInterpretedName("roppp.eth"), }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "domain": { "resolver": { "assigned": null } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query DomainResolver($name: InterpretedName!) { domain(by: { name: $name }) { resolver { assigned { records { edges { node { node keys coinTypes } } } permissions { resources { edges { node { resource users { edges { node { user { address } roles } } } } } } } events { totalCount edges { node { topics data timestamp } } } } } } }", "variables": { "name": "roppp.eth" } } EOF ``` Response ```json { "data": { "domain": { "resolver": { "assigned": null } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Domain Subdomains > Paginate child names under a parent domain. Paginate direct child names under a parent domain. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+DomainSubdomains%28%24name%3A+InterpretedName%21%29+%7B%0A++domain%28by%3A+%7Bname%3A+%24name%7D%29+%7B%0A++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++subdomains%28first%3A+10%29+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22name%22%3A+%22eth%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query DomainSubdomains($name: InterpretedName!) { domain(by: {name: $name}) { canonical { name { interpreted beautified } } subdomains(first: 10) { edges { node { canonical { name { interpreted beautified } } } } } } } ``` Variables ```json { "name": "eth" } ``` Output ```json { "data": { "domain": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainSubdomainsQuery = graphql(` query DomainSubdomains($name: InterpretedName!) { domain(by: {name: $name}) { canonical { name { interpreted beautified } } subdomains(first: 10) { edges { node { canonical { name { interpreted beautified } } } } } } } `); const result = await client.omnigraph.query({ query: DomainSubdomainsQuery, variables: { name: asInterpretedName("eth"), }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "domain": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { asInterpretedName } from "enssdk"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const DomainSubdomainsQuery = graphql(` query DomainSubdomains($name: InterpretedName!) { domain(by: {name: $name}) { canonical { name { interpreted beautified } } subdomains(first: 10) { edges { node { canonical { name { interpreted beautified } } } } } } } `); function DomainSubdomainsResult() { const [result] = useOmnigraphQuery({ query: DomainSubdomainsQuery, variables: { name: asInterpretedName("eth"), }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "domain": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query DomainSubdomains($name: InterpretedName!) { domain(by: {name: $name}) { canonical { name { interpreted beautified } } subdomains(first: 10) { edges { node { canonical { name { interpreted beautified } } } } } } }", "variables": { "name": "eth" } } EOF ``` Response ```json { "data": { "domain": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Account Domains > Omnigraph query listing domains for an address. Load domains owned by an address via the Omnigraph \`account\` root field. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+AccountDomains%28%0A++%24address%3A+Address%21%0A%29+%7B%0A++account%28by%3A+%7B+address%3A+%24address+%7D%29+%7B%0A++++domains+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++label+%7B+interpreted+%7D%0A++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22address%22%3A+%220x801d2e48d378f161dba7ad7ad002ad557714c191%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } ``` Variables ```json { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } ``` Output ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountDomainsQuery = graphql(` query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } `); const result = await client.omnigraph.query({ query: AccountDomainsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const AccountDomainsQuery = graphql(` query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } `); function AccountDomainsResult() { const [result] = useOmnigraphQuery({ query: AccountDomainsQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query AccountDomains( $address: Address! ) { account(by: { address: $address }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } }", "variables": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } EOF ``` Response ```json { "data": { "account": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } }, { "node": { "label": { "interpreted": "papa" }, "canonical": null } }, { "node": { "label": { "interpreted": "parker" }, "canonical": null } }, { "node": { "label": { "interpreted": "lowlife" }, "canonical": null } }, { "node": { "label": { "interpreted": "warrpp" }, "canonical": null } }, { "node": { "label": { "interpreted": "2year" }, "canonical": null } }, { "node": { "label": { "interpreted": "chakri" }, "canonical": null } }, { "node": { "label": { "interpreted": "pichin" }, "canonical": null } }, { "node": { "label": { "interpreted": "0xchakri" }, "canonical": null } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Find Domains > List domains by name filter with ordering and registration fields. List domains matching a name prefix with ordering and registration metadata. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+FindDomains%28%0A++%24name%3A+DomainsNameFilter%21%0A++%24order%3A+DomainsOrderInput%0A%29+%7B%0A++domains%28%0A++++where%3A+%7B+name%3A+%24name+%7D%0A++++order%3A+%24order%0A++++first%3A+20%0A++%29+%7B%0A++++edges+%7B%0A++++++node+%7B%0A++++++++__typename%0A++++++++id%0A++++++++label+%7B+interpreted+hash+%7D%0A++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A%0A++++++++registration+%7B+expiry+event+%7B+timestamp+%7D+%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22name%22%3A+%7B%0A++++%22starts_with%22%3A+%22sf%22%0A++%7D%2C%0A++%22order%22%3A+%7B%0A++++%22by%22%3A+%22NAME%22%2C%0A++++%22dir%22%3A+%22DESC%22%0A++%7D%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query FindDomains( $name: DomainsNameFilter! $order: DomainsOrderInput ) { domains( where: { name: $name } order: $order first: 20 ) { edges { node { __typename id label { interpreted hash } canonical { name { interpreted beautified } } registration { expiry event { timestamp } } } } } } ``` Variables ```json { "name": { "starts_with": "sf" }, "order": { "by": "NAME", "dir": "DESC" } } ``` Output ```json { "data": { "domains": { "edges": [ { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056", "label": { "interpreted": "sfmpfvtoicv2ok", "hash": "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cbd771cbc7" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "registration": { "expiry": "1781810940", "event": { "timestamp": "1779391740" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-37259562946248504018379781627254217478007643438678910837598068740925220192256", "label": { "interpreted": "sfmpfv44d0res", "hash": "0x52602a50858115661619fb28cf543ee766c182e0be6743c72d5bd674b3d12686" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "registration": { "expiry": "1816283593", "event": { "timestamp": "1779390804" } } } }, { "node": { "__typename": "ENSv1Domain", "id": "11155111-0xb6fb46e1458915dd828633d91e1df8e4c3f2d4dd-0x9b365136312d7ee6e232e3c98e459bc8667ec818c47fbbc55bb5e23d0a21e8cc", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1810926648", "event": { "timestamp": "1779390648" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1816283449", "event": { "timestamp": "1779390984" } } } } ] } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const FindDomainsQuery = graphql(` query FindDomains( $name: DomainsNameFilter! $order: DomainsOrderInput ) { domains( where: { name: $name } order: $order first: 20 ) { edges { node { __typename id label { interpreted hash } canonical { name { interpreted beautified } } registration { expiry event { timestamp } } } } } } `); const result = await client.omnigraph.query({ query: FindDomainsQuery, variables: { name: { "starts_with": "sf" }, order: { "by": "NAME", "dir": "DESC" }, }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "domains": { "edges": [ { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056", "label": { "interpreted": "sfmpfvtoicv2ok", "hash": "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cbd771cbc7" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "registration": { "expiry": "1781810940", "event": { "timestamp": "1779391740" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-37259562946248504018379781627254217478007643438678910837598068740925220192256", "label": { "interpreted": "sfmpfv44d0res", "hash": "0x52602a50858115661619fb28cf543ee766c182e0be6743c72d5bd674b3d12686" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "registration": { "expiry": "1816283593", "event": { "timestamp": "1779390804" } } } }, { "node": { "__typename": "ENSv1Domain", "id": "11155111-0xb6fb46e1458915dd828633d91e1df8e4c3f2d4dd-0x9b365136312d7ee6e232e3c98e459bc8667ec818c47fbbc55bb5e23d0a21e8cc", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1810926648", "event": { "timestamp": "1779390648" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1816283449", "event": { "timestamp": "1779390984" } } } } ] } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const FindDomainsQuery = graphql(` query FindDomains( $name: DomainsNameFilter! $order: DomainsOrderInput ) { domains( where: { name: $name } order: $order first: 20 ) { edges { node { __typename id label { interpreted hash } canonical { name { interpreted beautified } } registration { expiry event { timestamp } } } } } } `); function FindDomainsResult() { const [result] = useOmnigraphQuery({ query: FindDomainsQuery, variables: { name: { "starts_with": "sf" }, order: { "by": "NAME", "dir": "DESC" }, }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "domains": { "edges": [ { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056", "label": { "interpreted": "sfmpfvtoicv2ok", "hash": "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cbd771cbc7" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "registration": { "expiry": "1781810940", "event": { "timestamp": "1779391740" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-37259562946248504018379781627254217478007643438678910837598068740925220192256", "label": { "interpreted": "sfmpfv44d0res", "hash": "0x52602a50858115661619fb28cf543ee766c182e0be6743c72d5bd674b3d12686" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "registration": { "expiry": "1816283593", "event": { "timestamp": "1779390804" } } } }, { "node": { "__typename": "ENSv1Domain", "id": "11155111-0xb6fb46e1458915dd828633d91e1df8e4c3f2d4dd-0x9b365136312d7ee6e232e3c98e459bc8667ec818c47fbbc55bb5e23d0a21e8cc", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1810926648", "event": { "timestamp": "1779390648" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1816283449", "event": { "timestamp": "1779390984" } } } } ] } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query FindDomains( $name: DomainsNameFilter! $order: DomainsOrderInput ) { domains( where: { name: $name } order: $order first: 20 ) { edges { node { __typename id label { interpreted hash } canonical { name { interpreted beautified } } registration { expiry event { timestamp } } } } } }", "variables": { "name": { "starts_with": "sf" }, "order": { "by": "NAME", "dir": "DESC" } } } EOF ``` Response ```json { "data": { "domains": { "edges": [ { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056", "label": { "interpreted": "sfmpfvtoicv2ok", "hash": "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cbd771cbc7" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "registration": { "expiry": "1781810940", "event": { "timestamp": "1779391740" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-37259562946248504018379781627254217478007643438678910837598068740925220192256", "label": { "interpreted": "sfmpfv44d0res", "hash": "0x52602a50858115661619fb28cf543ee766c182e0be6743c72d5bd674b3d12686" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "registration": { "expiry": "1816283593", "event": { "timestamp": "1779390804" } } } }, { "node": { "__typename": "ENSv1Domain", "id": "11155111-0xb6fb46e1458915dd828633d91e1df8e4c3f2d4dd-0x9b365136312d7ee6e232e3c98e459bc8667ec818c47fbbc55bb5e23d0a21e8cc", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1810926648", "event": { "timestamp": "1779390648" } } } }, { "node": { "__typename": "ENSv2Domain", "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480", "label": { "interpreted": "sfmpfv44d0mig", "hash": "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a25430835b3f309e" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "registration": { "expiry": "1816283449", "event": { "timestamp": "1779390984" } } } } ] } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Namegraph > Explore the root tree with nested subdomain connections. Walk the root tree: root → domains → nested subdomains (depth-limited). omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+Namegraph+%7B%0A++root+%7B%0A++++id%0A++++domains+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A%0A++++++++++subdomains+%7B%0A++++++++++++edges+%7B%0A++++++++++++++node+%7B%0A++++++++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A%0A++++++++++++++++subdomains+%7B%0A++++++++++++++++++edges+%7B%0A++++++++++++++++++++node+%7B%0A++++++++++++++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++++++++++++++++++%7D%0A++++++++++++++++++%7D%0A++++++++++++++++%7D%0A++++++++++++++%7D%0A++++++++++++%7D%0A++++++++++%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query Namegraph { root { id domains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } } } } } } } } } } } } ``` Variables ```json {} ``` Output ```json { "data": { "root": { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e", "domains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } }, "subdomains": { "edges": [] } } } ] } } }, { "node": { "canonical": { "name": { "interpreted": "reverse", "beautified": "reverse" } }, "subdomains": { "edges": [] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const NamegraphQuery = graphql(` query Namegraph { root { id domains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } } } } } } } } } } } } `); const result = await client.omnigraph.query({ query: NamegraphQuery, variables: {}, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "root": { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e", "domains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } }, "subdomains": { "edges": [] } } } ] } } }, { "node": { "canonical": { "name": { "interpreted": "reverse", "beautified": "reverse" } }, "subdomains": { "edges": [] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const NamegraphQuery = graphql(` query Namegraph { root { id domains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } } } } } } } } } } } } `); function NamegraphResult() { const [result] = useOmnigraphQuery({ query: NamegraphQuery, variables: {}, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "root": { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e", "domains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } }, "subdomains": { "edges": [] } } } ] } } }, { "node": { "canonical": { "name": { "interpreted": "reverse", "beautified": "reverse" } }, "subdomains": { "edges": [] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query Namegraph { root { id domains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } subdomains { edges { node { canonical { name { interpreted beautified } } } } } } } } } } } } }", "variables": {} } EOF ``` Response ```json { "data": { "root": { "id": "11155111-0x835f0b284e78cd3f358bcf6cba3b53809f09b79e", "domains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "eth", "beautified": "eth" } }, "subdomains": { "edges": [ { "node": { "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } }, "subdomains": { "edges": [] } } }, { "node": { "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } }, "subdomains": { "edges": [] } } } ] } } }, { "node": { "canonical": { "name": { "interpreted": "reverse", "beautified": "reverse" } }, "subdomains": { "edges": [] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Permissions By Contract > Role assignments on resources for a registrar or registry contract. Roles and users granted on resources for a registrar or registry contract. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+PermissionsByContract%28%0A++%24contract%3A+AccountIdInput%21%0A%29+%7B%0A++permissions%28by%3A+%7B+contract%3A+%24contract+%7D%29+%7B%0A++++resources+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++resource%0A++++++++++users+%7B%0A++++++++++++edges+%7B%0A++++++++++++++node+%7B%0A++++++++++++++++id%0A++++++++++++++++user+%7B+address+%7D%0A++++++++++++++++roles%0A++++++++++++++%7D%0A++++++++++++%7D%0A++++++++++%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++++events+%7B+totalCount+edges+%7B+node+%7B+topics+data+timestamp+%7D+%7D+%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22contract%22%3A+%7B%0A++++%22chainId%22%3A+11155111%2C%0A++++%22address%22%3A+%220x64c81210d0e580cfc7746f3fb910bf0e8f6378e1%22%0A++%7D%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query PermissionsByContract( $contract: AccountIdInput! ) { permissions(by: { contract: $contract }) { resources { edges { node { resource users { edges { node { id user { address } roles } } } } } } events { totalCount edges { node { topics data timestamp } } } } } ``` Variables ```json { "contract": { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" } } ``` Output ```json { "data": { "permissions": { "events": { "totalCount": 13, "edges": [ { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001011100000000000000000000001000000100", "timestamp": "1779388956" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000de85e6b9928062fd2347d78a5bcac6266078f381" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389076" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389208" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000c8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389244" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389304" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a254308300000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000101100000", "timestamp": "1779390984" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391032" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779391140" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cb00000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779391740" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779454980" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xf8d7557a9078b7f2ec7a360343d930464290445144a57d86ff028b9e00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779455940" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x9731bb62095b2c3aff87771acb9595cf4069bc8d85f9b22a0d6f271500000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779708084" } } ] }, "resources": { "edges": [ { "node": { "resource": "0", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "roles": "65537", "user": { "address": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc", "roles": "16", "user": { "address": "0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xde85e6b9928062fd2347d78a5bcac6266078f381", "roles": "16", "user": { "address": "0xde85e6b9928062fd2347d78a5bcac6266078f381" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "23384048590936731391298299384680068791797867872512", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-112554048520345018269255786667391470421317806528110367240542760381540064034816-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "30078755955643454526763071980293195785165410039216352470119925106082295316480", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043062435250176", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "49509597771493908415463190501045916291230588437784211605615168713991762477056", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-68387108911874305622019956908914347119991166106996198835225265868637904830464-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const PermissionsByContractQuery = graphql(` query PermissionsByContract( $contract: AccountIdInput! ) { permissions(by: { contract: $contract }) { resources { edges { node { resource users { edges { node { id user { address } roles } } } } } } events { totalCount edges { node { topics data timestamp } } } } } `); const result = await client.omnigraph.query({ query: PermissionsByContractQuery, variables: { contract: { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" }, }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "permissions": { "events": { "totalCount": 13, "edges": [ { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001011100000000000000000000001000000100", "timestamp": "1779388956" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000de85e6b9928062fd2347d78a5bcac6266078f381" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389076" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389208" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000c8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389244" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389304" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a254308300000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000101100000", "timestamp": "1779390984" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391032" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779391140" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cb00000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779391740" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779454980" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xf8d7557a9078b7f2ec7a360343d930464290445144a57d86ff028b9e00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779455940" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x9731bb62095b2c3aff87771acb9595cf4069bc8d85f9b22a0d6f271500000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779708084" } } ] }, "resources": { "edges": [ { "node": { "resource": "0", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "roles": "65537", "user": { "address": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc", "roles": "16", "user": { "address": "0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xde85e6b9928062fd2347d78a5bcac6266078f381", "roles": "16", "user": { "address": "0xde85e6b9928062fd2347d78a5bcac6266078f381" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "23384048590936731391298299384680068791797867872512", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-112554048520345018269255786667391470421317806528110367240542760381540064034816-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "30078755955643454526763071980293195785165410039216352470119925106082295316480", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043062435250176", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "49509597771493908415463190501045916291230588437784211605615168713991762477056", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-68387108911874305622019956908914347119991166106996198835225265868637904830464-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const PermissionsByContractQuery = graphql(` query PermissionsByContract( $contract: AccountIdInput! ) { permissions(by: { contract: $contract }) { resources { edges { node { resource users { edges { node { id user { address } roles } } } } } } events { totalCount edges { node { topics data timestamp } } } } } `); function PermissionsByContractResult() { const [result] = useOmnigraphQuery({ query: PermissionsByContractQuery, variables: { contract: { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" }, }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "permissions": { "events": { "totalCount": 13, "edges": [ { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001011100000000000000000000001000000100", "timestamp": "1779388956" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000de85e6b9928062fd2347d78a5bcac6266078f381" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389076" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389208" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000c8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389244" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389304" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a254308300000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000101100000", "timestamp": "1779390984" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391032" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779391140" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cb00000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779391740" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779454980" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xf8d7557a9078b7f2ec7a360343d930464290445144a57d86ff028b9e00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779455940" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x9731bb62095b2c3aff87771acb9595cf4069bc8d85f9b22a0d6f271500000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779708084" } } ] }, "resources": { "edges": [ { "node": { "resource": "0", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "roles": "65537", "user": { "address": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc", "roles": "16", "user": { "address": "0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xde85e6b9928062fd2347d78a5bcac6266078f381", "roles": "16", "user": { "address": "0xde85e6b9928062fd2347d78a5bcac6266078f381" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "23384048590936731391298299384680068791797867872512", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-112554048520345018269255786667391470421317806528110367240542760381540064034816-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "30078755955643454526763071980293195785165410039216352470119925106082295316480", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043062435250176", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "49509597771493908415463190501045916291230588437784211605615168713991762477056", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-68387108911874305622019956908914347119991166106996198835225265868637904830464-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query PermissionsByContract( $contract: AccountIdInput! ) { permissions(by: { contract: $contract }) { resources { edges { node { resource users { edges { node { id user { address } roles } } } } } } events { totalCount edges { node { topics data timestamp } } } } }", "variables": { "contract": { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" } } } EOF ``` Response ```json { "data": { "permissions": { "events": { "totalCount": 13, "edges": [ { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001011100000000000000000000001000000100", "timestamp": "1779388956" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000de85e6b9928062fd2347d78a5bcac6266078f381" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389076" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389208" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000c8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "timestamp": "1779389244" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779389304" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x427ff8f9f4fd0a505202d30de02a518dccf3124efeffec27a254308300000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000101100000", "timestamp": "1779390984" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000dfcafafa8fe3976a48c0b4c0076d30347756803a" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391020" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000004d78defc01d3f1cee1f338ef29ef1b449189d76d" ], "data": "0x00000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000000", "timestamp": "1779391032" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000b68e594a47fe057bd31e7a8229ffcfd85b2e28af" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001", "timestamp": "1779391140" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x6d757075b889ecac34ee231b86f1d6dd54544aa24ade226b398204cb00000000", "0x000000000000000000000000ffffffffff52d316b7bd028358089bc8066b8f80" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779391740" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xc32a1c1f67a98f2a48153a0e85ddb762207044cc7beb9517b6da41c100000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779454980" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0xf8d7557a9078b7f2ec7a360343d930464290445144a57d86ff028b9e00000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779455940" } }, { "node": { "topics": [ "0x0d35bf721a39b614de00ca5038e1deb0cb0c69a278645e83405a7226cf80ba3c", "0x9731bb62095b2c3aff87771acb9595cf4069bc8d85f9b22a0d6f271500000000", "0x000000000000000000000000801d2e48d378f161dba7ad7ad002ad557714c191" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001110000000000000000000000000000001100000", "timestamp": "1779708084" } } ] }, "resources": { "edges": [ { "node": { "resource": "0", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af", "roles": "65537", "user": { "address": "0xb68e594a47fe057bd31e7a8229ffcfd85b2e28af" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc", "roles": "16", "user": { "address": "0xc8283ef6e8b596d28f0fab1d77a2b6d5c11a56cc" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xde85e6b9928062fd2347d78a5bcac6266078f381", "roles": "16", "user": { "address": "0xde85e6b9928062fd2347d78a5bcac6266078f381" } } }, { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-0-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "23384048590936731391298299384680068791797867872512", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-112554048520345018269255786667391470421317806528110367240542760381540064034816-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "30078755955643454526763071980293195785165410039216352470119925106082295316480", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-30078755955643454526763071980293195785165410039216352470119925106082295316480-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043062435250176", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "49509597771493908415463190501045916291230588437784211605615168713991762477056", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-49509597771493908415463190501045916291230588437784211605615168713991762477056-0xffffffffff52d316b7bd028358089bc8066b8f80", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0xffffffffff52d316b7bd028358089bc8066b8f80" } } } ] } } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-68387108911874305622019956908914347119991166106996198835225265868637904830464-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "users": { "edges": [ { "node": { "id": "11155111-0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1-88275407146030613359050872632052369891139576190404928761656352489271755538432-0x801d2e48d378f161dba7ad7ad002ad557714c191", "roles": "97409655027181761882228017414928043058140282880", "user": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } } ] } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Permissions By User > Resources and roles granted to an address in the permissions graph. Resources and roles for an address in the permissions graph. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+PermissionsByUser%28%24address%3A+Address%21%29+%7B%0A++account%28by%3A+%7B+address%3A+%24address+%7D%29+%7B%0A++++permissions+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++resource%0A++++++++++roles%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22address%22%3A+%220x801d2e48d378f161dba7ad7ad002ad557714c191%22%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query PermissionsByUser($address: Address!) { account(by: { address: $address }) { permissions { edges { node { resource roles } } } } } ``` Variables ```json { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } ``` Output ```json { "data": { "account": { "permissions": { "edges": [ { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100596850956938885978174593669551872270111018338549503879635102532903930691584", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "102623694373211491555009035094559714354867743719557864344496925996064085901312", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "115061862606611006322947321926094058913838231075545733418125643349220558635008", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "17565556413878516582137143978832942513746058687678642436478257514785879883776", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "47555008405725925871881562820415724146935900447909861161766755986357052506112", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "5083230141678648537579652339825251396476183556159423196043475005611716902912", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100730731216472425149614051772411748752370404603892636293425280561699996303360", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "103868244565005153685472930816800188156072080527826024224867352621882598752256", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "112148247289050404383163358581446075174679349669810732817237246229525351628800", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "113496937590509701538466432478879907535189286588065688299471307765143325114368", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "13302394102300747465659074421300489273052104662126971087135273331541397209088", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "21938043553513802374538263015201796589121725328925699815953296099556081008640", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30031410956466539409329572042834626850714888309222059767211834742340763779072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30166873793351246120262010003076545765451990617869617561029632148999159939072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "39189147829333990189759366365378966893534837875361659862943329783516552495104", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "46412780725005246540648946880367418902470155183061763985435851320156938567680", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "49275396006068933882889624874132353425001708610589221730791169634520914722816", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "358292832037329894457564098653916839351943424" } }, { "node": { "resource": "61921648186461472109092181993588857623373756977923892800245020241413766381568", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "85982363319436916495878017611318277580058432505046647661384209306247170621440", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const PermissionsByUserQuery = graphql(` query PermissionsByUser($address: Address!) { account(by: { address: $address }) { permissions { edges { node { resource roles } } } } } `); const result = await client.omnigraph.query({ query: PermissionsByUserQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "account": { "permissions": { "edges": [ { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100596850956938885978174593669551872270111018338549503879635102532903930691584", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "102623694373211491555009035094559714354867743719557864344496925996064085901312", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "115061862606611006322947321926094058913838231075545733418125643349220558635008", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "17565556413878516582137143978832942513746058687678642436478257514785879883776", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "47555008405725925871881562820415724146935900447909861161766755986357052506112", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "5083230141678648537579652339825251396476183556159423196043475005611716902912", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100730731216472425149614051772411748752370404603892636293425280561699996303360", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "103868244565005153685472930816800188156072080527826024224867352621882598752256", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "112148247289050404383163358581446075174679349669810732817237246229525351628800", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "113496937590509701538466432478879907535189286588065688299471307765143325114368", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "13302394102300747465659074421300489273052104662126971087135273331541397209088", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "21938043553513802374538263015201796589121725328925699815953296099556081008640", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30031410956466539409329572042834626850714888309222059767211834742340763779072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30166873793351246120262010003076545765451990617869617561029632148999159939072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "39189147829333990189759366365378966893534837875361659862943329783516552495104", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "46412780725005246540648946880367418902470155183061763985435851320156938567680", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "49275396006068933882889624874132353425001708610589221730791169634520914722816", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "358292832037329894457564098653916839351943424" } }, { "node": { "resource": "61921648186461472109092181993588857623373756977923892800245020241413766381568", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "85982363319436916495878017611318277580058432505046647661384209306247170621440", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const PermissionsByUserQuery = graphql(` query PermissionsByUser($address: Address!) { account(by: { address: $address }) { permissions { edges { node { resource roles } } } } } `); function PermissionsByUserResult() { const [result] = useOmnigraphQuery({ query: PermissionsByUserQuery, variables: { address: "0x801d2e48d378f161dba7ad7ad002ad557714c191", }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "account": { "permissions": { "edges": [ { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100596850956938885978174593669551872270111018338549503879635102532903930691584", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "102623694373211491555009035094559714354867743719557864344496925996064085901312", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "115061862606611006322947321926094058913838231075545733418125643349220558635008", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "17565556413878516582137143978832942513746058687678642436478257514785879883776", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "47555008405725925871881562820415724146935900447909861161766755986357052506112", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "5083230141678648537579652339825251396476183556159423196043475005611716902912", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100730731216472425149614051772411748752370404603892636293425280561699996303360", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "103868244565005153685472930816800188156072080527826024224867352621882598752256", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "112148247289050404383163358581446075174679349669810732817237246229525351628800", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "113496937590509701538466432478879907535189286588065688299471307765143325114368", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "13302394102300747465659074421300489273052104662126971087135273331541397209088", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "21938043553513802374538263015201796589121725328925699815953296099556081008640", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30031410956466539409329572042834626850714888309222059767211834742340763779072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30166873793351246120262010003076545765451990617869617561029632148999159939072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "39189147829333990189759366365378966893534837875361659862943329783516552495104", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "46412780725005246540648946880367418902470155183061763985435851320156938567680", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "49275396006068933882889624874132353425001708610589221730791169634520914722816", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "358292832037329894457564098653916839351943424" } }, { "node": { "resource": "61921648186461472109092181993588857623373756977923892800245020241413766381568", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "85982363319436916495878017611318277580058432505046647661384209306247170621440", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query PermissionsByUser($address: Address!) { account(by: { address: $address }) { permissions { edges { node { resource roles } } } } }", "variables": { "address": "0x801d2e48d378f161dba7ad7ad002ad557714c191" } } EOF ``` Response ```json { "data": { "account": { "permissions": { "edges": [ { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "112554048520345018269255786667391470421317806528110367240542760381540064034816", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "68387108911874305622019956908914347119991166106996198835225265868637904830464", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "88275407146030613359050872632052369891139576190404928761656352489271755538432", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100596850956938885978174593669551872270111018338549503879635102532903930691584", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "102623694373211491555009035094559714354867743719557864344496925996064085901312", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "115061862606611006322947321926094058913838231075545733418125643349220558635008", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "17565556413878516582137143978832942513746058687678642436478257514785879883776", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "47555008405725925871881562820415724146935900447909861161766755986357052506112", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "5083230141678648537579652339825251396476183556159423196043475005611716902912", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "97409655027181761882228017414928043058140282880" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "100730731216472425149614051772411748752370404603892636293425280561699996303360", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "103868244565005153685472930816800188156072080527826024224867352621882598752256", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "112148247289050404383163358581446075174679349669810732817237246229525351628800", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "113496937590509701538466432478879907535189286588065688299471307765143325114368", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "13302394102300747465659074421300489273052104662126971087135273331541397209088", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "21938043553513802374538263015201796589121725328925699815953296099556081008640", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30031410956466539409329572042834626850714888309222059767211834742340763779072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "30166873793351246120262010003076545765451990617869617561029632148999159939072", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "39189147829333990189759366365378966893534837875361659862943329783516552495104", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "46412780725005246540648946880367418902470155183061763985435851320156938567680", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "49275396006068933882889624874132353425001708610589221730791169634520914722816", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "53769404736607288977878255120630826840934634369261042595868516633854242455552", "roles": "358292832037329894457564098653916839351943424" } }, { "node": { "resource": "61921648186461472109092181993588857623373756977923892800245020241413766381568", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "85982363319436916495878017611318277580058432505046647661384209306247170621440", "roles": "358292832037329894457564098653916839351947520" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } }, { "node": { "resource": "0", "roles": "7719472615821079694904732333912527190217998977709370935963838933860875309329" } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # Registry Domains > Domains registered under a v2 ETH registry contract. Enumerate domains under a specific v2 ETH registry contract. omnigraphenssdkenskitcurl [ Run in ENSAdmin ](https://admin.ensnode.io/api/omnigraph?query=query+RegistryDomains%28%0A++%24registry%3A+AccountIdInput%21%0A%29+%7B%0A++registry%28by%3A+%7B+contract%3A+%24registry+%7D%29+%7B%0A++++domains+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++label+%7B+interpreted+%7D%0A++++++++++canonical+%7B+name+%7B+interpreted+beautified+%7D+%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D\&connection=https%3A%2F%2Fapi.v2-sepolia.ensnode.io\&variables=%7B%0A++%22registry%22%3A+%7B%0A++++%22chainId%22%3A+11155111%2C%0A++++%22address%22%3A+%220x64c81210d0e580cfc7746f3fb910bf0e8f6378e1%22%0A++%7D%0A%7D)Open an interactive playground to execute this example on our [sepolia-v2 ](/docs/hosted-instances#ensnode-v2-sepolia)ENSNode instance. GraphQL ```graphql query RegistryDomains( $registry: AccountIdInput! ) { registry(by: { contract: $registry }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } ``` Variables ```json { "registry": { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" } } ``` Output ```json { "data": { "registry": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0mig" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0res" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfvtoicv2ok" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TypeScript ```typescript import { createEnsNodeClient } from "enssdk/core"; import { graphql, omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: process.env.ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const RegistryDomainsQuery = graphql(` query RegistryDomains( $registry: AccountIdInput! ) { registry(by: { contract: $registry }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } `); const result = await client.omnigraph.query({ query: RegistryDomainsQuery, variables: { registry: { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" }, }, }); if (result.errors) throw new Error(JSON.stringify(result.errors)); console.log(JSON.stringify(result.data, null, 2)); ``` Output ```json { "data": { "registry": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0mig" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0res" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfvtoicv2ok" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enssdk package manager setup npm pnpm ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script npm init -y && touch src/index.ts npm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies npm install enssdk@1.15.1 && npm install -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm start ``` ```bash # 1. Create project mkdir -p my-ens-script/src && cd my-ens-script pnpm init && touch src/index.ts pnpm pkg set type=module scripts.start="tsx src/index.ts" # 2. Install dependencies pnpm add enssdk@1.15.1 && pnpm add -D tsx typescript @types/node # 3. Paste the TypeScript snippet above into src/index.ts # 4. Run ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm start ``` See the [enssdk docs](/docs/integrate/integration-options/enssdk) for gql.tada plugin and tsconfig setup. Run in StackBlitz Opens an editable StackBlitz project with this snippet. TSX (React) ```tsx import { OmnigraphProvider, useOmnigraphQuery, graphql } from "enskit/react/omnigraph"; import { createEnsNodeClient } from "enssdk/core"; import { omnigraph } from "enssdk/omnigraph"; const client = createEnsNodeClient({ url: import.meta.env.VITE_ENSNODE_URL || "https://api.v2-sepolia.ensnode.io" }).extend(omnigraph); const RegistryDomainsQuery = graphql(` query RegistryDomains( $registry: AccountIdInput! ) { registry(by: { contract: $registry }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } } `); function RegistryDomainsResult() { const [result] = useOmnigraphQuery({ query: RegistryDomainsQuery, variables: { registry: { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" }, }, }); const { data, fetching, error } = result; if (!data && fetching) return

Loading…

; if (error) return

Error: {error.message}

; if (!data) return

No data returned.

; const formatted = JSON.stringify( data, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2, ); return {formatted}; } export default function App() { return ( ); } ``` Output ```json { "data": { "registry": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0mig" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0res" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfvtoicv2ok" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. ### enskit package manager setup npm pnpm ```bash # 1. Create project npm create vite@latest my-ens-app -- --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies npm install npm install enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm run dev ``` ```bash # 1. Create project pnpm create vite@latest my-ens-app --template react-ts --no-interactive --no-immediate cd my-ens-app # 2. Install dependencies pnpm install pnpm add enskit@1.15.1 enssdk@1.15.1 # 3. Copy the TSX snippet above into src/App.tsx # 4. Run VITE_ENSNODE_URL=https://api.v2-sepolia.ensnode.io pnpm run dev ``` See the [enskit docs](/docs/integrate/integration-options/enskit) for gql.tada plugin and provider setup. cURL ```bash # POST JSON to your ENSNode Omnigraph endpoint (same path enssdk uses). curl -sS -X POST "https://api.v2-sepolia.ensnode.io/api/omnigraph" \ -H "Content-Type: application/json" \ -d @- <<'EOF' { "query": "query RegistryDomains( $registry: AccountIdInput! ) { registry(by: { contract: $registry }) { domains { edges { node { label { interpreted } canonical { name { interpreted beautified } } } } } } }", "variables": { "registry": { "chainId": 11155111, "address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1" } } } EOF ``` Response ```json { "data": { "registry": { "domains": { "edges": [ { "node": { "label": { "interpreted": "katrenpadu" }, "canonical": { "name": { "interpreted": "katrenpadu.eth", "beautified": "katrenpadu.eth" } } } }, { "node": { "label": { "interpreted": "roppp" }, "canonical": { "name": { "interpreted": "roppp.eth", "beautified": "roppp.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0mig" }, "canonical": { "name": { "interpreted": "sfmpfv44d0mig.eth", "beautified": "sfmpfv44d0mig.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfv44d0res" }, "canonical": { "name": { "interpreted": "sfmpfv44d0res.eth", "beautified": "sfmpfv44d0res.eth" } } } }, { "node": { "label": { "interpreted": "sfmpfvtoicv2ok" }, "canonical": { "name": { "interpreted": "sfmpfvtoicv2ok.eth", "beautified": "sfmpfvtoicv2ok.eth" } } } }, { "node": { "label": { "interpreted": "wrapnation" }, "canonical": { "name": { "interpreted": "wrapnation.eth", "beautified": "wrapnation.eth" } } } } ] } } } } ``` Output matches a GraphQL Response snapshot; live output depends on your ENSNode instance. [Back to Examples](/docs/integrate/omnigraph/examples) # ENS Protocol Acceleration > How the ENS Omnigraph API accelerates ENS resolution by implementing the ENS Universal Resolver over indexed ENS data. The ENS Omnigraph API can resolve ENS records against ENSNode’s indexed data instead of live RPC using **ENS Protocol Acceleration**. The Omnigraph effectively implements the ENS Universal Resolver on top of the indexed ENS data model in [ENSDb](/docs/services/ensdb), so most resolutions complete in a single API call — no RPC round trips and no CCIP-Read gateways for your app to orchestrate — while still returning results that follow the ENS resolution protocol exactly. Omnigraph Resolution Coming Soon 🚧 Protocol Accelerated Resolution is currently available via the [ENSApi Resolution API](/docs/services/ensapi/reference/api-reference#tag/resolution) but will be moved to the Omnigraph in an upcoming release. The following document describes how Protocol Acceleration *currently* works (via ENSApi’s HTTP API), as well as how it will eventually work within the Omnigraph API. ## The problem with RPC-based resolution [Section titled “The problem with RPC-based resolution”](#the-problem-with-rpc-based-resolution) Resolving an ENS name the traditional way means talking to an Ethereum node over RPC, and that path is slow and complex: * **Round trips and latency.** A single, simple resolution may be one RPC call to the UniversalResolver (*100ms*), but many names (like all `.base.eth` and `.linea.eth` names) result in CCIP-Reads to offchain gateways, providing highly variable request durations (*100-1000ms*). * **RPC resolutions produce ‘raw’ results.** When resolving records directly, your app receives the raw data stored onchain, which requires further interpretation to produce user-friendly and actionable values. * **Resolving records for an address doubles this cost.** Many apps start from an address → look up its primary name → forward-resolve the records on that name. Each stage stacks more round trips on top of the last. * **It happens at the last mile.** These round trips run on end-user devices, far from internet backbones — not on servers co-located in major data centers — so the latency hits exactly where it hurts UX most. * **Don’t forget about batching!** Imagine loading the display names and avatars for a 100-person leaderboard — without extra work on your end, that’s 100+ RPC requests (plus CCIP-Read requests for any offchain names!) just to show avatars, and you’re looking at latencies in the 100-500ms range, sometimes up to multiple seconds depending on the speed of the offchain gateway in that moment. ## The problem with existing tooling [Section titled “The problem with existing tooling”](#the-problem-with-existing-tooling) * **Thick clients are language-locked.** Libraries like viem, wagmi, and ensjs tie you to a single programming language. Building a protocol-compliant ENS client from raw RPC in another language is a serious effort: studying the ENSIPs, rigorous testing, and continuous maintenance as the protocol evolves. * **RPC access is rate-limited, paid, or self-hosted.** You either accept rate limits, pay for a provider, or take on the cost and complexity of running your own Ethereum node. * **Existing ENS APIs are constrained.** Some support only a handful of profile records; others use caching strategies that serve stale ENS data. ## Why it matters [Section titled “Why it matters”](#why-it-matters) * **Latency shapes ENS UX.** Slow resolution makes for bad UX, and apps with tens or hundreds of millions of users won’t integrate ENS if it feels slow. This is especially acute for social apps rendering many user profiles at once. * **Complexity throttles growth.** Difficult resolution damages ENS’s network effects. * **Stale records are dangerous.** Consider sending a high-value transaction to `yourname.eth` and having the funds go to the wrong address because resolution served stale data. ## How it works [Section titled “How it works”](#how-it-works) The Omnigraph answers resolution requests by implementing the ENS Universal Resolver’s Forward and Reverse Resolution logic directly over ENSNode’s indexed data. For every record a resolution touches, ENSNode uses its indexed data to accelerate that record as much as possible — serving it straight from the index rather than making an RPC call. Acceleration is applied per record: each record in a resolution is independently accelerated wherever the indexed data makes it possible. When a particular record can’t be accelerated from indexed data — for example a resolver with behaviour ENSNode can’t reproduce from its index — ENSNode transparently falls back to RPC-based resolution, performing the lookup according to the ENS Forward Resolution protocol. That includes carrying out any CCIP-Read operations on your behalf for offchain names, so your app never has to orchestrate CCIP-Read itself. The result is identical to what a fully protocol-compliant resolution would return; acceleration only changes how fast you get there, never what you get. In general, a fully accelerated request can return in *\~10ms* rather than the standard *\~100ms* from an RPC. ## Strengthen the decentralization and “unstoppability” of ENS [Section titled “Strengthen the decentralization and “unstoppability” of ENS”](#strengthen-the-decentralization-and-unstoppability-of-ens) By resolving against indexed ENS data, ENSNode removes the strict dependency on centralized offchain gateway servers whose downtime would otherwise shut down impacted multichain ENS names that are key to the future of ENSv2 — subnames of `base.eth`, `linea.eth`, tokenized DNS names like `.box`, and more. ## How it optimizes the developer experience [Section titled “How it optimizes the developer experience”](#how-it-optimizes-the-developer-experience) Protocol Acceleration isn’t only about speed — the Omnigraph also makes resolution easier to consume: * **Semantic query patterns.** Convenience patterns on top of raw resolutions — e.g. query by chain ID or a common chain name rather than computing a coin type. * **Interpreted responses.** Normalized and validated records rather than ‘raw’ onchain data. * **It’s an API, not an RPC client.** Because resolution is exposed through the [ENS Omnigraph API](/docs/integrate/omnigraph), any app in any programming language can integrate in a type-safe way without a new language-specific client. * **Ready-to-go thin clients.** For TypeScript and React, `enssdk` and `enskit` give you a typed client on top of the Omnigraph API, including support for Protocol Accelerated Resolution. [Integrate with ENSNode ](/docs/integrate/integration-options)Integrate the Omnigraph API to power record resolution in your app # ENS Unigraph SQL > Query the unified onchain state of ENSv1 and ENSv2 directly over SQL. The **ENS Unigraph** combines every ENSv1 “Nametree” (the mainnet ENS Registry, Basenames on Base, Lineanames on Linea, 3DNS on Optimism, and more) and the ENSv2 “Namegraph” into a **single unified data model** — indexed into [ENSDb](/docs/services/ensdb), a standard PostgreSQL database. **ENS Unigraph SQL** is direct, read-only SQL access to that data. The [ENS Omnigraph API](/docs/integrate/omnigraph) is built *on top of* the Unigraph; Unigraph SQL is the layer beneath it, for use cases that go past what the Omnigraph exposes — custom analytics, data pipelines, dashboards, or building your own service. Because it’s just Postgres, you can query it from **any language with a Postgres driver** — TypeScript, Python, Rust, Go, and more. Requires a self-hosted ENSNode SQL access connects directly to your ENSDb (PostgreSQL) instance, so you need to run your own ENSNode — NameHash’s hosted instances don’t expose direct database access. See the [self-hosting overview](/docs/self-host) to get an instance running. If you’d rather use a hosted API, reach for the [ENS Omnigraph API](/docs/integrate/omnigraph) instead. ## One model across ENSv1 and ENSv2 [Section titled “One model across ENSv1 and ENSv2”](#one-model-across-ensv1-and-ensv2) The Unigraph models both protocol versions with the **same polymorphic entities**, so the shape of your query rarely changes between an ENSv1 and an ENSv2 name: * **Domain** — any name, on either protocol version. A `type` discriminator (`ENSv1Domain` / `ENSv2Domain`) tells you which, but the columns you read are shared. * **Registry** — the contracts that hold subnames. ENSv1 and ENSv2 both follow the same `Registry → Domain → (sub)Registry → Domain → …` namegraph shape. * **Account** — an address; owners, registrants, and permission holders all point here. * **Registration** & **Renewal** — the lifecycle of a name, polymorphic across registration types (`.eth` `BaseRegistrar`, `NameWrapper`, 3DNS, and ENSv2 Registry registrations). * **Resolver** records — indexed resolver state (addresses, text records, content hashes). * **Label** — the `labelHash` → label mapping that heals hashed labels into human-readable names. * **Permissions** — the ENSv2 roles model for who can do what in a specific contract. ## The Canonical Nametree [Section titled “The Canonical Nametree”](#the-canonical-nametree) The **Canonical Nametree** is the set of all Domains that have an inferrable **Canonical Name** — materialized from the namegraph. For every Domain in it, the canonical fields are populated — `canonical_name`, `canonical_path`, `canonical_node`, and `canonical_depth` — across both ENSv1 and ENSv2. That means you can look a name up by `canonical_name = 'vitalik.eth'`, order by `canonical_depth`, or walk a name’s path **without branching on protocol version** and without traversing the namegraph yourself. Multichain coverage is part of the same model: Basenames (`.base.eth`), Lineanames (`.linea.eth`), and 3DNS names (`.box`) are materialized into the same Unigraph as mainnet `.eth`, so a single query spans every indexable name. Not a substitute for ENS Resolution The Unigraph mirrors onchain state; ENS’s resolution-time behavior is applied *on top* of it by [ENSApi](/docs/services/ensapi). A direct SQL read of resolver records is therefore **not** ENSIP-10 / CCIP-Read compliant and **cannot** be used as a source of truth for name resolution. For correct resolution, use the [ENS Omnigraph API](/docs/integrate/omnigraph) (which adds ENS Protocol Acceleration on top of the Unigraph). Use Unigraph SQL for analytics, discovery, and custom indexing. ## Get started [Section titled “Get started”](#get-started) [Examples ](/docs/integrate/unigraph/examples)Domain by name, account domains, and indexing status — in raw SQL and with the ensdb-sdk. [Schema Reference ](/docs/integrate/unigraph/schema-reference)Every table, column, and index in the Unigraph data model. [ENSDb (service) ](/docs/services/ensdb)How the Unigraph is indexed, stored, and self-hosted. # Unigraph SQL Examples > Connect to an ENSDb instance and query the Unigraph in raw SQL or with the ensdb-sdk. These examples show the same queries two ways: in **raw SQL** (any PostgreSQL client, any language) and with the typed **[`@ensnode/ensdb-sdk`](https://www.npmjs.com/package/@ensnode/ensdb-sdk)** for TypeScript projects. SQL access requires connecting to your own [self-hosted](/docs/self-host) ENSDb instance. `unigraph` plugin required Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration) ## Connect [Section titled “Connect”](#connect) The Unigraph lives in [ENSDb](/docs/services/ensdb), a PostgreSQL database. Each ENSIndexer instance writes to its own **ENSIndexer Schema** (e.g. `ensindexer_0`); shared operational metadata lives in the `ensnode` schema. * SQL ```bash psql postgresql://user:password@host:5432/ensdb_devnet ``` Discover the available ENSIndexer Schemas: ```sql SELECT DISTINCT ens_indexer_schema_name FROM ensnode.metadata; ``` * ensdb-sdk (TypeScript) ```bash npm install @ensnode/ensdb-sdk ``` ENSDb Reader The `EnsDbReader` object enables you to build custom queries against the Unigraph data model in ENSDb. Use the `ensDb` field to build queries with the Drizzle ORM, and the `ensIndexerSchema` field to reference database objects (i.e. the Unigraph tables) within the [ENSIndexer Schema](/docs/services/ensdb/concepts/glossary#ensindexer-schema) in a type-safe way. ```typescript import { EnsDbReader } from "@ensnode/ensdb-sdk"; // Connect by providing a connection string and the ENSIndexer Schema Name to query const ensDbReader = new EnsDbReader(ensDbConnectionString, ensIndexerSchemaName); const { ensDb, ensIndexerSchema } = ensDbReader; ``` ## Examples [Section titled “Examples”](#examples) [Domain by Name ](/docs/integrate/unigraph/examples/domain-by-name) [Account Domains ](/docs/integrate/unigraph/examples/account-domains) [Indexing Status ](/docs/integrate/unigraph/examples/indexing-status) # Account Domains > Count an address's Domains, grouped by protocol version (ENSv1 vs ENSv2). `unigraph` plugin required Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration) Count the Domains owned by an address, grouped by Domain `type` (`ENSv1Domain` vs `ENSv2Domain`) — a single query spanning both protocol versions. See [Connect](/docs/integrate/unigraph/examples) for setup. * SQL What is an ENSIndexer Schema? An [ENSIndexer Schema](/docs/services/ensdb/concepts/glossary#ensindexer-schema) is a database schema within an [ENSDb instance](/docs/services/ensdb/concepts/glossary#ensdb-instance), used to store indexed ENS data from a given [ENSIndexer instance](/docs/services/ensdb/concepts/glossary#ensindexer-instance). We use `ensindexer_0` as the [ENSIndexer Schema Name](/docs/services/ensdb/concepts/glossary#ensindexer-schema-name) in examples on this page, but your [ENSIndexer instance](/docs/services/ensdb/concepts/glossary#ensindexer-instance) may be configured to use a different schema name based on how you configured its \`ENSINDEXER\_SCHEMA\_NAME\` environment variable. Make sure to replace `ensindexer_0` with the actual schema name used by your ENSIndexer instance when querying the ENSDb instance directly. ```sql SELECT type, count(*) FROM ensindexer_0.domains WHERE owner_id = '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' GROUP BY type; ``` View Query Result ``` [ { "type": "ENSv1Domain", "count": 2 }, { "type": "ENSv2Domain", "count": 14 } ] ``` * ensdb-sdk (TypeScript) Reading data from your ENSDb See how to connect to ENSDb and get access to the `ensDb` query builder and `ensIndexerSchema` schema definition in the [Connect](/docs/integrate/unigraph/examples) section if you haven't already. ```typescript import { count, eq } from "drizzle-orm"; const counts = await ensDb .select({ type: ensIndexerSchema.domain.type, count: count() }) .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.ownerId, "0x70997970c51812dc3a010c7d01b50e0d17dc79c8")) .groupBy(ensIndexerSchema.domain.type); ``` View Query Result ``` [ { "type": "ENSv1Domain", "count": 2 }, { "type": "ENSv2Domain", "count": 14 } ] ``` # Domain by Name > Fetch a Domain from the Unigraph by its canonical name. `unigraph` plugin required Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration) Fetch a Domain by its canonical name. Because `canonical_name` is materialized across both ENSv1 and ENSv2, the same lookup works regardless of protocol version. See [Connect](/docs/integrate/unigraph/examples) for setup. Canonical fields Canonical fields are populated on every Domain reachable from the canonical root, across both ENSv1 and ENSv2 — query them uniformly without branching by `type`. In SQL, these columns are `canonical_name`, `canonical_path`, `canonical_node`, and `canonical_depth`; in `ensdb-sdk`, the corresponding fields are `canonicalName`, `canonicalPath`, `canonicalNode`, and `canonicalDepth`. * SQL What is an ENSIndexer Schema? An [ENSIndexer Schema](/docs/services/ensdb/concepts/glossary#ensindexer-schema) is a database schema within an [ENSDb instance](/docs/services/ensdb/concepts/glossary#ensdb-instance), used to store indexed ENS data from a given [ENSIndexer instance](/docs/services/ensdb/concepts/glossary#ensindexer-instance). We use `ensindexer_0` as the [ENSIndexer Schema Name](/docs/services/ensdb/concepts/glossary#ensindexer-schema-name) in examples on this page, but your [ENSIndexer instance](/docs/services/ensdb/concepts/glossary#ensindexer-instance) may be configured to use a different schema name based on how you configured its \`ENSINDEXER\_SCHEMA\_NAME\` environment variable. Make sure to replace `ensindexer_0` with the actual schema name used by your ENSIndexer instance when querying the ENSDb instance directly. ```sql SELECT * FROM ensindexer_0.domains WHERE canonical_name = 'vitalik.eth'; ``` View Query Result ``` [ { "id": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "type": "ENSv1Domain", "registry_id": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", "subregistry_id": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "token_id": null, "node": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "label_hash": "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", "owner_id": "0x220866b1a2219f40e72f5c628b65d54268ca3a9d", "root_registry_owner_id": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "canonical": true, "canonical_name": "vitalik.eth", "canonical_label_hash_path": [ "0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0", "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc" ], "canonical_path": [ "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835" ], "canonical_depth": 2, "canonical_node": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835" } ] ``` * ensdb-sdk (TypeScript) Reading data from your ENSDb See how to connect to ENSDb and get access to the `ensDb` query builder and `ensIndexerSchema` schema definition in the [Connect](/docs/integrate/unigraph/examples) section if you haven't already. ```typescript import { eq } from "drizzle-orm"; const [vitalik] = await ensDb .select() .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.canonicalName, "vitalik.eth")); console.log(vitalik); ``` View Query Result ``` [ { "id": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "type": "ENSv1Domain", "registryId": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", "subregistryId": "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "tokenId": null, "node": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", "labelHash": "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", "ownerId": "0x220866b1a2219f40e72f5c628b65d54268ca3a9d", "rootRegistryOwnerId": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "canonical": true, "canonicalName": "vitalik.eth", "canonicalLabelHashPath": [ "0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0", "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc" ], "canonicalPath": [ "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", "1-0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e-0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835" ], "canonicalDepth": 2, "canonicalNode": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835" } ] ``` # Indexing Status > Read the indexing status snapshot for an ENSIndexer instance from ENSDb. `unigraph` plugin required Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration) Read the indexing status snapshot for an ENSIndexer instance from the shared `ensnode.metadata` table. See [Connect](/docs/integrate/unigraph/examples) for setup. * SQL What is ENSNode Schema? The [ENSNode Schema](/docs/services/ensdb/concepts/glossary#ensnode-schema) is a database schema within an [ENSDb instance](/docs/services/ensdb/concepts/glossary#ensdb-instance), used to store metadata about [ENSIndexer instances](/docs/services/ensdb/concepts/glossary#ensindexer-instance) that have ever connected to the ENSDb instance. There can be only one ENSNode Schema per ENSDb instance, and its name is always `ensnode`. ```sql -- Indexing status snapshot for the `ensindexer_0` ENSIndexer Schema SELECT value -> 'indexingStatus' as indexing_status_snapshot FROM "ensnode"."metadata" WHERE ens_indexer_schema_name = 'ensindexer_0' AND key = 'indexing_metadata_context'; ``` View Query Result ``` { "indexing_status_snapshot": { "strategy": "omnichain", "snapshotTime": 1779795066, "omnichainSnapshot": { "chains": { "1": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 3327417, "timestamp": 1489165544 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 25171597, "timestamp": 1779703391 }, "latestIndexedBlock": { "number": 21224717, "timestamp": 1732054691 } }, "10": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 110393959, "timestamp": 1696386695 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 152052671, "timestamp": 1779704119 }, "latestIndexedBlock": { "number": 128226309, "timestamp": 1732051395 } }, "8453": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 17522624, "timestamp": 1721834595 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 46457386, "timestamp": 1779704119 }, "latestIndexedBlock": { "number": 22632818, "timestamp": 1732054983 } }, "42161": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 349263357, "timestamp": 1750406457 } }, "chainStatus": "chain-queued" }, "59144": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 6682888, "timestamp": 1720768992 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 30774477, "timestamp": 1779703911 }, "latestIndexedBlock": { "number": 12280006, "timestamp": 1732054967 } }, "534352": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 16604272, "timestamp": 1750406415 } }, "chainStatus": "chain-queued" } }, "omnichainStatus": "omnichain-backfill", "omnichainIndexingCursor": 1732054983 }, "slowestChainIndexingCursor": 1732054983 } } ``` * ensdb-sdk (TypeScript) Reading data from your ENSDb See how to connect to ENSDb and get access to the `ensDb` query builder and `ensIndexerSchema` schema definition in the [Connect](/docs/integrate/unigraph/examples) section if you haven't already. ```typescript import { IndexingMetadataContextStatusCodes } from "@ensnode/ensdb-sdk"; const indexingMetadataContext = await ensDbReader.getIndexingMetadataContext(); if (indexingMetadataContext.statusCode === IndexingMetadataContextStatusCodes.Initialized) { const indexingStatusSnapshot = indexingMetadataContext.indexingStatus; console.log(indexingStatusSnapshot); } ``` View Query Result ``` { "strategy": "omnichain", "snapshotTime": 1779795066, "omnichainSnapshot": { "chains": { "1": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 3327417, "timestamp": 1489165544 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 25171597, "timestamp": 1779703391 }, "latestIndexedBlock": { "number": 21224717, "timestamp": 1732054691 } }, "10": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 110393959, "timestamp": 1696386695 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 152052671, "timestamp": 1779704119 }, "latestIndexedBlock": { "number": 128226309, "timestamp": 1732051395 } }, "8453": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 17522624, "timestamp": 1721834595 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 46457386, "timestamp": 1779704119 }, "latestIndexedBlock": { "number": 22632818, "timestamp": 1732054983 } }, "42161": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 349263357, "timestamp": 1750406457 } }, "chainStatus": "chain-queued" }, "59144": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 6682888, "timestamp": 1720768992 } }, "chainStatus": "chain-backfill", "backfillEndBlock": { "number": 30774477, "timestamp": 1779703911 }, "latestIndexedBlock": { "number": 12280006, "timestamp": 1732054967 } }, "534352": { "config": { "rangeType": "left-bounded", "startBlock": { "number": 16604272, "timestamp": 1750406415 } }, "chainStatus": "chain-queued" } }, "omnichainStatus": "omnichain-backfill", "omnichainIndexingCursor": 1732054983 }, "slowestChainIndexingCursor": 1732054983 } ``` # Unigraph Schema Reference > The unified ENSv1 + ENSv2 data model of the ENS Unigraph — every table, column, and index. This is the canonical reference for the **Unigraph schema** — the unified, polymorphic data model that the [`unigraph` plugin](/docs/services/ensindexer) materializes into [ENSDb](/docs/services/ensdb). It models ENSv1 and ENSv2 with shared, polymorphic entities (Domains, Registries, Registrations, Renewals, Resolvers), so the same query shape works across both protocol versions. Each ENSIndexer instance owns a dedicated database schema (e.g. `ensindexer_0`) holding all of its indexed Unigraph data, fully isolated from other instances. Unigraph plugin hard dependency The Unigraph plugin has a hard dependency on the **Protocol Acceleration** plugin (Domain–Resolver relations, resolver records, ENSv1 Registry-migration state). Those tables, the shared **ENSNode Schema**, and the other ENSIndexer plugins (Registrars, Subgraph, tokenscope) are documented in the [ENSDb · Database Schemas](/docs/services/ensdb/concepts/database-schemas) reference. Defined in [`unigraph.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts). ## Design Principles [Section titled “Design Principles”](#design-principles) This schema takes a balanced approach to materialization: it mimics on-chain state as closely as possible, with the exception of materializing specific state that must be trivially filterable. Then, resolution-time logic is applied on *top* of this index, at query-time, mimicking ENS’s own resolution-time behavior. This forces our implementation to match the protocol as closely as possible, with the obvious note that the performance tradeoffs of evm code and our app are different. For example, it’s more expensive for us to recursively traverse the `namegraph` (like evm code does) because our individual roundtrips from the db are relatively more expensive, but can be batched. In general: the indexed schema should match on-chain state as closely as possible, and resolution-time behavior within the ENS protocol should *also* be implemented at resolution time in ENSApi. The current obvious exception is that `domains.owner_id` for ENSv1 Domains is the *materialized* *effective* owner. ENSv1 includes a diverse number of ways to ‘own’ a domain, including the ENSv1 Registry, various Registrars, and the NameWrapper. The ENSv1 indexing logic within this Unigraph plugin materializes the effective owner to simplify this aspect of ENS and enable efficient queries against `domains.owner_id`. Additionally, a Domain’s canonicality-derived fields (`canonical_name`, `canonical_label_hash_path`, `canonical_path`, `canonical_depth`, `canonical_node`) are materialized to facilitate query-time performance. When necessary, all data models are shared or polymorphic between ENSv1 and ENSv2, including Domains, Registries, Registrations, Renewals, and Resolvers. Registrations are polymorphic between the defined RegistrationTypes, depending on the associated guarantees (for example, ENSv1 BaseRegistrar Registrations may have a gracePeriod, but ENSv2 Registry Registrations do not). The `Label` entity (`labelHash` → `InterpretedLabel`) remains the source of truth for label values. ENSv1 and ENSv2 both fit the `Registry` → `Domain` → (`Sub`)`Registry` → `Domain` → … `namegraph` model. For ENSv1, each domain that has children implicitly owns a “virtual” `Registry` (a row of type `ENSv1VirtualRegistry`) whose sole parent is that domain; children of the parent then point their `registryId` at the virtual registry. Concrete `ENSv1Registry` rows (e.g. the mainnet ENS Registry, the Basenames Registry, the Lineanames Registry) sit at the top. ENSv2 namegraphs are rooted in a single `ENSv2Registry` RootRegistry on the ENS Root Chain and are possibly circular directed graphs. The full namegraph is never materialized, only *navigated* at resolution-time, with the exception of the Canonical Nametree (the set of Domains with an inferrable Canonical Name), which *is* materialized inline. Note also that the Protocol Acceleration plugin is a hard requirement for the Unigraph plugin. This allows us to rely on the shared logic for indexing: a) ENSv1RegistryOld -> ENSv1Registry migration status b) Domain-Resolver Relations for both ENSv1 and ENSv2 Domains As such, none of that information is present in this `unigraph.schema.ts` file. In general, entities are keyed by a nominally-typed `id` that uniquely references them. This allows us to trivially implement cursor-based pagination and allow consumers to reference these deeply nested entities by a straightforward string ID. In cases where an entity’s `id` is composed of multiple pieces of information (for example, a `registries` record is identified by (`chain_id`, `address`)), then that information is, as well, included in the entity’s columns, not just encoded in the id. Nowhere in this application, nor in user applications, should an entity’s id be parsed for its constituent parts; all should be available, with their various type guarantees, on the entity itself. Events are structured as a single “events” table which tracks EVM Event Metadata for any on-chain Event. Then, join tables (`domain_events`, `resolver_events`, etc) track the relationship between an entity that has many events (`domains`, `resolvers`) to the relevant set of Events. A Registration references the event that initiated the Registration. A Renewal, too, references the Event responsible for its existence. ## Enums [Section titled “Enums”](#enums) **`RegistryType`** | Value | | ---------------------- | | `ENSv1Registry` | | `ENSv1VirtualRegistry` | | `ENSv2Registry` | **`DomainType`** | Value | | ------------- | | `ENSv1Domain` | | `ENSv2Domain` | **`RegistrationType`** | Value | | --------------------------- | | `NameWrapper` | | `BaseRegistrar` | | `ThreeDNS` | | `ENSv2RegistryRegistration` | | `ENSv2RegistryReservation` | ## events [Section titled “events”](#events) | Column | Type | Nullable | Description | | ------------------- | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | Ponder’s event ID. Primary key. | | `chain_id` | `bigint` | no | Chain the event was emitted on. | | `block_number` | `numeric(78)` | no | Block number. | | `block_hash` | `text` | no | Block hash. | | `timestamp` | `numeric(78)` | no | Block timestamp. | | `transaction_hash` | `text` | no | Transaction hash. | | `transaction_index` | `integer` | no | Index of the transaction within the block. | | `from` | `text` | no | Transaction sender address (`tx.from`). Never HCA-aware — always the EOA/relayer that submitted the transaction. Use `sender` for the HCA-aware actor. | | `sender` | `text` | no | The HCA account address if used, otherwise `Transaction.from`. For ENSv2 events that emit an explicit `sender` / `owner` / `account` argument, this is set from that argument. For all other events (and all ENSv1 events), this falls back to `from` (i.e. `tx.from`). | | `to` | `text` | yes | Transaction recipient address. A `null` value means this was a contract-deployment transaction. | | `address` | `text` | no | Address of the contract that emitted the log. | | `log_index` | `integer` | no | Index of the log within the transaction. | | `selector` | `text` | no | Event topic\[0] (the event signature hash). | | `topics` | `text[]` | no | All log topics. | | `data` | `text` | no | Log data. | **Indexes:** `selector`, `from`, `sender`, `timestamp`. ## domain\_events [Section titled “domain\_events”](#domain_events) Join table linking a `domains` record to its associated `events`. | Column | Type | Nullable | | ----------- | ------ | -------- | | `domain_id` | `text` | no | | `event_id` | `text` | no | **Primary key:** `(domain_id, event_id)`. ## resolver\_events [Section titled “resolver\_events”](#resolver_events) Join table linking a `resolvers` record to its associated `events`. | Column | Type | Nullable | | ------------- | ------ | -------- | | `resolver_id` | `text` | no | | `event_id` | `text` | no | **Primary key:** `(resolver_id, event_id)`. ## permissions\_events [Section titled “permissions\_events”](#permissions_events) Join table linking a `permissions` record to its associated `events`. | Column | Type | Nullable | | ---------------- | ------ | -------- | | `permissions_id` | `text` | no | | `event_id` | `text` | no | **Primary key:** `(permissions_id, event_id)`. ## permissions\_user\_events [Section titled “permissions\_user\_events”](#permissions_user_events) Join table linking a `permissions_users` record to its associated `events` — i.e. the per-`(contract, resource, user)` history of role grants, revokes, and bitmap mutations. | Column | Type | Nullable | | --------------------- | ------ | -------- | | `permissions_user_id` | `text` | no | | `event_id` | `text` | no | **Primary key:** `(permissions_user_id, event_id)`. ## accounts [Section titled “accounts”](#accounts) | Column | Type | Nullable | Description | | ------ | ------ | -------- | ------------------------------ | | `id` | `text` | no | Ethereum address. Primary key. | **Relations:** has many `registrations` (as registrant), has many `domains`, has many `permissions_users`. ## registries [Section titled “registries”](#registries) For ENSv1, each domain that has children implicitly owns a “virtual” Registry (`ENSv1VirtualRegistry`) whose sole parent is that domain. Children of the parent then point their `registry_id` at the virtual registry. Concrete `ENSv1Registry` rows (e.g. the mainnet ENS Registry, the Basenames Registry, the Lineanames Registry) sit at the top. ENSv2 namegraphs are rooted in a single `ENSv2Registry` RootRegistry. | Column | Type | Nullable | Description | | --------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | See `RegistryId` for guarantees. Primary key. | | `type` | `RegistryType` | no | Registry type. | | `chain_id` | `bigint` | no | Chain the registry contract is deployed on. | | `address` | `text` | no | Address of the registry contract. | | `node` | `text` | yes | If this is an `ENSv1VirtualRegistry`, the namehash of the parent ENSv1 domain that owns it, otherwise `null`. | | `canonical_domain_id` | `text` | yes | The Registry’s declared Canonical Domain (unidirectional). | | `canonical` | `boolean` | no | Whether this Registry is part of the canonical nametree. This encodes bi-directional agreement between `domains.subregistry_id` and `registries.canonical_domain_id`, so traversal of the canonical nametree filtered to domains/registries where `canonical=true` is safe and doesn’t require edge-authenticating oneself (i.e. don’t need to compare `domains.subregistry_id` and `registries.canonical_domain_id` in the query, can just `WHERE canonical = true`). Default `false`. | | `has_children` | `boolean` | no | Internal bookkeeping field. Synthetic monotonic sentinel flipped to `true` the first time a child Domain is registered under this Registry. Used to optimize canonicality cascades. Default `false`. | **Indexes:** `(chain_id, address)` — non-unique, because multiple rows can share `(chain_id, address)` across virtual registries. **Relations:** has many `domains` (as parent registry), has many `domains` (as subregistry), has one `permissions` via `(chain_id, address)`. ## domains [Section titled “domains”](#domains) The `domains.owner_id` for ENSv1 Domains is the materialized effective owner. ENSv1 includes a diverse number of ways to ‘own’ a domain, including the ENSv1 Registry, various Registrars, and the NameWrapper. The ENSv1 indexing logic materializes the effective owner to simplify this aspect of ENS and enable efficient queries against `domains.owner_id`. Note Domain-Resolver relations are tracked via the Protocol Acceleration plugin, not stored on the domain row. Parent-domain traversal of the canonical nametree is supported directly via the materialized `canonical_path` / `canonical_label_hash_path` arrays; non-canonical traversal walks the `registries.canonical_domain_id` ↔ `domains.subregistry_id` pointers at query-time. | Column | Type | Nullable | Description | | --------------------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | ENSv1DomainId: `{ENSv1RegistryId}/{node}`. ENSv2DomainId: CAIP-19 asset identifier. Primary key. | | `type` | `DomainType` | no | `ENSv1Domain` or `ENSv2Domain`. | | `registry_id` | `text` | no | The registry this domain belongs to. | | `subregistry_id` | `text` | yes | The registry that manages subdomains of this domain, if any. | | `token_id` | `numeric(78)` | yes | ENSv2 only: the TokenId within the ENSv2Registry. `null` for ENSv1 domains. | | `node` | `text` | yes | ENSv1 only: the domain’s namehash. `null` for ENSv2 domains. | | `label_hash` | `text` | no | Represents a labelHash. References `labels.label_hash`. | | `owner_id` | `text` | yes | If `ENSv1Domain`, the materialized effective owner address. If `ENSv2Domain`, the on-chain owner address (the HCA account address if used). | | `root_registry_owner_id` | `text` | yes | ENSv1 only: the owner recorded in the root ENSv1 registry. `null` for ENSv2 domains. | | `canonical` | `boolean` | no | Whether this Domain is part of the canonical nametree. This encodes bi-directional agreement between `domains.subregistry_id` and `registries.canonical_domain_id`, so traversal of the canonical nametree filtered to domains/registries where `canonical=true` is safe and doesn’t require edge-authenticating oneself (i.e. don’t need to compare `domains.subregistry_id` and `registries.canonical_domain_id` in the query, can just `WHERE canonical = true`). Mirrors the parent Registry’s flag. Default `false`. | | `canonical_name` | `text` | yes | Materialized Canonical Name, `NULL` iff `canonical = false`. Maintained by `canonicality-db-helpers.ts`. Example: `"vitalik.eth"`. | | `canonical_label_hash_path` | `text[]` | yes | Materialized Canonical LabelHashPath, `NULL` iff `canonical = false`. Head-first (root → leaf), i.e. `[labelhash("eth"), labelhash("vitalik")]` for `"vitalik.eth"`. Maintained by `canonicality-db-helpers.ts`. | | `canonical_path` | `text[]` | yes | Materialized Canonical Domain Path, `NULL` iff `canonical = false`. Head-first (root → leaf), i.e. `[DomainId("eth"), DomainId("vitalik")]` for `"vitalik.eth"`. Maintained by `canonicality-db-helpers.ts`. | | `canonical_depth` | `integer` | yes | Materialized Canonical Depth, `NULL` iff `canonical = false`. The depth of this Domain in the Canonical Nametree, i.e. the number of Labels in its Canonical Name (e.g. `"eth"` depth 1, `"vitalik.eth"` depth 2). Maintained by `canonicality-db-helpers.ts`. | | `canonical_node` | `text` | yes | Materialized Canonical Node, `NULL` iff `canonical = false`. The computed Node (via `namehash`) of this Domain’s Canonical Name. Maintained by `canonicality-db-helpers.ts`. | **Indexes:** `type`, `subregistry_id` (partial: non-null only), `owner_id`, `label_hash`, `(registry_id, label_hash)` (composite; leading-column prefix also serves `WHERE registry_id = X` lookups, so no separate `registry_id` index is needed), `(registry_id, left(canonical_name, 256), id)` (composite expression index for registry-scoped `WHERE registry_id = X ORDER BY canonical_name LIMIT N` — the `Domain.subdomains` shape; the 256-char prefix bounds the index tuple under btree’s per-tuple max, and NAME-ordered queries must sort by the same `left(...)` expression for the planner to use this index for ordered scan), `canonical_name` (hash, exact match — avoids the btree 8191-byte row-size hazard for spam names), `canonical_name` (GIN trigram for substring / similarity queries), `canonical_label_hash_path` (GIN containment for `cascadeLabelHeal`’s `canonical_label_hash_path @> ARRAY[lh]` lookup), `canonical_node` (hash, for resolver-record → canonical-domain joins), `canonical_depth` (btree, for `ORDER BY canonical_depth` — typeahead and depth-ordered browse). **Relations:** belongs to one `registries` record, belongs to one `registries` record (as subregistry), has one `accounts` record (owner), has one `accounts` record (rootRegistryOwner), has one `labels` record, has many `registrations` records. ## labels [Section titled “labels”](#labels) Internal rainbow table mapping a `label_hash` to its interpreted label string. Domains reference labels by hash; names are healed at resolution-time. | Column | Type | Nullable | Description | | ------------- | ------ | -------- | -------------------------------------- | | `label_hash` | `text` | no | `keccak256` of the label. Primary key. | | `interpreted` | `text` | no | The interpreted label string. | **Indexes:** `interpreted` (hash index for exact match), `interpreted` (GIN trigram index for prefix/substring `LIKE`) **Relations:** has many `domains`. ## registrations [Section titled “registrations”](#registrations) A registration is keyed by `id`. | Column | Type | Nullable | Description | | -------------------- | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | A key derived from `(domain_id, registration_index)`. Primary key. | | `domain_id` | `text` | no | The registered domain. | | `registration_index` | `integer` | no | Monotonically increasing index per domain. | | `type` | `RegistrationType` | no | The mechanism through which this registration was made. | | `start` | `numeric(78)` | no | Unix timestamp of registration start. | | `expiry` | `numeric(78)` | yes | Unix timestamp of expiry, if applicable. | | `grace_period` | `numeric(78)` | yes | Grace period duration in seconds. `BaseRegistrar` only. | | `registrar_chain_id` | `bigint` | no | Chain of the registrar contract. | | `registrar_address` | `text` | no | Address of the registrar contract. | | `registrant_id` | `text` | yes | Account that initiated the registration. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). | | `unregistrant_id` | `text` | yes | Account that triggered an unregistration, if applicable. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). | | `referrer` | `text` | yes | Encoded referrer value emitted at registration time. | | `fuses` | `integer` | yes | Fuse bitmap. `NameWrapper` and wrapped `BaseRegistrar` only. | | `base` | `numeric(78)` | yes | Base registration cost in wei. `BaseRegistrar` and `ENSv2Registrar` only. | | `premium` | `numeric(78)` | yes | Premium cost in wei above base. `BaseRegistrar` only. | | `wrapped` | `boolean` | no | Whether the registration is currently wrapped by the NameWrapper. Default `false`. | | `event_id` | `text` | no | The event that created this registration record. | **Indexes:** unique on `(domain_id, registration_index)`. **Relations:** belongs to one `domains` record, has one `accounts` record (registrant), has one `accounts` record (unregistrant), has many `renewals`, has one `events` record. ## latest\_registration\_indexes [Section titled “latest\_registration\_indexes”](#latest_registration_indexes) Tracks the highest `registration_index` seen for each domain. Used to sequence registrations. | Column | Type | Nullable | | -------------------- | --------- | -------- | | `domain_id` | `text` | no | | `registration_index` | `integer` | no | **Primary key:** `domain_id`. ## renewals [Section titled “renewals”](#renewals) A renewal is keyed by `id` and belongs to a specific registration. | Column | Type | Nullable | Description | | -------------------- | ------------- | -------- | --------------------------------------------------------------------------------- | | `id` | `text` | no | A key derived from `(domain_id, registration_index, renewal_index)`. Primary key. | | `domain_id` | `text` | no | The renewed domain. | | `registration_index` | `integer` | no | Index of the parent registration. | | `renewal_index` | `integer` | no | Monotonically increasing index per registration. | | `duration` | `numeric(78)` | no | Duration added by this renewal, in seconds. | | `referrer` | `text` | yes | Encoded referrer value emitted at renewal time. | | `base` | `numeric(78)` | yes | Base renewal cost in wei. | | `premium` | `numeric(78)` | yes | Premium cost in wei above base. ENSv1 `RegistrarControllers` only. | | `event_id` | `text` | no | The event that created this renewal record. | **Indexes:** unique on `(domain_id, registration_index, renewal_index)`. **Relations:** belongs to one `registrations` record via `(domain_id, registration_index)`, has one `events` record via `(event_id)`. ## latest\_renewal\_indexes [Section titled “latest\_renewal\_indexes”](#latest_renewal_indexes) Tracks the highest `renewal_index` seen for each registration. Used to sequence renewals. | Column | Type | Nullable | | -------------------- | --------- | -------- | | `domain_id` | `text` | no | | `registration_index` | `integer` | no | | `renewal_index` | `integer` | no | **Primary key:** `(domain_id, registration_index)`. ## permissions [Section titled “permissions”](#permissions) An ENSv2 permissions contract instance. | Column | Type | Nullable | Description | | ---------- | -------- | -------- | ---------------------------------------------- | | `id` | `text` | no | Primary key. | | `chain_id` | `bigint` | no | Chain the permissions contract is deployed on. | | `address` | `text` | no | Address of the permissions contract. | **Indexes:** unique on `(chain_id, address)`. **Relations:** has many `permissions_resources`, has many `permissions_users`. ## permissions\_resources [Section titled “permissions\_resources”](#permissions_resources) A resource managed by a `permissions` contract. | Column | Type | Nullable | Description | | ---------- | ------------- | -------- | ------------------------------------------------------ | | `id` | `text` | no | Primary key. | | `chain_id` | `bigint` | no | Chain of the parent permissions contract. | | `address` | `text` | no | Address of the parent permissions contract. | | `resource` | `numeric(78)` | no | Resource identifier (a `uint256` token ID or similar). | **Indexes:** unique on `(chain_id, address, resource)`. **Relations:** belongs to one `permissions` via `(chain_id, address)`. ## permissions\_users [Section titled “permissions\_users”](#permissions_users) A user’s role bitmap for a specific resource within a `permissions` contract. | Column | Type | Nullable | Description | | ---------- | ------------- | -------- | ----------------------------------------------------------------------------------------- | | `id` | `text` | no | Primary key. | | `chain_id` | `bigint` | no | Chain of the parent permissions contract. | | `address` | `text` | no | Address of the parent permissions contract. | | `resource` | `numeric(78)` | no | Resource identifier. | | `user` | `text` | no | The user/grantee address this Permission is granted to (the HCA account address if used). | | `roles` | `numeric(78)` | no | Roles bitmap for this user on this resource. | **Indexes:** unique on `(chain_id, address, resource, user)`. **Relations:** has one `accounts` record (user), belongs to one `permissions` record via `(chain_id, address)`, belongs to one `permissions_resource` record via `(chain_id, address, resource)`. # Why ENSNode? > Why ENSNode is the best way to build ENSv2 apps. ENSNode is the full-stack ENSv2 development platform — a single, unified API over **both ENSv1 and ENSv2**, with first-class multichain support and the integration surfaces (React, TypeScript, GraphQL, Postgres) you need to ship. ## Built for ENSv2 from day one [Section titled “Built for ENSv2 from day one”](#built-for-ensv2-from-day-one) [ENSv2](https://ens.domains/ensv2) is a fundamental change to ENS’s onchain data model. The legacy ENS Subgraph wasn’t designed for it, and it never will be — single-chain, no resolution, no understanding of the new ENSv2 Namegraph. Apps that stay on the Subgraph will be left behind the moment ENSv2 launches. ENSNode is built around ENSv2 from the ground up. The same query works against an ENSv1 name today and an ENSv2 name the moment it goes live — with **zero downtime or code changes** in your app. [ENSv2 Readiness ](/docs/integrate/why-ensnode/ensv2-readiness)What you need to do to prepare your app for ENSv2. ## One unified API over ENSv1 + ENSv2 [Section titled “One unified API over ENSv1 + ENSv2”](#one-unified-api-over-ensv1--ensv2) The [ENS Omnigraph](/docs/integrate/omnigraph) is a single GraphQL API that returns a polymorphic, unified view of every ENS Domain — regardless of whether it lives in an ENSv1 Nametree, the ENSv2 Namegraph, on mainnet, or on an L2 subregistry such as base.eth subnames on Base. ![ENS Omnigraph diagram](/ens-omnigraph-diagram.png) ENSv1 and ENSv2 coexist after the ENSv2 launch. The Omnigraph simplifies that split: your code asks for `domain(by: { name: "vitalik.eth" })` and gets a typed result, regardless of whether the underlying domain is on ENSv1 or ENSv2. ## Multichain by default [Section titled “Multichain by default”](#multichain-by-default) ENSNode provides the world’s best indexing coverage for ENS, spanning every chain that matters, including: * **Mainnet** — the canonical ENS root and `.eth` registrations * **Basenames** (`.base.eth`) on Base * **Lineanames** (`.linea.eth`) on Linea * **3DNS** names (`.box`, tokenized DNS) on Optimism and Base No more wiring up a per-chain Subgraph, reconciling overlapping ENS Nametrees, or writing CCIP-Read logic in your app — ENSNode handles forward and reverse resolution, including offchain gateways, server-side. ## Full-stack integration surfaces [Section titled “Full-stack integration surfaces”](#full-stack-integration-surfaces) Whatever your stack looks like, there’s an ENSNode integration shaped for it. [ENSNode integration options ](/docs/integrate/integration-options)Explore the different ways to integrate with ENSNode based on your stack, runtime, and needs. ## Why it matters beyond your app [Section titled “Why it matters beyond your app”](#why-it-matters-beyond-your-app) Indexed ENS data is critical infrastructure for ENS — and the legacy ENS Subgraph is fundamentally unsuitable for ENSv2. Closing that gap is required for many of ENS’s most important apps. [Keep ENS apps working 🚨 ](/docs/integrate/ens-subgraph)Much of the ENS ecosystem quietly relies on the Subgraph today, and risks going dark when ENSv2 launches. [Overcome critical limitations of the ENS Subgraph 🚨 ](/docs/integrate/ens-subgraph/key-limitations)Why the Subgraph falls short today, and breaks with ENSv2. ## Join those already building on ENSNode [Section titled “Join those already building on ENSNode”](#join-those-already-building-on-ensnode) A growing set of companies and apps have already moved onto ENSNode and have future-proofed their ENS applications. * **[ENS Labs](https://enslabs.org/)** — [ENSv2 Explorer](https://explorer.ens.dev/), [ENSv2 App](https://app.ens.dev/), [ens-test-env](https://github.com/ensdomains/ens-test-env). * **[EthId Foundation](https://ethid.org/)** — [Grails](https://grails.app/), [ENS Market Bot](https://x.com/ENSMarketBot), [Grails CLI](https://github.com/grailsmarket/cli). * **[Blockful](https://www.blockful.io/)** — [Anticapture](https://app.anticapture.com/). * **[Enscribe](https://www.enscribe.xyz/)** * **[JustaName](https://www.justaname.id/)** — [ENSvolution](https://www.ensvolution.xyz/), [ENS MCP Service](https://discuss.ens.domains/t/introducing-the-ens-mcp-server-bringing-ens-to-claude-and-other-ai-assistants/20347). * **[ENS Tools](https://ens.tools/)** * **[ENS Vision](https://ensvision.com/)** * **[Namespace](https://www.namespace.ninja/)** — [ENS MCP Service](https://github.com/thenamespace/ens-mcp), [Subpages](https://github.com/thenamespace/subpages). * **[Lighthouse](https://lighthouse.cx/)** — [ENS Metadata Manager](https://ensmetadata.app/). * **[Ethereum Comments Protocol](https://www.ethcomments.xyz/)** * **[FusionENS](https://www.fusionens.com/)** — [Fusion ENS Extension](https://github.com/FranzQ/fusion-ens-extension). * **[Interface Social](https://www.interface.social/)** — likely reaching our hosted ENSNode indirectly through its Ethereum Comments Protocol integration (not yet fully confirmed). **Indie developers** * **[Atlas](https://github.com/stevedylandev/atlas)** (ENS CLI) — by [Steve Dylan](https://x.com/stevedylandev). * **[Ensemble](https://github.com/estmcmxci/ensemble-beta)** (ENS CLI) — by [estmcmxci](https://x.com/estmcmxci). * **[Basenames CLI](https://github.com/estmcmxci/basenames-cli)** — by [estmcmxci](https://x.com/estmcmxci). * **[Dapp Rank](https://dapprank.eth.link/)** — by [Joel Thorstensson](https://x.com/joelthorst). * **[Klyra](https://github.com/Jossyboydgenius/Klyra)** * **[pier](https://github.com/Quantumlyy/pier)** ## Next steps [Section titled “Next steps”](#next-steps) [Quickstart ](/docs/integrate)Ship your first Omnigraph query in minutes [ENSv2 Readiness ](/docs/integrate/why-ensnode/ensv2-readiness)Prepare your app for the ENSv2 launch [Integration Options ](/docs/integrate/integration-options)Pick the integration surface that fits your stack # ENSv2 Readiness > How building with ENSNode today prepares your application for ENSv2, even before ENSv2 launches. Be ready! Build with the **ENS Omnigraph** today and your app will support **ENSv2** the moment it launches — with zero downtime or code changes. ## Historical limitations of building on ENS [Section titled “Historical limitations of building on ENS”](#historical-limitations-of-building-on-ens) Full access to ENS data formerly required two separate data-fetching strategies working in parallel: **ENS resolution** (RPC calls with CCIP-Read, e.g. via `viem` or `wagmi`) and **indexed ENS data** (the ENS Subgraph). Neither system alone was complete — resolution is “close to the metal” and the Subgraph carries a long list of [Key Limitations](/docs/integrate/ens-subgraph/key-limitations). And **with ENSv2, the complexity of ENS’s onchain state space meaningfully increases**. [Key Limitations of the ENS Subgraph ](/docs/integrate/ens-subgraph/key-limitations)The full breakdown of what the Subgraph can't do — and why it breaks with ENSv2. ## How ENSNode solves this [Section titled “How ENSNode solves this”](#how-ensnode-solves-this) **The Unigraph Data Model** — [ENSIndexer](/docs/services/ensindexer)’s `unigraph` plugin builds a single, **unified** indexed data model in [ENSDb](/docs/services/ensdb) that combines all of ENSv1 — mainnet `.eth`, Basenames on Base, Lineanames on Linea, 3DNS on Optimism — together with ENSv2. One data model, every chain, both protocol versions. **The Omnigraph API** — The [ENS Omnigraph API](/docs/integrate/omnigraph), delivered by [ENSApi](/docs/services/ensapi), is a fully typed GraphQL API on top of that Unigraph data model. It handles the ENS protocol’s many implementation details for you, so you can focus on building your app instead of wiring up the protocol’s internals. **Accelerated ENS Resolution** — The ENS Omnigraph API, through ENSApi, internally implements the ENS Universal Resolver on top of the indexed ENS Unigraph data model in ENSDb. We refer to this idea as “[ENS Protocol Acceleration](/docs/integrate/omnigraph/protocol-acceleration)”. For cases where ENS Resolution requires offchain data, ENSApi internally performs the CCIP-read operations on your behalf to ensure every resolution request accurately follows all ENS protocol standards. No need for any RPC calls in your app, and your ENS resolutions for indexed names could speed up by an order of magnitude or more! ## What You Need to Do [Section titled “What You Need to Do”](#what-you-need-to-do) **Resolving Records** — If you’re exclusively interested in resolving ENS names, you need to ensure that your `resolve` RPC calls go to the new `UniversalResolverV2` contract or risk stale and incorrect results being returned. Depending on your situation, this update may be handled simply by updating `viem` or `wagmi`, but for many apps building on indexed ENS data or with more advanced requirements, additional upgrade actions may be required. For many, the simple solution for ENSv2 readiness will be to adopt the ENS Omnigraph API from ENSNode for all their ENS data needs (both ENSv1 and ENSv2). ENSNode also provides **[ENS Protocol Acceleration](/docs/integrate/omnigraph/protocol-acceleration)**, dramatically reducing resolution latency for most names people use. **Querying ENS Data** — If you’re querying ENS names via a legacy API such as the ENS Subgraph you need to update to an ENSv2-ready service like ENSNode; as soon as ENSv2 launches, data served by the Subgraph (and any ENSv1-only indexers) will be instantly unreliable for the true state of ENS. ## Pick Your Integration [Section titled “Pick Your Integration”](#pick-your-integration) ENSNode meets you wherever you are — drop-in React components, a typed TypeScript SDK, raw GraphQL, direct ENSDb access, CLI tooling, or AI agent integration. Pick the surface that fits your stack and ship ENSv2-ready code today. [Integration Options ](/docs/integrate/integration-options)enskit (React), enssdk (TypeScript), the ENS Omnigraph GraphQL API, direct ENSDb access, CLI tooling, and more. # Reference > Go deeper into mastery of ENSNode. Go deeper into mastery of ENSNode, including terminology and becoming an active contributor to ENSNode development. [Terminology ](/docs/reference/terminology)Terminology for ENS development used across these docs. [Hosted ENSNode instances ](/docs/hosted-instances)Public NameHash-hosted URLs (alpha, mainnet, Sepolia, ENSv2 previews). [REST API ](/docs/services/ensapi/reference/api-reference)Interactive OpenAPI reference for REST APIs in ENSApi. [Contributing ](/docs/reference/contributing)Learn how to contribute to the development of ENSNode and related projects. # ENSNode Development and Contributions > Learn how to run ENSNode locally for development and contributions. Note This guide covers running ENSNode locally for development and contributions. ### Prerequisites [Section titled “Prerequisites”](#prerequisites) * [Git](https://git-scm.com/) * [Postgres 17](https://www.postgresql.org/) with the [`pg_trgm`](https://www.postgresql.org/docs/current/pgtrgm.html) extension available for installation (ships with stock Postgres contrib; ENSIndexer runs `CREATE EXTENSION IF NOT EXISTS pg_trgm` at startup to back partial-name search indexes) * [Node.js](https://nodejs.org/) * It’s recommended you install Node.js through [nvm](https://github.com/nvm-sh/nvm) or [asdf](https://asdf-vm.com/). * see `.nvmrc` and `.tool-versions` for the specific version of Node.js * [pnpm](https://pnpm.io/) * Run `npm install -g pnpm` or see [other installation options](https://pnpm.io/installation). ### Prepare Workspace [Section titled “Prepare Workspace”](#prepare-workspace) Clone this repository: ```bash git clone git@github.com:namehash/ensnode.git cd ensnode ``` ### Install Dependencies [Section titled “Install Dependencies”](#install-dependencies) ```bash pnpm install ``` ## Running ENSNode [Section titled “Running ENSNode”](#running-ensnode) Note ENSNode is a suite of services, and some depend on others. Refer to the `docker/docker-compose.yml` in the docker directory for a full spec on the relationship between services. [View docker-compose.yml on GitHub ](https://github.com/namehash/ensnode/blob/main/docker/docker-compose.yml)See the Docker Compose configuration file defining service relationships ### 1. Running ENSDb [Section titled “1. Running ENSDb”](#1-running-ensdb) Ensure ENSDb is running in the background, providing its connection details to ENSIndexer via `ENSDB_URL`. ### 2. Running ENSRainbow [Section titled “2. Running ENSRainbow”](#2-running-ensrainbow) ```bash # from monorepo root pnpm run -F @ensnode/ensrainbow serve # or from apps/ensrainbow pnpm run serve ``` ### 3. Running ENSIndexer [Section titled “3. Running ENSIndexer”](#3-running-ensindexer) Caution ENSIndexer’s `.env.local` should be placed at `apps/ensindexer/.env.local`, *not* at the monorepo root. Running ENSIndexer in Development Follow instructions in the [ENSIndexer Contribution Guide](/docs/services/ensindexer/contributing) to set up your local environment. ```bash # from monorepo root pnpm run -F ensindexer dev # or from apps/ensindexer pnpm run dev ``` ### 4. Running ENSApi [Section titled “4. Running ENSApi”](#4-running-ensapi) Caution ENSApi’s `.env.local` should be placed at `apps/ensapi/.env.local`, *not* at the monorepo root. ```bash # from monorepo root pnpm run -F ensapi dev # or from apps/ensapi pnpm run dev ``` ### 5. Running ENSAdmin [Section titled “5. Running ENSAdmin”](#5-running-ensadmin) Caution ENSAdmins’s `.env.local` should be placed at `apps/ensadmin/.env.local`, *not* at the monorepo root. ```bash cd apps/ensadmin cp .env.local.example .env.local ``` ```bash # from monorepo root pnpm run -F ensadmin dev # or from apps/ensadmin pnpm run dev ``` ## Using Docker Compose [Section titled “Using Docker Compose”](#using-docker-compose) You can use Docker Compose to set up the ENSNode suite, along with its dependencies. Docker in Development Re-building Docker containers is slow and inefficient, and doesn’t lend itself to rapid development. The first half of this guide showed how to run ENSNode on your host machine, for faster iteration and maximum control. The Docker Compose spec is helpful for describing the suite of services and running them in a structured way, which we’ll discuss below. ### Prerequisites [Section titled “Prerequisites”](#prerequisites-1) Before you can use Docker Compose, ensure you have the following installed on your machine: * [Docker](https://www.docker.com/get-started) * [Docker Compose](https://docs.docker.com/compose/install/) ### Building the Docker Images [Section titled “Building the Docker Images”](#building-the-docker-images) Before running `docker compose` the images must be built with the latest changes: see the [Building Docker Images](/docs/reference/contributing/building) guide. If you make changes in the application code and wish to run those updates, you must build the relevant Docker container again. ### Running the Applications [Section titled “Running the Applications”](#running-the-applications) For local development, use the devnet stack — no environment setup required: ```bash docker compose -f docker/docker-compose.devnet.yml up -d ``` For mainnet/sepolia, first configure your environment: ```bash cp docker/envs/.env.docker.example docker/envs/.env.docker.local ``` Edit `docker/envs/.env.docker.local` to set `NAMESPACE`, `PLUGINS`, and your RPC endpoints, then run: ```bash docker compose -f docker/docker-compose.yml up -d ``` * **ENSIndexer**: Available at * **ENSApi**: Available at * **ENSRainbow**: Available at * **ENSAdmin**: Available at * **PostgreSQL**: Available on port `5432` For all available commands and configuration options, see the [Deploying with Docker](/docs/self-host/docker) guide and [`docker/README.md`](https://github.com/namehash/ensnode/blob/main/docker/README.md). ### Stopping the Applications [Section titled “Stopping the Applications”](#stopping-the-applications) To stop the running applications, you can press `Ctrl + C` in the terminal where Docker Compose is running. To remove the containers and networks: ```bash docker compose -f docker/docker-compose.yml down ``` Postgres Volume `docker compose down` will *not* delete the Postgres data volume, as it is a **named** volume. To fully delete it and start from scratch, use `docker compose -f docker/docker-compose.yml down -v`. # Building the Docker Images Building Local Docker Images This guide covers building the Docker images for local development/testing. To build all of the images in serial, use the following script: ```bash # from the monorepo root pnpm run docker:build:ensnode ``` ## ENSIndexer [Section titled “ENSIndexer”](#ensindexer) ```bash # from the monorepo root pnpm run docker:build:ensindexer ``` ## ENSApi [Section titled “ENSApi”](#ensapi) ```bash # from the monorepo root pnpm run docker:build:ensapi ``` ## ENSAdmin [Section titled “ENSAdmin”](#ensadmin) ```bash # from the monorepo root pnpm run docker:build:ensadmin ``` ## ENSRainbow [Section titled “ENSRainbow”](#ensrainbow) ```bash # from the monorepo root pnpm run docker:build:ensrainbow ``` # Code Standards ## Formatting / Linting [Section titled “Formatting / Linting”](#formatting--linting) We use Biome to format most of the code in the ENSNode monorepo, and Prettier to cover the rest: * Biome handles JS, TS, JSON, CSS, and the frontmatter of `.astro` files. * Prettier handles Markdown (`.md` and `.mdx`) across the monorepo, and the template portion of `.astro` files in `docs/ensnode.io` and `docs/ensrainbow.io` via [`prettier-plugin-astro`](https://github.com/withastro/prettier-plugin-astro). The `astroSkipFrontmatter: true` option keeps the frontmatter under Biome’s ownership. ### Running the formatters [Section titled “Running the formatters”](#running-the-formatters) From the monorepo root: ```bash pnpm lint ``` CI runs `pnpm lint:ci` (check-only) on every PR. ### Windows: line-ending diffs [Section titled “Windows: line-ending diffs”](#windows-line-ending-diffs) Git on Windows checks files out with CRLF (`\r\n`) line endings by default. Biome and Prettier write LF (`\n`). After `pnpm lint`, files may appear modified, but Git normalizes the endings back to LF when staging, so nothing changes in the resulting commit. To clear the noise from your working tree: * One-off: run `git add --all`. The index normalizes the endings and the files drop out of `git status`. * Permanent: run `git config --global core.autocrlf input`, then `git checkout -- .`. Your working tree converts to LF and future checkouts keep it that way. # Creating Pull Requests # Pull Request Templates [Section titled “Pull Request Templates”](#pull-request-templates) We use pull request templates to help guide contributors through the process of creating a pull request. * [Lite PR](https://github.com/namehash/ensnode/blob/main/.github/pull_request_template.md) * *The default PR template for non-significant or low-risk changes.* * [Substantial PR](https://github.com/namehash/ensnode/blob/main/.github/PULL_REQUEST_TEMPLATE/substantial.md) * *Template for significant changes or PRs that are complex or higher-risk to review.* # Standard PR Process [Section titled “Standard PR Process”](#standard-pr-process) 1. Apply the appropriate [Pull Request Template](#pull-request-templates) based on the nature of the changes. 2. Create your PR as a “Draft” first until it is ready for review. 3. Complete all of your work, self-reviews, and PR description updates on the PR. * If your PR aims to close an issue, [link the PR to the issue through the PR description](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword). 4. Complete all checkboxes on the PR description. 5. Convert the PR from “Draft” to “Ready for Review”. 6. Wait for automated PR reviews from Greptile and GitHub Copilot. Resolve any of their suggestions as appropriate. * After applying updates, if you want Greptile to make additional follow-up reviews, add a comment to the PR with the phrase “@greptile”. * GitHub Copilot will automatically re-review each push to the PR. 7. Send a message to notify the ENSNode team that the PR is ready for manual-review. # Changesets [Section titled “Changesets”](#changesets) We use [changesets](https://github.com/changesets/changesets) to manage version bumps and release notes for our monorepo’s artifacts (NPM packages, Docker images, and Lambda zips). ## ”Logical Changes” [Section titled “”Logical Changes””](#logical-changes) A “logical change” is a cohesive set of related modifications that share a common narrative, even if it spans multiple apps/packages. ## Changeset Criteria [Section titled “Changeset Criteria”](#changeset-criteria) Changesets should not be created robotically for every “logical change” in a PR. We create changesets specifically for the purposes of managing version bumps and autogenerated release notes. Only the following types of “logical changes” should be documented in a changeset: * Changes that should bump versions. * Changes that are relevant to communicate to the ENS ecosystem in release notes. If a PR does not introduce any “logical changes” meeting the above criteria then the PR should not include a changeset. ## Mapping “Logical Changes” to Changesets [Section titled “Mapping “Logical Changes” to Changesets”](#mapping-logical-changes-to-changesets) Ensure that each “logical change” described in a changeset is appropriately mapped to the relevant package/app. Example: assume changes A, B, and C in a PR pass the criteria for changesets, with the following applicability: change A applies only to package/app X, change B applies to both package/app X and package/app Y, and change C applies only to package/app Y. This means multiple distinct changesets should be created as follows: * Changeset 1: Documenting change A for package/app X * Changeset 2: Documenting change B for package/app X and package/app Y * Changeset 3: Documenting change C for package/app Y In other words, avoid attaching a changeset to a package/app that is not affected by the changes. ## Adding a Changeset to Your PR [Section titled “Adding a Changeset to Your PR”](#adding-a-changeset-to-your-pr) When you open a PR you should include relevant changeset files that document your changes. 1. Run `pnpm changeset` in the monorepo root. 2. Select the packages/apps that the “logical change” affects using the interactive prompt. 3. Choose the version bump type: * *NOTE: At this time, we are NOT using [semantic versioning](https://semver.org/). Breaking changes are classified as “minor” rather than “major”.* * “major” - currently reserved only for specially approved cases * “minor” - breaking changes (note: ENSNode doesn’t follow standard semver where breaking = major) * “patch” - non-breaking changes 4. Write a clear description of the “logical change” - this will appear in the next autogenerated release notes. The `changesets/bot` will automatically comment on your PR to either remind you to add a changeset or confirm the version changes that will happen when your PR is merged. **Important notes:** * Changesets should optimize for the narrative of the next [autogenerated release notes](https://github.com/namehash/ensnode/releases). Optimize for how the resulting release notes will read to a developer in the ENS Ecosystem. Communicate all ideas with a positive frame by emphasizing benefits to users, describing fixes in terms of what now works, and using active, solution‑oriented language rather than focusing on past problems. * All apps and packages in the ENSNode monorepo use “fixed” versioning - they all share the same version number regardless of which app or package triggered the version bump. * An exception to the above “fixed” versioning is the “fallback-ensapi” app. This is a Lambda containing logic specific to NameHash deployments of ENSNode and is therefore versioned independently. ## Changesets and Release Notes [Section titled “Changesets and Release Notes”](#changesets-and-release-notes) Upon the publishing of a new release, your change will be included in the produced artifacts and your contributions (as described in changesets) will be referenced in the autogenerated GitHub Release notes. # PR Description Checks [Section titled “PR Description Checks”](#pr-description-checks) The GitHub Action for “Require PR Description Checks” will report a failing CI check on non-draft PRs where there are unchecked checkboxes in the description. You should therefore make your PR a draft PR until it is ready for review. # Creating a Release There are three different types of ENSNode releases: Full Release, Snapshot Release, and Preview Release. ## Full Release [Section titled “Full Release”](#full-release) Workflow File: [release.yml](https://github.com/namehash/ensnode/blob/main/.github/workflows/release.yml) If your PR includes a changeset and is merged to `main` then it will automatically be added to a new automated Release PR by the Changesets bot. As more changesets are added to `main` the Release PR will continue to update. Once a Release PR is merged into `main` it triggers a “full” release that will: * Build and publish all of the monorepo’s artifacts (NPM packages, Docker images, and Lambda zips). * Create a release on GitHub with autogenerated release notes from all the included changesets. **Important notes:** * Among all release types, only Full Releases are considered stable. * Full releases are triggered through merging the Release PR to `main`. * All ENSNode packages use “fixed” versioning. Once a full release is published they will all advance to the version indicated in the Release PR based on the included changesets. * Only members of the NameHash Labs `ensnode` team have the required permissions to merge the Release PR to `main`. * Full releases will create GitHub tags and release notes. ## Snapshot Release [Section titled “Snapshot Release”](#snapshot-release) Workflow File: [release\_snapshot.yml](https://github.com/namehash/ensnode/blob/main/.github/workflows/release_snapshot.yml) Each commit to `main` will automatically trigger the `release_snapshot.yml` workflow to build and publish all of the monorepo’s artifacts. These public releases will be under the tag `@next`, allowing anyone to use the artifacts associated with each commit to main. To install snapshot releases run `pnpm install @ensnode/[package-name]@next` or `docker run ghcr.io/namehash/ensnode/[app-name]:next`. **Important notes:** * Snapshot releases are part of the pre-stable state of the `main` branch and should be installed with caution until a [full release](#full-release) is published. * Release snapshots are automatic and cannot be triggered manually. * Snapshot releases will include the `@next` tag for published artifacts. * Published artifacts will advance to the version that was included in the changeset of the PR merged to `main`. * The `main` branch of `ensnode` is protected. Only PRs approved by the `ensnode` team can be merged to `main` and trigger a snapshot release. * No GitHub releases or tags are created for snapshot releases. ## Preview Release [Section titled “Preview Release”](#preview-release) Workflow File: [release\_preview.yml](https://github.com/namehash/ensnode/blob/main/.github/workflows/release_preview.yml) To test or install a package before merging it into `main`, a preview release can be used. Each preview release is associated with a PR, and the PR will receive a comment with installation instructions. To manually trigger a preview release, follow these steps: 1. Navigate to [Actions > Release Preview](https://github.com/namehash/ensnode/actions/workflows/release_preview.yml) 2. Click “Run workflow” and select from the following options: * The branch on which to run the preview release workflow. The branch must have an open PR. * Choose which artifacts to build and publish: * `npm-only`: NPM packages only * `npm-and-lambdas`: NPM packages + Lambda functions * `npm-and-ghcr`: NPM packages + Docker images * `all`: NPM packages + Lambda functions + Docker images * (Optional) Provide a custom suffix for the preview release tag. For example, if you had a branch called `feat/add-api-route` and left this custom suffix field blank, the preview release would be `@ensnode/[package-name]@preview-feat-add-api-route`. If you fill in the custom suffix field with `users-route` then the resulting tag name would be `@ensnode/[package-name]@preview-users-route`. 3. The workflow will post a comment on the PR with installation instructions. If multiple preview releases are triggered for the same PR, the comment will update with the latest release info. 4. Install preview packages with: `npm install @ensnode/[package-name]@preview-branch-name`. **Important notes:** * Preview releases require an open PR. The workflow will abort if no PR exists for the branch. * Preview releases are not guaranteed to be stable as they are still under active development. * Preview releases can only be triggered manually by authorized NameHash team members. * Preview releases will include the `@preview-*` tag for published artifacts, followed by either the name of the branch or the custom suffix chosen during the action trigger. * Published artifacts will advance to the version that was included in the changeset of the selected branch. * No GitHub releases or tags are created for preview releases. # Selecting a Release for Deployment or Installation [Section titled “Selecting a Release for Deployment or Installation”](#selecting-a-release-for-deployment-or-installation) Caution ENSNode is currently not using [Semantic Versioning](https://semver.org/). Patches and minor releases may include breaking changes. When using ENSNode artifacts, you have several release types to choose from. ## Where to Find Releases [Section titled “Where to Find Releases”](#where-to-find-releases) * **NPM Packages**: Available on the [npm registry](https://www.npmjs.com/search?q=%40ensnode) under the `@ensnode` organization. * **Docker Images**: Available on [GitHub Container Registry](https://github.com/orgs/namehash/packages?repo_name=ensnode). * **GitHub Releases**: Full releases are documented with release notes on the [ENSNode GitHub releases page](https://github.com/namehash/ensnode/releases). * **Lambda Zip Artifacts**: Available in the [Artifact section](https://github.com/actions/upload-artifact?tab=readme-ov-file#where-does-the-upload-go) of a successful workflow run. These Action Artifacts are [retained for 90 days](https://github.com/actions/upload-artifact?tab=readme-ov-file#retention-period). ## Choosing the Right Release Type [Section titled “Choosing the Right Release Type”](#choosing-the-right-release-type) ### Pinned Full Release Versions [Section titled “Pinned Full Release Versions”](#pinned-full-release-versions) When deploying ENSNode to production environments, it is advisable to use a Pinned Full Release. Pinned full releases are required for those who want to use any published ENSNode artifacts. By using a pinned version you can maintain full control over features or patches that might be included. Review the release notes on the [Releases Page](https://github.com/namehash/ensnode/releases) to help decide which version to install. ```bash npm install @ensnode/[package-name]@[version] docker run ghcr.io/namehash/ensnode/[app-name]:[version] ``` Caution When installing NPM packages for use in production environments, it is also advisable to pin a specific Pinned Full Release version number. ENSNode patch version increments MAY include breaking changes, so the usage of exact version package specifiers is encouraged. For example: ✅ `"@ensnode/[package-name]": "1.0.0"`\ ❌ `"@ensnode/[package-name]": "^1.0.0"` And when installing from the command line: ✅ `pnpm install @ensnode/[package-name]@1.0.0`\ ❌ `pnpm install @ensnode/[package-name]` Caution In particular, when deploying ENSNode to production environments, using the `latest` Docker tag without strict pull policies could result in a version mismatch between interdependent ENSNode apps (which will helpfully crash at startup). Due to this, we highly recommend using a specific Pinned Full Release version number tag like `1.0.0` instead of the tag `latest`, which could point to different versions of the ENSNode app depending on the platform’s pull policy. Caution Each ENSIndexer version update is likely to produce an updated [Ponder Build Id](https://ponder.sh/docs/api-reference/ponder/database). When updating your ENSIndexer version, you should expect to update the `ENSINDEXER_SCHEMA_NAME` environment variable to point to a new ENSIndexer Schema in ENSDb for a complete reindexing with the new Ponder Build Id. A complete reindexing may take over 24 hours depending on your configuration. ENSNode version updates require special coordination and should not be assumed to be a simple version bump. ### Snapshot Releases [Section titled “Snapshot Releases”](#snapshot-releases) Caution The `next` tag is a floating pointer that always references the most recent snapshot release. When using Docker images with the `next` tag, you must run `docker pull` to update your local Docker cache if you want to get the actual latest image. Without pulling, you may continue using an older cached version. ```bash npm install @ensnode/[package-name]@next docker run ghcr.io/namehash/ensnode/[app-name]:next ``` Snapshot releases should be used by those who need to test features or patches before they are included in full releases. These releases follow the `main` branch and are not referenced in the GitHub Releases page. Instead they are installed by using the `next` tag for published artifacts. Caution Snapshot releases may contain unstable changes and should only be used in development environments. ### Preview Releases [Section titled “Preview Releases”](#preview-releases) Note The example below is a mock of what a preview release might look like. Read the [preview releases section](#preview-release) for more information. ```bash npm install @ensnode/[package-name]@[preview-branch-name] docker run ghcr.io/namehash/ensnode/[app-name]:[preview-branch-name] ``` Preview releases are designed to test features or patches on a PR branch before it is merged to `main`. Each preview release is associated with a PR, and the PR will have a comment with installation instructions. Since preview releases can contain experimental and unstable changes, they should be avoided unless you are actively contributing through a PR and need to test work on a branch. Caution Avoid using preview releases unless you are actively contributing to ENSNode and need to test changes on an active PR. # REST API Documentation > How REST API documentation is generated and kept in sync. Our REST API docs are rendered by [Scalar](https://scalar.com) directly from a committed OpenAPI spec at [`docs/ensnode.io/ensapi-openapi.json`](https://github.com/namehash/ensnode/blob/main/docs/ensnode.io/ensapi-openapi.json). The Scalar component in the [API Reference](/docs/services/ensapi/reference/api-reference) page imports the spec at build time, so any changes to the spec are reflected on the next docs build. ## Updating the OpenAPI Spec [Section titled “Updating the OpenAPI Spec”](#updating-the-openapi-spec) If you modify any API route schemas in `apps/ensapi`, regenerate and commit the updated spec from the monorepo root: ```bash # from the monorepo root pnpm generate:openapi ``` Then commit the updated `docs/ensnode.io/ensapi-openapi.json`. ## OpenAPI Spec Sync Check [Section titled “OpenAPI Spec Sync Check”](#openapi-spec-sync-check) On every PR, our CI runs an `openapi-sync-check` job that: 1. Regenerates the OpenAPI spec from the ENSApi route definitions by running `pnpm generate:openapi` 2. Compares the freshly generated spec against the committed [`docs/ensnode.io/ensapi-openapi.json`](https://github.com/namehash/ensnode/blob/main/docs/ensnode.io/ensapi-openapi.json), failing with a diff if they don’t match # Terminology New terminology (beyond the [official ENS glossary](https://docs.ens.domains/terminology)) has been needed in the course of building ENSNode. The following should be understood as a draft that is open to community feedback. Each definition below aims to maximize alignment with the official ENS glossary where possible, and reduce instances where a piece of terminology may have multiple meanings in the context of the protocol. ## ENS Name Resolution Fundamentals [Section titled “ENS Name Resolution Fundamentals”](#ens-name-resolution-fundamentals) Understanding how ENS names are processed and displayed requires precise terminology around name components and their various states. The following sections build from basic name resolution concepts to the specific classification and encoding systems used in ENSNode. ### name, node, namehash [Section titled “name, node, namehash”](#name-node-namehash) A **name** is a human-readable string like `"vitalik.eth"` composed of **labels**. A **name** may or may not be *normalized*. > **Normalization** is process of canonicalizing a name before running it through the Namehash algorithm. [source](https://docs.ens.domains/resolution/names#normalize) The [`namehash`](https://docs.ens.domains/resolution/names#namehash) function computes a UUID 32-byte hash of a given **name**. If the name provided to `namehash` is not *normalized* then the resulting **node** is considered *invalid*. > The node is a hex-encoded 32-byte value that is derived from the name using the namehash algorithm defined in ENSIP-1. [source](https://docs.ens.domains/resolution/names#namehash) For the *normalized* name `vitalik.eth`, its **node** is `0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835`. ## Label Processing and Classification [Section titled “Label Processing and Classification”](#label-processing-and-classification) ENSNode must process labels from various onchain and offchain sources, each potentially in different states of normalization and availability. This section defines the core label processing concepts. ### Labels, LabelHashes, labelhash function [Section titled “Labels, LabelHashes, labelhash function”](#labels-labelhashes-labelhash-function) A **Label** is a human-readable string used as a segment of a **name** — i.e. `vitalik` and `eth` are the **Labels** of `vitalik.eth`. Labels are arbitrary unicode strings and may or may not be: *normalized* or *unnormalized*, *known* or *unknown*. In the ENSv1 `Registry` contract, only the **name**’s **node** is registered on-chain. Because of this there is no guarantee that a registered **node** is composed of *normalized* Labels or that those Labels are *known*. That is, when observing the `Registry` in isolation, the human-readable **Labels** that make up a **name** are not available (either on-chain or off-chain). That said, in many cases the **Labels** that make up a name can be made **known**: human-readable **Labels** can be emitted by contracts (i.e. the `ETHRegistrarController` or the `NameWrapper`), and in other cases the human-readable **Label** for a given **LabelHash** can be determined via rainbow table lookups (via [ENSRainbow](/docs/services/ensrainbow)) and other strategies. #### Rendering *Unknown* Labels [Section titled “Rendering Unknown Labels”](#rendering-unknown-labels) When rendering a **name**, one must take care to differentiate between an *unknown* **Label** (only identifiable by its 32-byte **LabelHash**) and a *known* **Label** potentially composed of hex characters. To do so, the *unknown* **Label**’s **LabelHash** is *encoded* as `[{LabelHash}]`. Examples are likely helpful: * `vitalik.eth` — a *normalized* **name** w/ *normalized* & *known* Labels * `[731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22].eth` — a **name** composed of two Labels 1. an *unknown* Label (of unknown *normalization*): `0x731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22` 2. a *normalized*, *known* Label: `eth` * `731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22.eth` — a *normalized* **name** composed of two Labels 1. a *normalized*, *known* Label (that just so happens to be a set of 64 literal hex characters): `731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22` 2. a *normalized*, *known* Label: `eth` ### LabelHash, labelhash function [Section titled “LabelHash, labelhash function”](#labelhash-labelhash-function) In this terminology reference, we say that the **LabelHash** of a **Label** is the result of calling the **`labelhash` function** with that **Label** as input. For the complete technical definition, see the [ENSRainbow Glossary](/docs/services/ensrainbow/concepts/glossary#labelhash). That is, `0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc` is the **LabelHash** of `vitalik`, which is the result of calling the **`labelhash` function** like so: example.ts ```ts import { labelhashInterpretedLabel, asInterpretedLabel } from "enssdk"; const labelHash = labelhashInterpretedLabel(asInterpretedLabel("vitalik")); ``` LabelHash Terminology Outside of this Reference Outside of this terminology reference & ENSNode, the word ‘labelhash’ may refer to either the function or the result of the function. ## Name and Label State Classifications [Section titled “Name and Label State Classifications”](#name-and-label-state-classifications) As ENSNode processes ENS data from multiple sources, labels and names exist in various states depending on normalization and data availability. These classifications are essential for proper display and validation. ### ENS name normalization standard [Section titled “ENS name normalization standard”](#ens-name-normalization-standard) The **ENS name normalization standard** is defined by [ENSIP-15: Name Normalization](https://docs.ens.domains/ensip/15/). ### ENS Normalize [Section titled “ENS Normalize”](#ens-normalize) We define **ENS Normalize** as a specific implementation of the **ENS name normalization standard** associated with a versioned release of the [`@adraffy/ens-normalize` package](https://www.npmjs.com/package/@adraffy/ens-normalize) to NPM. It is important to note that the logic in this package changes across time with new releases. Therefore, labels / names that were unnormalized when using version X of this package may become normalized when using version X+N of this package. Generally such logic changes are based on [updates to the Unicode standard](https://www.unicode.org/versions/#schedule), such as how Unicode tends to officially release a set of new emojis each year. The ENSIndexer Config API provides details of the specific **ENS Normalize** release being used. It is also important for apps building on ENSNode to match their **ENS Normalize** version with the version of the **ENS Normalize** package used by ENSIndexer. If ENS normalization implementations are not equivalent, the guarantees ENSNode provides for **Interpreted Labels** and **Interpreted Names** may not be met. For example, if ENSNode is using a more recent version of **ENS Normalize** than an app building on ENSNode, the app may receive an Interpreted Label / Interpreted Name from ENSNode that is not formatted as an encoded labelhash (because it is normalized in version X+N of **ENS Normalize**) but the app might consider that to be an unnormalized label / name because the app is still using version X of **ENS Normalize**. ### Normalized Label [Section titled “Normalized Label”](#normalized-label) A **Normalized Label** is a **Label** that has been processed through the **ENS Normalize** and represents the canonical form of that label. The normalization process ensures consistent representation and prevents homograph attacks. ### Unnormalized Label [Section titled “Unnormalized Label”](#unnormalized-label) An **Unnormalized Label** is a **Label** that has not been processed through the **ENS Normalize**, or one that would change if processed through normalization. ### Unknown Label [Section titled “Unknown Label”](#unknown-label) An **Unknown Label** is a **Label** for which the human-readable string is not available, and only the **LabelHash** is known. These labels are displayed using the **Encoded LabelHash** format: `[{LabelHash}]`. ### Normalized Name [Section titled “Normalized Name”](#normalized-name) A **Normalized Name** is a **name** composed entirely of **Normalized Labels**. This represents the canonical form of a name that can be safely processed through the `namehash` function to produce a valid **node**. ### Unnormalized Name [Section titled “Unnormalized Name”](#unnormalized-name) An **Unnormalized Name** is a **name** that contains one or more **Unnormalized Labels**, meaning it has not been fully processed through the **ENS Normalize**. ## Display Encoding Formats [Section titled “Display Encoding Formats”](#display-encoding-formats) When human-readable names or labels are unavailable, ENSNode uses specific encoding formats for consistent display. These formats ensure users can distinguish between actual hex content and placeholder representations. ### Encoded LabelHash [Section titled “Encoded LabelHash”](#encoded-labelhash) An **Encoded LabelHash** is a **LabelHash** that has been formatted for display when the corresponding **Label** is *unknown*. The encoding format wraps the hex representation of the **LabelHash** in square brackets: `[{LabelHash}]`. For example, if a **LabelHash** is `0x731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22`, its **Encoded LabelHash** representation would be `[731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22]`. ### Encoded NameHash [Section titled “Encoded NameHash”](#encoded-namehash) An **Encoded NameHash** is a **NameHash** that has been formatted for display when the corresponding **Name** is *unknown* or not human-readable. The encoding format wraps the hex representation of the **NameHash** in square brackets: `[{NameHash}]`. For example, if a **NameHash** is `0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835`, its **Encoded NameHash** representation would be `[ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835]`. ## Data Processing Pipeline [Section titled “Data Processing Pipeline”](#data-processing-pipeline) ENSNode’s indexing pipeline transforms raw blockchain data into user-friendly formats. This requires distinguishing between literal onchain data and the interpreted forms used in applications. When processing ENS data, it’s important to distinguish between the literal data read from blockchain sources and the interpreted form used in applications: ### Literal Label [Section titled “Literal Label”](#literal-label) A **Literal Label** is the raw label data as it exists in onchain storage or event data, before any processing or interpretation. This represents the exact bytes or string as emitted by contracts. ### Interpreted Label [Section titled “Interpreted Label”](#interpreted-label) An **Interpreted Label** is a **Label** that is either: * (if normalized): a normalized **Literal Label** * (if not normalized or **Unknown**): an **Encoded LabelHash** Apps building on ENSNode should take special note to align their implementation of **ENS Normalize** with the **ENS Normalize** used by the ENSNode they use. The **ENS Normalize** version used by each ENSNode can be referenced in the ENSIndexer Config API. ### Beautified Label [Section titled “Beautified Label”](#beautified-label) A **Beautified Label** is a **Label** produced from an **Interpreted Label** for presentation in a UI. It is either: * an **Encoded LabelHash**, preserved verbatim from the source **Interpreted Label**, or * a label produced by passing a normalized label through [ENSIP-15](https://docs.ens.domains/ensip/15) beautification, which is guaranteed to be normalizable back to the original normalized label but is itself NOT necessarily normalized (e.g. `"♾"` → `"♾️"`). Because the beautified form is not guaranteed to be normalized, a **Beautified Label** is NOT an **Interpreted Label** and MUST NOT be used as a lookup key, or anywhere else that expects an **Interpreted Label**. ### Literal Name [Section titled “Literal Name”](#literal-name) A **Literal Name** is a Name exclusively composed of 0 or more **Literal Labels**. ### Interpreted Name [Section titled “Interpreted Name”](#interpreted-name) An **Interpreted Name** is a Name exclusively composed of 0 or more **Interpreted Labels**. ### Beautified Name [Section titled “Beautified Name”](#beautified-name) A **Beautified Name** is a Name produced from an **Interpreted Name** for presentation in a UI, where each label is either: * an **Encoded LabelHash**, preserved verbatim from the source **Interpreted Name**, or * a label produced by passing a normalized label through [ENSIP-15](https://docs.ens.domains/ensip/15) beautification, which is guaranteed to be normalizable back to the original normalized label but is itself NOT necessarily normalized (e.g. `"♾"` → `"♾️"`). Because the beautified form is not guaranteed to be normalized, a **Beautified Name** is NOT an **Interpreted Name** and MUST NOT be used as a navigation target, lookup key, or anywhere else that expects an **Interpreted Name**. ### Resolvable Name [Section titled “Resolvable Name”](#resolvable-name) A **Resolvable Name** is a Name that can be used as input to ENS Forward Resolution. It contains only literal label segments (normalized or unnormalized), and each label is at most 255 bytes due to the [DNS Encoding](https://docs.ens.domains/resolution/names/#dns-encoding) standard. Interpreted Name !== Resolvable Name Because Forward Resolution does not support **Encoded LabelHashes**, an **Interpreted Name** containing an **Encoded LabelHash** is NOT a **Resolvable Name**: it can be used to traverse the Namegraph, but the records for that name cannot be resolved. ## Subgraph Indexability & Label/Name Interpretation [Section titled “Subgraph Indexability & Label/Name Interpretation”](#subgraph-indexability--labelname-interpretation) ### Subgraph-Indexable Labels / Subgraph-Unindexable Labels [Section titled “Subgraph-Indexable Labels / Subgraph-Unindexable Labels”](#subgraph-indexable-labels--subgraph-unindexable-labels) The legacy ENS Subgraph specifies that **Unknown Labels** and labels containing certain UTF-8 characters are “invalid”. We refer to this concept as `subgraph-indexable` and `subgraph-unindexable`. A **Literal Label** is `subgraph-unindexable` if it: * is an **Unknown Label**, or * contains any of the following prohibited UTF-8 characters. The `subgraph-unindexable` UTF-8 characters are: 1. `\0` (null byte) - PostgreSQL does not allow storing this character in text fields 2. `.` (period) - Conflicts with ENS label separator logic 3. `[` (left square bracket) - Conflicts with “unknown label” representations 4. `]` (right square bracket) - Conflicts with “unknown label” representations A `subgraph-indexable` **Literal Label** is a **Known Literal Label** that does NOT contain any of the prohibited UTF-8 characters. ### Subgraph Interpreted Label [Section titled “Subgraph Interpreted Label”](#subgraph-interpreted-label) A **Subgraph Interpreted Label** is a **Label** that is either: * (if `subgraph-indexable`): a **Literal Label** guaranteed to not contain any of the `subgraph-unindexable` UTF-8 characters, or * (if `subgraph-unindexable`): an Encoded LabelHash. ### Subgraph Interpreted Name [Section titled “Subgraph Interpreted Name”](#subgraph-interpreted-name) A **Subgraph Interpreted Name** is a name exclusively composed of 0 or more **Subgraph Interpreted Labels**. ## Core Protocol Extension Concepts [Section titled “Core Protocol Extension Concepts”](#core-protocol-extension-concepts) The following concepts extend the core ENS protocol to handle multichain and off-chain scenarios that arise in modern ENS usage. ### Subregistry [Section titled “Subregistry”](#subregistry) A **Subregistry** is any data structure outside of the [Registry](https://docs.ens.domains/terminology#registry) that manages supplemental state for a set of [subnames](https://docs.ens.domains/terminology#subname-subdomain). Each [name](https://docs.ens.domains/terminology#name) has the potential for association with at least 1 subregistry (through the [Name Wrapper](https://docs.ens.domains/terminology#name-wrapper)) and may optionally be associated with multiple subregistries. When a name is associated with multiple subregistries, this means that the full state of a name must be combined across the Registry and each associated subregistry. For example, the state of all direct subnames of .eth is distributed across the Registry and two subregistries: the BaseRegistrar and the Name Wrapper. The ENS protocol does not currently define standards for subregistries: subregistries currently exist outside the scope of ENS protocol standards. For example, subregistries could live on L1, on L2s, or offchain (in a database or even in a Google Sheet). The ENS protocol currently provides no standardized mechanism to discover subregistries or to interact with subregistries. Some specific implementations of subregistries include: * The [BaseRegistrar](https://github.com/ensdomains/ens-contracts/blob/staging/contracts/ethregistrar/BaseRegistrarImplementation.sol) that holds supplemental state for direct subnames of .eth. This includes state for ERC721 NFTs and expiry times. * The [NameWrapper](https://docs.ens.domains/terminology#name-wrapper), which serves as a subregistry for the entire ENS root (all ENS names). This includes state for ERC1155 NFTs, expiry times, and fuses. * Note how direct subnames of .eth are an example of multiple subregistries potentially holding supplemental state for a name outside the Registry. * The contracts on Base that manage supplemental state for direct subnames of [base.eth](https://www.base.org/names). * The contracts on Linea that manage supplemental state for direct subnames of [linea.eth](https://names.linea.build/). * The contracts on Base / Optimism that manage supplemental state for DNS names managed by [3DNS](https://3dns.box/). * The offchain databases that manage supplemental state for direct subnames of [uni.eth](https://blog.uniswap.org/introducing-uni-eth-your-unique-web3-username). * The offchain databases that manage supplemental state for direct subnames of [cb.id](https://help.coinbase.com/en/wallet/managing-account/coinbase-ens-support). * DNS nameservers for (essentially) all DNS names. Since ENS is a superset of DNS, (essentially) any DNS name is an ENS name. Therefore, whenever supplemental state associated with a DNS name is updated in a DNS nameserver, a subregistry is being updated. ### Subregistrar [Section titled “Subregistrar”](#subregistrar) A **Subregistrar** is any system that is a [Registrar](https://docs.ens.domains/terminology#registrar) or that writes to a subregistry. This definition expands the definition of Registrar to include cases such as: * The [ETHRegistrarController](https://github.com/ensdomains/ens-contracts/blob/staging/contracts/ethregistrar/ETHRegistrarController.sol) that writes to BaseRegistrar (the owner of the “eth” TLD). Note how the definition of “Registrar” in the official ENS glossary only includes contracts that are pointed to by the owner field of the Registry. Therefore, the BaseRegistrar is a Registrar (and a Subregistry), while the ETHRegistrarController is a Subregistrar. * The contracts on Base that write to the Subregistry for direct subnames of base.eth. These contracts live on Base, therefore they cannot meet the definition of Registrar because they can’t be set as the owner in the Registry on Ethereum mainnet. * The offchain systems that write to the offchain databases associated with direct subnames of uni.eth and cb.id. * Any NFT marketplace that supports the exchange of an NFT representing ownership of an ENS name. Each time a NFT is exchanged, state about that NFT must be updated within a related subregistry. Therefore the marketplace enabling that trade is a Subregistrar. * Any DNS registrar, as ENS is a superset of DNS. ### Shadow Registry [Section titled “Shadow Registry”](#shadow-registry) A **Shadow Registry** is a Subregistry meeting ALL of the following constraints: 1. Not the Registry; 2. Implemented as a smart contract exposing the same interface as the Registry; 3. Used as part of the source of truth for a CCIP-Read Gateway Server for ENSIP-10 (wildcard resolution) powered subnames. A specific implementation of a Shadow Registry can be found in [this contract](https://github.com/base-org/basenames/blob/v1.0.4/src/L2/Registry.sol) storing a subset of the state of base.eth subnames on Base. # Self-host ENSNode > Decide whether to self-host ENSNode and understand the architecture. This page will help you decide whether to self-host ENSNode or use the hosted instances. Includes architecture overview of the ENSNode stack (ENSIndexer, ENSDb, ENSRainbow, ENSApi). Note Follow these guides to deploy an ENSNode instance to the cloud. Running your own ENSNode instance is helpful for those that wish to: * Maintain control over their own infrastructure * Ensure control over their own availability and uptime guarantees * Customize ENSNode’s behavior * Own the resulting Postgres index for custom queries or `JOIN`s Private Networking Note that because ENSNode makes many label healing requests to ENSRainbow while indexing, it’s *imperative* that they be on the same local network to minimize request time. ### Bootstrap from a snapshot (coming soon) [Section titled “Bootstrap from a snapshot (coming soon)”](#bootstrap-from-a-snapshot-coming-soon) Self-hosting today means standing up a fresh ENSNode instance from zero and waiting on an initial backfill - including the RPC bill that comes with it. **[ENSDb snapshots and `ensdb-cli`](/docs/integrate/integration-options/ensdb-cli)** will make it cheap and easy to begin working with indexed ENS data. Cut your RPC bill Skip the historical RPC fanout entirely. Pull down a published ENSDb snapshot instead of replaying 9+ years of ENS history through your own RPC provider - saving real money on every bootstrap, every CI run, every fresh environment. Spin up in minutes, not days Skip the long historical backfill too. A nearly up-to-date ENS index is ready as fast as you can download it - no specially configured Postgres required, no days of waiting. Perfect for hackathons, demos, prototypes, and any other time you need a fresh ENSDb fast. Repeatable CI environments Use snapshots as deterministic fixtures for tests and CI pipelines. Every run starts from the same well-known ENS state, with zero RPC dependency. Confident release reviews Diff snapshots across ENSNode versions to catch unexpected changes in indexed data - a powerful way to validate indexing-logic refactors before a release ships. Easier self-hosting Bootstrap your own ENSNode instance from a recent snapshot, then catch up to realtime from there. Less time waiting, more time building. ensdb-cli: purpose-built ops tooling \`ensdb-cli\` surfaces the operations you actually need day-to-day: inspect indexer instances, manage snapshots, clean up unused schemas, and more. [More about ENSDb Snapshots and ensdb-cli ](/docs/integrate/integration-options/ensdb-cli)Roadmap for ENSDb Snapshots and ensdb-cli ### Massive scalability [Section titled “Massive scalability”](#massive-scalability) [Learn about ENSNode scalability ](/docs/self-host/scalability)Architecture overview of how to scale ENSNode to handle a massive volume of requests. ### Deploying with Docker [Section titled “Deploying with Docker”](#deploying-with-docker) The Docker deployment option provides the easiest way to run the full ENSNode suite of services both locally and in the cloud. [Deploy with Docker ](/docs/self-host/docker) ### Deploying with Terraform [Section titled “Deploying with Terraform”](#deploying-with-terraform) An example Terraform deployment reference is available, showing an example of deploying the full ENSNode suite on Render with AWS managed domain names. [Deploy with Terraform ](/docs/self-host/terraform) # Deploying ENSNode with Docker The Docker images are the easiest way to run or deploy the ENSNode suite of services, both locally and in the cloud. Postgres Requirement ENSIndexer runs `CREATE EXTENSION IF NOT EXISTS pg_trgm` at startup to back partial-name search indexes. If you’re swapping out the bundled `postgres:17` image for a managed or custom Postgres, make sure the [`pg_trgm`](https://www.postgresql.org/docs/current/pgtrgm.html) extension is available for installation (it ships with stock Postgres contrib and is available for installation on most managed providers). [Configure Environment Variables ](/docs/services/ensindexer/usage/configuration) ENSNode provides several [Docker Compose](https://docs.docker.com/compose/) files for different use cases: * **`docker/docker-compose.yml`** — base stack for mainnet/sepolia: ensindexer, ensapi, ensrainbow, ensadmin, ensdb (postgres) * **`docker/docker-compose.devnet.yml`** — full stack against local devnet (ens-test-env), works out of the box with no configuration required ### Mainnet / Sepolia [Section titled “Mainnet / Sepolia”](#mainnet--sepolia) Copy the example env file and configure it: ```bash cp docker/envs/.env.docker.example docker/envs/.env.docker.local ``` Edit `docker/envs/.env.docker.local` to set your `NAMESPACE`, `PLUGINS`, and RPC endpoints (e.g. `ALCHEMY_API_KEY` or `RPC_URL_1`), then run: ```bash docker compose -f docker/docker-compose.yml up -d ``` ### Local devnet [Section titled “Local devnet”](#local-devnet) Configuration is optional. To customize defaults (e.g. change `PLUGINS`), copy the example as in previous step and edit it. Otherwise, skip setup and run directly: ```bash docker compose -f docker/docker-compose.devnet.yml up -d ``` [Docker Compose usage and commands ](https://github.com/namehash/ensnode/blob/main/docker/README.md)See all use cases, commands, and configuration options in docker/README.md on GitHub. [View Docker Compose files on GitHub ](https://github.com/namehash/ensnode/blob/main/docker)Browse the full docker/ directory including service definitions. [Building ENSNode Docker Images ](/docs/reference/contributing/building)Learn about building docker images for ENSNode services. Port Mappings Note that while the default `docker/docker-compose.yml` exposes each container’s port to the host machine (useful for development), you may only wish to expose ENSApi’s `4334` and avoid exposing services like ENSRainbow, ENSAdmin, and Postgres to the wider internet. # ENSNode Scalability > Understand how ENSNode's architecture enables horizontal scaling. ENSNode’s architecture achieves effectively limitless horizontal scalability, no matter how large your query demands might be. By separating read / write workloads and making use of PostgreSQL’s async replication you can scale your ENSNode deployment to handle any request volumes while continuing to index the latest ENS data without impact to indexing performance. ## Horizontally scale ENSApi [Section titled “Horizontally scale ENSApi”](#horizontally-scale-ensapi) The ENSNode architecture naturally separates writes from reads: * **ENSIndexer** is the sole writer to ENSDb. It processes blockchain events and updates the database with the latest ENS state. * **ENSApi** is a read-only service. It serves GraphQL and REST API requests by querying ENSDb, but never writes to it. This separation insulates ENSIndexer from high request volumes. Whether you serve 10 requests per second or 10,000, the indexing process remains unaffected because ENSApi does not compete with ENSIndexer for database write locks or indexer resources. ## Horizontally Scale ENSDb [Section titled “Horizontally Scale ENSDb”](#horizontally-scale-ensdb) ENSDb is built on PostgreSQL, which provides mature, production-proven mechanisms for horizontal scaling. The most common approach is **asynchronous replication**: 1. **Primary ENSDb**: ENSIndexer writes to your single ENSDb primary instance. You can fully isolate this ENSDb primary instance from any incoming API requests coming through ENSApi. 2. **Replica ENSDb**: Configure the asynchronous replication of your primary ENSDb instance to one or more ENSDb read replica instances. 3. **Read scaling**: Connect your horizontally scaled ENSApi instances exclusively to your ENSDb read replica instances if you wish to fully isolate extreme levels of API request volumes from indexing. Because ENSApi is read-only, it can safely connect to an ENSDb replica without write-routing or write-conflict concerns. The tradeoff is that, with asynchronous replication, replica reads may briefly lag behind the primary. As your query load grows, you simply add more PostgreSQL replicas for the ENSDb primary instance and more ENSApi instances pointing to them. ## Going Massive [Section titled “Going Massive”](#going-massive) An ENSNode deployment with *massive* horizontal scaling might look like this: ![ENSNode Scalable Deployment Architecture](/ensnode-scalability.svg) * **One ENSIndexer instance** → writes to the ENSDb primary instance * **One ENSDb primary instance** → replicates asynchronously to `M` replicas * **Multiple ENSApi instances** → each of `N` instances connected to one of the ENSDb replicas (often load-balanced across replicas) * **One ENSRainbow instance** → co-located with ENSIndexer instance on the same local network for fast label healing Only the largest ENSNode deployments will need such a complex setup. Still, it’s good to know that if you need massive scalability, we have you covered! The ENSNode architecture lets you scale reads independently of writes, matching your infrastructure to your actual traffic patterns. ## Further Reading [Section titled “Further Reading”](#further-reading) [ENSApi Overview ](/docs/services/ensapi) [ENSDb Overview ](/docs/services/ensdb) [ENSIndexer Overview ](/docs/services/ensindexer) [ENSRainbow Overview ](/docs/services/ensrainbow) # Deploying ENSNode with Terraform This guide will help you deploy ENSNode using Terraform to Render. The Terraform configuration provides a complete infrastructure setup including database, ENSIndexer, ENSRainbow, and other required services. Important These Terraform scripts are currently specific to ENSNode instances hosted by NameHash Labs. While these scripts provide a good starting point for deploying your own ENSNode instance, you will need to make modifications to suit your specific deployment needs. We plan to generalize these scripts in the future to better support community deployments. [Example Terraform Configuration ](https://github.com/namehash/ensnode/tree/main/terraform)View the example Terraform configuration on GitHub. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * [Terraform](https://www.terraform.io/downloads.html) installed * [Render](https://render.com/) account * Render API token (generate from ) * RPC URLs for the chains you want to support (Mainnet, Sepolia, Base, Linea) * AWS account (for DNS management) * AWS S3 bucket defined inside AWS account - `ensnode-terraform` (for Terraform state) Postgres Requirement ENSIndexer runs `CREATE EXTENSION IF NOT EXISTS pg_trgm` at startup to back partial-name search indexes. Render’s managed Postgres has [`pg_trgm`](https://www.postgresql.org/docs/current/pgtrgm.html) available by default; if you adapt this configuration to a different provider, confirm the extension is available for installation on your Postgres plan. ## Configuration [Section titled “Configuration”](#configuration) Copy `.env.sample` to `.env.local` and fill in your configuration values: ```bash # ENSNode configuration ensnode_version = "0.0.0" # pin to the specific version you want to use as found at https://github.com/namehash/ensnode/releases ensrainbow_searchlight_label_set_version = "1" # pin to the specific version, see https://ensnode.io/ensrainbow/concepts/glossary#label_set_version anthropic_api_key = "your_anthropic_api_key" # Render configuration render_api_key = "your_render_api_key" render_owner_id = "your_render_owner_id" render_environment = "your_render_environment" # Mainnet RPC URLs ethereum_mainnet_rpc_url = "your_ethereum_mainnet_rpc_url" base_mainnet_rpc_url = "your_base_mainnet_rpc_url" linea_mainnet_rpc_url = "your_linea_mainnet_rpc_url" arbitrum_mainnet_rpc_url = "your_arbitrum_mainnet_rpc_url" scroll_mainnet_rpc_url = "your_scroll_mainnet_rpc_url" # Sepolia RPC URLs ethereum_sepolia_rpc_url = "your_ethereum_sepolia_rpc_url" base_sepolia_rpc_url = "your_base_sepolia_rpc_url" linea_sepolia_rpc_url = "your_linea_sepolia_rpc_url" optimism_sepolia_rpc_url = "your_optimism_sepolia_rpc_url" arbitrum_sepolia_rpc_url = "your_arbitrum_sepolia_rpc_url" scroll_sepolia_rpc_url = "your_scroll_sepolia_rpc_url" ``` ## Infrastructure Components [Section titled “Infrastructure Components”](#infrastructure-components) The Terraform configuration sets up the following components: * Render * Render project and environment * PostgreSQL database * ENSIndexer services * ENSRainbow service * AWS * DNS configuration ## Deployment Steps [Section titled “Deployment Steps”](#deployment-steps) 1. Initialize Terraform: ```bash terraform init ``` 2. Review the planned changes: ```bash terraform plan ``` 3. Apply the configuration: ```bash terraform apply ``` ## Cleanup [Section titled “Cleanup”](#cleanup) To destroy the infrastructure: ```bash terraform destroy ``` # ENSNode services > Introduction to the services that make up the ENSNode stack, including ENSApi, ENSIndexer, ENSDb, ENSRainbow, ENSAdmin, and the planned ENSEngine. **Self-hosting or contributing to ENSNode development?** Use these sections for concepts, deployment, configuration, and contribution guides for the part of the ENSNode stack you care about. Each ENSNode instance is composed of **a set of services**. Each service has a clear role and set of responsibilities to form the full-stack ENSv2 development platform. [![](/_astro/ENSAdmin3D.D0Oj6vU8.png)](/docs/services/ensadmin) #### [ENSAdmin](/docs/services/ensadmin) [ENSNode operator dashboard and ENS Protocol Inspector.](/docs/services/ensadmin) [![](/_astro/ENSApi3D.8LUimORJ.png)](/docs/services/ensapi) #### [ENSApi](/docs/services/ensapi) [APIs for building on ENS, including the ENS Omnigraph API.](/docs/services/ensapi) [![](/_astro/ENSDb3D.DcZ4wRm5.png)](/docs/services/ensdb) #### [ENSDb](/docs/services/ensdb) [The live, onchain state of ENS in your DB.](/docs/services/ensdb) [![](/_astro/ENSIndexer3D.BXO95wC5.png)](/docs/services/ensindexer) #### [ENSIndexer](/docs/services/ensindexer) [Multichain indexer based on Ponder that combines any number of ENSv1 "Nametrees" and the ENSv2 "Namegraph" into a single unified "ENS Unigraph" indexed data model inside your ENSDb.](/docs/services/ensindexer) [![](/_astro/ENSRainbow3D.DNPXAdy5.png)](/docs/services/ensrainbow) #### [ENSRainbow](/docs/services/ensrainbow) [Heals unknown ENS names.](/docs/services/ensrainbow) #### [ENSEngine](/docs/services/ensengine) [Coming soon — watches ENSDb for changes and delivers ENS-aware webhooks.](/docs/services/ensengine) # What is ENSAdmin? > An overview of the ENSAdmin service. ENSAdmin is a user interface designed to help you monitor and manage your ENSNode instances effectively. It provides a clear overview of your nodes’ operational status, the plugins they are currently running, and the progress of their indexing. ## Key Features [Section titled “Key Features”](#key-features) ### Omnigraph API Playground [Section titled “Omnigraph API Playground”](#omnigraph-api-playground) A powerful GraphQL interface for querying the new Omnigraph API of your ENSNode. ![ENSAdmin: Omnigraph API Playground](/_astro/ensadmin-omnigraph.CUJ7G_k9_ZssTx3.webp) ### ENS Protocol Inspector [Section titled “ENS Protocol Inspector”](#ens-protocol-inspector) Inspect and analyze ENSNode data in detail. For example, use the ENS Protocol Inspector for the Primary Name Resolution. ![ENSAdmin: ENS Protocol Inspector](/_astro/ensadmin-primary-name-inspector.DIaQBWzy_Z1qFnOT.webp) ### ENSNode Stack Info [Section titled “ENSNode Stack Info”](#ensnode-stack-info) Get real-time insights into the config of all services running on your ENSNode. ![ENSAdmin: ENSNode Stack Info](/_astro/ensadmin-connection-view.Bmuo-a6r_7EXqk.webp) ### Indexing Status [Section titled “Indexing Status”](#indexing-status) Track the indexing status of your ENSNode to understand its current performance. ![ENSAdmin: Indexing Status](/_astro/ensadmin-status-view.CY2Tiblp_Zeu5Lo.webp) # Running ENSAdmin Locally Follow ENSNode’s Contribution Guide First Follow ENSNode’s [contribution guide](/docs/reference/contributing) to prepare your workspace environment & install dependencies. ## Install dependencies [Section titled “Install dependencies”](#install-dependencies) ```bash pnpm install ``` ## Set configuration [Section titled “Set configuration”](#set-configuration) ```bash cp .env.local.example .env.local ``` Available environment variables: * `NEXT_PUBLIC_SERVER_CONNECTION_LIBRARY` - Comma-separated list of ENSNode URLs offered as connection options (defaults to NameHash’s hosted instances) * `ENSADMIN_PUBLIC_URL` - The public URL where ENSAdmin is hosted (optional) ## Run development server [Section titled “Run development server”](#run-development-server) Starts the Next.js development server: ```bash pnpm dev ``` Visit to build with ENSAdmin locally. ## Preview production build [Section titled “Preview production build”](#preview-production-build) Create an optimized static export and serve it locally: ```bash pnpm build pnpm start ``` The production preview runs on using the `serve` package. Static Export ENSAdmin uses Next.js to output a Single Page Application (SPA). The production build generates a static site in the `out` directory, which is then served by nginx in the Docker container. # Using Docker The easiest way to run ENSAdmin is via Docker container. ## Running with Docker [Section titled “Running with Docker”](#running-with-docker) ENSAdmin is deployed as a Single Page React Application served by nginx on port 4173. ### Using pre-built image [Section titled “Using pre-built image”](#using-pre-built-image) ```bash docker run \ -d \ -p 4173:4173 \ ghcr.io/namehash/ensnode/ensadmin:latest ``` ### Running with custom ENSNode URLs [Section titled “Running with custom ENSNode URLs”](#running-with-custom-ensnode-urls) To configure the server connection library: ```bash docker run -d \ -p 4173:4173 \ -e NEXT_PUBLIC_SERVER_CONNECTION_LIBRARY=http://localhost:42069 \ ghcr.io/namehash/ensnode/ensadmin:latest ``` The `NEXT_PUBLIC_SERVER_CONNECTION_LIBRARY` environment variable accepts a comma-separated list of ENSNode URLs that will be offered as connection options in the ENSAdmin interface. # ENSApi Overview > Serve ENSNode's APIs with ENSApi ENSApi is a web service built on top of [ENSDb](/docs/services/ensdb), and provides access to indexed ENS data through multiple HTTP APIs. ## GraphQL APIs [Section titled “GraphQL APIs”](#graphql-apis) ### ENS Omnigraph API [Section titled “ENS Omnigraph API”](#ens-omnigraph-api) The **Omnigraph API** is the *new* primary GraphQL API for ENSNode that provides a unified access model for both ENSv1 and ENSv2 data. Write your queries once and get automatic support for ENSv1 and ENSv2 data! [ENS Omnigraph API ](/docs/integrate/omnigraph)Learn more about integrating the ultimate ENSv2 API into your app. ### ENS Subgraph API [Section titled “ENS Subgraph API”](#ens-subgraph-api) The Subgraph API is the **deprecated** *legacy* ENSv1-only GraphQL API. It is now deprecated in favor of the Omnigraph API, but is still available for backwards compatibility reasons with The Graph’s ENS subgraph. ## REST APIs [Section titled “REST APIs”](#rest-apis) A growing set of REST APIs are also being added to ENSApi. [ENSApi Reference ](/docs/services/ensapi/reference/api-reference)Learn more about ENSApi's REST APIs ## Configuration [Section titled “Configuration”](#configuration) ENSApi can be configured via environment variables. [ENSApi Configuration Options ](/docs/services/ensapi/usage/configuration) ## ENS Protocol Acceleration [Section titled “ENS Protocol Acceleration”](#ens-protocol-acceleration) We are engineering radically accelerated resolution of most ENS queries. ENSNode’s architecture unlocks near-instant bulk resolution of live ENS records, including name resolution configurations that span across multiple chains. [ENS Protocol Acceleration ](/docs/integrate/omnigraph/protocol-acceleration)How ENSApi accelerates ENS resolution by implementing the Universal Resolver over indexed data. # ENSApi Development and Contributions > Learn how to run ENSApi locally for development and contributions. Note This guide covers running ENSApi locally for development and contributions. ## Configure Environment Variables [Section titled “Configure Environment Variables”](#configure-environment-variables) ```bash cd apps/ensapi cp .env.local.example .env.local ``` Caution ENSApi’s `.env.local` should be placed at `apps/ensapi/.env.local`, *not* at the monorepo root. [ENSApi Configuration Options ](/docs/services/ensapi/usage/configuration) ## Running ENSApi [Section titled “Running ENSApi”](#running-ensapi) ```bash # from monorepo root pnpm run -F ensapi dev # from apps/ensapi pnpm run dev ``` # ENSApi API Reference > Interactive API reference documentation for ENSApi endpoints. # Configuring ENSApi ENSApi’s behavior can be configured through environment variables. Copy `.env.local.example` to `.env.local` and configure all required values. .env.local.example ```bash # The port ENSApi listens on. # Optional. If this is not set, the default value is 4334. # PORT=4334 # ENSDb: Database URL # Required. This is the connection string for the ENSDb database where your ENSIndexer is writing data. # It should match the ENSDB_URL used by your ENSIndexer. # It should be in the format of `postgresql://:@:/` # # See https://ensnode.io/ensindexer/usage/configuration for additional information. ENSDB_URL=postgresql://dbuser:abcd1234@localhost:5432/my_database # ENSDb: ENSIndexer Schema Name # Required. This is the name of the ENSIndexer Schema in ENSDb where ENSApi should read indexed data stored by your ENSIndexer. # It should match the ENSINDEXER_SCHEMA_NAME used by your ENSIndexer. ENSINDEXER_SCHEMA_NAME=ensindexer_0 # ENSApi: RPC Configuration # Required. ENSApi requires a HTTP RPC exclusively for the ENS Root Chain of the ENS namespace (ex: mainnet, sepolia, ens-test-env) that your ENSIndexer is configured to use. # This ENS Root Chain RPC is used to power the Resolution API in situations where Protocol Acceleration is not possible and dynamic RPC calls are required to serve a resolution request. # Note that ENS resolution only requires an RPC for the ENS Root Chain. All lookups of data off of the ENS Root Chain are achieved through CCIP-read. # # When ENSApi starts up it loads an ENSIndexer public config from ENSDb and verifies that # the ENS Root Chain for the configured ENS namespace (ex: mainnet / sepolia / etc) has an RPC defined for ENSApi to use. # # NOTE: You must configure your own private RPC endpoints. Public RPC endpoints are rate limited and # will likely not provide acceptable performance (though this depends on how many non-acceleratable # Resolution requests you make to ENSApi). # # Private RPC service options include: # - Alchemy (paid plan) - https://www.alchemy.com/ # - QuickNode (paid plan) - https://www.quicknode.com # - drpc.org (paid plan) - https://drpc.org/ # - Infura (paid plan) - https://infura.io/ # # Example RPC endpoint URL formats: # - Alchemy RPC endpoints # - https://eth-mainnet.g.alchemy.com/v2/ # - wss://eth-mainnet.g.alchemy.com/v2/ # - https://base-sepolia.g.alchemy.com/v2/ # - QuickNode RPC endpoints # - https://.quiknode.pro/ # - wss://.quiknode.pro/ # - https://.base-sepolia.quiknode.pro/ # - dRPC RPC endpoints # - https://lb.drpc.live/ethereum/ # - wss://lb.drpc.live/ethereum/ # - https://lb.drpc.live/base-sepolia/ # - Infura RPC endpoints # - https://mainnet.infura.io/v3/ # - wss://mainnet.infura.io/ws/v3/ # - https://base-sepolia.infura.io/v3/ # # Configuring the following environment variables enables auto-generation of # RPC endpoint URLs for each indexed chain (with limitations as noted below): # - ALCHEMY_API_KEY — API key for your Alchemy app, if set, Alchemy RPC URLs (HTTP) will be autogenerated for chains Alchemy supports. # - QUICKNODE_API_KEY - API key for your multi-chain QuickNode endpoint. # - QUICKNODE_ENDPOINT_NAME — endpoint name of your multi-chain QuickNode endpoint. # - DRPC_API_KEY — if set, an dRPC RPC URL (HTTP) will be autogenerated for chains dRPC supports. # - RPC_URL_${chainId} — specific, per-chain RPC settings. # # If both, QUICKNODE_API_KEY and QUICKNODE_ENDPOINT_NAME are specified, # a QuickNode RPC URL will be autogenerated for chains QuickNode supports. # If only one of QUICKNODE_API_KEY or QUICKNODE_ENDPOINT_NAME is set but not the other, # the configuration will be rejected with an error. # Note key constraints of QuickNode RPC endpoints: # - Only multi-chain QuickNode endpoints can be used for setting # QUICKNODE_API_KEY and QUICKNODE_ENDPOINT_NAME environment variables. # A multi-chain endpoint allows sharing the same endpoint name and API key # across all chains supported by QuickNode platform. Read more in QuickNode docs: # https://www.quicknode.com/guides/quicknode-products/how-to-use-multichain-endpoint # - QuickNode platform does not support Linea Sepolia RPC (as of 2025-12-03). # https://www.quicknode.com/docs/linea # # Each RPC_URL_${chainId} environment variable, if specified, will take precedence over # all auto-generated RPC URLs for the specified chainId from Alchemy, QuickNode, or dRPC. # It must be a comma-separated list of HTTP/HTTPS RPC endpoints. # ENSApi provides all the resulting RPC URLs to Viem Client. If multiple HTTP RPC URLs are provided to Viem Client, # Viem Client automatically balances requests between them (see below). # # Auto-generated RPC URLs: # ALCHEMY_API_KEY=xyz # QUICKNODE_API_KEY=your-api-key # QUICKNODE_ENDPOINT_NAME=your-endpoint-name # DRPC_API_KEY=xyz # # For full control of chain-specific RPC configuration, use the RPC_URL_{chainId} environment variable. # Its value is a comma-separated list of one or more HTTP RPC URLs. # # Example (single HTTP RPC URL): # RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY # RPC_URL_11155111=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY # RPC_URL_1337=http://localhost:8545 # # Example (multiple HTTP RPC URL, single WebSocket RPC URL): # RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY,https://lb.drpc.org/ethereum/YOUR_API_KEY # # The RPC_URL_${chainId} value has the following invariants: # - Must always include at least one HTTP/HTTPS RPC endpoint. RPC endpoints can # fail or experience downtime. To optimize resiliency of ENSIndexer, # defining more than one HTTP/HTTPS endpoint (from more than one RPC provider) # per indexed chain is strongly encouraged. # Log Level # Optional. If this is not set, the default value is "info". # Allowed values: "fatal", "error", "warn", "info", "debug", "trace", "silent". # LOG_LEVEL=info # The Graph Subgraph API Fallback # Optional. If this value is set, on the condition that the connected ENSIndexer is not # sufficiently "realtime", ENSApi's Subgraph API will fallback to proxying subgraph queries # it receives to The Graph's hosted subgraphs using this API key. # THEGRAPH_API_KEY= # Referral Program Edition Config Set Definition (optional) # URL that returns JSON for a referral program edition config set. # If not set, ENSApi treats the referral program as having zero configured editions: # the editions endpoint returns an empty list, edition-specific queries (leaderboard, # referrer detail) return 404, and no referral-related work is performed against ENSDb. # # JSON Structure: # The JSON must be an array of edition config objects (SerializedReferralProgramEditionConfig[]). # For the complete schema definition, see makeReferralProgramEditionConfigSetArraySchema in @namehash/ens-referrals # # Fetching Behavior: # - Fetched proactively when ENSApi starts up (non-blocking; ENSApi may begin accepting requests before load completes) # - Once successfully loaded, cached indefinitely (never expires or revalidates) # - On load failure: # * Error is logged # * ENSApi continues running # * Failed state is cached for 1 minute, then retried on subsequent requests # * Referral analytics API requests (/v1/ensanalytics/*) receive error responses # until successful load; other API endpoints are unaffected. # # Configuration Notes: # - Include all edition configs you want active in the JSON # - The array may be empty # - All edition slugs must be unique # # REFERRAL_PROGRAM_EDITIONS=https://example.com/editions.json ``` [.env.local.example ](https://github.com/namehash/ensnode/blob/main/apps/ensapi/.env.local.example)View this file on GitHub. # Getting started with ENSDb > ENSDb is an open standard for bi-directional ENS integration. An ENSDb instance is a PostgreSQL database served from a PostgreSQL server — and a single server can serve multiple ENSDb instances. ## Vision [Section titled “Vision”](#vision) Get the whole, live onchain state of ENS in your database. ## Core Philosophy [Section titled “Core Philosophy”](#core-philosophy) ### Open Standard [Section titled “Open Standard”](#open-standard) ENSDb is an open standard for bi-directional ENS integration. It defines a carefully crafted set of database schema designs, rules, and constraints for storing the ENS onchain state in a PostgreSQL database — making the data accessible from any programming language. The [ENSDb standard](/docs/services/ensdb/concepts/glossary#ensdb-standard) is implementation-agnostic, so you have options: implement your own [ENSDb Writer](/docs/services/ensdb/concepts/glossary#ensdb-writer) or [ENSDb Reader](/docs/services/ensdb/concepts/glossary#ensdb-reader) in any language, or use the initial reference implementations provided by ENSNode — [ENSIndexer](/docs/services/ensindexer/) as an ENSDb Writer and [ENSApi](/docs/services/ensapi/) as an ENSDb Reader. Build in Any Language Each ENSDb instance runs in a standard PostgreSQL database, so to interact with it, you can use *any programming language* that has a PostgreSQL driver. Zig, Rust, Go, Python, JavaScript, Ruby, Java, C# — the choice is yours. ## What You Get [Section titled “What You Get”](#what-you-get) ### Complete ENS State [Section titled “Complete ENS State”](#complete-ens-state) An **ENSDb instance** contains the **live onchain state of ENS**, including: * Domains (ENSv1 and ENSv2) * Registrations and renewals * Resolver records and text records * Events and ownership history * NFT/token data for names * … and more! ### PostgreSQL Benefits [Section titled “PostgreSQL Benefits”](#postgresql-benefits) By building on a PostgreSQL database, ENSDb inherits world-class capabilities: * ACID transactions — Data integrity guarantees * Complex queries — Joins, aggregations, window functions * Scalability — Replication, sharding, connection pooling * Ecosystem — Mature tools, ORMs, dashboards, analytics platforms * Reliability — Decades of production-proven technology ## What You Can Build [Section titled “What You Can Build”](#what-you-can-build) ENSDb unlocks a new universe of ENS applications: Custom APIs Build specialized GraphQL or REST APIs tailored to your use case. Query exactly the data you need with full SQL power. Analytics & Dashboards Create real-time dashboards and analytics pipelines. Better than Dune — you have the live ENS state locally with sub-second query latency. CLIs & Developer Tools Build command-line tools for ENS operations. Query domains, and any specialized lookup — all from your terminal. Event-Based Engines Build reactive systems that respond to ENS state changes. Respond to registration lifecycle updates, ownership transfers, resolver updates. Data Pipelines Feed ENS data into your existing data infrastructure. Sync to data warehouses, trigger webhooks, populate search indexes. AI Agents Build AI agents that can query ENS state and perform actions based on that data. ## Quick Start [Section titled “Quick Start”](#quick-start) [Use the ENSDb SDK ](/docs/services/ensdb/usage/sdk)Quick start with the TypeScript SDK for ENSDb, providing type-safe database access and utilities for working with ENSDb. [Use the SQL Interface ](/docs/services/ensdb/usage/sql)Quick start with the SQL interface for ENSDb, allowing you to interact with your ENSDb instance using any PostgreSQL client and SQL queries. ## Documentation Structure [Section titled “Documentation Structure”](#documentation-structure) [Concepts ](/docs/services/ensdb/concepts)Understand ENSDb architecture, schemas, and data flow [Usage ](/docs/services/ensdb/usage)Learn how to use ENSDb with the SDK and SQL interface [Integrations ](/docs/services/ensdb/integrations/ensnode)Learn how to integrate ENSDb with other systems and tools ## What’s next [Section titled “What’s next”](#whats-next) Coming soon: ensdb-cli & ENSDb snapshots We’re building [**`ensdb-cli` & ENSDb snapshots**](/docs/integrate/integration-options/ensdb-cli) so you can pull down a fresh ENSDb in minutes instead of paying for a full historical RPC backfill among many other benefits. ## Related Projects [Section titled “Related Projects”](#related-projects) * **[ENSIndexer](/docs/services/ensindexer/)** — The reference [ENSDb Writer](/docs/services/ensdb/concepts/glossary#ensdb-writer) implementation * **[ENSApi](/docs/services/ensapi/)** — The reference [ENSDb Reader](/docs/services/ensdb/concepts/glossary#ensdb-reader) implementation # Overview > Understanding the fundamentals of ENSDb architecture, schemas, and data flow. ## Who Should Read This [Section titled “Who Should Read This”](#who-should-read-this) * **Developers integrating with ENSDb** — Understand the schema structure to write effective queries * **DevOps operators running ENSNode** — Understand how ENSDb behaves during different indexing phases * **Anyone building custom ENSDb Writers or ENSDb Readers** — Learn the architecture for implementing the ENSDb standard ## Core Concepts [Section titled “Core Concepts”](#core-concepts) Understanding these fundamentals will help you work effectively with ENSDb, whether you’re querying data, integrating with third-party services, or operating an ENSNode instance. [Glossary ](/docs/services/ensdb/concepts/glossary)Terminology and definitions used throughout ENSDb documentation. [Database Schemas ](/docs/services/ensdb/concepts/database-schemas)Detailed explanation of database schemas: ENSNode Schema, and the ENSIndexer Schema. ## Next Steps [Section titled “Next Steps”](#next-steps) After understanding these concepts: 1. **[Query ENSDb with SQL](/docs/services/ensdb/usage/sql)** — Language-agnostic SQL examples 2. **[Use the TypeScript SDK](/docs/services/ensdb/usage/sdk)** — Type-safe database access # Database Schemas > Detailed explanation of all database schemas that make up ENSDb. This page explains the different database schemas in ENSDb, including the ENSNode Schema, and the modular ENSIndexer Schema. ## Overview of ENSDb Schemas [Section titled “Overview of ENSDb Schemas”](#overview-of-ensdb-schemas) ENSDb instance can have two distinct kinds of database schemas: a single shared **ENSNode Schema** for operational metadata, and one **ENSIndexer Schema** per running ENSIndexer instance for all indexed ENS data. [View Interactive Diagram of ENSDb Schemas](https://dbdiagram.io/d/ENSDb-69f0d325ddb9320fdc7bd564). ## ENSNode Schema [Section titled “ENSNode Schema”](#ensnode-schema) The `ensnode` database schema contains shared operational metadata, called ENSNode Metadata, for the entire ENSDb database. Multi-tenancy ENSDb supports multiple ENSIndexer instances coexisting in the same database, each with its own isolated database schema. The ENSNode `metadata` table is the central registry that tracks all of them. Each instance identifies itself by writing rows scoped to its own database schema name via the `ens_indexer_schema_name` column. ### ENSNode Metadata [Section titled “ENSNode Metadata”](#ensnode-metadata) Possible key-value pairs are defined by the `EnsNodeMetadata` union type. As of now, it includes: `EnsNodeMetadataIndexingMetadataContext`. | Column | Type | Nullable | Description | | ------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ens_indexer_schema_name` | `text` | no | References the name of the ENSIndexer Schema that the metadata record belongs to. This allows multi-tenancy where multiple ENSIndexer instances can write to the same ENSNode Metadata table. | | `key` | `text` | no | Allowed keys: `indexing_metadata_context`. | | `value` | `jsonb` | no | Guaranteed to be a serialized representation of a JSON object. | **Primary key:** `(ens_indexer_schema_name, key)` — ensures that there is only one record for each key per ENSIndexer instance. #### Known keys [Section titled “Known keys”](#known-keys) | Key | TypeScript type | Description | | --------------------------- | ------------------------------------ | ------------------------------------------------------------------ | | `indexing_metadata_context` | `IndexingMetadataContextInitialized` | Stores indexing status and stack info for the ENSIndexer instance. | *** ## ENSIndexer Schema [Section titled “ENSIndexer Schema”](#ensindexer-schema) Each ENSIndexer instance owns a dedicated database schema in ENSDb. All indexed ENS data for that instance lives within it, fully isolated from other instances. On startup, an ENSIndexer instance registers itself in `ensnode.metadata` using its database schema name as `ens_indexer_schema_name`. The ENSIndexer Schema is modular, composed of multiple logical database sub-schemas, each implemented for specific requirements by a separate Ponder plugin. *** ### Unigraph [Section titled “Unigraph”](#unigraph) The Unigraph schema — the unified, polymorphic ENSv1 + ENSv2 data model — is documented in full in its canonical reference in the Integrate section: Unigraph Schema Reference The complete table-by-table reference for the Unigraph schema now lives at **[Unigraph Schema Reference](/docs/integrate/unigraph/schema-reference)**. *** ### Protocol Acceleration [Section titled “Protocol Acceleration”](#protocol-acceleration) Defined in [`protocol-acceleration.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/protocol-acceleration.schema.ts). Provides accelerated lookups for the Resolution API. Rather than traversing the full namegraph at query time for common operations, this database sub-schema materializes the minimal state needed to answer resolution queries efficiently. #### reverse\_name\_records [Section titled “reverse\_name\_records”](#reverse_name_records) Tracks an Account’s ENSIP-19 Reverse Name Records by CoinType. Not a source of truth for Primary Names This is **not** a cohesive, materialized index of all of an account’s Primary Names. It is **only** an index of its ENSIP-19 Reverse Name Records stored by a `StandaloneReverseRegistrar`: * `default.reverse` * `[coinType].reverse` * **Not** `*.addr.reverse` These records **cannot** be queried directly and used as a source of truth — you **must** perform Forward Resolution to resolve a consistent set of an Account’s ENSIP-19 Primary Names. These records are used to power Protocol Acceleration for those ReverseResolvers backed by a `StandaloneReverseRegistrar`. | Column | Type | Nullable | Description | | ----------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | `address` | `text` | no | The account address. Part of primary key. | | `coin_type` | `numeric(78)` | no | ENSIP-19 coin type. Part of primary key. | | `value` | `text` | no | Represents the ENSIP-19 Reverse Name Record for a given `(address, coin_type)`. Guaranteed to be a non-empty `InterpretedName`. | **Primary key:** `(address, coin_type)`. #### domain\_resolver\_relations [Section titled “domain\_resolver\_relations”](#domain_resolver_relations) Tracks Domain-Resolver relationships. This powers: (1) Domain-Resolver relationships within the GraphQL API, and (2) accelerated lookups of a Domain’s Resolver within the Resolution API. It is keyed by `(chain_id, address, domain_id)` to match the on-chain data model of Registry / (shadow)Registry Domain-Resolver relationships. | Column | Type | Nullable | Description | | ----------- | -------- | -------- | ----------------------------------------------------------------------------------- | | `chain_id` | `bigint` | no | Keyed by `(chain_id, address, domain_id)`. Part of primary key. | | `address` | `text` | no | The Registry (`ENSv1Registry` or `ENSv2Registry`)‘s AccountId. Part of primary key. | | `domain_id` | `text` | no | Part of primary key. | | `resolver` | `text` | no | The Domain’s assigned Resolver’s address. Always scoped to `chain_id`. | **Primary key:** `(chain_id, address, domain_id)`. **Indexes:** `domain_id` — secondary lookup off the PK. The namegraph-walk recursive CTE in `get-domain-by-interpreted-name` left-joins on `domain_id` alone, which the PK (leading-column `chain_id, address`) cannot serve. **Relations:** has one `resolver` via `(chain_id, resolver)`. #### resolvers [Section titled “resolvers”](#resolvers) Represents an individual `IResolver` contract that has emitted at least one event. Note that Resolver contracts can exist on-chain but not emit any events and still function properly, so checks against a Resolver’s existence and metadata must be done at runtime. | Column | Type | Nullable | Description | | ---------- | -------- | -------- | -------------------------------------------- | | `id` | `text` | no | Keyed by `(chain_id, address)`. Primary key. | | `chain_id` | `bigint` | no | Chain the resolver contract is deployed on. | | `address` | `text` | no | Address of the resolver contract. | **Indexes:** unique on `(chain_id, address)`. **Relations:** has many `resolver_records`. #### resolver\_records [Section titled “resolver\_records”](#resolver_records) Tracks a set of records for a specified `node` within a `resolver` contract on `chain_id`. Represents one `name` resolution record (see ENSIP-3), has many `resolver_address_records` (unique by `coin_type`, see ENSIP-9), and has many `resolver_text_records` (unique by key, see ENSIP-5). Not ENSIP-10 compliant These record values do **not** allow the caller to confidently resolve records for names without following Forward Resolution according to the ENS protocol. A direct query to the database for a record’s value is not ENSIP-10 nor CCIP-Read compliant. | Column | Type | Nullable | Description | | ------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | Keyed by `(chain_id, resolver, node)`. Primary key. | | `chain_id` | `bigint` | no | Part of the composite key. | | `address` | `text` | no | Resolver contract address. Part of the composite key. | | `node` | `text` | no | The name’s namehash. Part of the composite key. | | `name` | `text` | yes | The reverse-resolution (ENSIP-3) `name()` record, used for Reverse Resolution. If present, guaranteed to be a non-empty `InterpretedName`. | | `contenthash` | `text` | yes | ENSIP-7 contenthash raw bytes, or `null` if not set. | | `pubkeyX` | `text` | yes | PubkeyResolver X coordinate. Invariant: both `pubkeyX` and `pubkeyY` are either both `null` or both set. | | `pubkeyY` | `text` | yes | PubkeyResolver Y coordinate. Invariant: both `pubkeyX` and `pubkeyY` are either both `null` or both set. | | `dnszonehash` | `text` | yes | `IDNSZoneResolver` zone hash, or `null` if not set. | | `version` | `numeric(78)` | yes | `IVersionableResolver` version. `null` when no `VersionChanged` event has been seen for this `(chain_id, address, node)` — the resolver may not implement `IVersionableResolver`, or simply may never have been version-bumped. Consumers should treat `null` as “unknown” rather than `0`. | **Indexes:** unique on `(chain_id, address, node)`. **Relations:** belongs to one `resolvers` record via `(chain_id, address)`, has many `resolver_address_records`, has many `resolver_text_records`. #### resolver\_address\_records [Section titled “resolver\_address\_records”](#resolver_address_records) Tracks address records for a `node` by `coin_type` within a `resolver` on `chain_id`. Keyed by `(chain_id, resolver, node, coin_type)`, where the composite key segment `(chain_id, resolver, node)` describes a `resolver_records` entity. A `resolver_address_record` is then additionally keyed by `coin_type`. | Column | Type | Nullable | Description | | ----------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `chain_id` | `bigint` | no | Part of primary key. | | `address` | `text` | no | Resolver contract address. Part of primary key. | | `node` | `text` | no | Name namehash. Part of primary key. | | `coin_type` | `numeric(78)` | no | All well-known CoinTypes fit into a JavaScript number but NOT a Postgres `integer`, and must be stored as `bigint`. Part of primary key. | | `value` | `hex` | no | The value of the Address Record specified by `((chain_id, resolver, node), coin_type)`. Interpreted by `interpretAddressRecordValue` — see its implementation for additional context and specific guarantees. | **Primary key:** `(chain_id, address, node, coin_type)`. **Relations:** belongs to one `resolver_records` record via `(chain_id, address, node)`. #### resolver\_text\_records [Section titled “resolver\_text\_records”](#resolver_text_records) Tracks text records for a `node` by `key` within a `resolver` on `chain_id`. Keyed by `(chain_id, resolver, node, key)`, where the composite key segment `(chain_id, resolver, node)` describes a `resolver_records` entity. A `resolver_text_record` is then additionally keyed by `key`. | Column | Type | Nullable | Description | | ---------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `chain_id` | `bigint` | no | Part of primary key. | | `address` | `text` | no | Resolver contract address. Part of primary key. | | `node` | `text` | no | Name namehash. Part of primary key. | | `key` | `text` | no | Text record key. Part of primary key. | | `value` | `text` | no | The value of the Text Record specified by `((chain_id, resolver, node), key)`. Interpreted by `interpretTextRecordValue` — see its implementation for additional context and specific guarantees. | **Primary key:** `(chain_id, address, node, key)`. **Relations:** belongs to one `resolver_records` record via `(chain_id, address, node)`. #### migrated\_nodes\_by\_parent [Section titled “migrated\_nodes\_by\_parent”](#migrated_nodes_by_parent) Tracks the migration status of a node, keyed by `(parent_node, label_hash)`. Due to a security issue, ENS migrated from the `RegistryOld` contract to a new Registry contract. When indexing events, the indexer must ignore any events on `RegistryOld` for domains that have since been migrated to the new Registry. The set of nodes registered in the (new) Registry contract on the ENS Root Chain is stored here. When a `RegistryOld#NewOwner` event is encountered (which emits both `parent_node` and `label_hash` directly), the relevant row is looked up here; if it exists, the event is ignored. Note This logic is only necessary for the ENS Root Chain — the only chain that includes the Registry migration. This Registry migration tracking is isolated to the Protocol Acceleration plugin. The subgraph plugin implements its own Registry migration logic. By isolating this logic here, the Protocol Acceleration plugin can be run independently of other plugins. The Unigraph plugin depends on the Protocol Acceleration plugin in order to piggyback on this Registry migration logic. The composite key is chosen so that Ponder’s profile-pattern matcher can decompose it from event args directly, keeping the read on the indexing-cache prefetch hot-path. | Column | Type | Nullable | | ------------- | ------ | -------- | | `parent_node` | `text` | no | | `label_hash` | `text` | no | **Primary key:** `(parent_node, label_hash)`. #### migrated\_nodes\_by\_node [Section titled “migrated\_nodes\_by\_node”](#migrated_nodes_by_node) Sibling lookup-by-namehash table for `migrated_nodes_by_parent`, keyed by `node`. The three `RegistryOld` handlers (`Transfer` / `NewTTL` / `NewResolver`) emit only the post-namehash `node` and cannot reconstruct the `(parent_node, label_hash)` pair without an unprofileable reverse lookup. Existence in this table is equivalent to existence in `migrated_nodes_by_parent`; both rows are written together by the migration helper. See [`protocol-acceleration/migrated-node-db-helpers.ts`](https://github.com/namehash/ensnode/blob/main/apps/ensindexer/src/lib/protocol-acceleration/migrated-node-db-helpers.ts) for the full rationale. | Column | Type | Nullable | | ------ | ------ | -------- | | `node` | `text` | no | **Primary key:** `node`. *** ### Registrars [Section titled “Registrars”](#registrars) Defined in [`registrars.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/registrars.schema.ts). Models the lifecycle of ENS name registrations and renewals as logical actions, aggregating data from multiple on-chain events (e.g. a `BaseRegistrar` event and a `RegistrarController` event) into a single record per logical action. #### Enums [Section titled “Enums”](#enums) **`registrar_action_type`** — Types of “logical registrar action”. | Value | | -------------- | | `registration` | | `renewal` | #### subregistries [Section titled “subregistries”](#subregistries) A “subregistry” represents a smart contract that manages the subnames of a given parent name. The following simplifying assumptions are currently in place: 1. No two subregistries hold state for the same node. 2. The subregistry associated with name X in the ENS root registry exclusively holds state for subnames of X. These assumptions hold for the current scope of indexing logic but may not hold as indexing expands to handle more complex scenarios. | Column | Type | Nullable | Description | | ---------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `subregistry_id` | `text` | no | Identifies the chainId and address of the smart contract associated with the subregistry. Guaranteed to be a fully lowercase string formatted according to the CAIP-10 standard. Primary key. | | `node` | `text` | no | The node (namehash) of the name the subregistry manages subnames of. Examples: `eth`, `base.eth`, `linea.eth`. Guaranteed to be a fully lowercase hex string representation of 32 bytes. | **Indexes:** unique on `node`. **Relations:** has many `registration_lifecycles`. #### registration\_lifecycles [Section titled “registration\_lifecycles”](#registration_lifecycles) A “registration lifecycle” represents a single cycle of a name being registered once, followed by renewals (expiry date extensions) any number of times. Caution This data model only tracks the **most recently created** registration lifecycle record for a name, and does not track all registration lifecycle records for a name across time. Therefore, if a name goes through multiple cycles of (registration → expiry → release), this data model only stores data for the most recently created registration lifecycle. | Column | Type | Nullable | Description | | ---------------- | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `node` | `text` | no | The node (namehash) of the FQDN of the domain the registration lifecycle is associated with. Guaranteed to be a subname of the node of the subregistry identified by `subregistry_id`. Guaranteed to be a fully lowercase hex string representation of 32 bytes. Primary key. | | `subregistry_id` | `text` | no | Identifies the chainId and address of the subregistry smart contract that manages the registration lifecycle. Guaranteed to be a fully lowercase CAIP-10 string. | | `expires_at` | `numeric(78)` | no | Unix timestamp when the Registration Lifecycle is scheduled to expire. | **Indexes:** `subregistry_id`. **Relations:** belongs to one `subregistries` record, has many `registrar_actions` records. #### registrar\_actions [Section titled “registrar\_actions”](#registrar_actions) Models “logical actions” rather than “events” because a single logical action, such as a single registration or renewal, may emit multiple on-chain events from multiple contracts where each individual event may only provide a subset of the data about the full logical action. Each logical action in this table is associated with a single transaction. A single transaction may perform any number of logical actions. For example, consider the logical registrar action of registering a direct subname of `.eth`. This logical action spans interactions across multiple contracts: 1. The `EthBaseRegistrar` contract emits a `NameRegistered` event, enabling tracking of `node`, `incrementalDuration`, and `registrant`. 2. A `RegistrarController` contract emits its own `NameRegistered` event, enabling tracking of `baseCost`, `premium`, `total`, and `encodedReferrer`. The state from both events is aggregated into a single logical registrar action. | Column | Type | Nullable | Description | | ---------------------- | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | Deterministic and globally unique identifier for the logical registrar action. Represents the *initial* on-chain event associated with the action. Guaranteed to be the first element in `eventIds`. Primary key. See note below about the ID format. | | `type` | `registrar_action_type` | no | `registration` or `renewal`. | | `subregistry_id` | `text` | no | The ID of the subregistry the action was taken on. Identifies the chainId and address of the associated subregistry smart contract. Guaranteed to be a fully lowercase CAIP-10 string. | | `node` | `text` | no | The node (namehash) of the FQDN of the domain associated with the action. Guaranteed to be a fully lowercase hex string representation of 32 bytes. | | `incremental_duration` | `numeric(78)` | no | Duration added to the registration by this action, in seconds. May be `0`. See detailed description below. | | `base_cost` | `numeric(78)` | yes | Base cost in wei. Guaranteed to be `null` if and only if `total` is `null`. Otherwise a non-negative value. | | `premium` | `numeric(78)` | yes | Premium cost in wei above `base_cost`. Guaranteed to be `null` if and only if `total` is `null`. Guaranteed to be zero when `type` is `renewal`. | | `total` | `numeric(78)` | yes | Total cost in wei, equal to the sum of `base_cost` and `premium`. Guaranteed to be `null` if and only if both `base_cost` and `premium` are `null`. | | `registrant` | `text` | no | Identifies the address that initiated the action and is paying `total` (if applicable). May not be the owner of the name — there are no restrictions on who may renew a name, and the initial owner may be distinct from the registrant. Guaranteed to be a fully lowercase address. | | `encoded_referrer` | `text` | yes | The raw 32-byte referrer value emitted on-chain. `null` if no referrer information was present in the indexed events. | | `decoded_referrer` | `text` | yes | The referrer address decoded from `encoded_referrer` using strict left-zero-padding validation. `null` if `encoded_referrer` is `null`. May be the zero address to represent that an `encoded_referrer` is defined but interpreted as no referrer. Guaranteed to be a fully lowercase address. | | `block_number` | `numeric(78)` | no | Block number that includes the action. The chainId of this block is the same as is referenced in `subregistry_id`. | | `timestamp` | `numeric(78)` | no | Unix timestamp of the block referenced by `block_number`. | | `transaction_hash` | `text` | no | Transaction hash of the action. The chainId of this transaction is the same as referenced in `subregistry_id`. Note that a single transaction may be associated with any number of logical registrar actions. | | `event_ids` | `text[]` | no | Array of Ponder event IDs that contributed to this record. Guarantees: at least 1 element; ordered chronologically by `log_index` within `block_number`; the first element equals the `id` of this record. | **Indexes:** `decoded_referrer`, `timestamp`. **Relations:** belongs to one `registration_lifecycles` record via `node`. ##### id format [Section titled “id format”](#id-format) The `id` value is a Ponder checkpoint string — a fixed-length decimal string encoding the following fields (left to right, most to least significant): | Field | Width (digits) | Description | | ------------------- | -------------- | ----------------------------------------- | | `block_timestamp` | 10 | Unix seconds timestamp of the block | | `chain_id` | 16 | EIP-155 chain ID | | `block_number` | 16 | Block number | | `transaction_index` | 16 | Index of the transaction within the block | | `event_type` | 1 | Internal Ponder event type (always `5`) | | `event_index` | 16 | Index of the event within the transaction | All fields are zero-padded to their fixed widths, so the string has constant length and lexicographic order equals chronological order. Because all registrar actions originate from Ponder log (smart-contract event) handlers, every `id` shares the same `event_type` digit (`5`), making direct lexicographic or bigint comparison safe for establishing total chronological order. ##### incremental\_duration semantics [Section titled “incremental\_duration semantics”](#incremental_duration-semantics) If `type` is `registration`: represents the duration between `block_timestamp` and the initial `expires_at` value that the associated registration lifecycle will be initialized with. If `type` is `renewal`: represents the incremental increase in duration made to the `expires_at` value in the associated registration lifecycle. A registration lifecycle may be extended via renewal even after it expires, as long as it is still within its grace period. **Example:** A registration lifecycle is scheduled to expire on Jan 1 midnight UTC. It is currently 30 days past expiration, with 60 days of grace period remaining. 1. A renewal with 10 days incremental duration: the lifecycle remains “expired” but now has 70 days of grace period remaining. 2. A renewal with 50 days incremental duration: the lifecycle becomes “active” again but will expire again in 20 days. After the grace period expires entirely, the name is considered “released” and can no longer be renewed — it must be registered again, starting a new lifecycle. #### \_ensindexer\_registrar\_action\_metadata [Section titled “\_ensindexer\_registrar\_action\_metadata”](#_ensindexer_registrar_action_metadata) Internal implementation detail This table is an **internal implementation detail of ENSIndexer** and should not be queried outside of ENSIndexer. It is used to temporarily store data during event handler execution to correlate multiple on-chain events into a single `registrar_actions` record. Multiple logical registrar actions may be taken on the same `node` in the same `transaction_hash` (e.g. a single transaction that registers and then renews a name twice). To support this, when the last event handler for a logical registrar action has completed its processing, the record referenced by `logical_event_key` must be removed. **`_ensindexer_registrar_action_metadata_type`** enum: | Value | | ---------------------------------- | | `CURRENT_LOGICAL_REGISTRAR_ACTION` | | Column | Type | Nullable | Description | | ------------------- | -------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `metadata_type` | `_ensindexer_registrar_action_metadata_type` | no | The type of internal registrar action metadata being stored. Primary key. | | `logical_event_key` | `text` | no | A fully lowercase string formatted as `{domain_id}:{transaction_hash}`. | | `logical_event_id` | `text` | no | Holds the `id` value of the existing `registrar_actions` record currently being built as an aggregation of on-chain events. Used by subsequent event handlers to identify which logical registrar action to aggregate additional indexed state into. | *** ### Subgraph [Section titled “Subgraph”](#subgraph) Defined in [`subgraph.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/subgraph.schema.ts). A complete re-implementation of the legacy ENS Subgraph data model. When the `subgraph_` prefix is stripped and the resulting database schema is paired with `@ensnode/ponder-subgraph`, the resulting GraphQL API is fully compatible with the legacy ENS Subgraph. #### subgraph\_domains [Section titled “subgraph\_domains”](#subgraph_domains) | Column | Type | Nullable | Description | | --------------------- | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | The namehash of the name. Primary key. | | `name` | `text` | yes | The ENS name that this Domain represents. In subgraph-compatible mode: `null` for the root node, or a Subgraph Interpreted Name. Otherwise: an Interpreted Name (normalized, or consisting entirely of Interpreted Labels). The root node’s name is `''` (empty string) rather than `null` in practice. | | `label_name` | `text` | yes | The label associated with the Domain. In subgraph-compatible mode: `null` for the root node or a subgraph-unindexable label; otherwise a Subgraph Interpreted Label. In non-compatible mode: `null` exclusively for the root node; otherwise a normalized label, or an Encoded LabelHash for unknown or unnormalized labels. | | `labelhash` | `text` | yes | `keccak256(label_name)`. | | `parent_id` | `text` | yes | The namehash (`id`) of the parent name. | | `subdomain_count` | `integer` | no | The number of subdomains. Default `0`. | | `resolved_address_id` | `text` | yes | Address logged from the current resolver, if any. | | `resolver_id` | `text` | yes | The resolver that controls the domain’s settings. | | `ttl` | `numeric(78)` | yes | The time-to-live (TTL) value of the domain’s records. | | `is_migrated` | `boolean` | no | Indicates whether the domain has been migrated to a new registrar. Default `false`. | | `created_at` | `numeric(78)` | no | The time when the domain was created. | | `owner_id` | `text` | no | The account that owns the domain. | | `registrant_id` | `text` | yes | The account that owns the ERC721 NFT for the domain. | | `wrapped_owner_id` | `text` | yes | The account that owns the wrapped domain. | | `expiry_date` | `numeric(78)` | yes | The expiry date for the domain, from either the registration or the wrapped domain if PCC is burned. | **Indexes:** * `name` — hash index, because some `name` values exceed the btree max row size (8191 bytes). * `name` — GIN trigram index for partial-match filters (`_contains`, `_starts_with`, `_ends_with`). * `labelhash`, `parent_id`, `owner_id`, `registrant_id`, `wrapped_owner_id`, `resolved_address_id`. **Relations:** has one `subgraph_accounts` record (resolved\_address), has one `subgraph_accounts` record (owner), has one `subgraph_accounts` record (registrant), has one `subgraph_accounts` record (wrapped\_owner), has one `subgraph_resolvers` record, has one `subgraph_domains` record (parent), has many `subgraph_domains` records (subdomains), has one `subgraph_wrapped_domains` record, has one `subgraph_registrations` record, has many domain event tables. #### subgraph\_accounts [Section titled “subgraph\_accounts”](#subgraph_accounts) | Column | Type | Nullable | | ------ | ------ | -------- | | `id` | `text` | no | **Relations:** has many `subgraph_domains` records, has many `subgraph_wrapped_domains` records, has many `subgraph_registrations` records. #### subgraph\_resolvers [Section titled “subgraph\_resolvers”](#subgraph_resolvers) | Column | Type | Nullable | Description | | -------------- | --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | Unique identifier: concatenation of the domain namehash and the resolver address. Primary key. | | `domain_id` | `text` | no | The domain that this resolver is associated with. | | `address` | `text` | no | The address of the resolver contract. | | `addr_id` | `text` | yes | The current value of the `addr` record for this resolver, as determined by the associated events. | | `content_hash` | `text` | yes | The content hash for this resolver, in binary format. | | `texts` | `text[]` | yes | The set of observed text record keys for this resolver. Nullable (not defaulting to `[]`) to match subgraph behavior. | | `coin_types` | `numeric(78)[]` | yes | The set of observed SLIP-44 coin types for this resolver. Nullable (not defaulting to `[]`) to match subgraph behavior. | **Indexes:** `domain_id`. **Relations:** has one `subgraph_accounts` record (addr), has one `subgraph_domains` record, has many resolver event tables. #### subgraph\_registrations [Section titled “subgraph\_registrations”](#subgraph_registrations) | Column | Type | Nullable | Description | | ------------------- | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | The unique identifier of the registration (namehash). Primary key. | | `domain_id` | `text` | no | The domain name associated with the registration. | | `registration_date` | `numeric(78)` | no | The registration date of the domain. | | `expiry_date` | `numeric(78)` | no | The expiry date of the domain. | | `cost` | `numeric(78)` | yes | The cost associated with the domain registration. | | `registrant_id` | `text` | no | The account that registered the domain. | | `label_name` | `text` | yes | The label associated with the domain registration. In subgraph-compatible mode: `null` for a subgraph-unindexable label; otherwise a Subgraph Interpreted Label. In non-compatible mode: a normalized label, or an Encoded LabelHash for unnormalized labels. `null` is not expected in practice because there is no Registration entity for the root node (the only node with a null label\_name). | **Indexes:** `domain_id`, `registration_date`, `expiry_date`. **Relations:** has one `subgraph_domains` record, has one `subgraph_accounts` record (registrant), has many registration event tables. #### subgraph\_wrapped\_domains [Section titled “subgraph\_wrapped\_domains”](#subgraph_wrapped_domains) | Column | Type | Nullable | Description | | ------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | The unique identifier for each instance of the WrappedDomain entity. Primary key. | | `domain_id` | `text` | no | The domain that is wrapped by this WrappedDomain. | | `expiry_date` | `numeric(78)` | no | The expiry date of the wrapped domain. | | `fuses` | `integer` | no | The number of fuses remaining on the wrapped domain. | | `owner_id` | `text` | no | The account that owns this WrappedDomain. | | `name` | `text` | yes | The name that this WrappedDomain represents. Names are emitted by the NameWrapper contract as DNS-Encoded Names which may be malformed, resulting in `null`. In subgraph-compatible mode: `null` for malformed or subgraph-unindexable labels; otherwise a Subgraph Interpreted Label. In non-compatible mode: `null` for a malformed DNS-Encoded Name; otherwise an Interpreted Name. | **Indexes:** `domain_id`. **Relations:** has one `subgraph_domains` record, has one `subgraph_accounts` record (owner). #### Event tables [Section titled “Event tables”](#event-tables) All event tables share the base columns `id` (primary key), `block_number`, and `transaction_id`. Domain event tables additionally carry `domain_id`; registration event tables carry `registration_id`; resolver event tables carry `resolver_id`. The indexes on each event table are `(domain_id/resolver_id/registration_id)` for reverse lookups and `(domain_id/resolver_id/registration_id, id)` for sorted pagination. **Domain event tables** | Table | Additional columns | | ---------------------------- | ------------------------------------------ | | `subgraph_transfers` | `owner_id` | | `subgraph_new_owners` | `owner_id`, `parent_domain_id` | | `subgraph_new_resolvers` | `resolver_id` | | `subgraph_new_ttls` | `ttl` | | `subgraph_wrapped_transfers` | `owner_id` | | `subgraph_name_wrapped` | `name`, `fuses`, `owner_id`, `expiry_date` | | `subgraph_name_unwrapped` | `owner_id` | | `subgraph_fuses_set` | `fuses` | | `subgraph_expiry_extended` | `expiry_date` | **Registration event tables** | Table | Additional columns | | --------------------------- | ------------------------------ | | `subgraph_name_registered` | `registrant_id`, `expiry_date` | | `subgraph_name_renewed` | `expiry_date` | | `subgraph_name_transferred` | `new_owner_id` | **Resolver event tables** | Table | Additional columns | | --------------------------------- | ---------------------------------- | | `subgraph_addr_changed` | `addr_id` | | `subgraph_multicoin_addr_changed` | `coin_type`, `addr` | | `subgraph_name_changed` | `name` | | `subgraph_abi_changed` | `content_type` | | `subgraph_pubkey_changed` | `x`, `y` | | `subgraph_text_changed` | `key`, `value` | | `subgraph_contenthash_changed` | `hash` | | `subgraph_interface_changed` | `interface_id`, `implementer` | | `subgraph_authorisation_changed` | `owner`, `target`, `is_authorized` | | `subgraph_version_changed` | `version` | *** ### TokenScope [Section titled “TokenScope”](#tokenscope) Defined in [`tokenscope.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/tokenscope.schema.ts). Tracks ENS-related NFT token ownership and secondary market sales via the Seaport protocol. #### name\_sales [Section titled “name\_sales”](#name_sales) | Column | Type | Nullable | Description | | ------------------ | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `id` | `text` | no | Unique and deterministic identifier of the on-chain event associated with the sale. Composite key format: `{chain_id}-{block_number}-{log_index}` (e.g. `1-1234567-5`). Primary key. | | `chain_id` | `bigint` | no | The chain where the sale occurred. | | `block_number` | `numeric(78)` | no | The block number on `chain_id` where the sale occurred. | | `log_index` | `integer` | no | The log index position of the sale event within `block_number`. | | `transaction_hash` | `text` | no | The EVM transaction hash on `chain_id` associated with the sale. | | `order_hash` | `text` | no | The Seaport order hash. | | `contract_address` | `text` | no | The address of the contract on `chain_id` that manages `token_id`. | | `token_id` | `numeric(78)` | no | The tokenId managed by `contract_address` that was sold. | | `asset_namespace` | `text` | no | The CAIP-19 Asset Namespace of the token that was sold. Either `erc721` or `erc1155`. | | `asset_id` | `text` | no | The CAIP-19 Asset ID of the token that was sold. A globally unique reference to the specific asset. | | `domain_id` | `text` | no | The namehash (Node) of the ENS domain that was sold. | | `buyer` | `text` | no | The account that bought the token controlling ownership of `domain_id` from the seller, for the amount of currency associated with the sale. | | `seller` | `text` | no | The account that sold the token controlling ownership of `domain_id` to the buyer, for the amount of currency associated with the sale. | | `currency` | `text` | no | Currency of the payment. One of: ETH, USDC, or DAI. | | `amount` | `numeric(78)` | no | The amount of currency paid, denominated in the smallest unit. ETH/WETH: wei (1 ETH = 10^18). USDC: micro-units (1 USDC = 10^6). DAI: wei-equivalent (1 DAI = 10^18). | | `timestamp` | `numeric(78)` | no | Unix timestamp of the block when the sale occurred. | **Indexes:** `domain_id`, `asset_id`, `buyer`, `seller`, `timestamp`. #### name\_tokens [Section titled “name\_tokens”](#name_tokens) After an NFT is indexed, it is never deleted from the index. When an indexed NFT is burned on-chain, its record is retained and its `mint_status` is updated to `burned`. If the NFT is minted again after being burned, `mint_status` is updated back to `minted`. | Column | Type | Nullable | Description | | ------------------ | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `text` | no | The CAIP-19 Asset ID of the token. A globally unique reference to this token. Primary key. | | `domain_id` | `text` | no | The namehash (Node) of the ENS name associated with the token. An ENS name may have more than one distinct token across time. It is also possible for multiple distinct tokens for an ENS name to have a `mint_status` of `minted` at the same time — for example, when a direct subname of `.eth` is wrapped by the NameWrapper (one `minted` token managed by the `BaseRegistrar`, owned by the NameWrapper; one `minted` token managed by the NameWrapper, owned by the effective owner). | | `chain_id` | `bigint` | no | The chain that manages the token. | | `contract_address` | `text` | no | The address of the contract on `chain_id` that manages the token. | | `token_id` | `numeric(78)` | no | The tokenId of the token managed by `contract_address`. | | `asset_namespace` | `text` | no | The CAIP-19 Asset Namespace of the token. Either `erc721` or `erc1155`. | | `owner` | `text` | no | The account that owns the token. Value is the zero address if and only if `mint_status` is `burned`. Note: the owner of the token for a given `domain_id` may differ from the owner of the associated node in the registry. For example, if address X owns `foo.eth` in both the `BaseRegistrar` and the Registry, and X transfers registry ownership directly to Y, the `BaseRegistrar` token owner remains X. The `BaseRegistrar` implements a `reclaim` function allowing the token owner to reclaim registry ownership. | | `mint_status` | `text` | no | Either `minted` or `burned`. | **Indexes:** `domain_id`, `owner`. # Glossary > Core terminology used throughout ENSDb documentation. This page defines the **core terminology** used throughout ENSDb documentation. If you encounter an unfamiliar term elsewhere, check here for its definition. ## PostgreSQL Concepts [Section titled “PostgreSQL Concepts”](#postgresql-concepts) ### PostgreSQL Server [Section titled “PostgreSQL Server”](#postgresql-server) A running PostgreSQL server process that manages one or more [databases](#postgresql-database). ### PostgreSQL Database [Section titled “PostgreSQL Database”](#postgresql-database) A PostgreSQL database is an isolated collection of [database objects](#database-objects) managed by a [PostgreSQL server](#postgresql-server). ### Database Schema [Section titled “Database Schema”](#database-schema) A container that groups related [database objects](#database-objects). There can be multiple database schemas within a single [PostgreSQL database](#postgresql-database). Each database schema has a unique name within the [PostgreSQL database](#postgresql-database). Database Schema vs. Schema Definition Database Schema is a PostgreSQL concept referring to a namespace for database objects. Schema Definition is a concept referring to a code-based object that defines the structure of [database objects](#database-objects) within a [database schema](#database-schema). ### Database Objects [Section titled “Database Objects”](#database-objects) Objects contained within a [database schema](#database-schema), including: * Tables * Enums * Indexes * Constraints * Relations ## ENSDb Standard [Section titled “ENSDb Standard”](#ensdb-standard) An open standard for bi-directional ENS integration. The ENSDb Standard defines: * Schema designs for storing ENS data in a [PostgreSQL database](#postgresql-database) * Rules and constraints for [ENSDb Writers](#ensdb-writer) and [ENSDb Readers](#ensdb-reader) * A modular architecture enabling interoperability Any [PostgreSQL database](#postgresql-database) following the ENSDb Standard is an [ENSDb instance](#ensdb-instance). ### ENSDb Writer [Section titled “ENSDb Writer”](#ensdb-writer) Any application that writes ENS data into an [ENSDb instance](#ensdb-instance) following the [ENSDb Standard](#ensdb-standard). For example, [ENSIndexer](#ensindexer) is a reference implementation of an ENSDb Writer. Other implementations of ENSDb Writers can be built in any language, and can use different indexing frameworks (i.e. [Ponder](https://ponder.sh/), [rindexer](https://rindexer.xyz/)), as long as they follow the ENSDb Standard. ### ENSDb Reader [Section titled “ENSDb Reader”](#ensdb-reader) Any application that reads ENS data from an [ENSDb instance](#ensdb-instance) following the [ENSDb Standard](#ensdb-standard). For example, [ENSApi](#ensapi) is a reference implementation of an ENSDb Reader. Other implementations of ENSDb Readers can be built in any language, and can serve different types of APIs (GraphQL, REST, gRPC, etc.), as long as they follow the ENSDb Standard. ## ENSDb Concepts [Section titled “ENSDb Concepts”](#ensdb-concepts) ### Schema Definition [Section titled “Schema Definition”](#schema-definition) For ENSDb, a Drizzle ORM object that defines the structure of [database objects](#database-objects) within a [database schema](#database-schema). Schema Definitions specify: * Tables and their columns * Column types * Enums * Indexes * Constraints * Relations ### ENSDb Instance [Section titled “ENSDb Instance”](#ensdb-instance) A [PostgreSQL database](#postgresql-database) that follows the [ENSDb Standard](#ensdb-standard). An ENSDb instance stores indexed ENS data and is composed of two types of database schemas: * Exactly one [ENSNode Schema](#ensnode-schema) * One or more [ENSIndexer Schemas](#ensindexer-schema) #### Multi-Tenant ENSDb instance [Section titled “Multi-Tenant ENSDb instance”](#multi-tenant-ensdb-instance) > This example is based on reference implementations, but the ENSDb standard is implementation-agnostic, so this is just one possible design. An [ENSDb instance](#ensdb-instance) is considered multi-tenant when it stores data from multiple [ENSIndexer instances](#ensindexer-instance) (tenants) in isolated [ENSIndexer Schemas](#ensindexer-schema). Within a single [ENSDb instance](#ensdb-instance): * Each [ENSIndexer instance](#ensindexer-instance) gets its own [ENSIndexer Schema](#ensindexer-schema) — fully isolated data following the [ENSIndexer Schema Definition](#ensindexer-schema-definition) that was current when the ENSIndexer instance started. * All [ENSIndexer instances](#ensindexer-instance) share the [Ponder Schema](#ponder-schema), including the RPC cache. * Metadata of all [ENSIndexer instances](#ensindexer-instance) is tracked in the [ENSNode Schema](#ensnode-schema) This enables separate indexing by multiple [ENSIndexer instances](#ensindexer-instance) with different configs. The configs may require indexing just certain chains. For example, one [ENSIndexer instance](#ensindexer-instance) is configured to index data just from the Ethereum Mainnet, while another [ENSIndexer instance](#ensindexer-instance) is configured to index data from both, Ethereum Mainnet, and Base Mainnet. Each of these [ENSIndexer instances](#ensindexer-instance) would have its own [ENSIndexer Schema](#ensindexer-schema) in the same [ENSDb instance](#ensdb-instance). ### Ponder runtime [Section titled “Ponder runtime”](#ponder-runtime) Ponder runtime is executed by the ENSIndexer instance. It manages fetching onchain data, and having it cached in RPC cache inside the [Ponder Schema](#ponder-schema). The Ponder runtime also performs writes to the [ENSIndexer Schema](#ensindexer-schema) owned by the ENSIndexer instance. ### Ponder Schema [Section titled “Ponder Schema”](#ponder-schema) A [database schema](#database-schema) in the [ENSDb instance](#ensdb-instance) following the [Ponder Schema Definition](#ponder-schema-definition). It is an implementation detail of ENSIndexer. It has a fixed `ponder_sync` name, and it serves as a shared RPC cache for all [ENSIndexer instances](#ensindexer-instance) connected to the [ENSDb instance](#ensdb-instance). Its lifecycle is managed by Ponder runtime. The Ponder Schema enables multiple [ENSIndexer instances](#ensindexer-instance) to share RPC cache, reducing costs and improving indexing speed. #### Ponder Schema Definition [Section titled “Ponder Schema Definition”](#ponder-schema-definition) A [Schema Definition](#schema-definition) that defines the structure of the [Ponder Schema](#ponder-schema) in the [ENSDb instance](#ensdb-instance). This Schema Definition is an implementation detail of Ponder, so we treat it as an opaque black box in ENSDb documentation. ### ENSNode Schema [Section titled “ENSNode Schema”](#ensnode-schema) A [database schema](#database-schema) in the [ENSDb instance](#ensdb-instance) following the [ENSNode Schema Definition](#ensnode-schema-definition). It has a fixed `ensnode` name, and it serves as a registry for all [ENSIndexer instances](#ensindexer-instance) that have ever connected to the [ENSDb instance](#ensdb-instance). It also stores metadata about these [ENSIndexer instances](#ensindexer-instance). ### ENSNode Schema Definition [Section titled “ENSNode Schema Definition”](#ensnode-schema-definition) A [Schema Definition](#schema-definition) that defines the structure of the [ENSNode Schema](#ensnode-schema) in the [ENSDb instance](#ensdb-instance). The ENSNode Schema Definition is part of the ENSDb standard and is maintained in the ENSDb SDK. It includes the definition of the [ENSNode Metadata Table](#ensnode-metadata-table). ### ENSIndexer Schema [Section titled “ENSIndexer Schema”](#ensindexer-schema) A [database schema](#database-schema) within an [ENSDb instance](#ensdb-instance), used to store indexed ENS data. Each [ENSIndexer instance](#ensindexer-instance) owns exactly one ENSIndexer Schema, whose structure follows the [ENSIndexer Schema Definition](#ensindexer-schema-definition) that was current when the instance first started. The schema’s name is determined at startup, when the ENSIndexer instance connects to the ENSDb instance and creates its schema using the configured [ENSIndexer Schema Name](#ensindexer-schema-name). #### ENSIndexer Schema Definition [Section titled “ENSIndexer Schema Definition”](#ensindexer-schema-definition) A [Schema Definition](#schema-definition) that defines the structure of an [ENSIndexer Schema](#ensindexer-schema). It is part of the ENSDb standard, maintained in the [ENSDb SDK](#ensdb-sdk), and specifies the core tables and columns used to store indexed ENS data. ### ENSIndexer Schema Name [Section titled “ENSIndexer Schema Name”](#ensindexer-schema-name) The name of a specific [ENSIndexer Schema](#ensindexer-schema) in the [ENSDb instance](#ensdb-instance). This name is dynamic and determined by the [ENSIndexer instance](#ensindexer-instance) that owns the schema. Multiple [ENSIndexer Schema Names](#ensindexer-schema-name) exist in an [ENSDb instance](#ensdb-instance) when multiple [ENSIndexer instances](#ensindexer-instance) are connected. ### ENSNode Metadata Table [Section titled “ENSNode Metadata Table”](#ensnode-metadata-table) A table within the [ENSNode Schema](#ensnode-schema) that tracks all [ENSIndexer instances](#ensindexer-instance) that have ever connected to the [ENSDb instance](#ensdb-instance). | Column | Type | Purpose | | ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- | | `ens_indexer_schema_name` | `text` | References the [ENSIndexer Schema Name](#ensindexer-schema-name) of the ENSIndexer instance that manages this metadata record | | `key` | `text` | Type of metadata record | | `value` | `jsonb` | The context object (configuration, status, etc.) | Primary key: (`ens_indexer_schema_name`, `key`) The `value` column stores a JSON object which structure may evolve over time. To track this, the JSON object is guaranteed to always include a `version` field indicating the version of the structure. This allows for future-proofing as the metadata needs evolve. ### Metadata Keys [Section titled “Metadata Keys”](#metadata-keys) The `key` column identifies the type of metadata: | Key | Description | | --------------------------- | ---------------------------------------------------------------------------------------------------------- | | `indexing_metadata_context` | [Indexing metadata context](#indexing-metadata-context) of the [ENSIndexer instance](#ensindexer-instance) | #### Indexing Metadata Context [Section titled “Indexing Metadata Context”](#indexing-metadata-context) A JSON object that provides indexing metadata context. The actual context data, whose structure may evolve over time as the needs of the ENSNode stack evolve. The object may include fields such as: * `version`: The version of the Indexing Metadata Context structure * `indexingStatus`: the current Indexing Status of the [ENSIndexer instance](#ensindexer-instance). * `stackInfo`: a group of public config objects that describe the ENSIndexer instance and its dependencies. ### ENSApi [Section titled “ENSApi”](#ensapi) A reference implementation of an [ENSDb Reader](#ensdb-reader) that serves GraphQL and REST APIs from an [ENSDb instance](#ensdb-instance). It is a backend app built on top of [Hono](https://hono.dev/). ### ENSApi Instance [Section titled “ENSApi Instance”](#ensapi-instance) A running [ENSApi](#ensapi) process that serves GraphQL and REST APIs from an [ENSDb instance](#ensdb-instance). * Any [ENSApi instance](#ensapi-instance) can connect to any [ENSDb instance](#ensdb-instance) * Multiple [ENSApi instances](#ensapi-instance) can connect to the same [ENSDb instance](#ensdb-instance) for improved availability ### ENSIndexer [Section titled “ENSIndexer”](#ensindexer) A reference implementation of an [ENSDb Writer](#ensdb-writer) that indexes onchain ENS data and writes to an [ENSDb instance](#ensdb-instance). It is built on top of [Ponder](https://ponder.sh/), a modular blockchain indexing framework. ### ENSIndexer Instance [Section titled “ENSIndexer Instance”](#ensindexer-instance) A running [ENSIndexer](#ensindexer) process that indexes onchain ENS data and writes to an [ENSIndexer Schema](#ensindexer-schema) in ENSDb. * Each [ENSIndexer instance](#ensindexer-instance) owns exactly one [ENSIndexer Schema](#ensindexer-schema) * Each [ENSIndexer instance](#ensindexer-instance) writes to and reads from the [Ponder Schema](#ponder-schema) * Multiple [ENSIndexer instances](#ensindexer-instance) can connect to one [ENSDb instance](#ensdb-instance) * Each [ENSIndexer instance](#ensindexer-instance) has a row in the [ENSNode Metadata Table](#ensnode-metadata-table) ### Indexing Status [Section titled “Indexing Status”](#indexing-status) The status of an [ENSIndexer instance](#ensindexer-instance)’s indexing progress. The Indexing Status affects database behavior: * During **Backfill**, [indexes](#database-objects) on the [ENSIndexer Schema](#ensindexer-schema), if any, are dropped to optimize write performance. * When transitioning to **Following**, [indexes](#database-objects) on the [ENSIndexer Schema](#ensindexer-schema) are created to optimize read performance. ## Schema Discovery [Section titled “Schema Discovery”](#schema-discovery) The process of finding all [ENSIndexer Schemas](#ensindexer-schema) in an [ENSDb instance](#ensdb-instance) by querying the [ENSNode Metadata Table](#ensnode-metadata-table): example.sql ```sql SELECT DISTINCT ens_indexer_schema_name FROM ensnode.metadata; ``` This returns all [ENSIndexer Schema Names](#ensindexer-schema-name) that have been used by [ENSIndexer instances](#ensindexer-instance) connected to this [ENSDb instance](#ensdb-instance). ## ENSDb SDK [Section titled “ENSDb SDK”](#ensdb-sdk) A TypeScript package published as [`@ensnode/ensdb-sdk`](https://www.npmjs.com/package/@ensnode/ensdb-sdk) providing utilities for interacting with the [ENSDb instance](#ensdb-instance). The ENSDb SDK includes: * [EnsDbReader Client](#ensdb-reader-client) — An ENSDb client implementation for querying ENSDb Metadata, and building custom queries against the [ENSDb instance](#ensdb-instance). * [EnsDbWriter Client](#ensdb-writer-client) — An ENSDb client implementation for writing ENSNode Metadata and executing ENSNode migrations. * [Schema Definitions](#schema-definition) — Drizzle schemas for [ENSIndexer Schema](#ensindexer-schema) and [ENSNode Schema](#ensnode-schema). ### ENSDb Reader Client [Section titled “ENSDb Reader Client”](#ensdb-reader-client) A class in [ENSDb SDK](#ensdb-sdk) for querying data from the [ENSDb instance](#ensdb-instance). ### ENSDb Writer Client [Section titled “ENSDb Writer Client”](#ensdb-writer-client) A class in [ENSDb SDK](#ensdb-sdk) that extends [ENSDb Reader Client](#ensdb-reader-client) with write capabilities. ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Database Schemas](/docs/services/ensdb/concepts/database-schemas) — Deep dive on schema types # Reference Implementations > A directory of reference implementation of the ENSDb standard. ENSNode includes initial reference implementations of the [ENSDb standard](/docs/services/ensdb/concepts/glossary#ensdb-standard). This includes ENSIndexer as a reference ENSDb writer and ENSApi as a reference ENSDb reader. These are just a few examples of the ecosystem of tools and services that are possible when you build with ENSDb. This initial reference implementation consists of three main components: * An [ENSDb instance](/docs/services/ensdb/concepts/glossary#ensdb-instance) — A PostgreSQL database following the ENSDb standard. * An [ENSIndexer instance](/docs/services/ensdb/concepts/glossary#ensindexer-instance) — A reference ENSDb Writer implementation that writes data into the ENSDb instance. * An [ENSApi instance](/docs/services/ensdb/concepts/glossary#ensapi-instance) — A reference ENSDb Reader implementation that serves GraphQL and REST APIs. ``` flowchart LR subgraph DeploymentEnv["Deployment Environment"] ENSIndexer[ENSIndexer instance] ENSApi[ENSApi instance] subgraph PostgreSQLServer["PostgreSQL Server instance"] ENSDb[(ENSDb instance)] NS(ENSNode Schema) EIS(ENSIndexer Schema) ENSDb -->|has| NS ENSDb -->|has| EIS end end ENSIndexer -->|writes| ENSDb ENSApi -->|reads| ENSDb ``` Build Your Own You can build custom ENSDb Writers, or ENSDb Readers. The [ENSDb standard](/docs/services/ensdb/concepts/glossary#ensdb-standard) is implementation-agnostic. # Future Possibilities > Explore the future possibilities and potential applications of ENSDb in various domains, including custom APIs, analytics, developer tools, event-based engines, data pipelines, and AI agents. Use an ENSDb instance as an event source for building reactive systems that respond to ENS state changes. With sub-second query latency and real-time updates, you can build applications that react to registration lifecycle updates, ownership transfers, resolver updates, and more. ## Example Use Cases [Section titled “Example Use Cases”](#example-use-cases) ### Analytics & Dashboards [Section titled “Analytics & Dashboards”](#analytics--dashboards) Use [PeerDB](https://www.peerdb.io/) and stream indexed ENS data from ENSDb to your data warehouse or analytics tool. ### Event-Based Engines [Section titled “Event-Based Engines”](#event-based-engines) Integrate [Sequin](https://sequinstream.com/) to build reactive systems that respond in realtime to ENS state changes. ### Cache Layers [Section titled “Cache Layers”](#cache-layers) Try out [S2](https://s2.dev/) to build cache layers and materialized views on top of your ENSDb instance. # Using ENSDb > Guides for integrating and using ENSDb in your applications. You can interact with the ENSDb instance using the dedicated TypeScript SDK or the regular PostgreSQL interface. This section provides guides for both methods, as well as troubleshooting tips and answers to frequently asked questions. [ENSDb SDK ](/docs/services/ensdb/usage/sdk)Detailed explanation of interacting with ENSDb using the TypeScript SDK, including installation and usage examples. [ENSDb SQL Interface ](/docs/services/ensdb/usage/sql)Detailed explanation of interacting with ENSDb using the SQL interface, including queries and usage examples. # ENSDb SDK This page provides an overview of the ENSDb SDK and how to use it in your applications. ## Intro [Section titled “Intro”](#intro) For TypeScript projects, the [ENSDb SDK](/docs/services/ensdb/concepts/glossary#ensdb-sdk) provides a convenient and efficient way to interact with your ENSDb instance. ### Installation [Section titled “Installation”](#installation) You can install the [`@ensnode/ensdb-sdk`](https://www.npmjs.com/package/@ensnode/ensdb-sdk) package from the NPM registry, using your preferred package manager: ```bash npm install @ensnode/ensdb-sdk pnpm install @ensnode/ensdb-sdk yarn add @ensnode/ensdb-sdk ``` ### Example Usage [Section titled “Example Usage”](#example-usage) Canonical fields (`canonicalName`, `canonicalPath`, `canonicalNode`, `canonicalDepth`) are populated on every Domain reachable from the canonical root, across both ENSv1 and ENSv2 — query them uniformly without branching by `type`. example.ts ```typescript import { EnsDbReader, IndexingMetadataContextStatusCodes } from "@ensnode/ensdb-sdk"; import { count, eq } from "drizzle-orm"; // Connect to a specific ENSDb instance by providing its connection string and // the ENSIndexer Schema Name you want to query const ensDbReader = new EnsDbReader(ensDbConnectionString, ensIndexerSchemaName); const { ensDb, ensIndexerSchema } = ensDbReader; // Fetch a Domain by its canonical name const [vitalik] = await ensDb .select() .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.canonicalName, "vitalik.eth")); // Count an address's Domains, grouped by Domain type (ENSv1 vs ENSv2) const counts = await ensDb .select({ type: ensIndexerSchema.domain.type, count: count() }) .from(ensIndexerSchema.domain) .where(eq(ensIndexerSchema.domain.ownerId, "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")) .groupBy(ensIndexerSchema.domain.type); // Get indexing status snapshot const indexingMetadataContext = await ensDbReader.getIndexingMetadataContext(); if (indexingMetadataContext.statusCode === IndexingMetadataContextStatusCodes.Initialized) { const indexingStatusSnapshot = indexingMetadataContext.indexingStatus; // Do something with the indexing status snapshot } ``` # ENSDb SQL ## Intro [Section titled “Intro”](#intro) This page provides an overview of the ENSDb SQL interface and how to use it in your applications. ## Example Queries [Section titled “Example Queries”](#example-queries) ### Connect with Any PostgreSQL Client [Section titled “Connect with Any PostgreSQL Client”](#connect-with-any-postgresql-client) Connect to an ENSDb instance (a PostgreSQL database). The examples below assume you that ENSDb instances are served from a PostgreSQL server at `host:5432` with databases named `ensdb_mainnet`, `ensdb_testnet`, and `ensdb_devnet`: ```bash # Production environment (mainnet data) psql postgresql://user:password@host:5432/ensdb_mainnet # Pre-production environment (testnet data) psql postgresql://user:password@host:5432/ensdb_testnet # Staging / local development environment psql postgresql://user:password@host:5432/ensdb_devnet ``` ### Discover Available Schemas [Section titled “Discover Available Schemas”](#discover-available-schemas) Once connected to an ENSDb instance, discover its ENSIndexer Schemas: discover-schemas.sql ```sql SELECT DISTINCT ens_indexer_schema_name FROM ensnode.metadata; ``` ### Query Data [Section titled “Query Data”](#query-data) Canonical fields (`canonical_name`, `canonical_path`, `canonical_node`, `canonical_depth`) are populated on every Domain reachable from the canonical root, across both ENSv1 and ENSv2 — query them uniformly without branching by `type`. example.sql ```sql -- Fetch a Domain by its canonical name SELECT * FROM ensindexer_0.domains WHERE canonical_name = 'vitalik.eth'; -- Count an address's Domains, grouped by Domain type (ENSv1 vs ENSv2) SELECT type, count(*) FROM ensindexer_0.domains WHERE owner_id = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' GROUP BY type; -- Get indexing status snapshot for the ENSNode Schema with the `ensindexer_0` ENSIndexer Schema Name SELECT value -> 'indexingStatus' FROM "ensnode"."metadata" WHERE ens_indexer_schema_name = 'ensindexer_0' AND key = 'indexing_metadata_context' AND value -> 'indexingStatus' -> 'omnichainSnapshot' ->> 'omnichainStatus' = 'omnichain-backfill'; ``` # ENSEngine > Coming soon - the ENSNode service for push-based, reactive ENS functionality. Watches your ENSDb for changes and turns them into ENS-aware webhooks. Coming soon **ENSEngine** is a planned ENSNode service. ## Vision [Section titled “Vision”](#vision) Make ENS state changes **subscribable**. Today, building an app that reacts to ENS - when an avatar changes, when a name is transferred, when a renewal happens - means either polling endlessly or running your own indexer. Both are heavy. ENSEngine exists to remove that work and turn ENS into a push-based protocol from the consumer’s point of view: subscribe, get notified, build. ## What it is [Section titled “What it is”](#what-it-is) ENSEngine is the part of ENSNode that **watches your ENSDb for changes in real time and delivers ENS-aware events** - including webhooks - to any sink you configure. It sits alongside [ENSIndexer](/docs/services/ensindexer), [ENSDb](/docs/services/ensdb), [ENSRainbow](/docs/services/ensrainbow), [ENSApi](/docs/services/ensapi), and [ENSAdmin](/docs/services/ensadmin) as the home for reactive / event-based functionality across the ENSNode stack. ## How it works [Section titled “How it works”](#how-it-works) ``` flowchart TD Indexer["ENSIndexer
(keeps ENSDb up to date)"] Db["ENSDb
(live onchain state of ENS)"] subgraph engine [ENSEngine] Watch["Watches ENSDb for changes"] Process["Enriches, filters, transforms, routes ENS events"] Watch --> Process end Sinks["Your sinks: webhooks, cache invalidation, notifications, db sync, ..."] Indexer --> Db Db --> engine engine --> Sinks ``` In one sentence: **ENSIndexer keeps ENSDb fresh, ENSEngine watches ENSDb for changes and turns them into ENS-aware events for any sink you configure.** The internal pipeline is intentionally simple to describe: * **Watch** - observe ENSDb in real time as ENSIndexer updates it. * **Enrich** - attach ENS-level context to raw changes so consumers don’t have to. * **Filter** - apply subscription rules so each sink only sees events it cares about. * **Transform** - shape events into the right payload for each sink type. * **Route** - deliver to webhooks, cache-invalidation hooks, database sync targets, notification systems, and more. Implementation details behind the watch layer are intentionally not part of this teaser. Those will land in dedicated docs when the service ships. ## What it unlocks [Section titled “What it unlocks”](#what-it-unlocks) The top priority for ENSEngine is **cache invalidation**. The goal is an ENS-native invalidation signal that lets apps and CDNs cache ENS data much more aggressively, then react when relevant onchain state changes. That’s the foundation for web2-grade latency on a web3 protocol, especially for high-traffic ENS profile views. Additional key use cases: * **Notification services** - name-expiry reminders, transfer alerts, activity bots in Discord/Telegram/Twitter. * **Sync to alternative databases** - want to store alternative representations of ENSDb in databases that provide specialized benefits that Postgres doesn’t offer, such as Elasticsearch, Typesense, Meilisearch, Redis, Kafka, Google BigQuery, and more? ENSEngine will ensure 100% delivery of database changes to sinks with strict ordering and exactly-once processing. * **Realtime dashboards** - surface ENS analytics and activity data as it happens. For a longer use-case-focused overview from the *integrate* perspective, see the [ENSEngine teaser](/docs/integrate/integration-options/ensengine). ## Position in the ENSNode stack [Section titled “Position in the ENSNode stack”](#position-in-the-ensnode-stack) ENSEngine is one of several planned and existing services that build on top of [ENSDb](/docs/services/ensdb). ENSDb is the open standard that holds the live, onchain state of ENS in a plain PostgreSQL database. From there, different services consume that state in different ways: * **[ENSApi](/docs/services/ensapi)** - pull model: GraphQL / REST APIs for clients. * **[ensdb-cli & ENSDb snapshots](/docs/integrate/integration-options/ensdb-cli)** - operational model: portable snapshots and CLI tooling. * **[ENSEngine](/docs/services/ensengine)** - push model: real-time, ENS-aware events delivered to your sinks. ## Related [Section titled “Related”](#related) [ENSEngine Integration Quickstart ](/docs/integrate/integration-options/ensengine)The value-prop teaser written from the perspective of a developer adopting ENSEngine. [ENSDb overview ](/docs/services/ensdb)The foundation ENSEngine builds on - the live, onchain state of ENS in plain Postgres. [ensdb-cli & ENSDb snapshots ](/docs/integrate/integration-options/ensdb-cli)Portable snapshots and a CLI for bootstrapping fresh instances in minutes. # ENSIndexer Overview > Get started indexing ENS with ENSIndexer. ENSIndexer is a [Ponder](https://ponder.sh) app that indexes ENS contracts across multiple chains. It processes events from each relevant chain and transforms the data, storing it in your Postgres database. Note While ENSIndexer can be run independently in certain scenarios, it is designed to work as part of the complete ENSNode system, and we recommend following the [ENSNode Contribution Guide](/docs/reference/contributing) if you’d like to run ENSIndexer locally, or the [ENSNode Deployment Guide](/docs/self-host) if you’d like to run ENSIndexer in the cloud. ## Configuration [Section titled “Configuration”](#configuration) ENSIndexer can be configured via environment variables. [ENSIndexer Configuration Options ](/docs/services/ensindexer/usage/configuration) # ENSIndexer Startup Sequence > Learn about the startup sequence of ENSIndexer. Below are diagrams describing the ENSIndexer startup sequence. ## Ponder app lifecycle [Section titled “Ponder app lifecycle”](#ponder-app-lifecycle) Here is an overview of the ENSIndexer startup sequence. It summarizes the Ponder app lifecycle running inside an ENSIndexer instance. The example assumes that the ENSIndexer was started with the `ENSINDEXER_SCHEMA_NAME` environment variable set to `prod_0`, but the same sequence applies to any ENSIndexer Schema. [![Ponder app lifecycle](/ensindexer/startup-sequence/0-overview.png)](/ensindexer/startup-sequence/0-overview.png) ## ENSIndexer schema migrations [Section titled “ENSIndexer schema migrations”](#ensindexer-schema-migrations) When the Ponder app lifecycle reaches the step for “Execute migrations for the `prod_0` ENSIndexer Schema”, Ponder will execute any pending migrations for the `prod_0` ENSIndexer Schema. This ensures that the ENSIndexer Schema is up to date before indexing onchain events start. [![Executing ENSIndexer Schema migrations](/ensindexer/startup-sequence/1-ensindexer-schema-migrations.png)](/ensindexer/startup-sequence/1-ensindexer-schema-migrations.png) ## Omnichain indexing strategy [Section titled “Omnichain indexing strategy”](#omnichain-indexing-strategy) When the Ponder app lifecycle reaches the step to “Run omnichain indexing strategy”, Ponder will start writing data concurrently to the Ponder Schema (caching RPC calls) and the ENSIndexer Schema (storing indexed data). The ENSIndexer app lifecycle “injects” a special step for “Execute onchain event handlers preconditions”. [![Omnichain indexing strategy](/ensindexer/startup-sequence/2-omnichain-strategy-execution.png)](/ensindexer/startup-sequence/2-omnichain-strategy-execution.png) ### Init onchain event handlers [Section titled “Init onchain event handlers”](#init-onchain-event-handlers) The special step for “Execute onchain event handlers preconditions” occurs before any onchain event is indexed. This enables ENSIndexer to execute ENSNode Schema migrations and populate the ENSNode Metadata table with relevant data before any onchain event handlers are executed. [![Onchain event handlers preconditions](/ensindexer/startup-sequence/3-init-onchain-event-handlers.png)](/ensindexer/startup-sequence/3-init-onchain-event-handlers.png) # ENSIndexer Development and Contributions > Learn how to run ENSIndexer locally for development and contributions. Note This guide covers running ENSIndexer locally for development and contributions. ## Configure Environment Variables [Section titled “Configure Environment Variables”](#configure-environment-variables) ```bash cd apps/ensindexer cp .env.local.example .env.local ``` Caution ENSIndexer’s `.env.local` should be placed at `apps/ensindexer/.env.local`, *not* at the monorepo root. [ENSIndexer Configuration Options ](/docs/services/ensindexer/usage/configuration) ## Running ENSIndexer [Section titled “Running ENSIndexer”](#running-ensindexer) ```bash # from monorepo root pnpm run -F ensindexer dev # from apps/ensindexer pnpm run dev ``` # Creating a Plugin > Learn how to create a plugin for ENSIndexer ## ENSIndexer Plugins [Section titled “ENSIndexer Plugins”](#ensindexer-plugins) ENSNode is extensible through ENSIndexer plugins that implement custom indexing logic and produce custom indexed data models inside ENSDb. This allows you to tailor ENSNode to your specific use case and data needs. ENSIndexer will only activate the plugins you specify in the `PLUGINS` env variable, so you can choose to only activate the plugins that are relevant to your use case. The `PLUGINS` env variable accepts a comma-separated list of plugin names to be activated. For example, if you want to activate the `ensv2` and `protocol-acceleration` plugins, you can set `PLUGINS=ensv2,protocol-acceleration`. ## Plugin Architecture [Section titled “Plugin Architecture”](#plugin-architecture) Each ENSIndexer plugin is a standalone TypeScript module that can be developed, tested, and deployed independently. The ENSIndexer Plugin can define its own indexed data models and indexing logic. It must include two main files: * `event-handlers.ts`: This file defines the event handlers for the plugin, which are functions that will be called when specific onchain events are indexed. The event handlers contain the logic for how to process the indexed data and store it in the relevant indexed data model inside the ENSIndexer Schema. * `plugin.ts`: This file defines the plugin itself, including its name, description, and any configuration options, like datasources to be used for specifying onchain contracts that will be indexed. ### Event handlers [Section titled “Event handlers”](#event-handlers) The `event-handlers.ts` file has to export a function via default export. This function will only be called by ENSIndexer runtime when the plugin is activated. We leverage this approach to conditionally execute `addOnchainEventListener` calls for active plugins only. This way, ENSIndexer learns which specific onchain events it needs to listen to, and what indexing logic to execute for each indexed event, based on the event handlers from the active plugins. You need to import the `event-handlers.ts` file from each plugin into [`register-handlers.ts`](https://github.com/namehash/ensnode/blob/main/apps/ensindexer/ponder/src/register-handlers.ts) file. This way ENSIndexer can recognize the event handlers and know when to have them executed during indexing. For example, by checking `config.plugins` value for the specific plugin name, ENSIndexer runtime can conditionally execute relevant `event-handlers.ts` files across all active plugins. ### Plugin definition [Section titled “Plugin definition”](#plugin-definition) The `plugin.ts` file has a default export with the plugin definition object returned from `createPlugin` function. The plugin definition object includes the plugin name, datasources required for the plugin to operate, and the config for contracts to be indexed by ENSIndexer when the plugin is active. You need to import the plugin definition object into [`apps/ensindexer/src/plugins/index.ts`](https://github.com/namehash/ensnode/tree/main/apps/ensindexer/src/plugins/index.ts) file and have it included in the `ALL_PLUGINS` array. This way, you let the ENSIndexer runtime know that the plugin is available for activation, which in turn allows you to activate the plugin by including its name in the `PLUGINS` env variable. ## Existing Plugins [Section titled “Existing Plugins”](#existing-plugins) You can find the current list of existing ENSIndexer plugins in [the ENSIndexer repository](https://github.com/namehash/ensnode/tree/main/apps/ensindexer/src/plugins). ## Creating Your Own Plugin [Section titled “Creating Your Own Plugin”](#creating-your-own-plugin) If you’re interested in building a plugin, reach out to the NameHash Labs team who is happy to provide support and additional info. # Configuring ENSIndexer Custom RPC Configuration Required **ENSIndexer requires private RPC endpoints.** Public (rate limited) RPC endpoints will not provide acceptable performance. You must configure private (paid) RPC services from providers like Alchemy, QuickNode, drpc.org, or Infura or host your own RPC service that can handle high-volume requests. Performance Tip: ENSRainbow Server Warning: Configuring ENSIndexer to use the public ENSRainbow server (`https://api.ensrainbow.io`) will significantly slow down indexing. For optimal indexing performance, always colocate ENSIndexer and ENSRainbow. For example, if you are running ENSIndexer locally, you should also run a local instance of ENSRainbow and set `ENSRAINBOW_URL` to your local ENSRainbow server. ENSIndexer’s behavior can be configured through environment variables. Copy `.env.local.example` to `.env.local` and configure all required values. .env.local.example ```bash # The port ENSIndexer serves its API on. # Optional. If this is not set, the default value is Ponder's default of 42069. # PORT=42069 # RPC configuration # Required. When ENSIndexer starts up it verifies RPCs are defined for each of the chains # that are to be indexed based on the configured NAMESPACE and PLUGINS, along with the ENS Root Chain. # # NOTE: You must configure your own private RPC endpoints. # ENSIndexer makes millions of RPC requests during operation. # Public RPC endpoints are rate limited and will not provide acceptable performance. # You must use only private (paid) RPC endpoints or your own self-hosted RPC service # that is prepared to support millions of requests (ex: 500+ requests / second) # # Each configured RPC endpoint must be prepared to receive and quickly # process millions of RPC requests. High-volume RPC service options include: # - Alchemy (paid plan) - https://www.alchemy.com/ # - QuickNode (paid plan) - https://www.quicknode.com # - drpc.org (paid plan) - https://drpc.org/ # - Infura (paid plan) - https://infura.io/ # # Example RPC endpoint URL formats: # - Alchemy RPC endpoints # - https://eth-mainnet.g.alchemy.com/v2/ # - wss://eth-mainnet.g.alchemy.com/v2/ # - https://base-sepolia.g.alchemy.com/v2/ # - QuickNode RPC endpoints # - https://.quiknode.pro/ # - wss://.quiknode.pro/ # - https://.base-sepolia.quiknode.pro/ # - dRPC RPC endpoints # - https://lb.drpc.live/ethereum/ # - wss://lb.drpc.live/ethereum/ # - https://lb.drpc.live/base-sepolia/ # - Infura RPC endpoints # - https://mainnet.infura.io/v3/ # - wss://mainnet.infura.io/ws/v3/ # - https://base-sepolia.infura.io/v3/ # # RPC endpoints can be auto-generated based on configuration of the following: # 1) The RPC_AUTO_GEN_MODE environment variable, which controls what types of # RPC endpoints can be auto-generated. The available modes are: # - http-only (default): only attempts to auto-generate HTTP RPCs, # based on the configured provider-specific environment variables and # supported chains of those providers. # - http-and-ws: attempts to auto-generate both HTTP and WS RPCs, based on # the configured provider-specific environment variables, supported # chains of those providers, and the RPC provider support for WS endpoints. # 2) The provider-specific environment variables, which enable auto-generation # of the given provider's RPCs, according to configured RPC_AUTO_GEN_MODE, # for each indexed chain (with limitations as noted below): # - ALCHEMY_API_KEY — API key for your Alchemy app. If set, enables # auto-generation of Alchemy HTTP RPCs for supported chains, and WS RPCs # only when RPC_AUTO_GEN_MODE is set to http-and-ws. # - QUICKNODE_API_KEY - API key for your multi-chain QuickNode endpoint. # - QUICKNODE_ENDPOINT_NAME — endpoint name of your multi-chain QuickNode endpoint. # - DRPC_API_KEY — if set, enables auto-generation of dRPC's HTTP RPCs for # chains dRPC supports. # - RPC_URL_${chainId} — specific, per-chain RPC settings. # # If both, QUICKNODE_API_KEY and QUICKNODE_ENDPOINT_NAME are specified, # a QuickNode HTTP RPC will be auto-generated for chains supported by QuickNode. # If only one of QUICKNODE_API_KEY or QUICKNODE_ENDPOINT_NAME is set but not # the other, the configuration will be rejected with an error. # Note key constraints of QuickNode RPC endpoints: # - Only multi-chain QuickNode endpoints can be used for setting # QUICKNODE_API_KEY and QUICKNODE_ENDPOINT_NAME environment variables. # A multi-chain endpoint allows sharing the same endpoint name and API key # across all chains supported by QuickNode platform. Read more in QuickNode docs: # https://www.quicknode.com/guides/quicknode-products/how-to-use-multichain-endpoint # - QuickNode platform does not support Linea Sepolia RPC (as of 2025-12-03). # https://www.quicknode.com/docs/linea # # Each RPC_URL_${chainId} environment variable, if specified, will take precedence over # all auto-generated RPC URLs for the specified chainId from Alchemy, QuickNode, or dRPC. # It must be a comma-separated list of HTTP/HTTPS RPC endpoints. # ENSIndexer provides all the resulting RPC URLs to Ponder. If multiple HTTP RPC URLs are provided to Ponder, # Ponder automatically balances requests between them (see below). # Auto-generated RPC URLs: # ALCHEMY_API_KEY=xyz # QUICKNODE_API_KEY=your-api-key # QUICKNODE_ENDPOINT_NAME=your-endpoint-name # DRPC_API_KEY=xyz # # For full control of chain-specific RPC configuration, use the RPC_URL_{chainId} environment variable. # Its value is a comma-separated list of one or more HTTP RPC URLs and at most one WebSocket RPC URL. # # Example (single HTTP RPC URL): # RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY # RPC_URL_11155111=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY # # Example (multiple HTTP RPC URL, single WebSocket RPC URL): # RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY,https://lb.drpc.org/ethereum/YOUR_API_KEY # RPC_URL_11155111=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY,https://lb.drpc.org/ethereum-sepolia/YOUR_API_KEY # # The RPC_URL_${chainId} value has the following invariants: # - Must always include at least one HTTP/HTTPS RPC endpoint. RPC endpoints can # fail or experience downtime. To optimize resiliency of ENSIndexer, # defining more than one HTTP/HTTPS endpoint (from more than one RPC provider) # per indexed chain is strongly encouraged. # - Can include at most one WS/WSS (websocket) RPC endpoint. A WS/WSS (websocket) # RPC endpoint per indexed chain is optional. If defined it may optimize # the time to discover new blocks. Independent of a websocket RPC endpoint # being defined, Ponder automatically polls the HTTP/HTTPS RPC endpoint for # a chain to discover new blocks at an interval. # # To optimize performance, Ponder automatically adapts to the rate limits (429 errors) of each # provided HTTP/HTTPS RPC. More details at: https://ponder.sh/docs/config/chains#rpc-endpoints # # === ENS Namespace: Mainnet === # Ethereum Mainnet # - required if the configured namespace is mainnet # - required by plugins: subgraph, protocol-acceleration, registrars, tokenscope # RPC_URL_1= # Optimism Mainnet # - required by plugins: threedns, protocol-acceleration, tokenscope # RPC_URL_10= # Base Mainnet # - required by plugins: basenames, threedns, protocol-acceleration, registrars, tokenscope # RPC_URL_8453= # Arbitrum Mainnet # - required by plugins: protocol-acceleration # RPC_URL_42161= # Linea Mainnet # - required by plugins: lineanames, protocol-acceleration, registrars, tokenscope # RPC_URL_59144= # Scroll Mainnet # - required by plugins: protocol-acceleration # RPC_URL_534352= # === ENS Namespace: Sepolia === # Ethereum Sepolia (public testnet) # - required if the configured namespace is sepolia # - required by plugins: subgraph, protocol-acceleration, registrars, tokenscope # RPC_URL_11155111= # Base Sepolia (public testnet) # - required by plugins: basenames, protocol-acceleration, registrars, tokenscope # RPC_URL_84532= # Linea Sepolia (public testnet) # - required by plugins: lineanames, protocol-acceleration, registrars, tokenscope # RPC_URL_59141= # Optimism Sepolia (public testnet) # - required by plugins: protocol-acceleration, tokenscope # RPC_URL_11155420= # Arbitrum Sepolia (public testnet) # - required by plugins: protocol-acceleration # RPC_URL_421614= # Scroll Sepolia (public testnet) # - required by plugins: protocol-acceleration # RPC_URL_534351= # === ENS Namespace: ens-test-env === # ens-test-env (local testnet) # - required if the configured namespace is ens-test-env # RPC_URL_1337= # ENSDb config: ENSIndexer Schema Name # Required. This is the name of the ENSIndexer Schema in ENSDb where ENSIndexer will create tables to store indexed data. # Each ENSIndexer instance is the exclusive writer to its ENSIndexer Schema within an ENSDb. # Therefore, this must be a unique ENSIndexer Schema Name for all ENSIndexer instances writing to the same ENSDb. # Multiple ENSIndexer instances can write to the same ENSDb, but each requires a distinct ENSIndexer Schema Name. # # ENSIndexer can run in production mode (i.e. with `pnpm -F ensindexer start` command). # In production mode, the following rules apply: # - For safety, any changes to the indexing behavior in ENSIndexer (including # any changes to indexing code or any changes to environment variables that # influence indexing behavior) will cause ENSIndexer to refuse to use # an ENSIndexer Schema that was already built with different indexing behavior. # This is to prevent data corruption from multiple ENSIndexer instances writing # state to the same ENSIndexer Schema at the same time. # - If you wish to change the indexing behavior of your ENSIndexer (such as # upgrading to a new ENSIndexer version or changing environment variables # that influence indexing behavior) you must either: # - Configure a new ENSIndexer Schema Name # - Remove or rename the existing ENSIndexer Schema you previously built # - Each time you configure a new ENSIndexer Schema Name there is # no automatic "garbage collection" of any ENSIndexer Schemas you may have # previously built. Over time, this can result in the consumption of more and # more storage space consumption. To solve for this, manually garbage collect # any ENSIndexer Schemas you have previously built when they are # no longer needed. # - If ENSIndexer restarts without changes in the indexing behavior, # the indexing will continue from where it left off. # # ENSIndexer can also run in dev mode (i.e. with `pnpm -F ensindexer dev` command). # In dev mode, the following rules apply: # - ENSIndexer uses the ENSIndexer Schema Name that is configured inline in the `dev` script in package.json. # - Any change to indexing behavior in ENSIndexer results in the ENSIndexer instance being restarted. # - Each time ENSIndexer starts in dev mode, the ENSIndexer Schema for # the configured ENSIndexer Schema Name is recreated. This means that any existing ENSIndexer Schema with # the ENSIndexer Schema Name will be dropped and a new ENSIndexer Schema with the same name will be created. # # ENSIndexer manages the ENSIndexer Schemas according to the rules of Ponder-managed database schemas. # Read more about these rules here: # https://ponder.sh/docs/api-reference/ponder/database#database-schema-rules ENSINDEXER_SCHEMA_NAME=ensindexer_0 # Required. This is the connection string for the Postgres database where ENSIndexer will store data. # It should be in the format of `postgresql://:@:/` ENSDB_URL=postgresql://dbuser:abcd1234@localhost:5432/my_database # ENS Namespace Configuration # Required. Must be an ENS namespace's Identifier such as mainnet, sepolia, or ens-test-env. # (see `@ensnode/datasources` for available options). NAMESPACE=mainnet # Plugin Configuration # Required. Identify which indexer plugins to activate (see `src/plugins` for available plugins) # This is a comma separated list of one or more available plugin names (case-sensitive). # NOTE: # - for subgraph-compatible indexing, the only valid configuration is `PLUGINS=subgraph`. # - to support Protocol Acceleration in the Resolution API, enable the protocol-acceleration plugin. PLUGINS=subgraph,basenames,lineanames,threedns,unigraph,protocol-acceleration,registrars,tokenscope # ENSRainbow service URL # Required. This is the URL of the ENSRainbow server that ENSIndexer will use to heal # unknown labels. The best indexing performance requires a colocated deployments of # ENSIndexer and ENSRainbow services to minimize latency. For example, both services should # communicate over the same local network. # Read more about ENSRainbow here: # https://ensrainbow.io # If you need to temporarily use the public ENSRainbow server for testing, set the following: # ENSRAINBOW_URL=https://api.ensrainbow.io (NOT RECOMMENDED - WILL MAKE INDEXING VERY SLOW!!) ENSRAINBOW_URL=http://localhost:3223 # Pinned Label Set Configuration for requests to ENSRainbow # ENSRainbow label set configuration (see https://ensnode.io/ensrainbow/usage/configuration for details) # Required. ENSIndexer must be pinned to a specific label set ID and version to ensure deterministic # indexing results across time. # # For a list of available label sets and their configurations, visit: # https://ensnode.io/ensrainbow/usage/available-label-sets # # LABEL_SET_ID: see https://ensnode.io/ensrainbow/concepts/glossary#label-set-id. # Should match ENSRainbow's LABEL_SET_ID. LABEL_SET_ID=subgraph # LABEL_SET_VERSION: see https://ensnode.io/ensrainbow/concepts/glossary#label-set-version. LABEL_SET_VERSION=0 # A feature flag to enable/disable ENSIndexer's Subgraph Compatible Indexing Behavior # Optional. If this is not set, the default value is set to `DEFAULT_SUBGRAPH_COMPAT` (false). # # When SUBGRAPH_COMPAT is true, ENSIndexer will: # 1. Interpret the Literal Labels and Literal Names encountered as Subgraph Interpreted Labels / Names # - https://ensnode.io/docs/reference/terminology#subgraph-interpreted-label # - https://ensnode.io/docs/reference/terminology#subgraph-interpreted-name # 2. Apply the following default configuration, which can be overridden: # - PLUGINS=subgraph # - LABEL_SET_ID=subgraph # - LABEL_SET_VERSION=0 # # Note: If SUBGRAPH_COMPAT is true but the resulting config is not Subgraph Compatible, an invariant will # be thrown. # # When SUBGRAPH_COMPAT is false (default), ENSIndexer will: # 1. Interpret the Literal Labels and Literal Names encountered as Interpreted Labels / Names # - https://ensnode.io/docs/reference/terminology#interpreted-label # - https://ensnode.io/docs/reference/terminology#interpreted-name # 2. Heal all subnames of addr.reverse on the ENS Root Chain # 3. Apply the following default configuration, which can be overridden: # - PLUGINS=subgraph,basenames,lineanames,threedns # - LABEL_SET_ID=subgraph # - LABEL_SET_VERSION=0 # # SUBGRAPH_COMPAT=false ``` [.env.local.example ](https://github.com/namehash/ensnode/blob/main/apps/ensindexer/.env.local.example)View this file on GitHub. # Managing ENSIndexer Instances ## Database operations [Section titled “Database operations”](#database-operations) Each instance of ENSIndexer maintains its own ENSIndexer Schema instance in ENSDb. The same ENSIndexer instance will always use the same ENSIndexer Schema, even after restarts. However, if you need to update an ENSIndexer config, or deploy a new version of ENSIndexer, you will have to update the `ENSINDEXER_SCHEMA_NAME` env variable to point to a new schema, as otherwise, the ENSIndexer instance will detect that the ENSIndexer Schema was already owned by an ENSIndexer instance with different config / indexing logic and will refuse to start. These measures are taken to protect the state invariants indexing event handlers expect and require. ### ENSIndexer Schema lifecycle [Section titled “ENSIndexer Schema lifecycle”](#ensindexer-schema-lifecycle) * Let’s say you’re currently running ENSNode version X that’s indexing into `ENSINDEXER_SCHEMA_NAME=myschema_X` * Now, when you upgrade to ENSNode version Y, if you try to continue using `ENSINDEXER_SCHEMA_NAME=myschema_X` then ENSIndexer will immediately terminate with an error because indexing logic or config will be different. * Therefore, you must update the `ENSINDEXER_SCHEMA_NAME` to `myschema_Y`, which will: * Build a completely new index from scratch into `myschema_Y` which may grow to become 100+ GB depending on how ENSIndexer is configured. * Still retain all the data in `myschema_X` which could also be 100+ GB… * Now imagine you repeat this process across multiple updates to your `ENSINDEXER_SCHEMA_NAME`, you might have hundreds and hundreds of GB of ENSIndexer Schema instances no longer in use that are just taking up disk space in ENSDb instance until you completely run out of disk space. * The solution is that you need to manually delete all the old ENSIndexer Schema instances in your ENSDb instance you no longer use. ### Dropping orphaned ENSIndexer Schemas [Section titled “Dropping orphaned ENSIndexer Schemas”](#dropping-orphaned-ensindexer-schemas) If your ENSDb instance has orphaned ENSIndexer Schemas that you no longer use, you can drop them with a SQL command like the following: `DROP SCHEMA ... CASCADE` is a destructive operation! Once executed, this operation is irreversible. Double-check your database connection and schema name before running this in production. ```sql DROP SCHEMA myschema_X CASCADE; ``` ### Dropping the RPC Cache [Section titled “Dropping the RPC Cache”](#dropping-the-rpc-cache) Ponder internal state is kept in the `ponder_sync` database schema. Notably, it holds the RPC Cache. If you’d like to drop your RPC cache and any internal Ponder state, you can: ```sql DROP SCHEMA ponder_sync CASCADE; ``` # What is ENSRainbow? > ENSRainbow is an open-source service that [heals](/docs/services/ensrainbow/concepts/glossary#heal) unknown ENS names by converting encoded labelhashes back to human-readable labels. ENSRainbow is an open-source public good designed to make the “unknown, known” by healing millions of unknown ENS (Ethereum Name Service) names. It functions as a sidecar service for ENSNode, the full-stack development platform for ENSv2. ENSRainbow builds upon work from The Graph Protocol (original ENS rainbow tables) and ENS Labs (ENS Subgraph development and maintenance). It is part of the NameHash Labs suite of tools dedicated to supporting the growth of the ENS Protocol. ## The Problem: Unknown Labels [Section titled “The Problem: Unknown Labels”](#the-problem-unknown-labels) The ENS Registry allows subnames to be created onchain without revealing onchain what those subnames are. As a result, when querying indexed ENS names, some names include labels represented as encoded labelhashes (e.g., `[428...b0b]`). These represent unknown labels and are an unfortunate user experience issue in the ENS ecosystem. For a comprehensive explanation of unknown labels — what they are, why they exist, how they’re formatted, and their impact on indexing and applications — see [Unknown Labels](/docs/services/ensrainbow/concepts/unknown-labels/). ## How ENSRainbow Helps [Section titled “How ENSRainbow Helps”](#how-ensrainbow-helps) ENSRainbow significantly improves “name [healing](/docs/services/ensrainbow/concepts/glossary#heal)” coverage compared to relying solely on services like the ENS Subgraph. Its goal is to heal as many ENS names as possible, minimizing the probability that end-users encounter unknown labels. Key aspects of ENSRainbow include: * **Resolving Encoded Labelhashes:** It translates cryptic labelhashes into human-readable labels via [healing](/docs/services/ensrainbow/concepts/glossary#heal). * **Sidecar to ENSNode:** It integrates with ENSNode to provide deterministic name healing across time. * **Improved Healing Coverage:** Aims to minimize the probability that end-users encounter unknown labels, far exceeding previous capabilities. * **Extensible and Decentralized Data Management:** Uses a label set ID and label set version system for enabling any number of different label sets (collections of rainbow records) to support incremental updates across time. ## Documentation Structure [Section titled “Documentation Structure”](#documentation-structure) This documentation is organized into focused sections to help you find what you need quickly: ### Concepts [Section titled “Concepts”](#concepts) Understanding the fundamentals - terminology, data models, and why ENSRainbow works the way it does. [Core Concepts ](/docs/services/ensrainbow/concepts/)Terminology, data models, versioning, and architectural concepts ### Usage [Section titled “Usage”](#usage) Practical guides for integrating ENSRainbow into your applications. [Integration Guides ](/docs/services/ensrainbow/usage/)API reference, SDK usage, configuration, and usage patterns ### Deployment [Section titled “Deployment”](#deployment) Running your own ENSRainbow instance for control, privacy, or scale. [Deployment Guide ](/docs/services/ensrainbow/deploying/)Learn how to deploy your own ENSRainbow instance ### Contributing [Section titled “Contributing”](#contributing) Contributing to the project and running ENSRainbow locally for development. [Contributing Guide ](/docs/services/ensrainbow/contributing/)Learn how to run ENSRainbow locally and contribute to the project ## Quick Start [Section titled “Quick Start”](#quick-start) Ready to try ENSRainbow? Here are the fastest ways to get started: ### Try the Public API [Section titled “Try the Public API”](#try-the-public-api) Start with a simple HTTP request to heal labelhashes immediately. The public API requires no setup and provides instant access to ENSRainbow’s healing capabilities. [API Reference ](/docs/services/ensrainbow/usage/api/)Complete HTTP API documentation with curl examples and response formats ### Use the TypeScript SDK [Section titled “Use the TypeScript SDK”](#use-the-typescript-sdk) Get type-safe integration with built-in error handling and caching. Perfect for JavaScript/TypeScript applications. [Client SDK Guide ](/docs/services/ensrainbow/usage/client-sdk/)Installation instructions, usage examples, and TypeScript interfaces ### Deploy with Docker [Section titled “Deploy with Docker”](#deploy-with-docker) Run your own ENSRainbow instance for control, privacy, or high-volume usage. The HTTP server starts immediately; poll `/ready` to gate traffic until the database bootstrap completes. [Docker Deployment Guide ](/docs/services/ensrainbow/deploying/docker/)Complete setup instructions, environment variables, and production configurations Choose your path above and dive into the relevant documentation section! # ENSRainbow Concepts > Core concepts, terminology, and technical details for understanding ENSRainbow. This section covers the fundamental concepts needed to understand and work with ENSRainbow. Whether you’re integrating with the API, contributing to the project, or just want to understand how it works, start here. ## Essential Concepts [Section titled “Essential Concepts”](#essential-concepts) [Glossary ](/docs/services/ensrainbow/concepts/glossary)Comprehensive terminology and definitions used throughout ENSRainbow [Unknown Labels ](/docs/services/ensrainbow/concepts/unknown-labels)Understanding the fundamental problem of unknown labels in ENS and how ENSRainbow mitigates it [Data Model ](/docs/services/ensrainbow/concepts/data-model)Detailed schema and data organization in ENSRainbow's LevelDB database [Label Sets & Versioning ](/docs/services/ensrainbow/concepts/label-sets-and-versioning)Understanding ENSRainbow's versioning system and why it's essential for deterministic healing [TypeScript Interfaces ](/docs/services/ensrainbow/concepts/typescript-interfaces)Type definitions for ENSRainbow's server and client ## Technical Details [Section titled “Technical Details”](#technical-details) [Technical Versioning ](/docs/services/ensrainbow/concepts/versioning)Software versions, database schema compatibility, and operational considerations [Architecture ](/docs/services/ensrainbow/concepts/architecture)High-level system architecture and component interactions [Performance ](/docs/services/ensrainbow/concepts/performance)Performance characteristics and optimization considerations ## Learning Path [Section titled “Learning Path”](#learning-path) If you’re new to ENSRainbow, we recommend following this learning path: 1. **Start with the [Glossary](/docs/services/ensrainbow/concepts/glossary)** to familiarize yourself with key terms 2. **Read [Unknown Labels](/docs/services/ensrainbow/concepts/unknown-labels)** to understand the fundamental problem ENSRainbow solves 3. **Read [Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** to understand the core versioning concepts 4. **Review the [Data Model](/docs/services/ensrainbow/concepts/data-model)** to see how data is organized 5. **Check out [TypeScript Interfaces](/docs/services/ensrainbow/concepts/typescript-interfaces)** if you’re planning to use the SDK For deployment and operational considerations, also review [Technical Versioning](/docs/services/ensrainbow/concepts/versioning/) and [Architecture](/docs/services/ensrainbow/concepts/architecture/). After understanding these concepts, you’ll be ready to dive into the [Usage documentation](/docs/services/ensrainbow/usage/) or [Contributing guides](/docs/services/ensrainbow/contributing/). # ENSRainbow Architecture Overview > High-level overview and data-flow of ENSRainbow. ENSRainbow consists of four primary layers working together to “heal” unknown labels: 1. **Data Generation & Conversion** – CSV files are converted to the modern `.ensrainbow` format (SQL conversion is available only for migrating legacy ENS Subgraph data). 2. **Data Ingestion** – the `.ensrainbow` files are ingested into a LevelDB database using the `ingest-ensrainbow` CLI. 3. **HTTP API Service** – state in the database is exposed through a lightweight HTTP API. 4. **Client Integration** – applications call the API directly or via the TypeScript SDK. ``` flowchart TD subgraph Data_Generation CSV["CSV files"] SQL[".sql.gz files
(legacy only)"] ENSRB[".ensrainbow files"] CSV --> ConvertCSV["convert command"] SQL --> ConvertSQL["convert-sql command
(legacy migration)"] ConvertCSV --> ENSRB ConvertSQL --> ENSRB end ENSRB --> Ingest["ingest-ensrainbow"] Ingest --> DB[(LevelDB)] DB --> API[["HTTP API /v1/*"]] API --> SDK["@ensnode/ensrainbow-sdk"] SDK --> App["Your app"] ``` Why this matters Understanding the flow helps you pick the right starting point—whether you only need the hosted API, want to ingest your own data, or plan to contribute to the core. # Creating ENSRainbow Files > Complete guide to creating .ensrainbow files from CSV data. ENSRainbow provides two methods for creating `.ensrainbow` files from different data sources. This guide helps you choose the right method and provides step-by-step instructions. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before creating `.ensrainbow` files, ensure you have: 1. **ENSNode repository cloned**: ```bash git clone https://github.com/namehash/ensnode.git cd ensnode ``` 2. **Dependencies installed**: ```bash pnpm install ``` 3. **Working directory**: Navigate to the ENSRainbow directory: ```bash cd apps/ensrainbow ``` All commands in this guide assume you’re in the `apps/ensrainbow` directory unless otherwise specified. ## Overview [Section titled “Overview”](#overview) A `.ensrainbow` file is ENSRainbow’s binary format for storing label-to-labelhash mappings. It uses Protocol Buffers for efficient serialization and supports streaming for large datasets. For detailed information about the file format structure, see the [Data Model](/docs/services/ensrainbow/concepts/data-model) documentation. ## Choosing Your Conversion Method [Section titled “Choosing Your Conversion Method”](#choosing-your-conversion-method) | Method | Input Format | Use Case | Command | | ------------------ | ------------------------------------------- | ----------------------------------- | ---------------------- | | **CSV Conversion** | Single-column CSV file (one label per line) | Building new ENS rainbow tables | `pnpm run convert` | | **SQL Conversion** | Gzipped SQL dump (`ens_names.sql.gz`) | Converting legacy ENS Subgraph data | `pnpm run convert-sql` | ### When to Use CSV Conversion [Section titled “When to Use CSV Conversion”](#when-to-use-csv-conversion) * Creating new rainbow tables for ENSRainbow ### When to Use SQL Conversion [Section titled “When to Use SQL Conversion”](#when-to-use-sql-conversion) * **Legacy migration only**: Converting existing `ens_names.sql.gz` file from the legacy ENS Subgraph. This file can be obtained from [The Graph’s ENS Rainbow repository](https://github.com/graphprotocol/ens-rainbow). * **Note**: We recommend using CSV conversion for all new label sets. The SQL conversion method exists primarily for migrating away from legacy subgraph data, not for creating new subgraph-based label sets. ## Method 1: Converting from CSV Files [Section titled “Method 1: Converting from CSV Files”](#method-1-converting-from-csv-files) The `convert` command processes single-column CSV files with one label per line. ### Command Syntax [Section titled “Command Syntax”](#command-syntax) ```bash pnpm run convert \ --input-file \ --output-file \ --label-set-id \ [--progress-interval ] \ [--existing-db-path ] \ [--silent] ``` ### Required Parameters [Section titled “Required Parameters”](#required-parameters) * `--input-file`: Path to the CSV file * `--label-set-id`: Identifier for the output `.ensrainbow` file that will be created (used in file naming and metadata) ### Optional Parameters [Section titled “Optional Parameters”](#optional-parameters) * `--output-file`: Output file path (defaults to `{label-set-id}_{label-set-version}.ensrainbow`) * `--progress-interval`: Progress logging frequency (default: 50000 records) * `--existing-db-path`: Path to existing ENSRainbow database to filter out existing labels from the generated ensrainbow file and determine the next label set version * `--silent`: Disable progress bar (useful for scripts and automated workflows) ### CSV Format [Section titled “CSV Format”](#csv-format) The CSV converter expects a single-column CSV file with one label per line, **without a header row**. ```csv ethereum vitalik ens ``` The converter automatically computes labelhashes from labels using the `labelhash()` function. This ensures all label-to-labelhash mappings are deterministically correct. ### Label Filtering [Section titled “Label Filtering”](#label-filtering) The CSV converter includes built-in filtering capabilities to prevent duplicate labels: #### Filtering Existing Labels [Section titled “Filtering Existing Labels”](#filtering-existing-labels) Use `--existing-db-path` to filter out labels that already exist in an existing ENSRainbow database: ```bash pnpm run convert \ --input-file new-labels.csv \ --output-file incremental_1.ensrainbow \ --label-set-id my-dataset \ --existing-db-path data-my-dataset ``` This will: * Check each label against the existing database * Skip labels that already exist (avoiding duplicates) * Only write new labels to the output file * Log filtering statistics in the conversion summary #### Filtering Duplicate Labels Within CSV [Section titled “Filtering Duplicate Labels Within CSV”](#filtering-duplicate-labels-within-csv) The converter automatically filters duplicate labels within the same CSV file, keeping only the first occurrence of each label. #### Filtering Statistics [Section titled “Filtering Statistics”](#filtering-statistics) The conversion process logs detailed statistics: ```plaintext === Conversion Summary === Total lines processed: 1000 Valid records: 850 Filtered existing labels: 100 Filtered duplicates: 50 Duration: 150ms ``` ### Example: Creating Test Dataset [Section titled “Example: Creating Test Dataset”](#example-creating-test-dataset) ```bash # Create test dataset from CSV pnpm run convert \ --input-file test-labels.csv \ --output-file test-dataset_0.ensrainbow \ --label-set-id test-dataset ``` ### Example: Creating Discovery Dataset [Section titled “Example: Creating Discovery Dataset”](#example-creating-discovery-dataset) ```bash # Create discovery dataset (initially empty) echo "" > empty.csv pnpm run convert \ --input-file empty.csv \ --output-file discovery-a_0.ensrainbow \ --label-set-id discovery-a ``` ### How It Works [Section titled “How It Works”](#how-it-works) 1. **Streams** CSV parsing using fast-csv for memory efficiency 2. **Validates** that each row contains exactly one column (the label) 3. **Computes** labelhashes deterministically from labels using the `labelhash()` function 4. **Filters** existing labels if `--existing-db-path` is provided 5. **Filters** duplicate labels within the same CSV file 6. **Writes** .ensrainbow file as output ## Method 2: Migrating from ENS Subgraph [Section titled “Method 2: Migrating from ENS Subgraph”](#method-2-migrating-from-ens-subgraph) Legacy Method The `convert-sql` command processes gzipped SQL dump file from the legacy ENS Subgraph. This method exists for migrating away from legacy subgraph data. **For all new label sets, we strongly recommend using CSV conversion (Method 1) instead.** ### Command Syntax [Section titled “Command Syntax”](#command-syntax-1) ```bash pnpm run convert-sql \ --input-file \ --output-file \ --label-set-id \ --label-set-version ``` ### Required Parameters [Section titled “Required Parameters”](#required-parameters-1) * `--input-file`: Path to the gzipped SQL dump file * `--label-set-id`: Identifier for the output `.ensrainbow` file that will be created (used in file naming and metadata, e.g., `subgraph`) * `--label-set-version`: Version number for the output `.ensrainbow` file that will be created (used in file naming and metadata, non-negative integer) ### Optional Parameters [Section titled “Optional Parameters”](#optional-parameters-1) * `--output-file`: Output file path (defaults to `{label-set-id}_{label-set-version}.ensrainbow`) ### Example: Converting Legacy ENS Subgraph Data [Section titled “Example: Converting Legacy ENS Subgraph Data”](#example-converting-legacy-ens-subgraph-data) Legacy Migration Only This example shows how to convert existing legacy subgraph data. For new label sets, use CSV conversion instead. ```bash # Convert legacy ENS Subgraph data (migration use case only) pnpm run convert-sql \ --input-file ens_names.sql.gz \ --output-file subgraph_0.ensrainbow \ --label-set-id subgraph \ --label-set-version 0 ``` ### How It Works [Section titled “How It Works”](#how-it-works-1) 1. **Streams** the gzipped SQL file to avoid memory issues 2. **Parses** SQL COPY statements to extract label/labelhash pairs 3. **Validates** each record and skips invalid entries * **Invalid line format**: Lines that don’t contain exactly 2 tab-separated columns (labelHash and label) * **Invalid labelHash format**: LabelHash values that: * Don’t have exactly 66 characters (must be “0x” prefix + 64 hex digits) * Are not in lowercase (must be all lowercase hexadecimal) * Don’t start with “0x” prefix * Contain invalid hexadecimal characters * Invalid entries are safely skipped as they would be unreachable by the ENS Subgraph 4. **Writes** .ensrainbow file as output ## Common Workflows [Section titled “Common Workflows”](#common-workflows) ### Workflow 1: Migrating from Legacy ENS Subgraph [Section titled “Workflow 1: Migrating from Legacy ENS Subgraph”](#workflow-1-migrating-from-legacy-ens-subgraph) Legacy Migration Only This workflow is for migrating away from legacy ENS Subgraph data. For creating new label sets, use CSV conversion (see Workflow 3) instead. ```bash # 1. Convert legacy SQL dump to .ensrainbow pnpm run convert-sql \ --input-file ens_names.sql.gz \ --output-file subgraph_0.ensrainbow \ --label-set-id subgraph \ --label-set-version 0 # 2. Ingest into LevelDB pnpm run ingest-ensrainbow \ --input-file subgraph_0.ensrainbow \ --data-dir data-subgraph # 3. Validate the database pnpm run validate --data-dir data-subgraph # 4. Start the API server pnpm run serve --data-dir data-subgraph --port 3223 ``` ### Workflow 2: Creating Test Environment [Section titled “Workflow 2: Creating Test Environment”](#workflow-2-creating-test-environment) ```bash # 1. Convert test data pnpm run convert \ --input-file test/fixtures/ens_test_env_names.csv \ --output-file ens-test-env_0.ensrainbow \ --label-set-id ens-test-env # 2. Ingest test data pnpm run ingest-ensrainbow \ --input-file ens-test-env_0.ensrainbow \ --data-dir data-test-env # 3. Run with test data pnpm run serve --data-dir data-test-env --port 3223 ``` ### Workflow 3: Create a new Labelset [Section titled “Workflow 3: Create a new Labelset”](#workflow-3-create-a-new-labelset) ```bash # 1. Create CSV with your labels echo "mylabel1 mylabel2 mylabel3" > custom-labels.csv # 2. Convert to .ensrainbow pnpm run convert \ --input-file custom-labels.csv \ --output-file custom_0.ensrainbow \ --label-set-id custom # 3. Ingest and serve pnpm run ingest-ensrainbow \ --input-file custom_0.ensrainbow \ --data-dir data-custom pnpm run serve --data-dir data-custom --port 3223 ``` ### Workflow 4: Creating Incremental Label Set Versions [Section titled “Workflow 4: Creating Incremental Label Set Versions”](#workflow-4-creating-incremental-label-set-versions) ```bash # 1. Create initial labelset pnpm run convert \ --input-file initial-labels.csv \ --output-file my-dataset_0.ensrainbow \ --label-set-id my-dataset # 2. Ingest initial data pnpm run ingest-ensrainbow \ --input-file my-dataset_0.ensrainbow \ --data-dir data-my-dataset # 3. Create incremental update (filtering existing labels) pnpm run convert \ --input-file new-labels.csv \ --output-file my-dataset_1.ensrainbow \ --label-set-id my-dataset \ --existing-db-path data-my-dataset # 4. Ingest incremental update pnpm run ingest-ensrainbow \ --input-file my-dataset_1.ensrainbow \ --data-dir data-my-dataset # 5. Serve updated data pnpm run serve --data-dir data-my-dataset --port 3223 ``` ### Workflow 5: Using Custom Label Set Server [Section titled “Workflow 5: Using Custom Label Set Server”](#workflow-5-using-custom-label-set-server) ```bash # 1. Configure custom label set server export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # 2. Download from custom server # The script downloads to labelsets/ subdirectory ./scripts/download-ensrainbow-files.sh my-dataset 0 # 3. Ingest and serve # Files are downloaded to labelsets/ by the script pnpm run ingest-ensrainbow \ --input-file labelsets/my-dataset_0.ensrainbow \ --data-dir data-my-dataset pnpm run serve --data-dir data-my-dataset --port 3223 ``` Script Output Locations ENSRainbow download scripts save files to specific subdirectories: * **`.ensrainbow` files**: `labelsets/` * **Database archives**: `databases/{schema_version}/` * **Checksums and licenses**: Same directory as the downloaded file ## File Naming Conventions [Section titled “File Naming Conventions”](#file-naming-conventions) Follow the naming convention: `{label-set-id}_{label-set-version}.ensrainbow` **Examples:** * `subgraph_0.ensrainbow` - Legacy ENS data, version 0 * `discovery-a_0.ensrainbow` - Discovery dataset, version 0 * `ens-test-env_0.ensrainbow` - Test environment data, version 0 ## Next Steps [Section titled “Next Steps”](#next-steps) After creating your `.ensrainbow` file: 1. **[Ingest the data](/docs/services/ensrainbow/contributing/index#data-ingestion-ingest-ensrainbow)** into a ENSRainbow database 2. **[Validate the database](/docs/services/ensrainbow/contributing/index#database-validation-validate)** to ensure integrity 3. **[Start the API server](/docs/services/ensrainbow/contributing/index#api-server-serve)** to serve the data For complete CLI reference information, see the [CLI Reference](/docs/services/ensrainbow/contributing/cli-reference) documentation. ## Creating and Publishing Custom .ensrainbow Files [Section titled “Creating and Publishing Custom .ensrainbow Files”](#creating-and-publishing-custom-ensrainbow-files) If you want to create, publish, and distribute your own `.ensrainbow` files, follow these steps: ### 1. Create Your Dataset [Section titled “1. Create Your Dataset”](#1-create-your-dataset) First, prepare your data in CSV format, then convert it using the `convert` command: ```bash pnpm run convert \ --input-file my-labels.csv \ --output-file my-dataset_0.ensrainbow \ --label-set-id my-dataset # to create an incremental update, you can use the `--existing-db-path` flag to filter out existing labels: pnpm run convert \ --input-file my-labels2.csv \ --output-file my-dataset_1.ensrainbow \ --label-set-id my-dataset \ --existing-db-path data-my-dataset ``` ### 2. Validate Your File [Section titled “2. Validate Your File”](#2-validate-your-file) Test your `.ensrainbow` file by ingesting it locally: ```bash # Ingest your custom dataset pnpm run ingest-ensrainbow \ --input-file my-dataset_0.ensrainbow \ --data-dir data-my-dataset # Validate the database pnpm run validate --data-dir data-my-dataset # Test the API pnpm run serve --data-dir data-my-dataset --port 3223 ``` ### 3. Publish Your File [Section titled “3. Publish Your File”](#3-publish-your-file) #### Option A: Direct File Sharing [Section titled “Option A: Direct File Sharing”](#option-a-direct-file-sharing) * Upload your `.ensrainbow` file to a web server or cloud storage * Provide a direct download URL * Share checksums for integrity verification #### Option B: Package as Database Archive [Section titled “Option B: Package as Database Archive”](#option-b-package-as-database-archive) For better performance, package your data as a pre-built database: ```bash # Ingest your .ensrainbow file pnpm run ingest-ensrainbow \ --input-file my-dataset_0.ensrainbow \ --data-dir data-my-dataset # Package the database tar -czvf my-dataset_0.tgz ./data-my-dataset # Calculate checksum sha256sum my-dataset_0.tgz > my-dataset_0.tgz.sha256sum ``` ### 4. Document Your Label Set [Section titled “4. Document Your Label Set”](#4-document-your-label-set) Create documentation for your custom label set including: * **Label Set ID**: The identifier users will specify * **Description**: What labels are included and their source * **Version**: Current version number * **Download URLs**: Where to get the files * **Checksums**: For integrity verification * **Usage Examples**: How to use your dataset ### Example Documentation Format [Section titled “Example Documentation Format”](#example-documentation-format) ````markdown ## Custom Label Set: my-dataset **Label Set ID**: `my-dataset` **Current Version**: `0` **Description**: Custom ENS labels from [source description] ### Download - Database Archive: `https://example.com/my-dataset_0.tgz` - Checksum: `https://example.com/my-dataset_0.tgz.sha256sum` ### Usage # Using with Docker ```bash docker run -d \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="my-dataset" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` ```` ## Setting Up Your Own Label Set Server [Section titled “Setting Up Your Own Label Set Server”](#setting-up-your-own-label-set-server) A **Label Set Server** is a storage and hosting service for `.ensrainbow` files and prebuilt database archives. It’s not the ENSRainbow API server itself, but rather a way to distribute your custom datasets for others to download and use. ### 1. Choose Your Hosting Platform [Section titled “1. Choose Your Hosting Platform”](#1-choose-your-hosting-platform) You can host your label set files on any web server or cloud storage service: * **AWS S3**: Industry standard with versioning * **Cloudflare R2**: Cost-effective alternative to S3 * **Simple HTTP server**: For internal/private use ### 2. Organize Your Files [Section titled “2. Organize Your Files”](#2-organize-your-files) Structure your label set files following ENSRainbow conventions: ```plaintext my-label-set-server/ ├── labelsets/ │ ├── my-dataset_0.ensrainbow │ ├── my-dataset_0.ensrainbow.sha256sum │ ├── my-dataset_1.ensrainbow │ └── my-dataset_1.ensrainbow.sha256sum └── databases/ ├── 3/ # Schema version │ ├── my-dataset_0.tgz │ ├── my-dataset_0.tgz.sha256sum │ ├── my-dataset_1.tgz │ └── my-dataset_1.tgz.sha256sum └── 4/ # Future schema version ``` ### 3. Use Existing Download Scripts [Section titled “3. Use Existing Download Scripts”](#3-use-existing-download-scripts) ENSRainbow provides ready-to-use download scripts that users can configure to download from your label set server: #### Download .ensrainbow Files [Section titled “Download .ensrainbow Files”](#download-ensrainbow-files) ```bash # Configure your label set server URL export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # Download .ensrainbow file using the existing script ./scripts/download-ensrainbow-files.sh my-dataset 0 ``` #### Download Prebuilt Database Archives [Section titled “Download Prebuilt Database Archives”](#download-prebuilt-database-archives) ```bash # Configure your label set server URL export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # Download prebuilt database using the existing script ./scripts/download-prebuilt-database.sh 3 my-dataset 0 ``` #### Script Features [Section titled “Script Features”](#script-features) The existing scripts automatically handle: * **Checksum verification** for data integrity * **Resume downloads** if files already exist and are valid * **License file downloads** (optional) * **Progress reporting** for large files * **Error handling** with cleanup of partial downloads ### 4. Document Your Label Set Server [Section titled “4. Document Your Label Set Server”](#4-document-your-label-set-server) Create a README or documentation page for your label set server: ```markdown # My Label Set Server This server hosts custom ENS label sets for ENSRainbow. ## Available Label Sets ### my-dataset - **Description**: Custom ENS labels from [source] - **Versions**: 0, 1 - **Schema Versions**: 3 - **Base URL**: `https://my-label-set-server.com` ### another-dataset - **Description**: Additional labels from [source] - **Versions**: 0 - **Schema Versions**: 3 - **Base URL**: `https://my-label-set-server.com` ``` ## Usage [Section titled “Usage”](#usage) Users should have the ENSNode repository cloned and be in the `apps/ensrainbow` directory. ### Option 1: Download .ensrainbow Files [Section titled “Option 1: Download .ensrainbow Files”](#option-1-download-ensrainbow-files) ```bash # Configure your label set server export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # Download .ensrainbow file ./scripts/download-ensrainbow-files.sh my-dataset 0 # Ingest into ENSRainbow pnpm run ingest-ensrainbow \ --input-file labelsets/my-dataset_0.ensrainbow \ --data-dir data-my-dataset # Start ENSRainbow server pnpm run serve --data-dir data-my-dataset --port 3223 ``` ### Option 2: Download Prebuilt Databases (Faster) [Section titled “Option 2: Download Prebuilt Databases (Faster)”](#option-2-download-prebuilt-databases-faster) ```bash # Configure your label set server export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # Download prebuilt database ./scripts/download-prebuilt-database.sh 3 my-dataset 0 # Extract database tar -xzf databases/3/my-dataset_0.tgz -C data-my-dataset --strip-components=1 # Start ENSRainbow server pnpm run serve --data-dir data-my-dataset --port 3223 ``` ### 5. Version Management [Section titled “5. Version Management”](#5-version-management) Implement proper versioning for your label sets: ```bash # When releasing a new version LABEL_SET_ID="my-dataset" NEW_VERSION="1" # Create new .ensrainbow file pnpm run convert \ --input-file updated-labels.csv \ --output-file ${LABEL_SET_ID}_${NEW_VERSION}.ensrainbow \ --label-set-id ${LABEL_SET_ID} # Create prebuilt database pnpm run ingest-ensrainbow \ --input-file ${LABEL_SET_ID}_${NEW_VERSION}.ensrainbow \ --data-dir data-${LABEL_SET_ID}-${NEW_VERSION} tar -czvf ${LABEL_SET_ID}_${NEW_VERSION}.tgz ./data-${LABEL_SET_ID}-${NEW_VERSION} # Calculate checksums sha256sum ${LABEL_SET_ID}_${NEW_VERSION}.ensrainbow > ${LABEL_SET_ID}_${NEW_VERSION}.ensrainbow.sha256sum sha256sum ${LABEL_SET_ID}_${NEW_VERSION}.tgz > ${LABEL_SET_ID}_${NEW_VERSION}.tgz.sha256sum # Upload to your label set server # (implementation depends on your hosting platform) ``` ### 6. Testing Your Label Set Server [Section titled “6. Testing Your Label Set Server”](#6-testing-your-label-set-server) Before publishing, test that your label set server works correctly: ```bash # Set your test server URL export ENSRAINBOW_LABELSET_SERVER_URL="https://my-label-set-server.com" # Test downloading .ensrainbow file ./scripts/download-ensrainbow-files.sh my-dataset 0 # Verify checksum was validated # The script will fail if checksums don't match # Test downloading prebuilt database ./scripts/download-prebuilt-database.sh 3 my-dataset 0 # Verify the database works by ingesting the downloaded file pnpm run ingest-ensrainbow \ --input-file labelsets/my-dataset_0.ensrainbow \ --data-dir test-data pnpm run validate --data-dir test-data ``` ## Running Your Own ENSRainbow Server [Section titled “Running Your Own ENSRainbow Server”](#running-your-own-ensrainbow-server) If you want to run your own ENSRainbow API server (separate from the label set server), see the [Local Development](/docs/services/ensrainbow/contributing/local-development) guide for instructions on setting up and running ENSRainbow locally or in production. ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Data Model](/docs/services/ensrainbow/concepts/data-model)** - Understanding the `.ensrainbow` file format * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** - Managing label set versions * **[CLI Reference](/docs/services/ensrainbow/contributing/cli-reference)** - Complete command documentation * **[Local Development](/docs/services/ensrainbow/contributing/local-development)** - Setting up your development environment # Data Model > Detailed schema and data organization in ENSRainbow's LevelDB database. ENSRainbow stores its rainbow table in a **LevelDB** database. The schema is intentionally simple and append-only: ## Database Schema [Section titled “Database Schema”](#database-schema) | Component | Encoding | Description | | ---------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Record Key** | 32-byte raw `labelhash` (no `0x` prefix) | Guarantees optimal prefix lookups & avoids hex-string parsing overhead. | | **Record Value** | UTF-8 string formatted as `{labelSetVersion}:{label}` | This is produced by [`buildEncodedVersionedRainbowRecord`](https://github.com/ensdomains/ensnode/blob/main/apps/ensrainbow/src/lib/rainbow-record.ts). | | **System Keys** | Byte arrays *not* length 32 prefixed with `0xff…` | Hold metadata such as schema version, ingestion status, precalculated count, etc. | ## Versioned Rainbow Record [Section titled “Versioned Rainbow Record”](#versioned-rainbow-record) A [rainbow record](/docs/services/ensrainbow/concepts/glossary#rainbow-record) with versioning information: rainbow-record.ts ```ts interface VersionedRainbowRecord { label: string; // original label labelSetVersion: number; // version it belongs to } // Encoded on disk as: "{labelSetVersion}:{label}" ``` Storing the version alongside the label allows **incremental snapshots** to coexist. This makes deterministic client-side results across time straightforward, even as additional label set versions are ingested by ENSRainbow. ## Metadata Fields [Section titled “Metadata Fields”](#metadata-fields) The database keeps a handful of **system keys** that do not clash with labelhashes (their length ≠ 32 bytes): * `0xff 0xff 0xff 0xff` → precalculated record count * `0xff 0xff 0xff 0xfe` → ingestion status (`unstarted`/`unfinished`/`finished`) * `0xff 0xff 0xff 0xfd` → database schema version (currently `3`) * `0xff 0xff 0xff 0xfc` → highest label set version * `0xff 0xff 0xff 0xfb` → label set id ## ENSRainbow File Format [Section titled “ENSRainbow File Format”](#ensrainbow-file-format) A **`.ensrainbow` file** is the *on-disk representation* of a label set. It is a compressed, append-only collection that uses **Protocol Buffers (protobuf)** as its serialization format. #### About Protocol Buffers [Section titled “About Protocol Buffers”](#about-protocol-buffers) Protocol Buffers is Google’s language-neutral, platform-neutral extensible mechanism for serializing structured data. ENSRainbow uses protobuf because it provides: * **Compact binary encoding** - Much smaller than JSON or XML * **Schema evolution** - Forward and backward compatibility as the format evolves * **Cross-language support** - Can be read by any language with protobuf support * **Cross-system and cross-architecture compatibility** - Works seamlessly across different operating systems (Linux, Windows, macOS) and CPU architectures (x86, ARM, etc.) without endianness concerns * **Self-delimiting messages** - Protobuf uses **varint encoding** for field delimiters, where each field is prefixed with a tag that combines the field number and wire type. This eliminates the need for explicit field separators since the encoding itself contains all necessary boundary information. #### Protobuf Schema [Section titled “Protobuf Schema”](#protobuf-schema) The `.ensrainbow` file format uses two main message types: ```protobuf // Protobuf serialization format for [rainbow records](/docs/services/ensrainbow/concepts/glossary#rainbow-record) message RainbowRecord { bytes labelhash = 1; // 32-byte labelhash string label = 2; // original label string } message RainbowRecordCollection { string format_identifier = 1; // always "ensrainbow" uint32 ensrainbow_file_format_version = 2; // file format version string label_set_id = 3; // e.g., "subgraph" uint32 label_set_version = 4; // version number repeated RainbowRecord records = 5; // the actual records } ``` #### Delimited Encoding [Section titled “Delimited Encoding”](#delimited-encoding) ENSRainbow uses protobuf’s **length-delimited encoding** (`encodeDelimited`/`decodeDelimited`) where each message is prefixed with its byte length as a varint. This allows streaming multiple messages in a single file without needing explicit separators between records. #### File Structure [Section titled “File Structure”](#file-structure) The `.ensrainbow` file consists of length-delimited protobuf messages in the following order: ### 1. Header Message [Section titled “1. Header Message”](#1-header-message) A single `RainbowRecordCollection` message containing metadata (with empty `records` array): * `format_identifier` – always “ensrainbow” (file format identifier) * `ensrainbow_file_format_version` – file format version number * `label_set_id` – identifier like “subgraph” (lowercase ASCII letters and hyphens, 1-50 chars) * `label_set_version` – version number * `records` – empty array (records are stored separately) ### 2. Record Stream [Section titled “2. Record Stream”](#2-record-stream) A sequence of individual `RainbowRecord` messages, each containing: * `labelhash` – 32-byte hash of the label * `label` – the original label string Each message (header and records) is prefixed with its byte length as a protobuf varint, enabling streaming reads without loading the entire file into memory. ### File Naming Convention [Section titled “File Naming Convention”](#file-naming-convention) The filename convention mirrors the header so humans can eyeball it quickly: ```text subgraph_0.ensrainbow # labelSetId = "subgraph", version = 0 subgraph_1.ensrainbow # next version with incremental labelhash-to-label mappings added ``` ## Creating ENSRainbow Files [Section titled “Creating ENSRainbow Files”](#creating-ensrainbow-files) ENSRainbow provides two methods for creating `.ensrainbow` files from different data sources: * **CSV Conversion**: Convert custom datasets from CSV files using `pnpm run convert` * **SQL Conversion**: Convert legacy ENS Subgraph data (`ens_names.sql.gz`) using `pnpm run convert-sql`. These legacy data files can be obtained from [The Graph’s ENS Rainbow repository](https://github.com/graphprotocol/ens-rainbow). For complete instructions, examples, and workflow guidance, see the [Creating ENSRainbow Files](/docs/services/ensrainbow/concepts/creating-files) guide. ## Ingestion Process [Section titled “Ingestion Process”](#ingestion-process) During **ingestion** ENSRainbow: 1. Reads the header first 2. Validates it against any existing database metadata 3. Streams each pair, converting the plain *label* into an **encoded versioned rainbow record** (`{labelSetVersion}:{label}`) 4. Writes it to LevelDB Importing `subgraph_1.ensrainbow` will increment `SYSTEM_KEY_HIGHEST_LABEL_SET_VERSION` to `1` and mark the previous snapshot as superseded, while still allowing clients pinned to version `0` to heal deterministically. ## Data Flow Overview [Section titled “Data Flow Overview”](#data-flow-overview) ``` graph LR; A[".ensrainbow File"] --> B["Ingestion CLI"]; B --> C["LevelDB (Rainbow Records)"]; C --> D["HTTP API"]; D --> E["Client Cache"]; ``` ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Glossary](/docs/services/ensrainbow/concepts/glossary)** - Key terms like [System Key](/docs/services/ensrainbow/concepts/glossary#system-key), [Ingestion](/docs/services/ensrainbow/concepts/glossary#ingestion), etc. * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** - Understanding the versioning system * **[TypeScript Interfaces](/docs/services/ensrainbow/concepts/typescript-interfaces)** - Type definitions for working with the data # Glossary > Comprehensive terminology and definitions used throughout ENSRainbow. This page defines the **core terminology** used throughout the ENSRainbow codebase and documentation. If you notice an unfamiliar word elsewhere in the docs, come back to this page – it is probably defined here. ## Label [Section titled “Label”](#label) A single component of an ENS name (characters between two dots). Can contain **any** valid UTF-8 string – it may or may not be ENS-normalised. **Example:** `vitalik`, `😺`, `example.eth` has labels `example` & `eth` ## Labelhash [Section titled “Labelhash”](#labelhash) `keccak256` hash of the UTF-8 bytes of a label (no pre-normalisation), represented as a **0x-prefixed 64-digit lowercase hex** string (32 bytes). **Example:** `0xaf2caa…03cc` ## Heal [Section titled “Heal”](#heal) The act of converting a *labelhash* back to its original *label* via a rainbow table lookup. **Example:** `heal('0xaf2c…') → 'vitalik'` ## Rainbow Record [Section titled “Rainbow Record”](#rainbow-record) An entry mapping a `labelhash` ➜ `label`. Persisted as a LevelDB key (labelhash bytes) and UTF-8 value (*see Data Model*). ## Label Set [Section titled “Label Set”](#label-set) A logical collection of [rainbow records](#rainbow-record) that share a common **source** and **versioning** scheme. Each label set represents a dataset snapshot that enables deterministic healing across time. Label sets are identified by a `labelSetId` and `labelSetVersion`. **Example:** id: `subgraph`, version: `0` ## Label Set ID [Section titled “Label Set ID”](#label-set-id) String (1-50 chars) consisting of lowercase ASCII letters and hyphens that names a label set. **Example:** `subgraph`, `discovery-a`, `searchlight` ## Label Set Version [Section titled “Label Set Version”](#label-set-version) Non-negative integer that monotonically increases when new labelhash-to-label mappings are added to a label set. Each version contains incremental additions since the previous version. Version `0` is always the initial dataset. Enables deterministic healing across time by allowing clients to pin to specific versions for reproducible results. **Example:** `0`, `1`, `2` ## Healable Count [Section titled “Healable Count”](#healable-count) Total number of labels that can currently be healed by the running server. Exposed via `/v1/labels/count`. **Example:** `7 892 001` ## Status Code [Section titled “Status Code”](#status-code) High-level outcome of an API call – either `success` or `error`. ## Error Code [Section titled “Error Code”](#error-code) HTTP-style numeric code describing the error (`400`, `404`, `500`, `503`). ## Rainbow Table [Section titled “Rainbow Table”](#rainbow-table) A pre-computed set of `labelhash → label` pairs used for healing. ## Ingestion [Section titled “Ingestion”](#ingestion) One-off process that streams a `.ensrainbow` snapshot into LevelDB. **Example:** `pnpm run ingest subgraph_0.ensrainbow` ## System Key [Section titled “System Key”](#system-key) Special LevelDB key (length ≠ 32 bytes) storing metadata such as schema version, label set id, etc. **Example:** `0xff 0xff 0xff 0xfd` ## ENS Normalization [Section titled “ENS Normalization”](#ens-normalization) The ENSIP-15 canonicalisation process; ENSRainbow stores labels **as-is**, even if not normalised. ## Environment Variables [Section titled “Environment Variables”](#environment-variables) These environment variables are typically set in Docker containers, shell scripts, or system configuration files. See the [Configuration](/docs/services/ensrainbow/usage/configuration) guide for complete setup instructions. ### LABEL\_SET\_ID [Section titled “LABEL\_SET\_ID”](#label_set_id) Environment variable that specifies the identifier for a [label set](#label-set). See [Label Set ID](#label-set-id) for the definition of this identifier. **Used by:** Download scripts and Docker entrypoint to fetch the correct label set ### LABEL\_SET\_VERSION [Section titled “LABEL\_SET\_VERSION”](#label_set_version) Environment variable that specifies the version of a [label set](#label-set). See [Label Set Version](#label-set-version) for the definition of this version number. **Used by:** Download scripts and Docker entrypoint to fetch the correct label set version ### LOG\_LEVEL [Section titled “LOG\_LEVEL”](#log_level) Environment variable that controls the verbosity of logging output. **Format:** String\ **Valid Values:** `fatal`, `error`, `warn`, `info`, `debug`, `trace`, `silent`\ **Default:** `info` ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** - Understanding the versioning system * **[Data Model](/docs/services/ensrainbow/concepts/data-model)** - How data is stored and organized * **[TypeScript Interfaces](/docs/services/ensrainbow/concepts/typescript-interfaces)** - Type definitions for APIs # Label Sets & Versioning > Understanding ENSRainbow's versioning system and why it's essential for deterministic healing. ## Why Label Sets & Versions? [Section titled “Why Label Sets & Versions?”](#why-label-sets--versions) A **[label set](/docs/services/ensrainbow/concepts/glossary#label-set)** is analogous to a *dataset snapshot*. Every time upstream data grows (e.g. additional rainbow records created), we create a new **[label set version](/docs/services/ensrainbow/concepts/glossary#label-set-version)** so that: ### 1. Deterministic Results [Section titled “1. Deterministic Results”](#1-deterministic-results) Clients that pin *version `N`* are guaranteed to get the *exact same* heal response today, tomorrow, and two years from now. ### 2. Decentralized Ecosystem [Section titled “2. Decentralized Ecosystem”](#2-decentralized-ecosystem) Anyone can create and publish their own labelsets, opening ENSRainbow as a solution for the wider ENS ecosystem. Distinct `labelSetId` values enable diverse data sources, community-driven curation, and maintenance of labelhash-to-label mappings. ### 3. Extensibility Across Time [Section titled “3. Extensibility Across Time”](#3-extensibility-across-time) Label sets can grow organically as new labelhash-to-label mappings are discovered. Each new version incrementally adds fresh data without invalidating previous versions, ensuring applications can evolve their data coverage while maintaining backward compatibility. ## Version Management [Section titled “Version Management”](#version-management) ### Label Set IDs [Section titled “Label Set IDs”](#label-set-ids) * String (1-50 chars) consisting of lowercase ASCII letters and hyphens * Examples: `subgraph`, `discovery-a`, `ens-test-env` * Each ID represents a different data source or curation strategy ### Label Set Versions [Section titled “Label Set Versions”](#label-set-versions) * Non-negative integers that increase monotonically: `0`, `1`, `2`, etc. * Version `0` is always the initial dataset for a given label set ID * Each new version contains only the new labelhash-to-label mappings added since the previous version ### File Naming Convention [Section titled “File Naming Convention”](#file-naming-convention) ```text subgraph_0.ensrainbow # labelSetId = "subgraph", version = 0 subgraph_1.ensrainbow # next version with incremental labelhash-to-label mappings added discovery-a_0.ensrainbow # different dataset, initial version ``` ## Client Behavior [Section titled “Client Behavior”](#client-behavior) When constructing an ENSRainbow client, you can specify versioning preferences: 1. **No constraints** → Always use the latest available version 2. **Pin to label set ID** → Use a specific dataset but stay current 3. **Pin to exact version** → Lock to a specific snapshot for deterministic results This flexibility ensures applications can choose between **staying current** with the latest data or **maintaining consistency** for reproducible results. ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Glossary](/docs/services/ensrainbow/concepts/glossary)** - Key terminology including [label set](/docs/services/ensrainbow/concepts/glossary#label-set) and [label set version](/docs/services/ensrainbow/concepts/glossary#label-set-version) terms * **[TypeScript Interfaces](/docs/services/ensrainbow/concepts/typescript-interfaces)** - Type definitions for ENSRainbow’s server and client * **[Data Model](/docs/services/ensrainbow/concepts/data-model)** - How versions are stored in the database # Performance & Sizing | Dataset | `.ensrainbow` size | LevelDB size | Ingest time\* | | ------------------ | ------------------ | ------------ | ------------- | | `ens-test-env / 0` | 1 MB | 5 MB | < 30 s | | `subgraph / 0` | 3 GB | 7 GB | \~20 min | | `searchlight / 0` | 13 GB | 29 GB | \~30 min | `*` Times measured on a 4-core CPU & NVMe SSD. # TypeScript Interfaces > Type definitions for ENSRainbow's server and client. ENSRainbow’s TypeScript APIs expose two companion interfaces that describe **which [label sets](/docs/services/ensrainbow/concepts/glossary#label-set)** **are available (server-side) or requested (client-side)**. ## Server Label Set [Section titled “Server Label Set”](#server-label-set) ### EnsRainbowServerLabelSet [Section titled “EnsRainbowServerLabelSet”](#ensrainbowserverlabelset) Returned by `GET /version` and stored in database metadata: types.ts ```ts interface EnsRainbowServerLabelSet { labelSetId: string; // active label set ID (e.g. "subgraph") highestLabelSetVersion: number; // highest label set version the server has ingested for the label set id } ``` #### Fields [Section titled “Fields”](#fields) * **`labelSetId`** identifies **which [label set](/docs/services/ensrainbow/concepts/glossary#label-set)** the server is currently serving. * **`highestLabelSetVersion`** is the **highest version** available through the server for the label set id. The server will not return labels from a version *greater* than this value (unless it ingests another incremental label set). ## Client Label Set [Section titled “Client Label Set”](#client-label-set) ### EnsRainbowClientLabelSet [Section titled “EnsRainbowClientLabelSet”](#ensrainbowclientlabelset) Provided when constructing `new EnsRainbowApiClient({ clientLabelSet: … })` to pin client expectations: types.ts ```ts interface EnsRainbowClientLabelSet { labelSetId?: string; // optional – require a particular label set labelSetVersion?: number; // optional – maximum label set version accepted } ``` #### Usage Guidelines [Section titled “Usage Guidelines”](#usage-guidelines) 1. **Neither field** → accept any client label set and use the latest version (default behaviour). 2. **Only `labelSetId`** → insist on a specific client label set and use the latest version. 3. **Both fields** → insist on a specific client label set and version, locking the client to an exact snapshot. This handshake that occurs when **both fields** are set ensures both client and server interpret every heal operation against the **same logical label set snapshot**, enabling deterministic and reproducible healing results across time. ## Example Usage [Section titled “Example Usage”](#example-usage) ### Using the Client SDK [Section titled “Using the Client SDK”](#using-the-client-sdk) example.ts ```ts import { EnsRainbowApiClient } from "@ensnode/ensrainbow-sdk"; // Default: use latest version of any labelset const client1 = new EnsRainbowApiClient({ endpointUrl: "https://api.ensrainbow.io", }); // Pin to subgraph labelset, use latest version const client2 = new EnsRainbowApiClient({ endpointUrl: "https://api.ensrainbow.io", clientLabelSet: { labelSetId: "subgraph" }, }); // Pin to exact labelset snapshot for deterministic results across time const client3 = new EnsRainbowApiClient({ endpointUrl: "https://api.ensrainbow.io", clientLabelSet: { labelSetId: "subgraph", labelSetVersion: 0, }, }); ``` ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Client SDK](/docs/services/ensrainbow/usage/client-sdk/)** - How to use these interfaces in practice * **[API Reference](/docs/services/ensrainbow/usage/api/)** - HTTP API endpoints that return these types * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** - Understanding the versioning concepts # Unknown Labels > Understanding the fundamental problem of unknown labels in ENS and how ENSRainbow mitigates it. ## The Problem: Unknown Labels [Section titled “The Problem: Unknown Labels”](#the-problem-unknown-labels) When querying indexed ENS names, you may encounter labels represented as [**Encoded LabelHashes**](/docs/reference/terminology#encoded-labelhash) like `[428...b0b]` instead of human-readable strings. An **Unknown Label** is a [**Label**](/docs/services/ensrainbow/concepts/glossary#label) that is known to exist, but where only the [**LabelHash**](/docs/services/ensrainbow/concepts/glossary#labelhash) of the label is known, not the human-readable string. These unknown labels are represented by ENS indexers as encoded labelhashes using the format `[{LabelHash}]`. Unknown labels are an unfortunate user experience issue in the ENS ecosystem. They can make names difficult to read, understand, and work with in applications. ENSRainbow serves the goal of minimizing the number of unknown labels that exist, and therefore minimizing the probability that users experience interacting with them. ### Format: Encoded LabelHash [Section titled “Format: Encoded LabelHash”](#format-encoded-labelhash) When an unknown label is encountered, it is represented as an **Encoded LabelHash** in the format `[{labelhash}]`, where `{labelhash}` is the 64-character hexadecimal representation of the labelhash (without the `0x` prefix). **Examples:** * `vitalik.eth` — a normalized name with known labels * `[731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22].eth` — a name with an unknown label encoded as a labelhash, followed by the known label `eth` * `[731f7025b488151de311c24abc1f27f02940bde412246fbdb3dea0d4f0663b22].[af2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc].eth` — a name with multiple unknown labels, each encoded as a labelhash **Important:** Labels formatted as encoded labelhashes need to be carefully interpreted depending on the context as either [**literal labels**](/docs/reference/terminology#literal-label) or [**interpreted labels**](/docs/reference/terminology#interpreted-label). ENSNode (unlike the ENS Subgraph) guarantees that all labels it returns are interpreted labels so long as ENSNode’s `SUBGRAPH_COMPAT` is not activated (off by default). ## What Causes Unknown Labels? [Section titled “What Causes Unknown Labels?”](#what-causes-unknown-labels) Unknown labels arise from the fundamental design of the ENS protocol: ### The ENS Registry Design [Section titled “The ENS Registry Design”](#the-ens-registry-design) Unknown labels are a consequence of the ENS Registry not emitting the actual label in events when subnames are created. Only the labelhash is emitted. **Understanding the distinction:** * The ENS Registry contract ([`ENSRegistryWithFallback`](https://etherscan.io/address/0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e#code)) stores the **node** for each name (the node is a 32-byte hash of a name computed via `namehash`) * All subnames in the ENS Registry are created by calling [`setSubnodeOwner`](https://etherscan.io/address/0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e#code#F1#L84). This emits a `NewOwner` event for the newly created subname. However, this event contains a parameter misleadingly named `label` that is **not actually the label string**—it’s the **labelhash** of the new subname’s label. ENSRegistryWithFallback.sol ```solidity event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // ^^^^^ This is actually a labelhash, not a label! ``` **The critical issue:** When a subname is added to the Registry, the Registry doesn’t emit the label of the newly created subname—only the labelhash of the new subname’s label. This means: 1. **Indexers may only receive the labelhashes of subnames**, and are not guaranteed to always receive the human-readable label 2. **Many subnames are created through contracts that wrap the Registry** (like `ETHRegistrarController` or `NameWrapper`). These contracts emit additional events with the actual label string, enabling indexers to automatically discover such labels 3. **Direct Registry calls**, or calls from contracts that wrap the Registry but don’t emit the subname label in a separate known event will result in unknown labels for indexers ### Shadow Registries [Section titled “Shadow Registries”](#shadow-registries) The unknown label problem also impacts **Shadow Registries**, which are essentially clones of the “root” ENS Registry deployed on other chains. These are part of projects such as: * **Basenames** on Base * **Lineanames** on Linea Shadow Registries inherit the same architectural design as the original ENS Registry, meaning they also only emit labelhashes (not labels) in their events. ### When Labels Can Be Made Known [Section titled “When Labels Can Be Made Known”](#when-labels-can-be-made-known) In many cases, the labels that make up a name can be **healed** (made **known**) through several strategies: * **Contract events**: Some contracts (like `ETHRegistrarController` or `NameWrapper`) emit the human-readable label in their events * **[Rainbow table](/docs/services/ensrainbow/concepts/glossary#rainbow-table) lookups**: The human-readable label for a given labelhash can be determined via customizable and massive rainbow table lookups through ENSRainbow * **Heuristics for addr.reverse**: ENSNode uses specialized heuristics that heal 100% of subnames under `addr.reverse`, which represent reverse ENS records * **Intelligent expansion strategies**: ENSNode has implemented strategies to intelligently expand the set of rainbow tables in ENSRainbow over time, ensuring healing coverage improves continuously ENSNode uses a number of these strategies in combination to heal unknown labels. However, if none of these methods succeed, the label remains unknown and must be represented as an encoded labelhash. ## Why Unknown Labels Are Forever a Consideration [Section titled “Why Unknown Labels Are Forever a Consideration”](#why-unknown-labels-are-forever-a-consideration) Unknown labels are a **permanent architectural constraint** of the ENS protocol, not a temporary issue that can be fully eliminated. Here’s why: ### 1. Protocol Design Immutability [Section titled “1. Protocol Design Immutability”](#1-protocol-design-immutability) The ENS Registry contract is immutable—it cannot be changed or upgraded. The design decision to emit only labelhashes (not labels) in `NewOwner` events is permanent. ### 2. Cryptographic One-Way Function [Section titled “2. Cryptographic One-Way Function”](#2-cryptographic-one-way-function) The [**labelhash**](/docs/services/ensrainbow/concepts/glossary#labelhash) function computes a 32-byte hash of a label using `keccak256`: example.ts ```typescript import { labelhashInterpretedLabel, asInterpretedLabel } from "enssdk"; const labelHash = labelhashInterpretedLabel(asInterpretedLabel("vitalik")); // Returns: 0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc ``` `keccak256` is a cryptographic hash function that is **one-way**: * Given a label, you can compute its labelhash: `labelhash("vitalik") → 0xaf2c...` * Given a labelhash, you **cannot** reverse it to get the original label: `0xaf2c... → ???` This means that without external knowledge (rainbow tables, event logs, etc.), a labelhash cannot be converted back to its original label. This one-way property is exactly why rainbow tables—and ENSRainbow—are necessary. ### 3. Ongoing Subname Creation [Section titled “3. Ongoing Subname Creation”](#3-ongoing-subname-creation) New subnames continue to be created on-chain. While many modern contracts emit label information in events, the protocol itself does not guarantee this. ## How Unknown Labels Influence Indexing [Section titled “How Unknown Labels Influence Indexing”](#how-unknown-labels-influence-indexing) Unknown labels impact how ENS data is indexed and queried: ### Indexing Process [Section titled “Indexing Process”](#indexing-process) When ENSNode indexes onchain events where a subname is created in the ENS Registry: 1. **The labelhash is always known** from the onchain event data 2. **The label may be unknown** if it wasn’t emitted in the event 3. **ENSRainbow lookup is attempted**: ENSNode attempts to lookup the label for the labelhash through an attached ENSRainbow server 4. **Representation decision**: * If the lookup succeeds: ENSNode represents the subname using its true label * If the lookup fails: ENSNode represents the “unknown label” using its labelhash in the format `[labelhash]` ### Label Mutability Over Time [Section titled “Label Mutability Over Time”](#label-mutability-over-time) The representation of labels can change over time as ENSRainbow’s healing capabilities improve: * **Time 1**: ENSRainbow cannot heal label X → label is represented as `[labelhash]` * **Time 2**: ENSRainbow gains the ability to heal label X → label transitions from unknown to known This mutability means that: * Label representations should not be used as immutable identifiers * The node (computed via `namehash`) should always be used as the stable identifier for querying * For deterministic indexing results across time, pin healing to a specific label set ID + version (see [Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)) ### Subgraph-Unindexable Labels [Section titled “Subgraph-Unindexable Labels”](#subgraph-unindexable-labels) The legacy ENS Subgraph specifies that **Unknown Labels** and labels containing certain UTF-8 characters are “invalid” or “subgraph-unindexable”. These include: 1. `\0` (null byte) - PostgreSQL does not allow storing this character in text fields 2. `.` (period) - Conflicts with ENS label separator logic 3. `[` (left square bracket) - Conflicts with “unknown label” representations 4. `]` (right square bracket) - Conflicts with “unknown label” representations In ENSNode’s default *Interpreted Labels* mode (`SUBGRAPH_COMPAT=false`), when a `subgraph-unindexable` label is encountered, it will be represented as an **Encoded LabelHash** even if the actual label data is available. This simplifies handling for many edge cases. ## How Unknown Labels Influence Apps and User Interfaces [Section titled “How Unknown Labels Influence Apps and User Interfaces”](#how-unknown-labels-influence-apps-and-user-interfaces) Unknown labels create challenges for applications building on ENS: ### Display Challenges [Section titled “Display Challenges”](#display-challenges) Apps must support the possibility of needing to display names containing unknown labels / encoded labelhashes. ### Querying Challenges [Section titled “Querying Challenges”](#querying-challenges) Unknown labels create different challenges depending on which API you’re using: **Subgraph-compatible GraphQL APIs (ENS Subgraph & ENSNode’s `/subgraph` endpoint):** When querying ENSNode’s Subgraph-compatible GraphQL API or the legacy ENS Subgraph: 1. **Use nodes, not names**: Always use the node (computed via `namehash`) as the stable identifier, not the name string 2. **Normalization awareness**: When querying from user input, normalize first; when querying from onchain data, don’t normalize 3. **Encoded LabelHash-aware namehash**: Use implementations like [viem’s namehash](https://github.com/wevm/viem/blob/fe558fdef7e2e9cd5f3f57d8bdeae0c7ff67a1b0/src/utils/ens/namehash.ts#L36-L51) that handle encoded labelhashes correctly **ENSNode’s Resolution API:** ENSNode’s Resolution API (accessed via the `EnsNodeClient` SDK) accepts names directly without requiring you to compute the node (namehash) yourself. However, you must still normalize the name before passing it to the API. The key difference is that you don’t need to manually compute the namehash - you can call methods like `resolveRecords(normalizedName, selection)` directly with the name string. ## The Solution: How ENSRainbow Works [Section titled “The Solution: How ENSRainbow Works”](#the-solution-how-ensrainbow-works) ENSRainbow mitigates the unknown labels problem by providing a **[healing](/docs/services/ensrainbow/concepts/glossary#heal) service** that converts labelhashes back to human-readable labels via [rainbow table](/docs/services/ensrainbow/concepts/glossary#rainbow-table) lookups. ### What is Healing? [Section titled “What is Healing?”](#what-is-healing) [**Healing**](/docs/services/ensrainbow/concepts/glossary#heal) is the act of converting a labelhash back to its original label via a rainbow table lookup or other special strategies implemented in ENSNode. ENSRainbow maintains pre-computed mappings of `labelhash → label` pairs (called [rainbow records](/docs/services/ensrainbow/concepts/glossary#rainbow-record)) that enable this reverse lookup. ### How ENSRainbow Works [Section titled “How ENSRainbow Works”](#how-ensrainbow-works) ENSRainbow operates as a **sidecar service** to ENSNode: 1. **[Rainbow Table](/docs/services/ensrainbow/concepts/glossary#rainbow-table) Storage**: ENSRainbow maintains LevelDB databases containing millions of labelhash-to-label mappings 2. **HTTP API**: Provides a lightweight HTTP API (`GET /v1/heal/{labelhash}`) that returns the corresponding label if found (optionally scoped via `label_set_id` and `label_set_version` query parameters) 3. **Integration with ENSNode**: During indexing, ENSNode automatically queries ENSRainbow when it encounters an unknown labelhash 4. **Deterministic Healing**: Uses label set IDs and versions to ensure deterministic healing across time ### Healing Process [Section titled “Healing Process”](#healing-process) When ENSNode encounters an unknown label during indexing: ```typescript // 1. ENSIndexer encounters labelhash: 0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc // 2. ENSIndexer queries ENSRainbow: GET /v1/heal/0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc // 3. ENSRainbow looks up in rainbow table // 4. If found: Returns { "status": "success", "label": "vitalik" } // 5. ENSIndexer stores the name as "vitalik.eth" instead of "[af2c...].eth" ``` ### Coverage and Limitations [Section titled “Coverage and Limitations”](#coverage-and-limitations) ENSRainbow significantly improves healing coverage compared to relying solely on services like the ENS Subgraph. However: * **Not all labels can be healed**: Some labelhashes may never be recoverable if the label cannot be discovered from any available source (onchain events, offchain APIs, brute-force generation, user submissions, or other data sources) * **Growing coverage**: ENSRainbow’s goal is to heal as many ENS names as possible, minimizing the probability that end-users encounter unknown labels * **Multiple label sets**: Different [label sets](/docs/services/ensrainbow/concepts/glossary#label-set) (identified by [label set ID](/docs/services/ensrainbow/concepts/glossary#label-set-id)) can provide different coverage, allowing the ecosystem to contribute additional healing data ### Label Sets and Versioning [Section titled “Label Sets and Versioning”](#label-sets-and-versioning) ENSRainbow uses a [**label set**](/docs/services/ensrainbow/concepts/glossary#label-set) system to organize rainbow table data: * **[Label Set ID](/docs/services/ensrainbow/concepts/glossary#label-set-id)**: Identifies a collection of [rainbow records](/docs/services/ensrainbow/concepts/glossary#rainbow-record) (e.g., `subgraph`, `discovery-a`) * **[Label Set Version](/docs/services/ensrainbow/concepts/glossary#label-set-version)**: Monotonically increasing version numbers that enable incremental updates * **Deterministic Results**: ENSRainbow clients (such as ENSIndexer) can pin to specific versions for reproducible healing results This system enables: * **Extensibility**: New label sets can be created and published by anyone * **Incremental Updates**: New versions add mappings without invalidating previous versions * **Deterministic Healing**: Applications can rely on consistent results over time For more details, see [Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning). ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Glossary](/docs/services/ensrainbow/concepts/glossary)** - Key terminology including [labelhash](/docs/services/ensrainbow/concepts/glossary#labelhash), [heal](/docs/services/ensrainbow/concepts/glossary#heal), and [rainbow table](/docs/services/ensrainbow/concepts/glossary#rainbow-table) * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning)** - Understanding how ENSRainbow organizes healing data * **[Architecture](/docs/services/ensrainbow/concepts/architecture)** - High-level system architecture and data flow * **[API Reference](/docs/services/ensrainbow/usage/api)** - Complete HTTP API documentation for ENSRainbow * **[Terminology Reference](/docs/reference/terminology)** - Comprehensive ENSNode terminology including [Unknown Label](/docs/reference/terminology#unknown-label) and [Encoded LabelHash](/docs/reference/terminology#encoded-labelhash) # Technical Versioning & Compatibility > Understand how ENSRainbow handles software, database schema, and compatibility. ENSRainbow uses **three distinct version numbers**—each serving a different purpose. ## 1. ENSRainbow Software Version [Section titled “1. ENSRainbow Software Version”](#1-ensrainbow-software-version) The semantic version of the Node.js application itself (e.g. `0.4.2`). * Bumped when new features, fixes, or breaking changes are released. * Shown in `/v1/config` under the `version` field. ## 2. Database Schema Version (DB\_SCHEMA\_VERSION) [Section titled “2. Database Schema Version (DB\_SCHEMA\_VERSION)”](#2-database-schema-version-db_schema_version) Specifies the expected on-disk structure of the LevelDB database as well as the expected format of the downloadable pre-built LevelDB databases. * Passed as an **environment variable** when running ENSRainbow. Mixing versions The API server will refuse to start if the on-disk database schema is different than the running software’s expected schema. ## 3. Label-set Version (LABEL\_SET\_VERSION) [Section titled “3. Label-set Version (LABEL\_SET\_VERSION)”](#3-label-set-version-label_set_version) Identifies incremental sets of rainbow records for a given label-set (`LABEL_SET_ID`). * For each label-set, starts at **0** and increments by one with each incremental set of rainbow records. * Each increment contains **only incremental additions** from all previous versions. * Enables deterministic, reproducible healing results across time. ### Choosing Versions in Practice [Section titled “Choosing Versions in Practice”](#choosing-versions-in-practice) | Scenario | Recommended `DB_SCHEMA_VERSION` | `LABEL_SET_ID` / `LABEL_SET_VERSION` | | ----------------------------------- | ------------------------------- | ------------------------------------ | | Local dev / tests | Latest | `ens-test-env / 0` | | Production parity with ENS Subgraph | Latest | `subgraph / 0` | | Maximum coverage | Latest | `searchlight / latest` | For a complete list of available label sets and their versions, see the **[Available Label Sets](/docs/services/ensrainbow/usage/available-label-sets/)** documentation page. ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * **[Label Sets & Versioning](/docs/services/ensrainbow/concepts/label-sets-and-versioning/)** - Conceptual overview of why versioning matters * **[Data Model](/docs/services/ensrainbow/concepts/data-model/)** - How versions are stored in the database # ENSRainbow Development and Contributions > Learn how to run ENSRainbow locally for development and contributions. Note This guide covers running ENSRainbow locally for development and contributions. ## Quick Navigation [Section titled “Quick Navigation”](#quick-navigation) For focused guidance on specific topics, check out these dedicated pages: [Local Development ](/docs/services/ensrainbow/contributing/local-development) [Creating ENSRainbow Files ](/docs/services/ensrainbow/concepts/creating-files) [CLI Reference ](/docs/services/ensrainbow/contributing/cli-reference) [Service Management ](/docs/services/ensrainbow/contributing/service-management) [System Requirements ](/docs/services/ensrainbow/contributing/system-requirements) [Building Docker Images ](/docs/services/ensrainbow/contributing/building) Choose Your Path * **New to the project?** Start with [Local Development](/docs/services/ensrainbow/contributing/local-development) * **Creating custom datasets?** See [Creating ENSRainbow Files](/docs/services/ensrainbow/concepts/creating-files) * **Need CLI help?** Check the [CLI Reference](/docs/services/ensrainbow/contributing/cli-reference) * **Building for production?** See [Building Docker Images](/docs/services/ensrainbow/contributing/building) *** ## Getting Started [Section titled “Getting Started”](#getting-started) Follow these steps to start contributing to ENSRainbow: 1. **Follow ENSNode’s [contribution guide](/docs/reference/contributing)** to prepare your workspace environment & install dependencies 2. **Choose your development path** using the focused guides above 3. **Start with [Local Development](/docs/services/ensrainbow/contributing/local-development/)** for the quickest way to get ENSRainbow running locally ## Quick Reference [Section titled “Quick Reference”](#quick-reference) * **Need to build from source?** → [Building Docker Images](/docs/services/ensrainbow/contributing/building) * **Creating custom datasets?** → [Creating ENSRainbow Files](/docs/services/ensrainbow/concepts/creating-files) * **Looking for CLI commands?** → [CLI Reference](/docs/services/ensrainbow/contributing/cli-reference) * **Running into issues?** → [Troubleshooting](/docs/services/ensrainbow/usage/troubleshooting) * **Want to understand the data flow?** → [Data Model](/docs/services/ensrainbow/concepts/data-model) ### Data Ingestion (ingest-ensrainbow) [Section titled “Data Ingestion (ingest-ensrainbow)”](#data-ingestion-ingest-ensrainbow) Ingests data from `.ensrainbow` files into the LevelDB database. ```bash pnpm run ingest-ensrainbow --input-file [--data-dir path/to/db] ``` * `--input-file`: Path to the `.ensrainbow` file. Adheres to [ingestion rules](#ingest-rainbow-tables). * `--data-dir`: Directory for the LevelDB database (default: `data/`). The database stores `LABEL_SET_ID` and `HIGHEST_LABEL_SET_VERSION` metadata. ### Database Validation (validate) [Section titled “Database Validation (validate)”](#database-validation-validate) ```bash pnpm run validate [--data-dir path/to/db] [--lite] ``` Validates database integrity by: * Verifying the keys for all rainbow records are valid labelhashes * Ensuring stored labels match their corresponding labelhashes * Validating the total rainbow record count * Verifying no ingestion was interrupted before successful completion The `--lite` option performs a faster, less thorough validation by skipping hash verification and record count validation. It only checks that: * The ingestion was completed successfully * The schema version is correct * The precalculated count exists and can be retrieved The process will exit with: * Code 0: Validation successful * Code 1: Validation failed or errors encountered ### Database Purge (purge) [Section titled “Database Purge (purge)”](#database-purge-purge) ```bash pnpm run purge [--data-dir path/to/db] ``` Completely removes all files from the specified data directory. This is useful when you need to start fresh with a clean database. The process will exit with: * Code 0: Successful purge * Code 1: Error during purge operation ### API Server (serve) [Section titled “API Server (serve)”](#api-server-serve) ```bash pnpm run serve [--port 3223] [--data-dir path/to/db] ``` Starts the API server. The process will exit with: * Code 0: Clean shutdown * Code 1: Error during operation ## Using ENSRainbow with ens-test-env [Section titled “Using ENSRainbow with ens-test-env”](#using-ensrainbow-with-ens-test-env) The ens-test-env project provides a test environment for ENS development. It includes a small dataset of ENS names in the `ens_test_env_names.csv` file that can be used with ENSRainbow for testing purposes. ### Ingesting ens\_test\_env\_names.csv [Section titled “Ingesting ens\_test\_env\_names.csv”](#ingesting-ens_test_env_namescsv) To ingest the test data into ENSRainbow: 1. **Convert Test Data (if needed):** If you don’t have a pre-converted `ens-test-env-0.ensrainbow` file: ```bash # Navigate to apps/ensrainbow or adjust paths accordingly pnpm run convert --input-file test/fixtures/ens_test_env_names.csv --output-file ens-test-env-0.ensrainbow --label-set-id ens-test-env ``` This creates `ens-test-env-0.ensrainbow`. 2. **Download Test Data (Alternative):** Alternatively, download the pre-defined test data file: ```bash # In apps/ensrainbow directory ./scripts/download-ensrainbow-files.sh ens-test-env 0 ``` This will place `ens-test-env-0.ensrainbow` in `labelsets/`. Adjust the path for the ingest command accordingly. ### Ingesting Test Data [Section titled “Ingesting Test Data”](#ingesting-test-data) ```bash # Assuming ens-test-env-0.ensrainbow is in the current directory or accessible by path pnpm run ingest-ensrainbow --input-file ens-test-env-0.ensrainbow --data-dir data_ens_test_env ``` ### Validating the Test Data [Section titled “Validating the Test Data”](#validating-the-test-data) You can validate the ingested test data to ensure it was properly loaded: ```bash pnpm validate --data-dir data_ens_test_env ``` ### Running ENSRainbow with the test data [Section titled “Running ENSRainbow with the test data”](#running-ensrainbow-with-the-test-data) ```bash pnpm serve --data-dir data_ens_test_env --port 3223 ``` ### Using with Docker [Section titled “Using with Docker”](#using-with-docker) You can also run ENSRainbow with the test data using Docker. This involves running the standard ENSRainbow image and configuring it via environment variables to download and use the test dataset. 1. **Ensure you have the standard ENSRainbow Docker image:** If you haven’t already, pull the latest image: ```bash docker pull ghcr.io/namehash/ensnode/ensrainbow:latest ``` 2. **Run the ENSRainbow container with test data configuration:** You’ll need to mount a volume for persistent storage and set the `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, and `LABEL_SET_VERSION` environment variables to point to the test data. ```bash # Create a directory on your host machine for test data, e.g., ~/my_ensrainbow_test_data mkdir -p ~/my_ensrainbow_test_data docker run -d --name ensrainbow_test_env \ -v ~/my_ensrainbow_test_data:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="ens-test-env" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` * Adjust `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, and `LABEL_SET_VERSION` if the test data parameters differ. Typically, for the standard test data, `LABEL_SET_ID` is `ens-test-env` and `LABEL_SET_VERSION` is `0`. The `DB_SCHEMA_VERSION` should match the version of the database archives you intend to use (e.g., `3`). This setup allows the `entrypoint.sh` script within the container to download the `ens-test-env-0` database archive into the mounted volume on its first run. Subsequent runs will reuse the data from the volume. This test environment setup is particularly useful for running ENS tests (i.e. ens-test-env) that require label healing capabilities without needing the full production dataset. ### Environment Variables [Section titled “Environment Variables”](#environment-variables) When using ENSRainbow with Docker, the following environment variables control which pre-built ENSRainbow database archive is downloaded and used: * **`DB_SCHEMA_VERSION`**: Specifies the database schema version (e.g., `3`). This determines the format and structure of the pre-built ENSRainbow database archives and is not related to the API version. * **Goal**: Ensures compatibility between the ENSRainbow software and the structure of downloaded database files that are prebuilt for startup-time optimizations. * **Configuration**: It is strongly recommended to use the latest available schema version unless you have specific compatibility requirements. * **`LABEL_SET_ID`**: See **[Label Set ID](/docs/services/ensrainbow/concepts/glossary#label-set-id)**. * **Goal**: To enable the extensible definition of new label sets (e.g., subgraph vs. production vs. test). * **Configuration**: See the [Available Label Sets](/docs/services/ensrainbow/usage/available-label-sets) page for a complete list of currently available label set IDs and their descriptions. * **`LABEL_SET_VERSION`**: See **[Label Set Version](/docs/services/ensrainbow/concepts/glossary#label-set-version)**. * **Goal**: To support the deterministic evolution of datasets over time, allowing services to achieve reproducible results. * **Configuration**: Use the highest available version number for the most up-to-date data. Versions are sequential and incremental: * `0` - The initial/base version of the **Label Set**. * `1`, `2`, etc. - Incremental updates to the **Label Set**. **Example combinations:** ```bash # Latest production data DB_SCHEMA_VERSION=3 LABEL_SET_ID=subgraph LABEL_SET_VERSION=0 # The ens-test-env data DB_SCHEMA_VERSION=3 LABEL_SET_ID=ens-test-env LABEL_SET_VERSION=0 # Extended discovery data DB_SCHEMA_VERSION=3 LABEL_SET_ID=discovery-a LABEL_SET_VERSION=0 ``` ### Persistent Storage with Docker [Section titled “Persistent Storage with Docker”](#persistent-storage-with-docker) The ENSRainbow Docker image (built with the combined Dockerfile) now includes an `entrypoint.sh` script that supports persistent storage for the LevelDB database. This prevents re-downloading the database every time the container starts. **How it Works:** 1. **On First Run (with an empty volume):** * The script requires `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, and `LABEL_SET_VERSION` environment variables to be set. * It downloads the specified database archive into `/app/apps/ensrainbow/data` (the designated data directory). * After successful download, extraction, and validation, it creates a marker file (`.ensrainbow_db_ready`) in the data directory. 2. **On Subsequent Runs:** * The script checks for the presence of the data directory and the marker file. * If found and the data passes a quick validation, it skips the download and uses the existing database. * If the data is missing, invalid, or the marker file isn’t present, it will attempt to download it again (requiring the environment variables). **Using Docker Volumes:** To ensure the database persists even if the container is removed and recreated, you **must** mount a Docker volume to `/app/apps/ensrainbow/data` inside the container. **Example with a Host Directory:** ```bash # Create a directory on your host machine first, e.g., ~/my_ensrainbow_data mkdir -p ~/my_ensrainbow_data docker run -d --name ensrainbow_persistent \ -v ~/my_ensrainbow_data:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="subgraph" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` **Example with a Named Docker Volume (Recommended):** ```bash # Create a named volume (only needs to be done once) docker volume create ensrainbow_db_volume docker run -d --name ensrainbow_persistent \ -v ensrainbow_db_volume:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="subgraph" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` Adjust `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, and `LABEL_SET_VERSION` as needed for the initial download. Using a volume ensures that your downloaded and ingested data is not lost when the container stops or is removed, saving time and bandwidth on subsequent runs. ## Generating and Uploading Database Archives [Section titled “Generating and Uploading Database Archives”](#generating-and-uploading-database-archives) Note These steps are typically performed by project maintainers for releasing official pre-built ENSRainbow database archives. Assumes `rclone` is configured with a remote named `ENSRAINBOWR2`. ### 1. Prepare .ensrainbow Files [Section titled “1. Prepare .ensrainbow Files”](#1-prepare-ensrainbow-files) This section covers the conversion of source data (like CSV data or empty files for initial datasets) into the `.ensrainbow` format. For detailed conversion instructions and examples, see the [Creating ENSRainbow Files](/docs/services/ensrainbow/concepts/creating-files) guide. **For the `subgraph` Label Set (legacy migration only):** This command converts a SQL dump file (`ens_names.sql.gz`) from the legacy ENS Subgraph into an `.ensrainbow` file for version 0 of the `subgraph` Label Set. **Note:** SQL conversion is only for migrating legacy ENS Subgraph data. For all new label sets, use CSV conversion instead. ```bash # Assuming ens_names.sql.gz contains the dataset time pnpm run convert-sql --input-file ens_names.sql.gz --output-file subgraph_0.ensrainbow --label-set-id subgraph --label-set-version 0 ``` **For the `discovery-a` Label Set (initially empty for discovered labels):** This creates an empty `.ensrainbow` file for version 0 of the `discovery-a` Label Set, which is used for labels discovered dynamically. ```bash # Create empty CSV file for discovery dataset echo "" > empty.csv time pnpm run convert --input-file empty.csv --output-file discovery-a_0.ensrainbow --label-set-id discovery-a ``` **For the `ens-test-env` Label Set (for testing):** This converts a test dataset CSV file into an `.ensrainbow` file for version 0 of the `ens-test-env` Label Set. ```bash time pnpm run convert --input-file test/fixtures/ens_test_env_names.csv --output-file ens-test-env_0.ensrainbow --label-set-id ens-test-env ``` ### 2. Upload .ensrainbow Files to R2 Storage [Section titled “2. Upload .ensrainbow Files to R2 Storage”](#2-upload-ensrainbow-files-to-r2-storage) After generation, these `.ensrainbow` files are uploaded to the designated cloud storage (e.g., R2 via rclone). ```bash rclone copy ./subgraph_0.ensrainbow ENSRAINBOWR2:ensrainbow/labelsets/ rclone copy ./discovery-a_0.ensrainbow ENSRAINBOWR2:ensrainbow/labelsets/ rclone copy ./ens-test-env_0.ensrainbow ENSRAINBOWR2:ensrainbow/labelsets/ ``` ### 3. Calculate and Upload Checksums for .ensrainbow Files [Section titled “3. Calculate and Upload Checksums for .ensrainbow Files”](#3-calculate-and-upload-checksums-for-ensrainbow-files) SHA256 checksums are generated for each `.ensrainbow` file to ensure data integrity. These checksum files are then uploaded. ```bash # Calculate checksums sha256sum subgraph_0.ensrainbow > subgraph_0.ensrainbow.sha256sum sha256sum discovery-a_0.ensrainbow > discovery-a_0.ensrainbow.sha256sum sha256sum ens-test-env_0.ensrainbow > ens-test-env_0.ensrainbow.sha256sum # Upload checksums rclone copy ./subgraph_0.ensrainbow.sha256sum ENSRAINBOWR2:ensrainbow/labelsets/ rclone copy ./discovery-a_0.ensrainbow.sha256sum ENSRAINBOWR2:ensrainbow/labelsets/ rclone copy ./ens-test-env_0.ensrainbow.sha256sum ENSRAINBOWR2:ensrainbow/labelsets/ ``` ### 4. Ingest .ensrainbow Files into LevelDB Databases [Section titled “4. Ingest .ensrainbow Files into LevelDB Databases”](#4-ingest-ensrainbow-files-into-leveldb-databases) The `.ensrainbow` files are ingested into local LevelDB instances to create the actual databases. ```bash pnpm ingest-ensrainbow --input-file subgraph_0.ensrainbow --data-dir ./data-subgraph_0 pnpm ingest-ensrainbow --input-file discovery-a_0.ensrainbow --data-dir ./data-discovery-a_0 pnpm ingest-ensrainbow --input-file ens-test-env_0.ensrainbow --data-dir ./data-ens-test-env_0 ``` ### 5. Package LevelDB Databases [Section titled “5. Package LevelDB Databases”](#5-package-leveldb-databases) The LevelDB data directories, now populated, are packaged into compressed `tar.gz` archives. ```bash tar -czvf subgraph_0.tgz ./data-subgraph_0 tar -czvf discovery-a_0.tgz ./data-discovery-a_0 tar -czvf ens-test-env_0.tgz ./data-ens-test-env_0 ``` ### 6. Upload Database Archives to R2 Storage [Section titled “6. Upload Database Archives to R2 Storage”](#6-upload-database-archives-to-r2-storage) These database archives are uploaded to cloud storage, tagged with a schema version (e.g., `3` in this example). ```bash rclone copy ./subgraph_0.tgz ENSRAINBOWR2:ensrainbow/databases/3/ rclone copy ./discovery-a_0.tgz ENSRAINBOWR2:ensrainbow/databases/3/ rclone copy ./ens-test-env_0.tgz ENSRAINBOWR2:ensrainbow/databases/3/ ``` ### 7. Calculate and Upload Checksums for Database Archives [Section titled “7. Calculate and Upload Checksums for Database Archives”](#7-calculate-and-upload-checksums-for-database-archives) Finally, checksums for the database archives are calculated and uploaded to ensure their integrity. ```bash # Calculate checksums sha256sum subgraph_0.tgz > subgraph_0.tgz.sha256sum sha256sum discovery-a_0.tgz > discovery-a_0.tgz.sha256sum sha256sum ens-test-env_0.tgz > ens-test-env_0.tgz.sha256sum # Upload checksums rclone copy ./subgraph_0.tgz.sha256sum ENSRAINBOWR2:ensrainbow/databases/3/ rclone copy ./discovery-a_0.tgz.sha256sum ENSRAINBOWR2:ensrainbow/databases/3/ rclone copy ./ens-test-env_0.tgz.sha256sum ENSRAINBOWR2:ensrainbow/databases/3/ ``` # Building and Using ENSRainbow Docker Images ENSRainbow can be run as a Docker image, which includes the application server and scripts to manage its data. Official images are hosted on GitHub Container Registry (GHCR) at `ghcr.io/namehash/ensnode/`. You can view all available image tags and versions on the [GitHub package page](https://github.com/namehash/ensnode/pkgs/container/ensnode%2Fensrainbow). **Key Data Handling Strategy:** The ENSRainbow Docker image is designed to be lightweight. The LevelDB database required for its operation is **not** baked into the image during the build process. Instead, the requested database is downloaded and managed at **runtime** by an `entrypoint.sh` script within the container. This process is triggered when you run the container with **both** of the following requirements: 1. A Docker volume mounted to `/app/apps/ensrainbow/data` (the internal data path). 2. Specific environment variables set: `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, and `LABEL_SET_VERSION`. This approach allows for flexible data updates and management without needing to rebuild the application image. The pre-built image from GHCR is the recommended way to run ENSRainbow. ## Building the Application Image (Optional) [Section titled “Building the Application Image (Optional)”](#building-the-application-image-optional) If you need to build the ENSRainbow application image from source (e.g., for development or testing custom changes), you can use the `Dockerfile` located in the `apps/ensrainbow/` directory. ```bash # from the monorepo root docker build -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest . ``` This command builds an image containing the ENSRainbow application and the `entrypoint.sh` script responsible for runtime data provisioning. The resulting image will behave the same way as the official pre-built image regarding data handling. (Note: The `apps/ensrainbow/Dockerfile` uses a multi-stage setup for build optimizations. Data handling is deferred to the `entrypoint.sh` script at runtime.) ## Available Pre-built Image on GHCR [Section titled “Available Pre-built Image on GHCR”](#available-pre-built-image-on-ghcr) The following official image is available on GHCR and is recommended for use: * **`ghcr.io/namehash/ensnode/ensrainbow:latest`**: The standard ENSRainbow application image. You can pull this image using: ```bash docker pull ghcr.io/namehash/ensnode/ensrainbow:latest ``` ## Runtime Data Provisioning via entrypoint.sh [Section titled “Runtime Data Provisioning via entrypoint.sh”](#runtime-data-provisioning-via-entrypointsh) The ENSRainbow application image (whether self-built or pulled from GHCR) uses the `entrypoint.sh` script to manage the LevelDB data at runtime. When the container starts: * The script checks the designated data directory (mounted via a volume, typically at `/app/apps/ensrainbow/data`). * If the correct data (matching `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, `LABEL_SET_VERSION` environment variables) is present and valid, the server starts. * If data is missing, outdated, or invalid, the script will attempt to download the appropriate pre-built database archive. * A marker file (`.ensrainbow_db_ready`) is used within the data directory to indicate a successfully prepared database. This ensures that the container always runs with the correct and validated dataset. For detailed instructions on running the image with persistent storage and managing these environment variables, please refer to the [Persistent Storage with Docker section in the main contribution guide](/docs/services/ensrainbow/contributing#persistent-storage-with-docker). # ENSRainbow CLI Reference | Command | Purpose | Most useful flags | Example | | ------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `convert` | Convert CSV files to `.ensrainbow` format. **This is the primary method for creating new .ensrainbow files.** | `--input-file`, `--output-file`, `--label-set-id`, `--existing-db-path`, `--silent` | `pnpm run convert --input-file labels.csv --output-file my-dataset_0.ensrainbow --label-set-id my-dataset` | | `convert-sql` | Convert legacy `.sql.gz` rainbow tables (ENS Subgraph data) to `.ensrainbow` format | `--input-file`, `--output-file`, `--label-set-id`, `--label-set-version` | `pnpm run convert-sql --input-file ens_names.sql.gz --output-file subgraph_0.ensrainbow --label-set-id subgraph --label-set-version 0` | | `ingest-ensrainbow` | Stream a `.ensrainbow` file into LevelDB | `--input-file`, `--data-dir` | `pnpm run ingest-ensrainbow --input-file my-dataset_0.ensrainbow --data-dir ./data` | | `validate` | Verify DB integrity | `--data-dir`, `--lite` | `pnpm run validate --lite` | | `purge` | Delete all DB files in a directory | `--data-dir` | `pnpm run purge --data-dir ./data` | | `serve` | Launch the HTTP API server | `--data-dir`, `--port` | `pnpm run serve --port 3223` | ## Creating .ensrainbow Files [Section titled “Creating .ensrainbow Files”](#creating-ensrainbow-files) ### CSV Conversion (Recommended) [Section titled “CSV Conversion (Recommended)”](#csv-conversion-recommended) The `convert` command is the **primary method** for creating new `.ensrainbow` files from CSV data. **Full convert command syntax:** ```bash pnpm run convert \ --input-file path/to/labels.csv \ --output-file path/to/output.ensrainbow \ --label-set-id your-label-set-id \ [--existing-db-path path/to/existing/database] \ [--silent] ``` ### SQL Conversion (Legacy) [Section titled “SQL Conversion (Legacy)”](#sql-conversion-legacy) For converting legacy ENS Subgraph data from SQL dumps: ```bash pnpm run convert-sql \ --input-file path/to/ens_names.sql.gz \ --output-file path/to/output.ensrainbow \ --label-set-id subgraph \ --label-set-version 0 ``` Want more help? Append `--help` to any command, e.g. `pnpm run serve --help`. # Local Development ## Prerequisites [Section titled “Prerequisites”](#prerequisites) 1. Clone the monorepo and install dependencies (see the main ENSNode [Contribution Guide](/docs/reference/contributing)). 2. Make sure `pnpm`, `Node.js >= 18`, and Docker (optional) are installed. ## 1 Download a .ensrainbow file [Section titled “1 Download a .ensrainbow file”](#1-download-a-ensrainbow-file) ENSRainbow stores label-hash-to-label pairs in compressed snapshots with the file extension `.ensrainbow`. Use the helper script located in `apps/ensrainbow/scripts/`: Download .ensrainbow files ```bash cd apps/ensrainbow # Syntax: download-ensrainbow-files.sh ./scripts/download-ensrainbow-files.sh subgraph 0 # classic ENS Subgraph labelset, version 0 ``` Discover Available Label Sets Not sure which label set IDs and versions are available? You can check the [Available Label Sets](/docs/services/ensrainbow/usage/available-label-sets) page for a complete list. The script will: * fetch the `.ensrainbow` file * verify its SHA-256 checksum * place it in `apps/ensrainbow/labelsets/` ## 2 Ingest into LevelDB [Section titled “2 Ingest into LevelDB”](#2-ingest-into-leveldb) Ingest data into LevelDB ```bash pnpm run ingest-ensrainbow \ --input-file subgraph-0.ensrainbow \ --data-dir ./my-subgraph-data ``` Important rules: 1. The **first** ingest for a new directory must be *version 0* of a label-set. 2. All subsequent ingests must belong to the **same** `LABEL_SET_ID`. 3. Label-set versions must increment by **exactly +1** each time. ## 3 Start the API server [Section titled “3 Start the API server”](#3-start-the-api-server) Start the API server ```bash pnpm run serve --data-dir ./my-subgraph-data --port 3223 ``` Visit `http://localhost:3223/health` → `{ "status": "ok" }`. [CLI Reference ](/docs/services/ensrainbow/contributing/cli-reference) # Service Management > Graceful shutdown, database maintenance, and common operations. ## Graceful Shutdown [Section titled “Graceful Shutdown”](#graceful-shutdown) ENSRainbow listens for `SIGTERM` and `SIGINT`. Docker (and most PaaS) send these signals on termination, allowing the server to finish in-flight requests before exiting with code 0. ## Database Maintenance [Section titled “Database Maintenance”](#database-maintenance) * **Reset database** – stop the server, run `pnpm run purge --data-dir `, and ingest again. * **Validate database** – `pnpm run validate --data-dir ./data` ensures record integrity. * **Upgrade label-set** – ingest the next `.ensrainbow` file (version +1). The highest version is reported by `GET /version`. # System Requirements | Scenario | Disk | RAM | Notes | | --------------------------------------------------- | --------------------------------------------- | ------------------------- | -------------------------------------- | | **Data ingestion** (`convert`, `ingest-ensrainbow`) | `.ensrainbow` files size + \~7 GB for LevelDB | ≥ 4 GB | Temporary disk spikes during ingestion | | **API server** (`serve`) | 7-8 GB | ≥ 1 GB (4 GB recommended) | CPU usage is minimal | | **Test-env dataset** | < 50 MB | 512 MB | Ideal for CI and quick demos | # Deploying ENSRainbow Note Follow these guides to deploy an ENSRainbow instance. Running your own ENSRainbow instance is helpful for those that wish to: * Maintain control over their own infrastructure * Keep their requests private * Ensure control over their own availability and uptime guarantees ## Deploy to Railway [Section titled “Deploy to Railway”](#deploy-to-railway) The fastest way to deploy ENSRainbow is to use Railway’s one-click deploy. [Deploy to Railway ](/docs/services/ensrainbow/deploying/railway) ## Deploy with Docker [Section titled “Deploy with Docker”](#deploy-with-docker) You can also deploy the ENSRainbow Docker container to various cloud providers or run it as part of your own stack. [Deploy with Docker ](/docs/services/ensrainbow/deploying/docker) # Deploying ENSRainbow with Docker NameHash Labs publishes the latest version of ENSRainbow as a docker container. The Docker image is lightweight and downloads the requested database at runtime based on environment variables. ## Quick Start [Section titled “Quick Start”](#quick-start) For a quick test setup with test data: ```bash # Create a directory for persistent data storage mkdir -p ~/my_ensrainbow_data # Run with the ens-test-env data docker run -d --name ensrainbow \ -v ~/my_ensrainbow_data:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="ens-test-env" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` The service will be available at `http://localhost:3223`. Storage Requirements The ENSRainbow storage needed for a set of rainbow tables for healing unknown labels ranges from 1 MB to dozens of GB depending on the label set ID and label set version. See [System Requirements](/docs/services/ensrainbow/contributing/system-requirements/) for details. ## Production Setup [Section titled “Production Setup”](#production-setup) For full ENS Subgraph backward compatibility use the `subgraph` label set ID and 0 as the label set version (for maximized label healing use the `searchlight` label set ID and the latest label set version): ```bash # Create a directory for persistent data storage mkdir -p ~/ensrainbow_production_data # Run with production data docker run -d --name ensrainbow_production \ -v ~/ensrainbow_production_data:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="subgraph" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` ## Required Environment Variables [Section titled “Required Environment Variables”](#required-environment-variables) Complete Environment Variable Reference See the [Configuration](/docs/services/ensrainbow/usage/configuration/) page for detailed descriptions of all environment variables and configuration options. The Docker deployment requires these three variables for data management: | Variable | Purpose | Example | | ------------------- | ----------------------------------------------------------------------------------- | -------------------------- | | `DB_SCHEMA_VERSION` | Identifies the expected on-disk database schema version | `3` | | `LABEL_SET_ID` | Chooses which label-set to download | `subgraph`, `ens-test-env` | | `LABEL_SET_VERSION` | Downloads prebuilt snapshot containing all data from version 0 through this version | `0` | ## Persistent Storage [Section titled “Persistent Storage”](#persistent-storage) Persistent Storage Required You **must** mount a Docker volume to `/app/apps/ensrainbow/data` to ensure the database persists between container restarts. Without persistent storage, the database will be re-downloaded every time the container starts. ### Using Named Docker Volumes (Recommended) [Section titled “Using Named Docker Volumes (Recommended)”](#using-named-docker-volumes-recommended) ```bash # Create a named volume (only needs to be done once) docker volume create ensrainbow_db_volume # Run with named volume docker run -d --name ensrainbow \ -v ensrainbow_db_volume:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="subgraph" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` ### Using Host Directories [Section titled “Using Host Directories”](#using-host-directories) ```bash # Create a directory on your host machine mkdir -p ~/ensrainbow_data # Run with host directory mount docker run -d --name ensrainbow \ -v ~/ensrainbow_data:/app/apps/ensrainbow/data \ -e DB_SCHEMA_VERSION="3" \ -e LABEL_SET_ID="subgraph" \ -e LABEL_SET_VERSION="0" \ -p 3223:3223 \ ghcr.io/namehash/ensnode/ensrainbow:latest ``` ## Docker Compose [Section titled “Docker Compose”](#docker-compose) To use ENSRainbow as part of a [Docker Compose](https://docs.docker.com/compose/) setup, use the following service definition: ```yml version: "3.8" services: ensrainbow: container_name: ensrainbow image: ghcr.io/namehash/ensnode/ensrainbow:latest environment: - DB_SCHEMA_VERSION=3 - LABEL_SET_ID=subgraph - LABEL_SET_VERSION=0 volumes: - ensrainbow_data:/app/apps/ensrainbow/data ports: - "3223:3223" restart: unless-stopped volumes: ensrainbow_data: ``` For the ens-test-env: ```yml version: "3.8" services: ensrainbow: container_name: ensrainbow_test image: ghcr.io/namehash/ensnode/ensrainbow:latest environment: - DB_SCHEMA_VERSION=3 - LABEL_SET_ID=ens-test-env - LABEL_SET_VERSION=0 volumes: - ensrainbow_test_data:/app/apps/ensrainbow/data ports: - "3223:3223" restart: unless-stopped volumes: ensrainbow_test_data: ``` ## How Data Download Works [Section titled “How Data Download Works”](#how-data-download-works) 1. **First Run**: When the container starts with an empty volume, it downloads the specified pre-built ENSRainbow database archive based on the environment variables 2. **Subsequent Runs**: The container detects existing data and skips the download, using the persisted database 3. **Data Validation**: The container validates the existing data on startup ### Understanding Label Set Versions [Section titled “Understanding Label Set Versions”](#understanding-label-set-versions) When you specify `LABEL_SET_VERSION=N`, the system: * **Downloads a single file**: `{LABEL_SET_ID}_{LABEL_SET_VERSION}.tgz` (e.g., `subgraph_2.tgz`) * **Contains cumulative data**: This file includes **all** label-to-labelhash mappings from version 0 through version N * **Provides deterministic results**: The server will only use data up through version N, ensuring consistent healing results even if newer versions become available Version Behavior Setting `LABEL_SET_VERSION=2` does **not** download separate files for versions 0, 1, and 2. Instead, it downloads one prebuilt database snapshot that contains the cumulative data from all those versions. Port Mapping The port mapping (`-p 3223:3223` or `ports` in docker-compose) is only needed if you want to access ENSRainbow from the host machine. If you’re only accessing it from other containers in the same network, you can omit the port mapping. ## Next Steps [Section titled “Next Steps”](#next-steps) [Configuration Reference ](/docs/services/ensrainbow/usage/configuration/)Complete environment variable documentation and configuration options [Troubleshooting ](/docs/services/ensrainbow/usage/troubleshooting/)Common Docker deployment issues and solutions # Deploying ENSRainbow on Railway The easiest way to run ENSRainbow on Railway is to use our [pre-made template](https://railway.com/template/Ddy-Qg?referralCode=HxmgeB). [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/template/Ddy-Qg?referralCode=HxmgeB) [Configuration ](/docs/services/ensrainbow/usage/configuration/)View the ENSRainbow configuration options # Frequently Asked Questions This page answers the most common questions about ENSRainbow. ## Can I use ENSRainbow without running my own server? [Section titled “Can I use ENSRainbow without running my own server?”](#can-i-use-ensrainbow-without-running-my-own-server) Yes. NameHash Labs operates a free instance at `https://api.ensrainbow.io`. ## How often are new labels added? [Section titled “How often are new labels added?”](#how-often-are-new-labels-added) New label-set versions are generated manually for now: * **`subgraph`**: data from the ENS Subgraph rainbow tables, no plans to update * **`discovery-a`**: may be updated periodically as new labels are dynamically discovered * **`ens-test-env`**: Static test dataset, no plans to update * **`searchlight`**: Enhanced discovery dataset with additional label discoveries beyond the subgraph To stay informed about new versions, monitor the [Available Label Sets](/docs/services/ensrainbow/usage/available-label-sets/) documentation page. ## How can I create my own .ensrainbow file? [Section titled “How can I create my own .ensrainbow file?”](#how-can-i-create-my-own-ensrainbow-file) You can create your own `.ensrainbow` files from CSV data using the `convert` command, which generates new `.ensrainbow` files from your supplied CSV input. 1. **Prepare your data** as a single-column CSV file with one label per line 2. **Run the convert command:** ```bash pnpm run convert \ --input-file your_labels.csv \ --output-file custom.ensrainbow \ --label-set-id my-dataset ``` **Note:** You can also download existing `.ensrainbow` files using the download scripts. For complete instructions, examples, and workflow guidance, see the [Creating ENSRainbow Files](/docs/services/ensrainbow/concepts/creating-files) guide. See the [CLI Reference](/docs/services/ensrainbow/contributing/cli-reference/) for detailed command usage. ## Does ENSRainbow normalise labels? [Section titled “Does ENSRainbow normalise labels?”](#does-ensrainbow-normalise-labels) No. It returns labels exactly as stored. Your client should perform ENS Normalisation if required. Have a question that isn’t answered here? [Open an issue](https://github.com/namehash/ensnode/issues/new) or hop into our Telegram group. # Using ENSRainbow > Guides for integrating and using ENSRainbow in your applications. This section covers everything you need to know about using ENSRainbow in your applications, from quick API calls to advanced client SDK integration. ## Getting Started [Section titled “Getting Started”](#getting-started) [API Reference ](/docs/services/ensrainbow/usage/api/)Complete HTTP API documentation with examples and error handling [Client SDK ](/docs/services/ensrainbow/usage/client-sdk/)TypeScript SDK for easy integration with type safety and caching ## Configuration & Setup [Section titled “Configuration & Setup”](#configuration--setup) [Configuration ](/docs/services/ensrainbow/usage/configuration/)Configure ENSRainbow for different environments and use cases [Hosted Instances ](/docs/services/ensrainbow/usage/hosted-ensrainbow-instances/)Public ENSRainbow instances you can use immediately ## Support & Troubleshooting [Section titled “Support & Troubleshooting”](#support--troubleshooting) [Troubleshooting ](/docs/services/ensrainbow/usage/troubleshooting/)Common issues and their solutions ## Usage Patterns [Section titled “Usage Patterns”](#usage-patterns) ### Quick API Integration [Section titled “Quick API Integration”](#quick-api-integration) If you just need to heal a few labelhashes occasionally, the HTTP API is perfect: ```bash curl https://api.ensrainbow.io/v1/heal/0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc ``` ### Production Applications [Section titled “Production Applications”](#production-applications) For production applications with consistent usage, consider: 1. **Use the [Client SDK](/docs/services/ensrainbow/usage/client-sdk/)** for type safety and built-in retry logic 2. **Pin to specific [label set versions](/docs/services/ensrainbow/concepts/label-sets-and-versioning/)** for deterministic results 3. **Implement client-side caching** to reduce API calls 4. **Consider [self-hosting](/docs/services/ensrainbow/deploying/)** which is required when running your own ENSNode or for other high-volume use cases ### Enterprise Integration [Section titled “Enterprise Integration”](#enterprise-integration) For enterprise or high-volume applications: 1. **Deploy your own instance** using our [deployment guides](/docs/services/ensrainbow/deploying/) 2. **Configure appropriate [system requirements](/docs/services/ensrainbow/contributing/system-requirements/)** 3. **Set up monitoring** with `/health` (liveness), `/ready` (readiness), and `/v1/labels/count` (count) 4. **Review [performance characteristics](/docs/services/ensrainbow/concepts/performance/)** ## Next Steps [Section titled “Next Steps”](#next-steps) * **New to ENSRainbow?** Start with the [API Reference](/docs/services/ensrainbow/usage/api/) to understand the basics * **Building an app?** Check out the [Client SDK](/docs/services/ensrainbow/usage/client-sdk/) for the best developer experience * **Need high availability?** Review our [deployment options](/docs/services/ensrainbow/deploying/) * **Want to contribute?** See our [contributing guide](/docs/services/ensrainbow/contributing/) # ENSRainbow API > Complete HTTP API reference for ENSRainbow label healing service. ## Endpoint Summary [Section titled “Endpoint Summary”](#endpoint-summary) | Endpoint | Purpose | Key Response Types | | -------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | `GET /health` | Liveness probe — succeeds as soon as the HTTP server is accepting requests | `{ status: "ok" }` | | `GET /ready` | Readiness probe — succeeds once the database has been downloaded, validated, and opened | `ReadyResponse`, `ServiceUnavailableError` | | `GET /v1/heal/{labelhash}` | Heal a single labelhash | `HealSuccess`, `HealError` | | `GET /v1/labels/count` | Current healable label count | `CountSuccess`, `CountServerError`, `ServiceUnavailableError` | | `GET /v1/config` | Public configuration of the running instance — available immediately at startup | `ENSRainbowPublicConfig` | Deterministic Results Pin `labelSetVersion` in your client if you need **deterministic results** across time. See the [Label Sets & Versioning guide](/docs/services/ensrainbow/concepts/label-sets-and-versioning/) for details. ## Liveness vs. Readiness [Section titled “Liveness vs. Readiness”](#liveness-vs-readiness) ENSRainbow’s container starts its HTTP server immediately so that orchestrators (Docker, Kubernetes, ECS, …) can reach it for liveness checks while the database is still being downloaded and validated on first boot (which can take 30+ minutes for large label sets). * **`GET /health`** — pure **liveness** probe. Returns `200 { status: "ok" }` as soon as the HTTP server is bound. It does **not** guarantee the database is loaded. * **`GET /ready`** — **readiness** probe. Returns `200 { status: "ok" }` only after the database has been downloaded, lite-validated, and opened by the process. While the server is still bootstrapping, it returns `503 Service Unavailable` with a structured error body so clients can poll with backoff. * **`GET /v1/config`** — **available immediately** at startup. ENSRainbow builds the public config in-memory from its CLI/env arguments (`LABEL_SET_ID`, `LABEL_SET_VERSION`) before the database has finished bootstrapping, so downstream services (e.g. ENSIndexer) can read the public config without waiting on a cold start. When bootstrap finishes, ENSRainbow verifies the database’s stored label set matches the configured one and refuses to serve (terminating with a non-zero exit code) on mismatch. While the database is not yet ready, the following routes return `503` with the body: notReadyResponse.json ```json { "status": "error", "error": "ENSRainbow is still bootstrapping its database", "errorCode": 503 } ``` * `GET /v1/heal/{labelhash}` * `GET /v1/labels/count` Clients (including ENSIndexer) should poll `/ready` rather than `/health` before issuing heal requests. The official `ensrainbow-sdk` exposes `client.ready()` exactly for this purpose. ## Health Check [Section titled “Health Check”](#health-check) ```bash curl https://api.ensrainbow.io/health ``` Response: `{"status":"ok"}` ## Readiness Check [Section titled “Readiness Check”](#readiness-check) ```bash curl https://api.ensrainbow.io/ready ``` Ready response: `{"status":"ok"}` Not-ready response (HTTP 503): notReadyResponse.json ```json { "status": "error", "error": "ENSRainbow is still bootstrapping its database", "errorCode": 503 } ``` ## Heal Label [Section titled “Heal Label”](#heal-label) ### Basic Usage [Section titled “Basic Usage”](#basic-usage) ```bash curl https://api.ensrainbow.io/v1/heal/0x[labelhash] ``` Example: ```bash curl https://api.ensrainbow.io/v1/heal/0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc ``` Response: response.json ```json { "status": "success", "label": "vitalik" } ``` ### Advanced Usage with Query Parameters [Section titled “Advanced Usage with Query Parameters”](#advanced-usage-with-query-parameters) You can optionally specify `label_set_id` and `label_set_version` query parameters: ```bash curl "https://api.ensrainbow.io/v1/heal/0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc?label_set_id=subgraph&label_set_version=0" ``` #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * **`label_set_id`** (optional): Validates that the request matches the database label set ID. * **`label_set_version`** (optional): Specifies the highest label set version of label set ID to query. Enables deterministic heal results across time even if the ENSRainbow server ingests label sets with greater versions than this value. If provided, label\_set\_id is required and only labels from label sets with versions less than or equal to this value will be returned. Note on returned labels: The service returns labels exactly as they appear in the source data. This means: * Labels may or may not be ENS-normalized * Labels can contain any valid string, including dots, null bytes, or be empty * Clients should handle all possible string values appropriately #### Error Responses [Section titled “Error Responses”](#error-responses) * `400 Bad Request`: When the labelhash parameter is missing or invalid, or when query parameters are incorrectly specified invalidLabelhashResponse.json ```json { "status": "error", "error": "Invalid labelhash - must be a valid hex string", "errorCode": 400 } ``` incorrectVersionResponse.json ```json { "status": "error", "error": "Requested label set version is higher than available label set version", "errorCode": 400 } ``` invalidLabelSetIdResponse.json ```json { "status": "error", "error": "Server label set ID \"subgraph\" does not match client's requested label set ID \"ens-test-env\".", "errorCode": 400 } ``` * `404 Not Found`: When no label is found for the given labelhash noLabelFoundResponse.json ```json { "status": "error", "error": "Label not found", "errorCode": 404 } ``` * `500 Internal Server Error`: When an unexpected error occurs or database is not initialized unexpectedError.json ```json { "status": "error", "error": "Internal server error", "errorCode": 500 } ``` Having trouble with API errors? Check the [Troubleshooting guide](/docs/services/ensrainbow/usage/troubleshooting/) for solutions to common issues. ## Get Count of Healable Labels [Section titled “Get Count of Healable Labels”](#get-count-of-healable-labels) ```bash curl https://api.ensrainbow.io/v1/labels/count ``` Success Response: success.json ```json { "status": "success", "count": 133856894, "timestamp": "2024-01-30T11:18:56Z" } ``` #### Error Responses [Section titled “Error Responses”](#error-responses-1) error.json ```json { "status": "error", "error": "Label count not initialized. Check that the ingest command has been run.", "errorCode": 500 } ``` ## Get Public Config [Section titled “Get Public Config”](#get-public-config) ```bash curl https://api.ensrainbow.io/v1/config ``` Success Response: success.json ```json { "serverLabelSet": { "labelSetId": "subgraph", "highestLabelSetVersion": 0 }, "versionInfo": { "ensRainbow": "0.1.0" } } ``` The response contains: * `serverLabelSet`: The configured label set (from `LABEL_SET_ID` / `LABEL_SET_VERSION`) used at startup * `serverLabelSet.labelSetId`: Configured label set ID (validated against the database during bootstrap) * `serverLabelSet.highestLabelSetVersion`: Configured label set version (validated against the database during bootstrap) * `versionInfo.ensRainbow`: The current ENSRainbow service version # Available Label Sets > Discover which ENSRainbow label set IDs and versions are currently available for download. This page lists the **currently available label set IDs and their versions** that you can use with ENSRainbow’s download scripts and API. ## Quick Reference [Section titled “Quick Reference”](#quick-reference) Use these **currently available** identifiers with the [download script](/docs/services/ensrainbow/contributing/local-development#1--download-a-ensrainbow-file): ```bash # Syntax: download-ensrainbow-files.sh ./scripts/download-ensrainbow-files.sh subgraph 0 # Production dataset ./scripts/download-ensrainbow-files.sh ens-test-env 0 # Test dataset ./scripts/download-ensrainbow-files.sh discovery-a 0 # Discovery dataset ./scripts/download-ensrainbow-files.sh searchlight 1 # Extended discovery dataset ``` ## Available Label Sets [Section titled “Available Label Sets”](#available-label-sets) Real-time Availability The information below reflects actual availability as of the last check. ### Currently Available [Section titled “Currently Available”](#currently-available) #### subgraph [Section titled “subgraph”](#subgraph) **Source**: The Graph’s official ENS Subgraph\ **Description**: Main production dataset containing labelhash-to-label mappings from The Graph’s ENS subgraph. | Version | Status | Description | | ------- | ----------- | -------------------------- | | `0` | ✅ Available | Initial production dataset | #### ens-test-env [Section titled “ens-test-env”](#ens-test-env) **Source**: [ens-test-env](https://github.com/ensdomains/ens-test-env) project\ **Description**: Small, curated test dataset perfectly aligned with the ens-test-env labels for development and testing. | Version | Status | Description | | ------- | ----------- | ---------------------------------- | | `0` | ✅ Available | Test dataset for local development | #### discovery-a [Section titled “discovery-a”](#discovery-a) **Source**: Dynamic discovery pipeline\ **Description**: Dataset for dynamically discovered labels, initially empty but grows over time. | Version | Status | Description | | ------- | ----------- | --------------------------------------------- | | `0` | ✅ Available | Initial empty dataset for dynamic discoveries | #### searchlight [Section titled “searchlight”](#searchlight) **Source**: Extended discovery mechanisms\ **Description**: Enhanced dataset with additional label discoveries beyond the subgraph, providing maximum healing coverage. | Version | Status | Description | | ------- | ----------- | ------------------------------------------------------- | | `0` | ✅ Available | Extended dataset with additional discoveries | | `1` | ✅ Available | Extended dataset with a few more additional discoveries | ## Usage Examples [Section titled “Usage Examples”](#usage-examples) ### For Local Development [Section titled “For Local Development”](#for-local-development) Download test data ```bash cd apps/ensrainbow ./scripts/download-ensrainbow-files.sh ens-test-env 0 ``` ### For Production Use [Section titled “For Production Use”](#for-production-use) Download production data ```bash cd apps/ensrainbow ./scripts/download-ensrainbow-files.sh subgraph 0 ``` ### For Discovery/Research [Section titled “For Discovery/Research”](#for-discoveryresearch) Download discovery dataset ```bash cd apps/ensrainbow ./scripts/download-ensrainbow-files.sh discovery-a 0 ``` ### For Maximum Coverage [Section titled “For Maximum Coverage”](#for-maximum-coverage) Download extended discovery dataset ```bash cd apps/ensrainbow ./scripts/download-ensrainbow-files.sh searchlight 1 ``` ### For API Configuration [Section titled “For API Configuration”](#for-api-configuration) Environment variables ```bash export LABEL_SET_ID=subgraph export LABEL_SET_VERSION=0 export DB_SCHEMA_VERSION=3 ``` ## Checking Availability [Section titled “Checking Availability”](#checking-availability) ### Manual Verification [Section titled “Manual Verification”](#manual-verification) You can manually verify if a label set is available by checking the ENSRainbow labelset server: Check if a label set exists ```bash # Replace with your desired LABEL_SET_ID and LABEL_SET_VERSION LABEL_SET_ID="subgraph" LABEL_SET_VERSION="0" wget --spider "https://bucket.ensrainbow.io/labelsets/${LABEL_SET_ID}_${LABEL_SET_VERSION}.ensrainbow" ``` ## Understanding Versions [Section titled “Understanding Versions”](#understanding-versions) [Label Sets & Versioning ](/docs/services/ensrainbow/concepts/label-sets-and-versioning)Learn more about how ENSRainbow's versioning system works ### Key Points: [Section titled “Key Points:”](#key-points) * **Version 0** is always the initial dataset for any label set * **Higher versions** contain incremental additions (not replacements) * **Versions must be ingested sequentially** (0 → 1 → 2, etc.) * **Each label set ID** maintains its own independent versioning sequence ## Related Resources [Section titled “Related Resources”](#related-resources) [Local Development Setup ](/docs/services/ensrainbow/contributing/local-development)Step-by-step guide for setting up ENSRainbow locally [Configuration Guide ](/docs/services/ensrainbow/usage/configuration)Complete configuration options for ENSRainbow deployment [TypeScript Interfaces ](/docs/services/ensrainbow/concepts/typescript-interfaces)Type definitions for label sets in the ENSRainbow SDK # ENSRainbow Typescript Client SDK > Official TypeScript SDK for easy integration with type safety and caching. ENSRainbow provides an official TypeScript client SDK to simplify integrations with the API. You can find the SDK package at [@ensnode/ensrainbow-sdk](https://github.com/namehash/ensnode/tree/main/packages/ensrainbow-sdk). The SDK provides a simple, type-safe interface for all API endpoints and handles proper formatting of labelhashes and error handling. [@ensnode/ensrainbow-sdk ](https://github.com/namehash/ensnode/tree/main/packages/ensrainbow-sdk)View the ENSRainbow SDK source code on GitHub The easiest way to use the SDK: Install SDK ```bash pnpm add @ensnode/ensrainbow-sdk ``` heal.ts ```ts import { EnsRainbowApiClient } from "@ensnode/ensrainbow-sdk"; const client = new EnsRainbowApiClient({ endpointUrl: "https://api.ensrainbow.io", clientLabelSet: { labelSetId: "subgraph", labelSetVersion: 0 }, }); const res = await client.healLabel( "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", ); if (res.status === "success") { console.log(res.label); } else { console.error(res.error); } ``` # ENSRainbow SDK TypeScript library for interacting with the [ENSRainbow API](https://github.com/namehash/ensnode/tree/main/apps/ensrainbow). Learn more about [ENSRainbow](https://ensrainbow.io) and [ENSNode](https://ensnode.io). ## API Reference ### Heal Label Attempt to heal a labelhash to its original label. ```typescript const response = await client.heal( "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", ); console.log(response); // Output: // { // status: "success", // label: "vitalik" // } ``` ### Label Count Get Count of Healable Labels ```typescript const response = await client.count(); console.log(response); // { // "status": "success", // "count": 133856894, // "timestamp": "2024-01-30T11:18:56Z" // } ``` ### Health Check Simple verification that the service is running, either in your local setup or for the provided hosted instance ```typescript const response = await client.health(); console.log(response); // { // "status": "ok", // } ``` ### Response Types & Error Handling Each API endpoint has a designated response type that includes a successful and an erroneous response to account for possible mishaps that could occur during a request. Below is an example of a failed `heal` operation, that shows the resulting error returned by the SDK ```typescript const notFoundResponse = await client.heal( "0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264", ); console.log(notFoundResponse); // Output: // { // status: "error", // error: "Label not found", // errorCode: 404 // } ``` ## Contact Us Visit our [website](https://namehashlabs.org/) to get in contact. ## License Licensed under the MIT License, Copyright © 2025-present [NameHash Labs](https://namehashlabs.org). See [LICENSE](./LICENSE) for more information. # Configuring ENSRainbow > Environment variables and configuration options for ENSRainbow deployments. ENSRainbow can be configured via environment variables. ## Environment Variables [Section titled “Environment Variables”](#environment-variables) ENSRainbow uses two distinct categories of environment variables with different purposes and scopes. ### Runtime Configuration [Section titled “Runtime Configuration”](#runtime-configuration) These variables are read by the ENSRainbow application during startup and operation: * **`PORT`**: Server port (default: 3223) * **`LOG_LEVEL`**: Logging level, one of: “debug”, “info”, “warn”, “error” (default: “info”) ### Database Download Configuration [Section titled “Database Download Configuration”](#database-download-configuration) These variables are **only used by shell scripts** for downloading and setting up pre-built databases. **The ENSRainbow application itself does not read these environment variables** - it gets label set information from the database after setup is complete. * **`ENSRAINBOW_LABELSET_SERVER_URL`**: The base URL of the ENSRainbow Labelset Server. * **Goal**: To decentralize data sourcing. This allows any user to host their own pre-built ENSRainbow databases and configure their application to use them. * **Configuration**: Set this to the base URL of your chosen server. Defaults to `https://bucket.ensrainbow.io`. * **Used by**: Docker entrypoint script and manual download scripts only. * **`DB_SCHEMA_VERSION`**: Specifies the database schema version (e.g., `3`). This determines the format and structure of the pre-built ENSRainbow database archives and is not related to the API version. * **Goal**: Ensures compatibility between the ENSRainbow software and the structure of downloaded database files that are prebuilt for startup-time optimizations. * **Configuration**: It is strongly recommended to use the latest available schema version unless you have specific compatibility requirements. * **Used by**: Download scripts to fetch the correct database format. * **`LABEL_SET_ID`**: See **[Label Set ID](/docs/services/ensrainbow/concepts/glossary#label-set-id)**. * **Goal**: To enable the extensible definition of new label sets (e.g., subgraph vs. production vs. test). * **Configuration**: See the [Available Label Sets](/docs/services/ensrainbow/usage/available-label-sets) page for a complete list of currently available label set IDs and their descriptions. * **Used by**: Download scripts to fetch the correct label set. * **`LABEL_SET_VERSION`**: See **[Label Set Version](/docs/services/ensrainbow/concepts/glossary#label-set-version)**. * **Goal**: To support the deterministic evolution of datasets over time, allowing services to achieve reproducible results. * **Configuration**: Use the highest available version number for the most up-to-date data. Versions are sequential and incremental: * `0` - The initial/base version of the **label set**. * `1`, `2`, etc. - Incremental updates to the **label set**. * **Used by**: Download scripts to fetch the correct label set version. ### Example Combinations [Section titled “Example Combinations”](#example-combinations) ```bash # Latest production data DB_SCHEMA_VERSION=3 LABEL_SET_ID=subgraph LABEL_SET_VERSION=0 # The ens-test-env data DB_SCHEMA_VERSION=3 LABEL_SET_ID=ens-test-env LABEL_SET_VERSION=0 # Extended discovery data DB_SCHEMA_VERSION=3 LABEL_SET_ID=discovery-a LABEL_SET_VERSION=0 ``` Environment Variables Scope The database download variables (`ENSRAINBOW_LABELSET_SERVER_URL`, `DB_SCHEMA_VERSION`, `LABEL_SET_ID`, `LABEL_SET_VERSION`) are **only used by download scripts** and Docker’s entrypoint script to fetch pre-built databases. **The ENSRainbow application itself does not read these environment variables** - it gets label set information from the database after it’s been set up. **When you need these variables:** * Running with Docker (handled automatically by entrypoint script) * Using manual download scripts to fetch pre-built databases * Initial database setup **When you DON’T need these variables:** * Running ENSRainbow directly after database is already set up * Normal ENSRainbow operations (server reads label set info from database) # Hosted ENSRainbow Instances > Public ENSRainbow instances you can use immediately without hosting your own. NameHash Labs operates freely available instances of ENSRainbow for the ENS community. | Name | Provider | URL | Rate Limit | Label Set ID | Label Set Version | | ---------------------- | ------------- | --------------------------------------- | ---------- | ------------- | ----------------- | | ENSRainbow Subgraph | NameHash Labs | | None | `subgraph` | `0` | | ENSRainbow Searchlight | NameHash Labs | | None | `searchlight` | `1` | These services are provided free of charge with no API key required, have no rate limiting, and are maintained and monitored by the NameHash Labs team. No Uptime Guarantees While we aim for high availability, if you need guaranteed uptime or want to keep your requests private, we recommend [deploying your own instance](/docs/services/ensrainbow/deploying/). # Troubleshooting This page aggregates the most common issues users run into when operating ENSRainbow. ## Common Startup Errors [Section titled “Common Startup Errors”](#common-startup-errors) ### ”Database schema mismatch” [Section titled “”Database schema mismatch””](#database-schema-mismatch) * **Cause**: Your on-disk database was created with a newer/older schema than this binary understands. * **Fix**: Upgrade ENSRainbow to the latest version *or* download a matching database archive. ### ”Label set id mismatch” [Section titled “”Label set id mismatch””](#label-set-id-mismatch) * **Cause**: You attempted to ingest a `.ensrainbow` file whose `LABEL_SET_ID` differs from the database. * **Fix**: Create a fresh data directory or ensure you ingest files that share the same label-set id. ### Port already in use [Section titled “Port already in use”](#port-already-in-use) Check that no other service is bound to `3223` (the default port). You can change the port via the `PORT` environment variable as documented in [Configuration](/docs/services/ensrainbow/usage/configuration/). For frequently asked questions about ENSRainbow, please see the [FAQ page](/docs/services/ensrainbow/faq/).