diff --git a/docs/ensnode.io/astro.config.mjs b/docs/ensnode.io/astro.config.mjs index 2631833bfd..d65225a868 100644 --- a/docs/ensnode.io/astro.config.mjs +++ b/docs/ensnode.io/astro.config.mjs @@ -35,15 +35,26 @@ export default defineConfig({ redirects: { "/docs": "/docs/integrate", "/ensnode": "/docs/integrate", + "/examples": "/docs/integrate/omnigraph/examples", "/ensnode/deploying/railway": "/docs/services/ensrainbow/deploying/railway", - "/ensnode/concepts/what-is-the-ens-subgraph": - "/docs/reference/subgraph-legacy/what-is-the-ens-subgraph", - "/docs/reference/what-is-the-ens-subgraph": - "/docs/reference/subgraph-legacy/what-is-the-ens-subgraph", - "/docs/reference/querying-best-practices": - "/docs/reference/subgraph-legacy/querying-best-practices", - "/docs/reference/subgraph-compatibility-tooling": - "/docs/reference/subgraph-legacy/subgraph-compatibility-tooling", + "/docs/integrate/subgraph/what-is-the-ens-subgraph": "/docs/integrate/subgraph", + "/docs/integrate/subgraph/subgraph-api": "/docs/integrate/subgraph/schema-reference", + "/docs/integrate/subgraph/with-ensjs": "/docs/integrate/subgraph/examples/with-ensjs", + "/docs/integrate/subgraph/with-viem": "/docs/integrate/subgraph/examples/with-viem", + "/docs/integrate/subgraph/subgraph-compatibility-tooling": "/docs/integrate/subgraph", + "/ensnode/concepts/what-is-the-ens-subgraph": "/docs/integrate/subgraph", + "/docs/reference/what-is-the-ens-subgraph": "/docs/integrate/subgraph", + "/docs/reference/querying-best-practices": "/docs/integrate/subgraph/querying-best-practices", + "/docs/reference/subgraph-compatibility-tooling": "/docs/integrate/subgraph", + "/docs/reference/subgraph-legacy/what-is-the-ens-subgraph": "/docs/integrate/subgraph", + "/docs/reference/subgraph-legacy/subgraph-api": "/docs/integrate/subgraph/schema-reference", + "/docs/reference/subgraph-legacy/querying-best-practices": + "/docs/integrate/subgraph/querying-best-practices", + "/docs/reference/subgraph-legacy/subgraph-compatibility-tooling": "/docs/integrate/subgraph", + "/docs/reference/subgraph-legacy/with-ensjs": "/docs/integrate/subgraph/examples/with-ensjs", + "/docs/reference/subgraph-legacy/with-viem": "/docs/integrate/subgraph/examples/with-viem", + "/docs/reference/subgraph-legacy/subgraph-dependents": + "/docs/integrate/subgraph/subgraph-dependents", "/ensadmin": "/docs/services/ensadmin", "/ensapi": "/docs/services/ensapi", "/ensdb": "/docs/services/ensdb", diff --git a/docs/ensnode.io/config/integrations/llms-txt.ts b/docs/ensnode.io/config/integrations/llms-txt.ts index c4bcd3191f..7c65cf9c40 100644 --- a/docs/ensnode.io/config/integrations/llms-txt.ts +++ b/docs/ensnode.io/config/integrations/llms-txt.ts @@ -16,5 +16,6 @@ export const starlightLlmsTxtPlugin = starlightLlmsTxt({ "docs/integrate/integration-options/enssdk/example", "docs/integrate/integration-options/enskit/example", "docs/integrate/omnigraph/schema-reference", + "docs/integrate/subgraph/schema-reference", ], }); diff --git a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts index 857247c07b..ed96df1bea 100644 --- a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts +++ b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts @@ -15,12 +15,56 @@ export const integrateSidebarTopic = { label: "ENSv2 Readiness", link: "/docs/integrate/ensv2-readiness", }, + { + label: "ENS Subgraph", + collapsed: true, + badge: { + text: "LEGACY", + variant: "danger", + }, + items: [ + { + label: "Overview", + link: "/docs/integrate/subgraph", + }, + { + label: "Examples", + collapsed: true, + items: [ + { + label: "Overview", + link: "/docs/integrate/subgraph/examples", + }, + { + label: "With ENSjs", + link: "/docs/integrate/subgraph/examples/with-ensjs", + }, + { + label: "With Viem", + link: "/docs/integrate/subgraph/examples/with-viem", + }, + ], + }, + { + label: "Schema Reference", + link: "/docs/integrate/subgraph/schema-reference", + }, + { + label: "Querying Best Practices", + link: "/docs/integrate/subgraph/querying-best-practices", + }, + { + label: "Subgraph Dependents", + link: "/docs/integrate/subgraph/subgraph-dependents", + }, + ], + }, { label: "ENS Omnigraph API", collapsed: false, badge: { text: "NEW", - variant: "tip", + variant: "success", }, items: [ { @@ -91,6 +135,46 @@ export const integrateSidebarTopic = { }, ], }, + { + label: "ENS Unigraph SQL", + collapsed: false, + badge: { + text: "NEW", + variant: "success", + }, + items: [ + { + label: "Overview", + link: "/docs/integrate/unigraph", + }, + { + label: "Examples", + collapsed: true, + items: [ + { + label: "Overview", + link: "/docs/integrate/unigraph/examples", + }, + { + label: "Domain by Name", + link: "/docs/integrate/unigraph/examples/domain-by-name", + }, + { + label: "Account Domains", + link: "/docs/integrate/unigraph/examples/account-domains", + }, + { + label: "Indexing Status", + link: "/docs/integrate/unigraph/examples/indexing-status", + }, + ], + }, + { + label: "Schema Reference", + link: "/docs/integrate/unigraph/schema-reference", + }, + ], + }, { label: "Integration Options", collapsed: false, @@ -100,30 +184,30 @@ export const integrateSidebarTopic = { link: "/docs/integrate/integration-options", }, { - label: "enskit (React)", + label: "enssdk (TypeScript)", collapsed: false, items: [ { label: "Overview", - link: "/docs/integrate/integration-options/enskit", + link: "/docs/integrate/integration-options/enssdk", }, { label: "Interactive example ⚡", - link: "/docs/integrate/integration-options/enskit/example", + link: "/docs/integrate/integration-options/enssdk/example", }, ], }, { - label: "enssdk (TypeScript)", + label: "enskit (React)", collapsed: false, items: [ { label: "Overview", - link: "/docs/integrate/integration-options/enssdk", + link: "/docs/integrate/integration-options/enskit", }, { label: "Interactive example ⚡", - link: "/docs/integrate/integration-options/enssdk/example", + link: "/docs/integrate/integration-options/enskit/example", }, ], }, diff --git a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/reference.ts b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/reference.ts index d4265bd527..780bef8b21 100644 --- a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/reference.ts +++ b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/reference.ts @@ -11,40 +11,6 @@ export const referenceSidebarTopic = { label: "Terminology", link: "/docs/reference/terminology", }, - { - label: "Subgraph API (Legacy)", - collapsed: true, - items: [ - { - label: "What is the ENS Subgraph?", - link: "/docs/reference/subgraph-legacy/what-is-the-ens-subgraph", - }, - { - label: "Subgraph API", - link: "/docs/reference/subgraph-legacy/subgraph-api", - }, - { - label: "Querying Best Practices", - link: "/docs/reference/subgraph-legacy/querying-best-practices", - }, - { - label: "Subgraph Compatibility Tooling", - link: "/docs/reference/subgraph-legacy/subgraph-compatibility-tooling", - }, - { - label: "With ENSjs", - link: "/docs/reference/subgraph-legacy/with-ensjs", - }, - { - label: "With Viem", - link: "/docs/reference/subgraph-legacy/with-viem", - }, - { - label: "Subgraph Dependents", - link: "/docs/reference/subgraph-legacy/subgraph-dependents", - }, - ], - }, { label: "Contributing", collapsed: false, diff --git a/docs/ensnode.io/src/components/organisms/ExampleCard.astro b/docs/ensnode.io/src/components/organisms/ExampleCard.astro deleted file mode 100644 index 891fc457c3..0000000000 --- a/docs/ensnode.io/src/components/organisms/ExampleCard.astro +++ /dev/null @@ -1,51 +0,0 @@ ---- -import { ENSADMIN_URL } from "astro:env/client"; -import { Icon } from "astro-icon/components"; -import type { SavedQuery } from "@data/ens-v1-examples-queries"; - -export interface Props { - example: SavedQuery; -} - -const { example } = Astro.props; - -const createGraphiQLUrl = (query: string, variables: string): string => { - try { - const url = new URL("/api/subgraph", ENSADMIN_URL); - url.searchParams.set("query", query.trim()); - url.searchParams.set("variables", variables.trim()); - return url.toString(); - } catch (err) { - console.error("Failed to create GraphiQL URL: ", err); - return new URL("/api/subgraph", ENSADMIN_URL).toString(); - } -}; - -const graphiqlUrl = createGraphiQLUrl(example.query, example.variables); ---- - -
-
-
- - {example.category} - -
- -

{example.name}

- -

- {example.description} -

-
- - - - Open in ENSAdmin - -
diff --git a/docs/ensnode.io/src/components/organisms/GraphQLSchemaDocExplorer.tsx b/docs/ensnode.io/src/components/organisms/GraphQLSchemaDocExplorer.tsx new file mode 100644 index 0000000000..24604e5bdf --- /dev/null +++ b/docs/ensnode.io/src/components/organisms/GraphQLSchemaDocExplorer.tsx @@ -0,0 +1,38 @@ +import "@graphiql/react/style.css"; +import "@graphiql/plugin-doc-explorer/style.css"; + +import { DocExplorer } from "@graphiql/plugin-doc-explorer"; +import { GraphiQLProvider } from "@graphiql/react"; +import type { GraphQLSchema } from "graphql"; + +export default function GraphQLSchemaDocExplorer({ schema }: { schema: GraphQLSchema }) { + return ( +
+
+ Promise.resolve({})} + > + + +
+
+ ); +} diff --git a/docs/ensnode.io/src/components/organisms/OmnigraphSchemaDocExplorer.tsx b/docs/ensnode.io/src/components/organisms/OmnigraphSchemaDocExplorer.tsx index 4fd5e68419..467dbcd22f 100644 --- a/docs/ensnode.io/src/components/organisms/OmnigraphSchemaDocExplorer.tsx +++ b/docs/ensnode.io/src/components/organisms/OmnigraphSchemaDocExplorer.tsx @@ -1,41 +1,9 @@ -import "@graphiql/react/style.css"; -import "@graphiql/plugin-doc-explorer/style.css"; - -import { DocExplorer, DocExplorerStore } from "@graphiql/plugin-doc-explorer"; -import { GraphiQLProvider } from "@graphiql/react"; import omnigraphSchemaSdl from "enssdk/omnigraph/schema.graphql?raw"; import { buildSchema } from "graphql"; +import GraphQLSchemaDocExplorer from "./GraphQLSchemaDocExplorer.tsx"; const omnigraphSchema = buildSchema(omnigraphSchemaSdl); export default function OmnigraphSchemaDocExplorer() { - return ( -
-
- Promise.resolve({})} - > - - -
-
- ); + return ; } diff --git a/docs/ensnode.io/src/components/organisms/SubgraphSchemaDocExplorer.tsx b/docs/ensnode.io/src/components/organisms/SubgraphSchemaDocExplorer.tsx new file mode 100644 index 0000000000..66aa913928 --- /dev/null +++ b/docs/ensnode.io/src/components/organisms/SubgraphSchemaDocExplorer.tsx @@ -0,0 +1,9 @@ +import subgraphSchemaSdl from "@data/subgraph-schema.graphql?raw"; +import { buildSchema } from "graphql"; +import GraphQLSchemaDocExplorer from "./GraphQLSchemaDocExplorer.tsx"; + +const subgraphSchema = buildSchema(subgraphSchemaSdl); + +export default function SubgraphSchemaDocExplorer() { + return ; +} diff --git a/docs/ensnode.io/src/content.config.ts b/docs/ensnode.io/src/content.config.ts index 1779f2a927..68370a199c 100644 --- a/docs/ensnode.io/src/content.config.ts +++ b/docs/ensnode.io/src/content.config.ts @@ -3,17 +3,6 @@ import { z } from "astro/zod"; import { docsLoader } from "@astrojs/starlight/loaders"; import { docsSchema } from "@astrojs/starlight/schema"; -import { exampleQuerySchema, savedQueries } from "./data/ens-v1-examples-queries"; - -const examples = defineCollection({ - loader: () => - savedQueries.map((query) => ({ - ...query, - id: query.id, - })), - schema: exampleQuerySchema, -}); - export const collections = { docs: defineCollection({ loader: docsLoader(), @@ -24,5 +13,4 @@ export const collections = { }), }), }), - examples, }; diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/ensv2-readiness.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/ensv2-readiness.mdx index c48c180a75..04bfb9c346 100644 --- a/docs/ensnode.io/src/content/docs/docs/integrate/ensv2-readiness.mdx +++ b/docs/ensnode.io/src/content/docs/docs/integrate/ensv2-readiness.mdx @@ -16,7 +16,7 @@ import { LinkCard, Aside } from "@astrojs/starlight/components"; Full access to ENS data formerly required two separate data fetching strategies working in parallel: 1. **ENS resolution** — RPC calls with CCIP-read support for offchain data (e.g. via a popular package such as `viem` or `wagmi`) to perform forward or reverse ENS resolution. -2. **Indexed ENS data** — the [ENS Subgraph](/docs/reference/subgraph-legacy/what-is-the-ens-subgraph) for indexed ENS data, such as discovering names owned by an address and all other ENS state outside of ENS resolutions. +2. **Indexed ENS data** — the [ENS Subgraph](/docs/integrate/subgraph) for indexed ENS data, such as discovering names owned by an address and all other ENS state outside of ENS resolutions. Neither system alone was complete. - ENS resolution gives you resolver records, but no access to all the other state about ENS! ENS Resolutions as exposed through popular libraries such as `viem` and `wagmi` are also painfully "close to the metal", putting the burden on app developers to learn and carefully implement complex details about interpreting raw values returned by the ENS protocol. diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/index.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/index.mdx index fe587e22d0..31c824b196 100644 --- a/docs/ensnode.io/src/content/docs/docs/integrate/index.mdx +++ b/docs/ensnode.io/src/content/docs/docs/integrate/index.mdx @@ -41,7 +41,51 @@ ENSNode supports a full range of different integration options across the stack, Here's a summary of some popular integration strategies: -### 1. `enskit` + 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. + + + + +```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" }) { + id + canonical { name { interpreted } } + owner { address } + } + } +`); + +// `result` is fully typed! +const result = await client.omnigraph.query({ query: HelloWorldQuery }); +``` + + + + + + + + +### 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. @@ -137,50 +181,6 @@ export function RenderDomainAndSubdomains({ name }: { name: InterpretedName }) { href="/docs/integrate/integration-options/enskit/example" /> -### 2. `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. - - - - -```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" }) { - id - canonical { name { interpreted } } - owner { address } - } - } -`); - -// `result` is fully typed! -const result = await client.omnigraph.query({ query: HelloWorldQuery }); -``` - - - - - - - - ### 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. @@ -200,7 +200,7 @@ The ENS Omnigraph API is a GraphQL API following the Relay specification, so you ### 4. Further Integration Options -Beyond `enskit`, `enssdk`, and the Omnigraph GraphQL API, ENSNode exposes a deeper set of integration surfaces for advanced use cases: +Beyond `enssdk`, `enskit`, and the Omnigraph GraphQL API, ENSNode exposes a deeper set of integration surfaces for advanced use cases: - **ENSDb** — query the indexed ENS dataset directly over Postgres for custom analytics or your own service layer. - **enscli** — (coming soon) script ENSNode operations from the command line. diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/integration-options/index.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/integration-options/index.mdx index 5b5f7764b5..81529d2b8d 100644 --- a/docs/ensnode.io/src/content/docs/docs/integrate/integration-options/index.mdx +++ b/docs/ensnode.io/src/content/docs/docs/integrate/integration-options/index.mdx @@ -9,22 +9,22 @@ ENSNode takes the guesswork out of ENS integrations, whether you need to resolve There are a few different ways to integrate with ENSNode, depending on your app, runtime, and needs. -## 1. `enskit` +## 1. `enssdk` -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. +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. -## 2. `enssdk` +## 2. `enskit` -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. +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. ## 3. Omnigraph GraphQL API diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/index.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/index.mdx new file mode 100644 index 0000000000..ec65b658f0 --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/index.mdx @@ -0,0 +1,23 @@ +--- +title: ENS Subgraph Examples +description: Examples of integrating ENSNode's Subgraph-compatible API with popular ENS libraries. +sidebar: + order: 1 +--- + +import { LinkCard, CardGrid } from "@astrojs/starlight/components"; + +Examples of integrating ENSNode's Subgraph-compatible GraphQL API with popular ENS libraries. + + + + + diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/with-ensjs.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/with-ensjs.mdx similarity index 100% rename from docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/with-ensjs.mdx rename to docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/with-ensjs.mdx diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/with-viem.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/with-viem.mdx similarity index 100% rename from docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/with-viem.mdx rename to docs/ensnode.io/src/content/docs/docs/integrate/subgraph/examples/with-viem.mdx diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/what-is-the-ens-subgraph.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/index.mdx similarity index 73% rename from docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/what-is-the-ens-subgraph.mdx rename to docs/ensnode.io/src/content/docs/docs/integrate/subgraph/index.mdx index 87ca63a655..844daf2da6 100644 --- a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/what-is-the-ens-subgraph.mdx +++ b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/index.mdx @@ -1,17 +1,17 @@ --- -title: What is the ENS Subgraph? +title: Overview of Legacy Subgraph API sidebar: order: 1 --- import { LinkCard } from "@astrojs/starlight/components"; -:::note -The following serves as background information regarding the ENS Subgraph and describes the ecosystem & context in which ENSNode was developed. It is not necessary for running ENSNode. -::: +:::danger[The ENS Subgraph is legacy — do not build new ENSv2 apps on it] +The ENS Subgraph predates ENSv2 and **cannot** carry your app into it. If you're integrating ENS today, start with the [ENS Omnigraph API](/docs/integrate/omnigraph) or [ENS Unigraph SQL](/docs/integrate/unigraph) instead. Three reasons the Subgraph falls short: -:::caution[Subgraph API Deprecation] -As soon as ENSv2 launches in Summer 2026, the ENS Subgraph can no longer be relied on; ENSNode will still maintain support for the Subgraph-compatible API as described below, but integrators should prefer the ENSv2-ready Omnigraph API. +- **ENSv1 only — instantly out of date.** The Subgraph 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. +- **Single-chain only — misses most names.** The Subgraph indexes a single chain, so it never sees Basenames (`.base.eth`), Lineanames (`.linea.eth`), or 3DNS names (`.box`). The Unigraph indexes all of them into one unified model. +- **No resolution — and slow when you bolt it on.** The Subgraph doesn't resolve names; you're left making your own RPC calls, which are slow. ENS Protocol Acceleration resolves names against the indexed Unigraph up to **100× faster**, with no RPC round-trips in your app. ::: +:::note +The rest of 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. +::: + ## 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. @@ -63,18 +67,24 @@ When `SUBGRAPH_COMPAT=false` (default), ENSIndexer operates in enhanced mode wit ## Compatibility Tooling -ENSNode has developed tooling to verify subgraph compatibility and ease migration from the ENS Subgraph. +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. ## Subgraph-Compatible GraphQL API Reference ### Well-Known Queries -These are some of the popular queries we've seen in the wild (namely via ENSjs and ens-app-v3)—the Subgraph-compatible GraphQL API includes full compatibility with these use-cases (and all other possible queries with the only exception of unplanned features listed below). +These are some of the popular queries we've seen in the wild (namely via ENSjs and the ENSv1 Manager App)—the Subgraph-compatible GraphQL API includes full compatibility with these use-cases (and all other possible queries with the only exception of unplanned features listed below). :::note[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). @@ -106,7 +116,7 @@ If you'd like to highlight additional query patterns of the ENS Subgraph GraphQL - Supports ordering by expiry date, name, labelName, createdAt - Supports pagination by excluding previous results -#### from ens-app-v3 +#### from ENSv1 Manager App - [`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 @@ -118,5 +128,4 @@ The following features of the subgraph graphql api are explicitly unsupported an - [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) -- [subgraph `_Meta_` object](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#subgraph-metadata-example) - [fulltext search queries](https://thegraph.com/docs/en/subgraphs/querying/graphql-api#full-text-search-example) diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/querying-best-practices.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/querying-best-practices.mdx similarity index 98% rename from docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/querying-best-practices.mdx rename to docs/ensnode.io/src/content/docs/docs/integrate/subgraph/querying-best-practices.mdx index 9011c5fea1..5d81417650 100644 --- a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/querying-best-practices.mdx +++ b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/querying-best-practices.mdx @@ -139,7 +139,7 @@ ENSNode's handling of unnormalized labels is controlled by the `SUBGRAPH_COMPAT` - `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 -For more details, see [Subgraph Compatibility](/docs/reference/subgraph-legacy/what-is-the-ens-subgraph). +For more details, see [Subgraph Compatibility](/docs/integrate/subgraph#self-hosted-ensnode-instance-configuration-for-ens-subgraph-compatibility). ::: ### When `SUBGRAPH_COMPAT=true` diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/schema-reference.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/schema-reference.mdx new file mode 100644 index 0000000000..e9acb3b5cd --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/schema-reference.mdx @@ -0,0 +1,16 @@ +--- +title: Schema Reference +description: Full reference of the ENS Subgraph-compatible GraphQL schema. +tableOfContents: false +--- + +import SubgraphSchemaDocExplorer from "@components/organisms/SubgraphSchemaDocExplorer.tsx"; + +Browse the ENS Subgraph-compatible GraphQL schema — types, fields, and arguments. ENSNode serves this schema at the `/subgraph` endpoint for backwards compatibility with the [legacy ENS Subgraph](/docs/integrate/subgraph). + +* For example queries, see [ENS Subgraph examples](/docs/integrate/subgraph/examples) + +* For runnable queries in a web playground, see the [ENSAdmin playground](https://admin.ensnode.io/api/subgraph?connection=https%3A%2F%2Fapi.mainnet.ensnode.io%2F). + + + diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-dependents.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/subgraph/subgraph-dependents.mdx similarity index 100% rename from docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-dependents.mdx rename to docs/ensnode.io/src/content/docs/docs/integrate/subgraph/subgraph-dependents.mdx diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx new file mode 100644 index 0000000000..ee0cc5f7e4 --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx @@ -0,0 +1,31 @@ +--- +title: Account Domains +description: Count an address's Domains, grouped by protocol version (ENSv1 vs ENSv2). +sidebar: + label: Account Domains +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +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 +SELECT type, count(*) FROM ensindexer_0.domains +WHERE owner_id = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' +GROUP BY type; +``` + + +```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); +``` + + diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx new file mode 100644 index 0000000000..7df486e191 --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx @@ -0,0 +1,29 @@ +--- +title: Domain by Name +description: Fetch a Domain from the Unigraph by its canonical name. +sidebar: + label: Domain by Name +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +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. + + + +```sql +SELECT * FROM ensindexer_0.domains +WHERE canonical_name = 'vitalik.eth'; +``` + + +```typescript +import { eq } from 'drizzle-orm'; + +const [vitalik] = await ensDb + .select() + .from(ensIndexerSchema.domain) + .where(eq(ensIndexerSchema.domain.canonicalName, "vitalik.eth")); +``` + + diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/index.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/index.mdx new file mode 100644 index 0000000000..3669be87dd --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/index.mdx @@ -0,0 +1,54 @@ +--- +title: Unigraph SQL Examples +description: Connect to an ENSDb instance and query the Unigraph in raw SQL or with the ensdb-sdk. +sidebar: + label: Overview +--- + +import { LinkCard, Tabs, TabItem } from '@astrojs/starlight/components'; + +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. + +## 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. + + + +```bash +psql postgresql://user:password@host:5432/ensdb_devnet +``` + +Discover the available ENSIndexer Schemas: + +```sql +SELECT DISTINCT ens_indexer_schema_name +FROM ensnode.metadata; +``` + + +```bash +npm install @ensnode/ensdb-sdk +``` + +```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; +``` + + + +:::note[Canonical fields] +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`. +::: + +## Examples + + + + diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx new file mode 100644 index 0000000000..a1e3bcfa4d --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx @@ -0,0 +1,32 @@ +--- +title: Indexing Status +description: Read the indexing status snapshot for an ENSIndexer instance from ENSDb. +sidebar: + label: Indexing Status +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +Read the indexing status snapshot for an ENSIndexer instance from the shared `ensnode.metadata` table. See [Connect](/docs/integrate/unigraph/examples) for setup. + + + +```sql +-- Indexing status snapshot for the `ensindexer_0` ENSIndexer Schema +SELECT value -> 'indexingStatus' FROM "ensnode"."metadata" +WHERE ens_indexer_schema_name = 'ensindexer_0' +AND key = 'indexing_metadata_context'; +``` + + +```typescript +import { IndexingMetadataContextStatusCodes } from '@ensnode/ensdb-sdk'; + +const indexingMetadataContext = await ensDbReader.getIndexingMetadataContext(); +if (indexingMetadataContext.statusCode === IndexingMetadataContextStatusCodes.Initialized) { + const indexingStatusSnapshot = indexingMetadataContext.indexingStatus; + // Do something with the indexing status snapshot +} +``` + + diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/index.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/index.mdx new file mode 100644 index 0000000000..eb98bd8581 --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/index.mdx @@ -0,0 +1,60 @@ +--- +title: ENS Unigraph SQL +description: Query the unified onchain state of ENSv1 and ENSv2 directly over SQL. +sidebar: + label: Overview +--- + +import { LinkCard, CardGrid, Aside } from '@astrojs/starlight/components'; + +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. + + + +## 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 + +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. + + + +## Get started + + + + + + + + diff --git a/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx new file mode 100644 index 0000000000..405f4f74dc --- /dev/null +++ b/docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx @@ -0,0 +1,374 @@ +--- +title: Unigraph Schema Reference +description: The unified ENSv1 + ENSv2 data model of the ENS Unigraph — every table, column, and index. +sidebar: + label: Schema Reference +--- + +import { Aside } from '@astrojs/starlight/components'; + +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. + + + +Defined in [`unigraph.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts). + +:::note[Design principles] +While the initial approach was a highly materialized view of the ENS protocol, abstracting away +as many on-chain details as possible, in practice—due to the sheer complexity of the protocol at +resolution-time—full materialization of resolution behavior is impractical. The Canonical Nametree, +however, is materialized inline via synchronous handler-side cascades; see the `canonical*` fields +on `domains` and `canonicality-db-helpers.ts`. + +As a result, this schema takes a balanced approach. It mimics on-chain state as closely as possible, +with the obvious 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. + +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`. + +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. +Canonical-tree fields on `domains` (`canonical_name`, `canonical_label_hash_path`, `canonical_path`, +`canonical_depth`, `canonical_node`) are materialized inline by the handlers in +`canonicality-db-helpers.ts`. Label heals propagate to `canonical_name` via a GIN-indexed bulk +UPDATE outside Ponder's cache; cascade round-trips are bounded to events that already pay a +flush (canonicality flip, heal of an unknown label). + +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: the `registries.canonical` / `domains.canonical` membership flags plus the +`domains.canonical*` name/path/depth fields on the rows themselves. The bidirectional canonical edge is NOT +materialized in a parallel table; it is derived on demand by checking that the two unidirectional +pointers agree (`registries.canonical_domain_id = domains.id` ↔ `domains.subregistry_id = registries.id`). +Cascading canonicality flips through the subgraph run as either an in-memory PK update (when +`registries.has_children = false`, the dominant case for fresh ENSv1 virtual registries on first +wire-up) or a single recursive-CTE batch UPDATE otherwise (see `canonicality-db-helpers.ts`). + +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 + +**`RegistryType`** + +| Value | +| ---------------------- | +| `ENSv1Registry` | +| `ENSv1VirtualRegistry` | +| `ENSv2Registry` | + +**`DomainType`** + +| Value | +| ------------- | +| `ENSv1Domain` | +| `ENSv2Domain` | + +**`RegistrationType`** + +| Value | +| --------------------------- | +| `NameWrapper` | +| `BaseRegistrar` | +| `ThreeDNS` | +| `ENSv2RegistryRegistration` | +| `ENSv2RegistryReservation` | + +## `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` + +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` + +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` + +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` + +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` + +| 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` + +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` + +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` + +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` + +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` + +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` + +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` + +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` + +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` + +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` + +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)`. diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-api.mdx b/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-api.mdx deleted file mode 100644 index 78932235d2..0000000000 --- a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-api.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Subgraph API -sidebar: - order: 2 ---- - -import { LinkCard } from "@astrojs/starlight/components"; - -## Subgraph API -ENSNode exposes a Subgraph-Compatible GraphQL endpoint at `/subgraph`. - -:::caution[Subgraph API Deprecation] -With the launch of ENSv2 in Summer 2026, the Subgraph-compatible API will no longer be representative of the state of ENS; ENSNode still maintains support for the Subgraph-compatible API as described below for ENSv1 data, but integrators should prefer the ENSv2-ready Omnigraph API. - - -::: - -:::note[Subgraph API Compatibility] -ENSNode provides verified backwards compatibility with the ENS Subgraph. See configuration details below. - - -::: - -### GraphiQL Playground - -To explore the schema and run queries, use the GraphiQL Playground below. - - - -### GraphQL Examples - -Fetch data about the three most recently-created domains. - -```gql -{ - domains(orderBy: createdAt, orderDirection: desc, first: 3) { - name - expiryDate - } -} -``` - -```json -{ - "data": { - "domains": [ - { - "name": "ensanguo.eth", - "expiryDate": "1758170255" - }, - { - "name": "fiffer.eth", - "expiryDate": "2041994243" - }, - { - "name": "rifaisicilia.eth", - "expiryDate": "1758170039" - } - ] - } -} -``` - diff --git a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-compatibility-tooling.mdx b/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-compatibility-tooling.mdx deleted file mode 100644 index e596c61a63..0000000000 --- a/docs/ensnode.io/src/content/docs/docs/reference/subgraph-legacy/subgraph-compatibility-tooling.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Subgraph Compatibility Tooling -sidebar: - order: 3 ---- - -import { LinkCard } from '@astrojs/starlight/components'; - -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-equivalant 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. - - - - diff --git a/docs/ensnode.io/src/content/docs/docs/services/ensdb/concepts/database-schemas.mdx b/docs/ensnode.io/src/content/docs/docs/services/ensdb/concepts/database-schemas.mdx index f4a266ee4e..69adf1be42 100644 --- a/docs/ensnode.io/src/content/docs/docs/services/ensdb/concepts/database-schemas.mdx +++ b/docs/ensnode.io/src/content/docs/docs/services/ensdb/concepts/database-schemas.mdx @@ -50,364 +50,14 @@ The ENSIndexer Schema is modular, composed of multiple logical database sub-sche --- -### ENSv2 - -Defined in [`ensv2.schema.ts`](https://github.com/namehash/ensnode/blob/main/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts). - -:::note[Design principles] -While the initial approach was a highly materialized view of the ENS protocol, abstracting away -as many on-chain details as possible, in practice—due to the sheer complexity of the protocol at -resolution-time—it becomes more or less impossible to appropriately materialize the canonical -`namegraph`. - -As a result, this schema takes a balanced approach. It mimics on-chain state as closely as possible, -with the obvious exception of materializing specific state that must 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. - -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`. - -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. -Canonical-tree fields on `domains` (`canonical_name`, `canonical_label_hash_path`, `canonical_path`, -`canonical_depth`, `canonical_node`) are materialized inline by the handlers in -`canonicality-db-helpers.ts`. Label heals propagate to `canonical_name` via a GIN-indexed bulk -UPDATE outside Ponder's cache; cascade round-trips are bounded to events that already pay a -flush (canonicality flip, heal of an unknown label). - -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 subgraph, which is reflected via `registries.canonical` / -`domains.canonical` boolean flags on the rows themselves. The bidirectional canonical edge is NOT -materialized in a parallel table; it is derived on demand by checking that the two unidirectional -pointers agree (`registries.canonical_domain_id = domains.id` ↔ `domains.subregistry_id = registries.id`). -Cascading canonicality flips through the subgraph run as either an in-memory PK update (when -`registries.has_children = false`, the dominant case for fresh ENSv1 virtual registries on first -wire-up) or a single recursive-CTE batch UPDATE otherwise (see `canonicality-db-helpers.ts`). - -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 `ensv2.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 - -**`RegistryType`** - -| Value | -| ---------------------- | -| `ENSv1Registry` | -| `ENSv1VirtualRegistry` | -| `ENSv2Registry` | - -**`DomainType`** - -| Value | -| ------------- | -| `ENSv1Domain` | -| `ENSv2Domain` | - -**`RegistrationType`** - -| Value | -| --------------------------- | -| `NameWrapper` | -| `BaseRegistrar` | -| `ThreeDNS` | -| `ENSv2RegistryRegistration` | -| `ENSv2RegistryReservation` | - -#### `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` - -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` - -Join table linking a `resolvers` record to its associated `events`. - -| Column | Type | Nullable | -| ------------- | ------ | -------- | -| `resolver_id` | `text` | no | -| `event_id` | `text` | no | +### Unigraph -**Primary key:** `(resolver_id, event_id)`. +The Unigraph schema — the unified, polymorphic ENSv1 + ENSv2 data model — is documented in full in its canonical reference in the Integrate section: -#### `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` - -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` - -| 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` - -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` - -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. +:::tip[Unigraph Schema Reference] +The complete table-by-table reference for the Unigraph schema now lives at **[Unigraph Schema Reference](/docs/integrate/unigraph/schema-reference)**. ::: -| 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. `["eth"`'s `DomainId`, `"vitalik"`'s `DomainId]` 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` - -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` - -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` - -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` - -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` - -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` - -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` - -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` - -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)`. - --- ### Protocol Acceleration diff --git a/docs/ensnode.io/src/data/ens-v1-examples-queries.ts b/docs/ensnode.io/src/data/ens-v1-examples-queries.ts deleted file mode 100644 index c1cce0e160..0000000000 --- a/docs/ensnode.io/src/data/ens-v1-examples-queries.ts +++ /dev/null @@ -1,1338 +0,0 @@ -import { z } from "astro/zod"; - -export const exampleQueryCategorySchema = z.enum([ - "Domain", - "Registrar", - "Label", - "Resolver", - "Account", - "Meta", -]); - -export type ExampleQueryCategory = z.infer; - -export const ExampleQueryCategory = exampleQueryCategorySchema.enum; - -export const exampleQuerySchema = z.object({ - operationName: z.string(), - id: z.string(), - name: z.string(), - category: exampleQueryCategorySchema, - description: z.string(), - query: z.string(), - variables: z.string(), -}); - -export type SavedQuery = z.infer; - -export const savedQueries: SavedQuery[] = [ - { - operationName: "GetLatestDomains", - id: "1", - name: "Get Latest Domains", - category: ExampleQueryCategory.Domain, - description: - "Retrieves the most recently created domains in descending order by creation time.", - query: /* GraphQL */ ` - query GetLatestDomains($first: Int!) { - # This is an example inline comment about this query - domains(orderBy: createdAt, orderDirection: desc, first: $first) { - # this comment is about the name below - name - expiryDate # this is an inline comment - } - } - `, - variables: JSON.stringify( - { - first: 5, - }, - null, - 2, - ), - }, - { - operationName: "LatestRegistrations", - id: "21", - name: "Get Latest Registrations", - category: ExampleQueryCategory.Registrar, - description: - "Retrieves the most recent domain registrations ordered by registration date in descending order. Shows registration details including expiry dates and ownership information for newly registered domains.", - query: /* GraphQL */ ` - query LatestRegistrations($first: Int!) { - registrations( - first: $first - orderBy: registrationDate - orderDirection: desc - ) { - registrationDate - expiryDate - domain { - id - name - labelName - createdAt - expiryDate - owner { - id - } - wrappedOwner { - id - } - } - } - } - `, - variables: JSON.stringify( - { - first: 5, - }, - null, - 2, - ), - }, - { - operationName: "GetDomainsWithPagination", - id: "1a", - name: "Get Domains with Pagination", - category: ExampleQueryCategory.Domain, - description: - "Fetches domains with pagination support, ordered by creation time in ascending order. Use this when you need to iterate through all domains systematically or implement pagination in your application.", - query: /* GraphQL */ ` - query GetDomainsWithPagination($first: Int!, $skip: Int) { - domains( - orderBy: createdAt - orderDirection: asc - first: $first - skip: $skip - ) { - id - name - expiryDate - createdAt - } - } - `, - variables: JSON.stringify( - { - first: 10, - skip: 20, - }, - null, - 2, - ), - }, - { - operationName: "allDomainsByCreationTime", - id: "22", - name: "Get All Domains with Pagination by Creation Time", - category: ExampleQueryCategory.Domain, - description: - "Retrieves domains in batches for pagination, ordered by creation time in ascending order. Excludes reverse records and null names. Use the lastCreatedAt parameter to paginate through all domains by passing the createdAt timestamp of the last domain from the previous batch.", - query: /* GraphQL */ ` - query allDomainsByCreationTime($lastCreatedAt: BigInt, $first: Int!) { - domains( - first: $first - where: { - createdAt_gt: $lastCreatedAt - name_not_ends_with: ".addr.reverse" - name_not: null - } - orderBy: createdAt - orderDirection: asc - ) { - createdAt - name - } - } - `, - variables: JSON.stringify( - { - lastCreatedAt: "1489206542", - first: 10, - }, - null, - 2, - ), - }, - { - operationName: "GetDomainByNamehash", - id: "2", - name: "Get Domain by Namehash", - category: ExampleQueryCategory.Domain, - description: - "Retrieves a specific domain using its namehash (the unique identifier for ENS names). The namehash is the cryptographic hash of the domain name.", - query: /* GraphQL */ ` - query GetDomainByNamehash($id: String!) { - domain(id: $id) { - name - labelName - labelhash - createdAt - expiryDate - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetDomainByName", - id: "2a", - name: "Get Domain by Name", - category: ExampleQueryCategory.Domain, - description: - "Looks up a domain by its human-readable name (e.g., 'ens.eth'). This is user-friendly for ad-hoc queries, but not recommended for programmatic access because names are not stable identifiers. Use the 'Get Domain by Namehash' query for reliable lookups.", - query: /* GraphQL */ ` - query GetDomainByName($name: String!) { - domains(where: { name: $name }) { - id - name - labelName - labelhash - createdAt - expiryDate - } - } - `, - variables: JSON.stringify( - { - name: "ens.eth", - }, - null, - 2, - ), - }, - { - operationName: "GetDomainsByChildmostLabel", - id: "2c", - name: "Get Domains by the childmost-label substring", - category: ExampleQueryCategory.Domain, - description: - "Searches for domains containing a specific substring in their label (the leftmost part of the domain name). For example, searching for 'ens' would find 'ens.eth', 'myens.eth', etc. Useful for finding related domains or performing fuzzy searches.", - query: /* GraphQL */ ` - query GetDomainsByChildmostLabel($label: String!) { - domains(where: { labelName_contains: $label }) { - id - name - labelName - labelhash - createdAt - expiryDate - } - } - `, - variables: JSON.stringify( - { - label: "ens", - }, - null, - 2, - ), - }, - { - operationName: "getLabelByLabelhash", - id: "3", - name: "Get Label by Labelhash", - category: ExampleQueryCategory.Label, - description: - "Reverse lookup to find the human-readable label from its labelhash. This is useful when you have a labelhash and need to determine what the actual text label is.", - query: /* GraphQL */ ` - query getLabelByLabelhash($labelhash: String!) { - domains( - first: 1 - where: { labelhash: $labelhash, labelName_not: null } - ) { - labelName - } - } - `, - variables: JSON.stringify( - { - labelhash: "0x5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da", // labelhash("ens") - }, - null, - 2, - ), - }, - { - operationName: "GetDomainHistory", - id: "4", - name: "Get Complete Domain History", - category: ExampleQueryCategory.Domain, - description: - "Retrieves the complete historical timeline of events for a domain, including ownership transfers, resolver changes, registrations, renewals, and all resolver record updates. This provides an audit trail of activities related to the domain.", - query: /* GraphQL */ ` - query GetDomainHistory($id: String!) { - domain(id: $id) { - name - events { - id - blockNumber - transactionID - type: __typename - ... on Transfer { - owner { - id - } - } - ... on NewOwner { - owner { - id - } - } - ... on NewResolver { - resolver { - id - } - } - ... on NewTTL { - ttl - } - ... on WrappedTransfer { - owner { - id - } - } - ... on NameWrapped { - fuses - expiryDate - owner { - id - } - } - ... on NameUnwrapped { - owner { - id - } - } - ... on FusesSet { - fuses - } - ... on ExpiryExtended { - expiryDate - } - } - registration { - events { - id - blockNumber - transactionID - type: __typename - ... on NameRegistered { - registrant { - id - } - expiryDate - } - ... on NameRenewed { - expiryDate - } - ... on NameTransferred { - newOwner { - id - } - } - } - } - resolver { - events { - id - blockNumber - transactionID - type: __typename - ... on AddrChanged { - addr { - id - } - } - ... on MulticoinAddrChanged { - coinType - multiaddr: addr - } - ... on NameChanged { - name - } - ... on TextChanged { - key - value - } - ... on ContenthashChanged { - hash - } - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetDomainEvents", - id: "5", - name: "Get Domain Events Only", - category: ExampleQueryCategory.Domain, - description: - "Retrieves only the domain-level events (transfers, ownership changes, wrapping events) without registration or resolver events. Use this when you're specifically interested in domain ownership and management events.", - //events(orderBy: blockNumber, orderDirection: desc) is not working - query: /* GraphQL */ ` - query GetDomainEvents($id: String!) { - domain(id: $id) { - name - events { - id - blockNumber - transactionID - type: __typename - ... on Transfer { - owner { - id - } - } - ... on NewOwner { - owner { - id - } - } - ... on NewResolver { - resolver { - id - } - } - ... on NameWrapped { - fuses - expiryDate - owner { - id - } - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetResolverEvents", - id: "6", - name: "Get Resolver Events Only", - category: ExampleQueryCategory.Resolver, - description: - "Retrieves only the resolver-related events (address changes, text record updates, contenthash changes) for a domain. Useful when you're tracking how a domain's records have been updated over time.", - //events(orderBy: blockNumber, orderDirection: desc) is not working - query: /* GraphQL */ ` - query GetResolverEvents($id: String!) { - domain(id: $id) { - name - resolver { - events { - id - blockNumber - transactionID - type: __typename - ... on AddrChanged { - addr { - id - } - } - ... on MulticoinAddrChanged { - coinType - multiaddr: addr - } - ... on TextChanged { - key - value - } - ... on ContenthashChanged { - hash - } - ... on NameChanged { - name - } - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetDomainsForAddress", - id: "7", - name: "Get Domains for Address (owner, registrant, wrappedOwner, or resolvedAddress)", - category: ExampleQueryCategory.Account, - description: - "Finds all domains associated with an Ethereum address in any capacity - as owner, registrant, wrapped owner, or as the resolved address. Excludes reverse records and expired domains. This is a comprehensive way to find domains connected to an address.", - query: /* GraphQL */ ` - query GetDomainsForAddress( - $owner: String! - $first: Int! - $orderBy: Domain_orderBy! - $orderDirection: OrderDirection! - $date: BigInt! - ) { - domains( - where: { - or: [ - { owner: $owner } - { registrant: $owner } - { wrappedOwner: $owner } - { resolvedAddress: $owner } - ] - and: [ - # Exclude domains with parent addr.reverse - # namehash("addr.reverse") = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2 - { - parent_not: "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2" - } - { or: [{ expiryDate_gt: $date }, { expiryDate: null }] } - ] - } - orderBy: $orderBy - orderDirection: $orderDirection - first: $first - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - registrant { - id - } - wrappedOwner { - id - } - resolvedAddress { - id - } - registration { - registrationDate - expiryDate - } - } - } - `, - variables: JSON.stringify( - { - owner: "0xfe89cc7abb2c4183683ab71653c4cdc9b02d44b7", // ENS: DAO Wallet - first: 10, - orderBy: "name", - orderDirection: "asc", - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "getOwnedInRegistryDomains", - id: "8", - name: "Get Owned In Registry Domains Only", - category: ExampleQueryCategory.Account, - description: - "Retrieves domains where the specified address is the owner in the ENS registry (not registrant or wrapped owner). This shows domains where the address has direct control over the ENS records but may not be the original registrant.", - query: /* GraphQL */ ` - query getOwnedInRegistryDomains( - $owner: String! - $first: Int! - $date: BigInt! - ) { - domains( - where: { - owner: $owner - # Exclude domains with parent addr.reverse - # namehash("addr.reverse") = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2 - parent_not: "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2" - or: [{ expiryDate_gt: $date }, { expiryDate: null }] - } - orderBy: name - orderDirection: asc - first: $first - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - resolver { - id - } - } - } - `, - variables: JSON.stringify( - { - owner: "0xfe89cc7abb2c4183683ab71653c4cdc9b02d44b7", // ENS: DAO Wallet - first: 10, - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "GetRegisteredDomains", - id: "9", - name: "Get Registered Domains Only", - category: ExampleQueryCategory.Account, - description: - "Retrieves domains where the specified address is the original registrant (the one who initially registered the .eth domain). This shows domains the address actually purchased and registered, not just ones they received or control.", - query: /* GraphQL */ ` - query GetRegisteredDomains( - $registrant: String! - $first: Int! - $date: BigInt! - ) { - domains( - where: { - registrant: $registrant - # Exclude domains with parent addr.reverse - # namehash("addr.reverse") = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2 - parent_not: "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2" - or: [{ expiryDate_gt: $date }, { expiryDate: null }] - } - orderBy: expiryDate - orderDirection: desc - first: $first - ) { - id - name - labelName - createdAt - expiryDate - registrant { - id - } - registration { - registrationDate - expiryDate - cost - } - } - } - `, - variables: JSON.stringify( - { - registrant: "0xfe89cc7abb2c4183683ab71653c4cdc9b02d44b7", // ENS: DAO Wallet - first: 10, - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "GetNamesIncludingExpired", - id: "10", - name: "Get Names Including Expired", - category: ExampleQueryCategory.Account, - description: - "Retrieves all domains associated with an address (as owner, registrant, or wrapped owner) including those that have expired. Useful for historical analysis or when you need to see the domain portfolio of an address.", - query: /* GraphQL */ ` - query GetNamesIncludingExpired($owner: String!, $first: Int!) { - domains( - where: { - or: [ - { owner: $owner } - { registrant: $owner } - { wrappedOwner: $owner } - ] - # Exclude domains with parent addr.reverse - # namehash("addr.reverse") = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2 - parent_not: "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2" - } - orderBy: expiryDate - orderDirection: desc - first: $first - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - registrant { - id - } - wrappedOwner { - id - } - registration { - expiryDate - } - } - } - `, - variables: JSON.stringify( - { - owner: "0xfe89cc7abb2c4183683ab71653c4cdc9b02d44b7", // ENS: DAO Wallet - first: 10, - }, - null, - 2, - ), - }, - { - operationName: "GetSubgraphRegistrant", - id: "11", - name: "Get Registrant by Labelhash", // works with ENS only? - category: ExampleQueryCategory.Registrar, - description: - "Looks up registration information using a labelhash. This is primarily used for .eth domains and provides details about who registered the domain, when it was registered, when it expires, and what it cost. Note: This mainly works with .eth domains.", - query: /* GraphQL */ ` - query GetSubgraphRegistrant($id: String!) { - registration(id: $id) { - registrant { - id - } - registrationDate - expiryDate - cost - domain { - name - labelName - } - } - } - `, - variables: JSON.stringify( - { - id: "0x5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da", // labelhash("ens") - }, - null, - 2, - ), - }, - { - operationName: "GetSubnames", - id: "12", - name: "Get Subdomains", - category: ExampleQueryCategory.Domain, - description: - "Retrieves all subdomains under a given parent domain, filtering out expired domains and empty records. This is useful for exploring the subdomain hierarchy and finding active subdomains under a particular domain.", - query: /* GraphQL */ ` - query GetSubnames( - $id: String! - $first: Int! - $orderBy: Domain_orderBy! - $orderDirection: OrderDirection! - $date: BigInt! - ) { - domain(id: $id) { - name - subdomains( - orderBy: $orderBy - orderDirection: $orderDirection - first: $first - where: { - and: [ - { or: [{ expiryDate_gt: $date }, { expiryDate: null }] } - { - or: [ - { owner_not: "0x0000000000000000000000000000000000000000" } - { resolver_not: null } - ] - } - ] - } - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - resolver { - id - } - registration { - registrationDate - expiryDate - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - first: 10, - orderBy: "name", - orderDirection: "asc", - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "SearchSubnames", - id: "13", - name: "Search Subdomains by Label", - category: ExampleQueryCategory.Domain, - description: - "Searches for subdomains under a parent domain that contain a specific text string in their label. This enables fuzzy searching within a domain's subdomain space, useful for finding related or similarly named subdomains.", - query: /* GraphQL */ ` - query SearchSubnames( - $id: String! - $searchString: String! - $first: Int! - $date: BigInt! - ) { - domain(id: $id) { - name - subdomains( - orderBy: name - orderDirection: asc - first: $first - where: { - and: [ - { labelName_contains: $searchString } - { or: [{ expiryDate_gt: $date }, { expiryDate: null }] } - { - or: [ - { owner_not: "0x0000000000000000000000000000000000000000" } - { resolver_not: null } - ] - } - ] - } - ) { - id - name - labelName - createdAt - owner { - id - } - resolver { - id - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - searchString: "test", - first: 10, - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "GetSubnamesIncludingExpired", - id: "14", - name: "Get Subdomains Including Expired", - category: ExampleQueryCategory.Domain, - description: - "Retrieves all subdomains under a parent domain, including those that have expired. This provides a complete historical view of all subdomains that have ever existed under the parent domain.", - query: /* GraphQL */ ` - query GetSubnamesIncludingExpired($id: String!, $first: Int!) { - domain(id: $id) { - name - subdomains( - orderBy: expiryDate - orderDirection: desc - first: $first - where: { - or: [ - { owner_not: "0x0000000000000000000000000000000000000000" } - { resolver_not: null } - ] - } - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - resolver { - id - } - registration { - expiryDate - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - first: 10, - }, - null, - 2, - ), - }, - { - operationName: "GetLatestSubnames", - id: "15", - name: "Get Latest Subdomains", - category: ExampleQueryCategory.Registrar, - description: - "Retrieves the most recently created subdomains under a parent domain, ordered by creation time. This is useful for monitoring new subdomain activity and tracking the growth of a domain's subdomain ecosystem.", - query: /* GraphQL */ ` - query GetLatestSubnames($id: String!, $first: Int!, $date: BigInt!) { - domain(id: $id) { - name - subdomains( - orderBy: createdAt - orderDirection: desc - first: $first - where: { - and: [ - { or: [{ expiryDate_gt: $date }, { expiryDate: null }] } - { - or: [ - { owner_not: "0x0000000000000000000000000000000000000000" } - { resolver_not: null } - ] - } - ] - } - ) { - id - name - labelName - createdAt - expiryDate - owner { - id - } - resolver { - id - texts - coinTypes - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - first: 10, - date: Math.floor(Date.now() / 1000), - }, - null, - 2, - ), - }, - { - operationName: "GetSubgraphRecords", - id: "16", - name: "Get Domain Records (Inherited Resolver)", - category: ExampleQueryCategory.Resolver, - description: - "Retrieves a domain's resolver information including the types of records it supports (text records and coin types). This uses the domain's current resolver and shows what kind of records are available for the domain.", - query: /* GraphQL */ ` - query GetSubgraphRecords($id: String!) { - domain(id: $id) { - name - isMigrated - createdAt - resolver { - id - texts - coinTypes - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetSubgraphRecordsCustomResolver", - id: "17", - name: "Get Domain Records (Custom Resolver)", - category: ExampleQueryCategory.Resolver, - description: - "Retrieves domain information along with records from a specific resolver address.", - query: /* GraphQL */ ` - query GetSubgraphRecordsCustomResolver( - $id: String! - $resolverId: String! - ) { - domain(id: $id) { - name - isMigrated - createdAt - } - resolver(id: $resolverId) { - id - texts - coinTypes - domain { - name - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - resolverId: - "0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41-0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // ENS: Public Resolver 2 - }, - null, - 2, - ), - }, - { - operationName: "GetResolverDetails", - id: "18", - name: "Get Resolver Details by Address", - category: ExampleQueryCategory.Resolver, - description: - "Retrieves detailed information about a resolver by its contract address. This shows the domains using this resolver and what types of records it supports.", - query: /* GraphQL */ ` - query GetResolverDetails($resolverAddress: String!) { - resolvers(where: { address: $resolverAddress }, first: 10) { - id - texts - coinTypes - domain { - name - id - } - } - } - `, - variables: JSON.stringify( - { - resolverAddress: "0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41", // ENS: Public Resolver 2 - }, - null, - 2, - ), - }, - { - operationName: "GetDomainTextRecords", - id: "19", - name: "Get Domain Text Records", - category: ExampleQueryCategory.Resolver, - description: - "Retrieves the current text record keys for a domain and all resolver events history. The 'texts' field shows currently set text record keys, while 'events' shows all resolver events including TextChanged, ContenthashChanged, AddrChanged, and MulticoinAddrChanged.", - //events(where: { type_in: ["TextChanged"] }, first: 20) is not working - query: /* GraphQL */ ` - query GetDomainTextRecords($id: String!) { - domain(id: $id) { - name - resolver { - texts - events(first: 20) { - id - blockNumber - type: __typename - ... on TextChanged { - key - value - } - } - } - } - } - `, - variables: JSON.stringify( - { - id: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "GetHistoricalResolverRecords", - id: "20", - name: "Get Historical Resolver Records Evolution", - category: ExampleQueryCategory.Resolver, - description: - "Provides a comprehensive view of how a domain's resolver records have evolved over time. This tracks resolver changes and the history of text records, address records, and contenthash changes across all resolvers the domain has used.", - query: /* GraphQL */ ` - query GetHistoricalResolverRecords($ensName: String!) { - domains(where: { name: $ensName }) { - id - name - newResolvers { - resolverId - blockNumber - transactionID - resolver { - address - textChangeds { - key - value - blockNumber - transactionID - } - multicoinAddrChangeds { - coinType - addr - blockNumber - transactionID - } - contenthashChangeds { - hash - blockNumber - transactionID - } - } - } - } - } - `, - variables: JSON.stringify( - { - ensName: "ens.eth", - }, - null, - 2, - ), - }, - { - operationName: "getIndexerMetadata", - id: "23", - name: "Get Indexer Metadata", - category: ExampleQueryCategory.Meta, - description: - "Retrieves metadata information about the indexer including indexing status and current block number. Use this to check if the indexer has indexing errors and to monitor synchronization with the blockchain.", - query: /* GraphQL */ ` - query getIndexerMetadata { - _meta { - hasIndexingErrors - block { - number - } - } - } - `, - variables: JSON.stringify({}, null, 2), - }, - { - operationName: "getResolverExists", - id: "24", - name: "Check if Resolver Exists", - category: ExampleQueryCategory.Resolver, - description: - "Checks if a specific resolver exists by its ID. The resolver ID is constructed as 'resolverAddress-namehash' where resolverAddress is the contract address and namehash is the domain's namehash. Used to verify resolver existence before operations.", - query: /* GraphQL */ ` - query getResolverExists($id: String!) { - resolver(id: $id) { - id - } - } - `, - variables: JSON.stringify( - { - id: "0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41-0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // ENS: Public Resolver 2 + namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "getNameDates", - id: "25", - name: "Get Registration Data by Labelhash", - category: ExampleQueryCategory.Registrar, - description: - "Retrieves registration information for a domain using its labelhash (hash of the label without the TLD). Returns the registration date and the most recent registration transaction ID. Primarily used for .eth domains.", - query: /* GraphQL */ ` - query getNameDates($id: String!) { - registration(id: $id) { - registrationDate - } - nameRegistereds( - first: 1 - orderBy: blockNumber - orderDirection: desc - where: { registration: $id } - ) { - transactionID - } - } - `, - variables: JSON.stringify( - { - id: "0x5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da", // labelhash("ens") - }, - null, - 2, - ), - }, - { - operationName: "getDomainWithResolver", - id: "26", - name: "Get Domain with Resolver Address", - category: ExampleQueryCategory.Domain, - description: - "Retrieves domain information including resolver address and text records. Unlike other domain queries, this specifically includes the resolver's address field along with standard domain information.", - query: /* GraphQL */ ` - query getDomainWithResolver($tokenId: String!) { - domain(id: $tokenId) { - id - labelhash - name - createdAt - parent { - id - } - resolver { - texts - address - } - } - } - `, - variables: JSON.stringify( - { - tokenId: "0x4e34d3a81dc3a20f71bbdf2160492ddaa17ee7e5523757d47153379c13cb46df", // namehash("ens.eth") - }, - null, - 2, - ), - }, - { - operationName: "getEthDomainByLabelhash", - id: "27", - name: "Get .eth Domain by Labelhash", - category: ExampleQueryCategory.Domain, - description: - "Retrieves .eth domains by their labelhash under the .eth parent domain. This is specifically for finding .eth domains using their labelhash identifier.", - query: /* GraphQL */ ` - query getEthDomainByLabelhash($tokenId: String!) { - domains( - where: { - parent: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae" - labelhash: $tokenId - } - ) { - id - labelhash - name - createdAt - parent { - id - } - resolver { - texts - address - } - } - } - `, - variables: JSON.stringify( - { - tokenId: "0x5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da", // labelhash("ens") - }, - null, - 2, - ), - }, - { - operationName: "getRegistrationByTokenId", //similar to getSubgraphRegistrant - remove? - id: "28", - name: "Get Registration by Token ID", - category: ExampleQueryCategory.Registrar, - description: - "Retrieves registration information by token ID (labelhash), including label name, registration date, and expiry date. Ordered by registration date in descending order.", - query: /* GraphQL */ ` - query getRegistrationByTokenId($tokenId: String!) { - registrations( - orderBy: registrationDate - orderDirection: desc - where: { id: $tokenId } - ) { - labelName - registrationDate - expiryDate - } - } - `, - variables: JSON.stringify( - { - tokenId: "0x5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da", // labelhash("ens") - }, - null, - 2, - ), - }, - { - operationName: "getWrappedDomain", - id: "29", - name: "Get Wrapped Domain", - category: ExampleQueryCategory.Domain, - description: - "Retrieves wrapped domain information including owner, fuses, expiry date, and domain name. Used for ENS domains that have been wrapped using the Name Wrapper contract.", - query: /* GraphQL */ ` - query getWrappedDomain($tokenId: String!) { - wrappedDomain(id: $tokenId) { - id - owner { - id - } - fuses - expiryDate - domain { - name - } - } - } - `, - variables: JSON.stringify( - { - tokenId: "0x2c18815bc184e0d6d1d6817e9461e713acb6f3d6c1d2092babfbad56842a4085", // namehash("$$$.eth") - }, - null, - 2, - ), - }, -]; diff --git a/docs/ensnode.io/src/data/subgraph-schema.graphql b/docs/ensnode.io/src/data/subgraph-schema.graphql new file mode 100644 index 0000000000..7a19c17866 --- /dev/null +++ b/docs/ensnode.io/src/data/subgraph-schema.graphql @@ -0,0 +1,4168 @@ +""" +The `JSON` scalar type represents JSON values as specified by ECMA-404 +""" +scalar JSON + +""" +Arbitrary precision integer, useful for representing large numbers +""" +scalar BigInt + +""" +Information about pagination in a connection +""" +type PageInfo { + """ + Whether there are more records after the current page + """ + hasNextPage: Boolean! + + """ + Whether there are more records before the current page + """ + hasPreviousPage: Boolean! + + """ + Cursor pointing to the first record in the current page + """ + startCursor: String + + """ + Cursor pointing to the last record in the current page + """ + endCursor: String +} + +type Query { + abiChanged(id: String!): AbiChanged + abiChangeds( + where: AbiChanged_filter + orderBy: AbiChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AbiChanged!]! + account(id: String!): Account + accounts( + where: Account_filter + orderBy: Account_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Account!]! + addrChanged(id: String!): AddrChanged + addrChangeds( + where: AddrChanged_filter + orderBy: AddrChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AddrChanged!]! + authorisationChanged(id: String!): AuthorisationChanged + authorisationChangeds( + where: AuthorisationChanged_filter + orderBy: AuthorisationChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AuthorisationChanged!]! + contenthashChanged(id: String!): ContenthashChanged + contenthashChangeds( + where: ContenthashChanged_filter + orderBy: ContenthashChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [ContenthashChanged!]! + domain(id: String!): Domain + domains( + where: Domain_filter + orderBy: Domain_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Domain!]! + expiryExtended(id: String!): ExpiryExtended + expiryExtendeds( + where: ExpiryExtended_filter + orderBy: ExpiryExtended_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [ExpiryExtended!]! + fusesSet(id: String!): FusesSet + fusesSets( + where: FusesSet_filter + orderBy: FusesSet_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [FusesSet!]! + interfaceChanged(id: String!): InterfaceChanged + interfaceChangeds( + where: InterfaceChanged_filter + orderBy: InterfaceChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [InterfaceChanged!]! + multicoinAddrChanged(id: String!): MulticoinAddrChanged + multicoinAddrChangeds( + where: MulticoinAddrChanged_filter + orderBy: MulticoinAddrChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [MulticoinAddrChanged!]! + nameChanged(id: String!): NameChanged + nameChangeds( + where: NameChanged_filter + orderBy: NameChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameChanged!]! + nameRegistered(id: String!): NameRegistered + nameRegistereds( + where: NameRegistered_filter + orderBy: NameRegistered_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameRegistered!]! + nameRenewed(id: String!): NameRenewed + nameReneweds( + where: NameRenewed_filter + orderBy: NameRenewed_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameRenewed!]! + nameTransferred(id: String!): NameTransferred + nameTransferreds( + where: NameTransferred_filter + orderBy: NameTransferred_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameTransferred!]! + nameUnwrapped(id: String!): NameUnwrapped + nameUnwrappeds( + where: NameUnwrapped_filter + orderBy: NameUnwrapped_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameUnwrapped!]! + nameWrapped(id: String!): NameWrapped + nameWrappeds( + where: NameWrapped_filter + orderBy: NameWrapped_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameWrapped!]! + newOwner(id: String!): NewOwner + newOwners( + where: NewOwner_filter + orderBy: NewOwner_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewOwner!]! + newResolver(id: String!): NewResolver + newResolvers( + where: NewResolver_filter + orderBy: NewResolver_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewResolver!]! + newTTL(id: String!): NewTTL + newTTLs( + where: NewTTL_filter + orderBy: NewTTL_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewTTL!]! + pubkeyChanged(id: String!): PubkeyChanged + pubkeyChangeds( + where: PubkeyChanged_filter + orderBy: PubkeyChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [PubkeyChanged!]! + registration(id: String!): Registration + registrations( + where: Registration_filter + orderBy: Registration_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Registration!]! + resolver(id: String!): Resolver + resolvers( + where: Resolver_filter + orderBy: Resolver_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Resolver!]! + textChanged(id: String!): TextChanged + textChangeds( + where: TextChanged_filter + orderBy: TextChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [TextChanged!]! + transfer(id: String!): Transfer + transfers( + where: Transfer_filter + orderBy: Transfer_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Transfer!]! + versionChanged(id: String!): VersionChanged + versionChangeds( + where: VersionChanged_filter + orderBy: VersionChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [VersionChanged!]! + wrappedDomain(id: String!): WrappedDomain + wrappedDomains( + where: WrappedDomain_filter + orderBy: WrappedDomain_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [WrappedDomain!]! + wrappedTransfer(id: String!): WrappedTransfer + wrappedTransfers( + where: WrappedTransfer_filter + orderBy: WrappedTransfer_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [WrappedTransfer!]! + _meta: _Meta_ +} + +""" +Get a single an ABI changed event by address +""" +type AbiChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + The block number at which the event was emitted (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction in which the event was emitted (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The content type of the ABI change (exact match) + """ + contentType: BigInt! + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +""" +Get a single a resolver event by address +""" +interface ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + The block number that the event occurred on (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the event (exact match) + """ + transactionID: String! + resolverId: String! +} + +""" +Get a single a resolver by address +""" +type Resolver { + """ + The unique identifier for this resolver, which is a concatenation of the domain namehash and the resolver address (exact match) + """ + id: String! + + """ + The domain that this resolver is associated with (exact match) + """ + domainId: String! + + """ + The address of the resolver contract (exact match) + """ + address: String! + + """ + The current value of the 'addr' record for this resolver, as determined by the associated events (exact match) + """ + addrId: String + + """ + The content hash for this resolver, in binary format (exact match) + """ + contentHash: String + + """ + The set of observed text record keys for this resolver (exact match) + """ + texts: [String!] + + """ + The set of observed SLIP-44 coin types for this resolver (exact match) + """ + coinTypes: [BigInt!] + + """ + The current value of the 'addr' record for this resolver, as determined by the associated events (exact match) + """ + addr: Account + + """ + The domain that this resolver is associated with (exact match) + """ + domain: Domain + addrChangeds( + where: AddrChanged_filter + orderBy: AddrChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AddrChanged!]! + multicoinAddrChangeds( + where: MulticoinAddrChanged_filter + orderBy: MulticoinAddrChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [MulticoinAddrChanged!]! + nameChangeds( + where: NameChanged_filter + orderBy: NameChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameChanged!]! + abiChangeds( + where: AbiChanged_filter + orderBy: AbiChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AbiChanged!]! + pubkeyChangeds( + where: PubkeyChanged_filter + orderBy: PubkeyChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [PubkeyChanged!]! + textChangeds( + where: TextChanged_filter + orderBy: TextChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [TextChanged!]! + contenthashChangeds( + where: ContenthashChanged_filter + orderBy: ContenthashChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [ContenthashChanged!]! + interfaceChangeds( + where: InterfaceChanged_filter + orderBy: InterfaceChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [InterfaceChanged!]! + authorisationChangeds( + where: AuthorisationChanged_filter + orderBy: AuthorisationChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [AuthorisationChanged!]! + versionChangeds( + where: VersionChanged_filter + orderBy: VersionChanged_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [VersionChanged!]! + + """ + The events associated with this resolver (exact match) + """ + events( + where: ResolverEvent_filter + orderBy: ResolverEvent_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [ResolverEvent!]! +} + +""" +Get a single an account by address +""" +type Account { + """ + The unique identifier for the account (exact match) + """ + id: String! + + """ + The domains owned by the account (exact match) + """ + domains( + where: Domain_filter + orderBy: Domain_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Domain!]! + + """ + The WrappedDomains owned by the account (exact match) + """ + wrappedDomains( + where: WrappedDomain_filter + orderBy: WrappedDomain_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [WrappedDomain!]! + + """ + The Registrations made by the account (exact match) + """ + registrations( + where: Registration_filter + orderBy: Registration_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Registration!]! +} + +""" +Get a single a domain by address +""" +type Domain { + """ + The namehash of the name (exact match) + """ + id: String! + + """ + The human readable name, if known. Unknown portions replaced with hash in square brackets (eg, foo.[1234].eth) (exact match) + """ + name: String + + """ + The human readable label name (imported from CSV), if known (exact match) + """ + labelName: String + + """ + keccak256(labelName) (exact match) + """ + labelhash: String + + """ + The namehash (id) of the parent name (exact match) + """ + parentId: String + + """ + The number of subdomains (exact match) + """ + subdomainCount: Int! + + """ + Address logged from current resolver, if any (exact match) + """ + resolvedAddressId: String + + """ + The resolver that controls the domain's settings (exact match) + """ + resolverId: String + + """ + The time-to-live (TTL) value of the domain's records (exact match) + """ + ttl: BigInt + + """ + Indicates whether the domain has been migrated to a new registrar (exact match) + """ + isMigrated: Boolean! + + """ + The time when the domain was created (exact match) + """ + createdAt: BigInt! + + """ + The account that owns the domain (exact match) + """ + ownerId: String! + + """ + The account that owns the ERC721 NFT for the domain (exact match) + """ + registrantId: String + + """ + The account that owns the wrapped domain (exact match) + """ + wrappedOwnerId: String + + """ + The expiry date for the domain, from either the registration, or the wrapped domain if PCC is burned (exact match) + """ + expiryDate: BigInt + + """ + Currently resolved Address (exact match) + """ + resolvedAddress: Account + + """ + The account that owns the domain (exact match) + """ + owner: Account + + """ + The parent Domain of this Domain, if any (exact match) + """ + parent: Domain + + """ + The resolver that controls the domain's settings (exact match) + """ + resolver: Resolver + + """ + List of Domains under this Domain, if any (exact match) + """ + subdomains( + where: Domain_filter + orderBy: Domain_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Domain!]! + + """ + The account that owns the ERC721 NFT for the domain (exact match) + """ + registrant: Account + + """ + The account that owns the wrapped domain (exact match) + """ + wrappedOwner: Account + + """ + The wrapped domain associated with the domain (exact match) + """ + wrappedDomain: WrappedDomain + + """ + The registration associated with the domain (exact match) + """ + registration: Registration + transfers( + where: Transfer_filter + orderBy: Transfer_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [Transfer!]! + newOwners( + where: NewOwner_filter + orderBy: NewOwner_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewOwner!]! + newResolvers( + where: NewResolver_filter + orderBy: NewResolver_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewResolver!]! + newTTLs( + where: NewTTL_filter + orderBy: NewTTL_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NewTTL!]! + wrappedTransfers( + where: WrappedTransfer_filter + orderBy: WrappedTransfer_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [WrappedTransfer!]! + nameWrappeds( + where: NameWrapped_filter + orderBy: NameWrapped_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameWrapped!]! + nameUnwrappeds( + where: NameUnwrapped_filter + orderBy: NameUnwrapped_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameUnwrapped!]! + fusesSets( + where: FusesSet_filter + orderBy: FusesSet_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [FusesSet!]! + expiryExtendeds( + where: ExpiryExtended_filter + orderBy: ExpiryExtended_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [ExpiryExtended!]! + + """ + The events associated with the domain (exact match) + """ + events( + where: DomainEvent_filter + orderBy: DomainEvent_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [DomainEvent!]! +} + +input Domain_filter { + and: [Domain_filter] + or: [Domain_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + name: String + name_not: String + name_in: [String] + name_not_in: [String] + name_contains: String + name_not_contains: String + name_starts_with: String + name_ends_with: String + name_not_starts_with: String + name_not_ends_with: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + labelName: String + labelName_not: String + labelName_in: [String] + labelName_not_in: [String] + labelName_contains: String + labelName_not_contains: String + labelName_starts_with: String + labelName_ends_with: String + labelName_not_starts_with: String + labelName_not_ends_with: String + labelName_gt: String + labelName_lt: String + labelName_gte: String + labelName_lte: String + labelhash: String + labelhash_not: String + labelhash_in: [String] + labelhash_not_in: [String] + labelhash_contains: String + labelhash_not_contains: String + labelhash_starts_with: String + labelhash_ends_with: String + labelhash_not_starts_with: String + labelhash_not_ends_with: String + labelhash_gt: String + labelhash_lt: String + labelhash_gte: String + labelhash_lte: String + parentId: String + parentId_not: String + parentId_in: [String] + parentId_not_in: [String] + parentId_contains: String + parentId_not_contains: String + parentId_starts_with: String + parentId_ends_with: String + parentId_not_starts_with: String + parentId_not_ends_with: String + parentId_gt: String + parentId_lt: String + parentId_gte: String + parentId_lte: String + subdomainCount: Int + subdomainCount_not: Int + subdomainCount_in: [Int] + subdomainCount_not_in: [Int] + subdomainCount_gt: Int + subdomainCount_lt: Int + subdomainCount_gte: Int + subdomainCount_lte: Int + resolvedAddressId: String + resolvedAddressId_not: String + resolvedAddressId_in: [String] + resolvedAddressId_not_in: [String] + resolvedAddressId_contains: String + resolvedAddressId_not_contains: String + resolvedAddressId_starts_with: String + resolvedAddressId_ends_with: String + resolvedAddressId_not_starts_with: String + resolvedAddressId_not_ends_with: String + resolvedAddressId_gt: String + resolvedAddressId_lt: String + resolvedAddressId_gte: String + resolvedAddressId_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + ttl: BigInt + ttl_not: BigInt + ttl_in: [BigInt] + ttl_not_in: [BigInt] + ttl_gt: BigInt + ttl_lt: BigInt + ttl_gte: BigInt + ttl_lte: BigInt + isMigrated: Boolean + isMigrated_not: Boolean + isMigrated_in: [Boolean] + isMigrated_not_in: [Boolean] + createdAt: BigInt + createdAt_not: BigInt + createdAt_in: [BigInt] + createdAt_not_in: [BigInt] + createdAt_gt: BigInt + createdAt_lt: BigInt + createdAt_gte: BigInt + createdAt_lte: BigInt + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + registrantId: String + registrantId_not: String + registrantId_in: [String] + registrantId_not_in: [String] + registrantId_contains: String + registrantId_not_contains: String + registrantId_starts_with: String + registrantId_ends_with: String + registrantId_not_starts_with: String + registrantId_not_ends_with: String + registrantId_gt: String + registrantId_lt: String + registrantId_gte: String + registrantId_lte: String + wrappedOwnerId: String + wrappedOwnerId_not: String + wrappedOwnerId_in: [String] + wrappedOwnerId_not_in: [String] + wrappedOwnerId_contains: String + wrappedOwnerId_not_contains: String + wrappedOwnerId_starts_with: String + wrappedOwnerId_ends_with: String + wrappedOwnerId_not_starts_with: String + wrappedOwnerId_not_ends_with: String + wrappedOwnerId_gt: String + wrappedOwnerId_lt: String + wrappedOwnerId_gte: String + wrappedOwnerId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + resolvedAddress: String + resolvedAddress_not: String + owner: String + owner_not: String + parent: String + parent_not: String + resolver: String + resolver_not: String + registrant: String + registrant_not: String + wrappedOwner: String + wrappedOwner_not: String + wrappedDomain: String + wrappedDomain_not: String + registration: String + registration_not: String +} + +enum Domain_orderBy { + id + name + labelName + labelhash + parentId + subdomainCount + resolvedAddressId + resolverId + ttl + isMigrated + createdAt + ownerId + registrantId + wrappedOwnerId + expiryDate +} + +enum OrderDirection { + asc + desc +} + +""" +Get a single a wrapped domain by address +""" +type WrappedDomain { + """ + The unique identifier for each instance of the WrappedDomain entity (exact match) + """ + id: String! + + """ + The domain that is wrapped by this WrappedDomain (exact match) + """ + domainId: String! + + """ + The expiry date of the wrapped domain (exact match) + """ + expiryDate: BigInt! + + """ + The number of fuses remaining on the wrapped domain (exact match) + """ + fuses: Int! + + """ + The account that owns this WrappedDomain (exact match) + """ + ownerId: String! + + """ + The name of the wrapped domain (exact match) + """ + name: String + + """ + The domain that is wrapped by this WrappedDomain (exact match) + """ + domain: Domain + + """ + The account that owns this WrappedDomain (exact match) + """ + owner: Account +} + +""" +Get a single a domain registration by address +""" +type Registration { + """ + The unique identifier of the registration (exact match) + """ + id: String! + + """ + The domain name associated with the registration (exact match) + """ + domainId: String! + + """ + The registration date of the domain (exact match) + """ + registrationDate: BigInt! + + """ + The expiry date of the domain (exact match) + """ + expiryDate: BigInt! + + """ + The cost associated with the domain registration (exact match) + """ + cost: BigInt + + """ + The account that registered the domain (exact match) + """ + registrantId: String! + + """ + The human-readable label name associated with the domain registration (exact match) + """ + labelName: String + + """ + The domain name associated with the registration (exact match) + """ + domain: Domain + + """ + The account that registered the domain (exact match) + """ + registrant: Account + nameRegistereds( + where: NameRegistered_filter + orderBy: NameRegistered_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameRegistered!]! + nameReneweds( + where: NameRenewed_filter + orderBy: NameRenewed_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameRenewed!]! + nameTransferreds( + where: NameTransferred_filter + orderBy: NameTransferred_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [NameTransferred!]! + + """ + The events associated with the domain registration (exact match) + """ + events( + where: RegistrationEvent_filter + orderBy: RegistrationEvent_orderBy + orderDirection: OrderDirection + first: Int + skip: Int + ): [RegistrationEvent!]! +} + +""" +Get a single a name registered event by address +""" +type NameRegistered implements RegistrationEvent { + """ + The unique identifier of the NameRegistered event (exact match) + """ + id: String! + + """ + The block number of the event (exact match) + """ + blockNumber: Int! + + """ + The transaction ID associated with the event (exact match) + """ + transactionID: String! + registrationId: String! + registrantId: String! + + """ + The expiry date of the registration (exact match) + """ + expiryDate: BigInt! + + """ + The registration associated with the event (exact match) + """ + registration: Registration + + """ + The account that registered the name (exact match) + """ + registrant: Account +} + +""" +Get a single a registration event by address +""" +interface RegistrationEvent { + """ + The unique identifier of the registration event (exact match) + """ + id: String! + + """ + The block number of the event (exact match) + """ + blockNumber: Int! + + """ + The transaction ID associated with the event (exact match) + """ + transactionID: String! + registrationId: String! +} + +input NameRegistered_filter { + and: [NameRegistered_filter] + or: [NameRegistered_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + registrationId: String + registrationId_not: String + registrationId_in: [String] + registrationId_not_in: [String] + registrationId_contains: String + registrationId_not_contains: String + registrationId_starts_with: String + registrationId_ends_with: String + registrationId_not_starts_with: String + registrationId_not_ends_with: String + registrationId_gt: String + registrationId_lt: String + registrationId_gte: String + registrationId_lte: String + registrantId: String + registrantId_not: String + registrantId_in: [String] + registrantId_not_in: [String] + registrantId_contains: String + registrantId_not_contains: String + registrantId_starts_with: String + registrantId_ends_with: String + registrantId_not_starts_with: String + registrantId_not_ends_with: String + registrantId_gt: String + registrantId_lt: String + registrantId_gte: String + registrantId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + registration: String + registration_not: String + registrant: String + registrant_not: String +} + +enum NameRegistered_orderBy { + id + blockNumber + transactionID + registrationId + registrantId + expiryDate +} + +""" +Get a single a name renewed event by address +""" +type NameRenewed implements RegistrationEvent { + """ + The unique identifier of the NameRenewed event (exact match) + """ + id: String! + + """ + The block number of the event (exact match) + """ + blockNumber: Int! + + """ + The transaction ID associated with the event (exact match) + """ + transactionID: String! + registrationId: String! + + """ + The new expiry date of the registration (exact match) + """ + expiryDate: BigInt! + + """ + The registration associated with the event (exact match) + """ + registration: Registration +} + +input NameRenewed_filter { + and: [NameRenewed_filter] + or: [NameRenewed_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + registrationId: String + registrationId_not: String + registrationId_in: [String] + registrationId_not_in: [String] + registrationId_contains: String + registrationId_not_contains: String + registrationId_starts_with: String + registrationId_ends_with: String + registrationId_not_starts_with: String + registrationId_not_ends_with: String + registrationId_gt: String + registrationId_lt: String + registrationId_gte: String + registrationId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + registration: String + registration_not: String +} + +enum NameRenewed_orderBy { + id + blockNumber + transactionID + registrationId + expiryDate +} + +""" +Get a single a name transferred event by address +""" +type NameTransferred implements RegistrationEvent { + """ + The ID of the event (exact match) + """ + id: String! + + """ + The block number of the event (exact match) + """ + blockNumber: Int! + + """ + The transaction ID of the event (exact match) + """ + transactionID: String! + registrationId: String! + newOwnerId: String! + + """ + The registration associated with the event (exact match) + """ + registration: Registration + + """ + The new owner of the domain (exact match) + """ + newOwner: Account +} + +input NameTransferred_filter { + and: [NameTransferred_filter] + or: [NameTransferred_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + registrationId: String + registrationId_not: String + registrationId_in: [String] + registrationId_not_in: [String] + registrationId_contains: String + registrationId_not_contains: String + registrationId_starts_with: String + registrationId_ends_with: String + registrationId_not_starts_with: String + registrationId_not_ends_with: String + registrationId_gt: String + registrationId_lt: String + registrationId_gte: String + registrationId_lte: String + newOwnerId: String + newOwnerId_not: String + newOwnerId_in: [String] + newOwnerId_not_in: [String] + newOwnerId_contains: String + newOwnerId_not_contains: String + newOwnerId_starts_with: String + newOwnerId_ends_with: String + newOwnerId_not_starts_with: String + newOwnerId_not_ends_with: String + newOwnerId_gt: String + newOwnerId_lt: String + newOwnerId_gte: String + newOwnerId_lte: String + registration: String + registration_not: String + newOwner: String + newOwner_not: String +} + +enum NameTransferred_orderBy { + id + blockNumber + transactionID + registrationId + newOwnerId +} + +input RegistrationEvent_filter { + and: [RegistrationEvent_filter] + or: [RegistrationEvent_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + registrationId: String + registrationId_not: String + registrationId_in: [String] + registrationId_not_in: [String] + registrationId_contains: String + registrationId_not_contains: String + registrationId_starts_with: String + registrationId_ends_with: String + registrationId_not_starts_with: String + registrationId_not_ends_with: String + registrationId_gt: String + registrationId_lt: String + registrationId_gte: String + registrationId_lte: String + registration: String + registration_not: String +} + +enum RegistrationEvent_orderBy { + id + blockNumber + transactionID + registrationId +} + +""" +Get a single a transfer event by address +""" +type Transfer implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + ownerId: String! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain + + """ + The account that owns the domain after the transfer (exact match) + """ + owner: Account +} + +""" +Get a single an event related to a Domain by address +""" +interface DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! +} + +input Transfer_filter { + and: [Transfer_filter] + or: [Transfer_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + domain: String + domain_not: String + owner: String + owner_not: String +} + +enum Transfer_orderBy { + id + blockNumber + transactionID + domainId + ownerId +} + +""" +Get a single a new owner event by address +""" +type NewOwner implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + ownerId: String! + parentDomainId: String! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain + + """ + The new account that owns the domain (exact match) + """ + owner: Account + + """ + The parent domain of the domain name associated with the event (exact match) + """ + parentDomain: Domain +} + +input NewOwner_filter { + and: [NewOwner_filter] + or: [NewOwner_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + parentDomainId: String + parentDomainId_not: String + parentDomainId_in: [String] + parentDomainId_not_in: [String] + parentDomainId_contains: String + parentDomainId_not_contains: String + parentDomainId_starts_with: String + parentDomainId_ends_with: String + parentDomainId_not_starts_with: String + parentDomainId_not_ends_with: String + parentDomainId_gt: String + parentDomainId_lt: String + parentDomainId_gte: String + parentDomainId_lte: String + domain: String + domain_not: String + owner: String + owner_not: String + parentDomain: String + parentDomain_not: String +} + +enum NewOwner_orderBy { + id + blockNumber + transactionID + domainId + ownerId + parentDomainId +} + +""" +Get a single a new resolver event by address +""" +type NewResolver implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + resolverId: String! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain + + """ + The new resolver contract address associated with the domain (exact match) + """ + resolver: Resolver +} + +input NewResolver_filter { + and: [NewResolver_filter] + or: [NewResolver_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + domain: String + domain_not: String + resolver: String + resolver_not: String +} + +enum NewResolver_orderBy { + id + blockNumber + transactionID + domainId + resolverId +} + +""" +Get a single a new TTL event by address +""" +type NewTTL implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + + """ + The new TTL value (in seconds) associated with the domain (exact match) + """ + ttl: BigInt! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain +} + +input NewTTL_filter { + and: [NewTTL_filter] + or: [NewTTL_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + ttl: BigInt + ttl_not: BigInt + ttl_in: [BigInt] + ttl_not_in: [BigInt] + ttl_gt: BigInt + ttl_lt: BigInt + ttl_gte: BigInt + ttl_lte: BigInt + domain: String + domain_not: String +} + +enum NewTTL_orderBy { + id + blockNumber + transactionID + domainId + ttl +} + +""" +Get a single a wrapped transfer event by address +""" +type WrappedTransfer implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + ownerId: String! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain + + """ + The account that owns the wrapped domain after the transfer (exact match) + """ + owner: Account +} + +input WrappedTransfer_filter { + and: [WrappedTransfer_filter] + or: [WrappedTransfer_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + domain: String + domain_not: String + owner: String + owner_not: String +} + +enum WrappedTransfer_orderBy { + id + blockNumber + transactionID + domainId + ownerId +} + +""" +Get a single a name wrapped event by address +""" +type NameWrapped implements DomainEvent { + """ + The unique identifier of the wrapped domain (exact match) + """ + id: String! + + """ + The block number at which the wrapped domain was wrapped (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that wrapped the domain (exact match) + """ + transactionID: String! + domainId: String! + + """ + The human-readable name of the wrapped domain (exact match) + """ + name: String + + """ + The number of fuses associated with the wrapped domain (exact match) + """ + fuses: Int! + ownerId: String! + + """ + The expiry date of the wrapped domain registration (exact match) + """ + expiryDate: BigInt! + + """ + The domain name associated with the wrapped domain (exact match) + """ + domain: Domain + + """ + The account that owns the wrapped domain (exact match) + """ + owner: Account +} + +input NameWrapped_filter { + and: [NameWrapped_filter] + or: [NameWrapped_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + name: String + name_not: String + name_in: [String] + name_not_in: [String] + name_contains: String + name_not_contains: String + name_starts_with: String + name_ends_with: String + name_not_starts_with: String + name_not_ends_with: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + fuses: Int + fuses_not: Int + fuses_in: [Int] + fuses_not_in: [Int] + fuses_gt: Int + fuses_lt: Int + fuses_gte: Int + fuses_lte: Int + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + domain: String + domain_not: String + owner: String + owner_not: String +} + +enum NameWrapped_orderBy { + id + blockNumber + transactionID + domainId + name + fuses + ownerId + expiryDate +} + +""" +Get a single a name unwrapped event by address +""" +type NameUnwrapped implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + ownerId: String! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain + + """ + The account that owns the domain after it was unwrapped (exact match) + """ + owner: Account +} + +input NameUnwrapped_filter { + and: [NameUnwrapped_filter] + or: [NameUnwrapped_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + domain: String + domain_not: String + owner: String + owner_not: String +} + +enum NameUnwrapped_orderBy { + id + blockNumber + transactionID + domainId + ownerId +} + +""" +Get a single a fuses set event by address +""" +type FusesSet implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + + """ + The number of fuses associated with the domain after the set event (exact match) + """ + fuses: Int! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain +} + +input FusesSet_filter { + and: [FusesSet_filter] + or: [FusesSet_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + fuses: Int + fuses_not: Int + fuses_in: [Int] + fuses_not_in: [Int] + fuses_gt: Int + fuses_lt: Int + fuses_gte: Int + fuses_lte: Int + domain: String + domain_not: String +} + +enum FusesSet_orderBy { + id + blockNumber + transactionID + domainId + fuses +} + +""" +Get a single an expiry extended event by address +""" +type ExpiryExtended implements DomainEvent { + """ + The unique identifier of the event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash of the transaction that triggered the event (exact match) + """ + transactionID: String! + domainId: String! + + """ + The new expiry date associated with the domain after the extension event (exact match) + """ + expiryDate: BigInt! + + """ + The domain name associated with the event (exact match) + """ + domain: Domain +} + +input ExpiryExtended_filter { + and: [ExpiryExtended_filter] + or: [ExpiryExtended_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + domain: String + domain_not: String +} + +enum ExpiryExtended_orderBy { + id + blockNumber + transactionID + domainId + expiryDate +} + +input DomainEvent_filter { + and: [DomainEvent_filter] + or: [DomainEvent_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + domain: String + domain_not: String +} + +enum DomainEvent_orderBy { + id + blockNumber + transactionID + domainId +} + +input WrappedDomain_filter { + and: [WrappedDomain_filter] + or: [WrappedDomain_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + fuses: Int + fuses_not: Int + fuses_in: [Int] + fuses_not_in: [Int] + fuses_gt: Int + fuses_lt: Int + fuses_gte: Int + fuses_lte: Int + ownerId: String + ownerId_not: String + ownerId_in: [String] + ownerId_not_in: [String] + ownerId_contains: String + ownerId_not_contains: String + ownerId_starts_with: String + ownerId_ends_with: String + ownerId_not_starts_with: String + ownerId_not_ends_with: String + ownerId_gt: String + ownerId_lt: String + ownerId_gte: String + ownerId_lte: String + name: String + name_not: String + name_in: [String] + name_not_in: [String] + name_contains: String + name_not_contains: String + name_starts_with: String + name_ends_with: String + name_not_starts_with: String + name_not_ends_with: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + domain: String + domain_not: String + owner: String + owner_not: String +} + +enum WrappedDomain_orderBy { + id + domainId + expiryDate + fuses + ownerId + name +} + +input Registration_filter { + and: [Registration_filter] + or: [Registration_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + registrationDate: BigInt + registrationDate_not: BigInt + registrationDate_in: [BigInt] + registrationDate_not_in: [BigInt] + registrationDate_gt: BigInt + registrationDate_lt: BigInt + registrationDate_gte: BigInt + registrationDate_lte: BigInt + expiryDate: BigInt + expiryDate_not: BigInt + expiryDate_in: [BigInt] + expiryDate_not_in: [BigInt] + expiryDate_gt: BigInt + expiryDate_lt: BigInt + expiryDate_gte: BigInt + expiryDate_lte: BigInt + cost: BigInt + cost_not: BigInt + cost_in: [BigInt] + cost_not_in: [BigInt] + cost_gt: BigInt + cost_lt: BigInt + cost_gte: BigInt + cost_lte: BigInt + registrantId: String + registrantId_not: String + registrantId_in: [String] + registrantId_not_in: [String] + registrantId_contains: String + registrantId_not_contains: String + registrantId_starts_with: String + registrantId_ends_with: String + registrantId_not_starts_with: String + registrantId_not_ends_with: String + registrantId_gt: String + registrantId_lt: String + registrantId_gte: String + registrantId_lte: String + labelName: String + labelName_not: String + labelName_in: [String] + labelName_not_in: [String] + labelName_contains: String + labelName_not_contains: String + labelName_starts_with: String + labelName_ends_with: String + labelName_not_starts_with: String + labelName_not_ends_with: String + labelName_gt: String + labelName_lt: String + labelName_gte: String + labelName_lte: String + domain: String + domain_not: String + registrant: String + registrant_not: String +} + +enum Registration_orderBy { + id + domainId + registrationDate + expiryDate + cost + registrantId + labelName +} + +""" +Get a single an address changed event by address +""" +type AddrChanged implements ResolverEvent { + """ + Unique identifier for this event (exact match) + """ + id: String! + + """ + The block number at which this event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction ID for the transaction in which this event occurred (exact match) + """ + transactionID: String! + resolverId: String! + addrId: String! + + """ + The resolver associated with this event (exact match) + """ + resolver: Resolver + + """ + The new address associated with the resolver (exact match) + """ + addr: Account +} + +input AddrChanged_filter { + and: [AddrChanged_filter] + or: [AddrChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + addrId: String + addrId_not: String + addrId_in: [String] + addrId_not_in: [String] + addrId_contains: String + addrId_not_contains: String + addrId_starts_with: String + addrId_ends_with: String + addrId_not_starts_with: String + addrId_not_ends_with: String + addrId_gt: String + addrId_lt: String + addrId_gte: String + addrId_lte: String + resolver: String + resolver_not: String + addr: String + addr_not: String +} + +enum AddrChanged_orderBy { + id + blockNumber + transactionID + resolverId + addrId +} + +""" +Get a single a multicoin address changed event by address +""" +type MulticoinAddrChanged implements ResolverEvent { + """ + Unique identifier for the event (exact match) + """ + id: String! + + """ + Block number in which this event was emitted (exact match) + """ + blockNumber: Int! + + """ + Transaction ID in which this event was emitted (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The coin type of the changed address (exact match) + """ + coinType: BigInt! + + """ + The new address value for the given coin type (exact match) + """ + addr: String! + + """ + Resolver associated with this event (exact match) + """ + resolver: Resolver +} + +input MulticoinAddrChanged_filter { + and: [MulticoinAddrChanged_filter] + or: [MulticoinAddrChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + coinType: BigInt + coinType_not: BigInt + coinType_in: [BigInt] + coinType_not_in: [BigInt] + coinType_gt: BigInt + coinType_lt: BigInt + coinType_gte: BigInt + coinType_lte: BigInt + addr: String + addr_not: String + addr_in: [String] + addr_not_in: [String] + addr_contains: String + addr_not_contains: String + addr_starts_with: String + addr_ends_with: String + addr_not_starts_with: String + addr_not_ends_with: String + addr_gt: String + addr_lt: String + addr_gte: String + addr_lte: String + resolver: String + resolver_not: String +} + +enum MulticoinAddrChanged_orderBy { + id + blockNumber + transactionID + resolverId + coinType + addr +} + +""" +Get a single a name changed event by address +""" +type NameChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + Block number where event occurred (exact match) + """ + blockNumber: Int! + + """ + Unique transaction ID where event occurred (exact match) + """ + transactionID: String! + resolverId: String! + + """ + New ENS name value (exact match) + """ + name: String! + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +input NameChanged_filter { + and: [NameChanged_filter] + or: [NameChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + name: String + name_not: String + name_in: [String] + name_not_in: [String] + name_contains: String + name_not_contains: String + name_starts_with: String + name_ends_with: String + name_not_starts_with: String + name_not_ends_with: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + resolver: String + resolver_not: String +} + +enum NameChanged_orderBy { + id + blockNumber + transactionID + resolverId + name +} + +input AbiChanged_filter { + and: [AbiChanged_filter] + or: [AbiChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + contentType: BigInt + contentType_not: BigInt + contentType_in: [BigInt] + contentType_not_in: [BigInt] + contentType_gt: BigInt + contentType_lt: BigInt + contentType_gte: BigInt + contentType_lte: BigInt + resolver: String + resolver_not: String +} + +enum AbiChanged_orderBy { + id + blockNumber + transactionID + resolverId + contentType +} + +""" +Get a single a pubkey changed event by address +""" +type PubkeyChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + Block number of the Ethereum block where the event occurred (exact match) + """ + blockNumber: Int! + + """ + Transaction hash of the Ethereum transaction where the event occurred (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The x-coordinate of the new public key (exact match) + """ + x: String! + + """ + The y-coordinate of the new public key (exact match) + """ + y: String! + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +input PubkeyChanged_filter { + and: [PubkeyChanged_filter] + or: [PubkeyChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + x: String + x_not: String + x_in: [String] + x_not_in: [String] + x_contains: String + x_not_contains: String + x_starts_with: String + x_ends_with: String + x_not_starts_with: String + x_not_ends_with: String + x_gt: String + x_lt: String + x_gte: String + x_lte: String + y: String + y_not: String + y_in: [String] + y_not_in: [String] + y_contains: String + y_not_contains: String + y_starts_with: String + y_ends_with: String + y_not_starts_with: String + y_not_ends_with: String + y_gt: String + y_lt: String + y_gte: String + y_lte: String + resolver: String + resolver_not: String +} + +enum PubkeyChanged_orderBy { + id + blockNumber + transactionID + resolverId + x + y +} + +""" +Get a single a text changed event by address +""" +type TextChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + Block number of the Ethereum block in which the event occurred (exact match) + """ + blockNumber: Int! + + """ + Hash of the Ethereum transaction in which the event occurred (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The key of the text record that was changed (exact match) + """ + key: String! + + """ + The new value of the text record that was changed (exact match) + """ + value: String + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +input TextChanged_filter { + and: [TextChanged_filter] + or: [TextChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + key: String + key_not: String + key_in: [String] + key_not_in: [String] + key_contains: String + key_not_contains: String + key_starts_with: String + key_ends_with: String + key_not_starts_with: String + key_not_ends_with: String + key_gt: String + key_lt: String + key_gte: String + key_lte: String + value: String + value_not: String + value_in: [String] + value_not_in: [String] + value_contains: String + value_not_contains: String + value_starts_with: String + value_ends_with: String + value_not_starts_with: String + value_not_ends_with: String + value_gt: String + value_lt: String + value_gte: String + value_lte: String + resolver: String + resolver_not: String +} + +enum TextChanged_orderBy { + id + blockNumber + transactionID + resolverId + key + value +} + +""" +Get a single a content hash changed event by address +""" +type ContenthashChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + The block number where the event occurred (exact match) + """ + blockNumber: Int! + + """ + The ID of the transaction where the event occurred (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The new content hash for the domain (exact match) + """ + hash: String! + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +input ContenthashChanged_filter { + and: [ContenthashChanged_filter] + or: [ContenthashChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + hash: String + hash_not: String + hash_in: [String] + hash_not_in: [String] + hash_contains: String + hash_not_contains: String + hash_starts_with: String + hash_ends_with: String + hash_not_starts_with: String + hash_not_ends_with: String + hash_gt: String + hash_lt: String + hash_gte: String + hash_lte: String + resolver: String + resolver_not: String +} + +enum ContenthashChanged_orderBy { + id + blockNumber + transactionID + resolverId + hash +} + +""" +Get a single an interface changed event by address +""" +type InterfaceChanged implements ResolverEvent { + """ + Concatenation of block number and log ID (exact match) + """ + id: String! + + """ + The block number in which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction ID for the transaction in which the event occurred (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The ID of the EIP-1820 interface that was changed (exact match) + """ + interfaceID: String! + + """ + The address of the contract that implements the interface (exact match) + """ + implementer: String! + + """ + Used to derive relationships to Resolvers (exact match) + """ + resolver: Resolver +} + +input InterfaceChanged_filter { + and: [InterfaceChanged_filter] + or: [InterfaceChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + interfaceID: String + interfaceID_not: String + interfaceID_in: [String] + interfaceID_not_in: [String] + interfaceID_contains: String + interfaceID_not_contains: String + interfaceID_starts_with: String + interfaceID_ends_with: String + interfaceID_not_starts_with: String + interfaceID_not_ends_with: String + interfaceID_gt: String + interfaceID_lt: String + interfaceID_gte: String + interfaceID_lte: String + implementer: String + implementer_not: String + implementer_in: [String] + implementer_not_in: [String] + implementer_contains: String + implementer_not_contains: String + implementer_starts_with: String + implementer_ends_with: String + implementer_not_starts_with: String + implementer_not_ends_with: String + implementer_gt: String + implementer_lt: String + implementer_gte: String + implementer_lte: String + resolver: String + resolver_not: String +} + +enum InterfaceChanged_orderBy { + id + blockNumber + transactionID + resolverId + interfaceID + implementer +} + +""" +Get a single an authorisation changed event by address +""" +type AuthorisationChanged implements ResolverEvent { + """ + Unique identifier for this event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash associated with the event (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The owner of the authorisation (exact match) + """ + owner: String! + + """ + The target of the authorisation (exact match) + """ + target: String! + + """ + Whether the authorisation was added or removed (exact match) + """ + isAuthorized: Boolean! + + """ + The resolver associated with this event (exact match) + """ + resolver: Resolver +} + +input AuthorisationChanged_filter { + and: [AuthorisationChanged_filter] + or: [AuthorisationChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + owner: String + owner_not: String + owner_in: [String] + owner_not_in: [String] + owner_contains: String + owner_not_contains: String + owner_starts_with: String + owner_ends_with: String + owner_not_starts_with: String + owner_not_ends_with: String + owner_gt: String + owner_lt: String + owner_gte: String + owner_lte: String + target: String + target_not: String + target_in: [String] + target_not_in: [String] + target_contains: String + target_not_contains: String + target_starts_with: String + target_ends_with: String + target_not_starts_with: String + target_not_ends_with: String + target_gt: String + target_lt: String + target_gte: String + target_lte: String + isAuthorized: Boolean + isAuthorized_not: Boolean + isAuthorized_in: [Boolean] + isAuthorized_not_in: [Boolean] + resolver: String + resolver_not: String +} + +enum AuthorisationChanged_orderBy { + id + blockNumber + transactionID + resolverId + owner + target + isAuthorized +} + +""" +Get a single a version changed event by address +""" +type VersionChanged implements ResolverEvent { + """ + Unique identifier for this event (exact match) + """ + id: String! + + """ + The block number at which the event occurred (exact match) + """ + blockNumber: Int! + + """ + The transaction hash associated with the event (exact match) + """ + transactionID: String! + resolverId: String! + + """ + The new version number of the resolver (exact match) + """ + version: BigInt! + + """ + The resolver associated with this event (exact match) + """ + resolver: Resolver +} + +input VersionChanged_filter { + and: [VersionChanged_filter] + or: [VersionChanged_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + version: BigInt + version_not: BigInt + version_in: [BigInt] + version_not_in: [BigInt] + version_gt: BigInt + version_lt: BigInt + version_gte: BigInt + version_lte: BigInt + resolver: String + resolver_not: String +} + +enum VersionChanged_orderBy { + id + blockNumber + transactionID + resolverId + version +} + +input ResolverEvent_filter { + and: [ResolverEvent_filter] + or: [ResolverEvent_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + blockNumber: Int + blockNumber_not: Int + blockNumber_in: [Int] + blockNumber_not_in: [Int] + blockNumber_gt: Int + blockNumber_lt: Int + blockNumber_gte: Int + blockNumber_lte: Int + transactionID: String + transactionID_not: String + transactionID_in: [String] + transactionID_not_in: [String] + transactionID_contains: String + transactionID_not_contains: String + transactionID_starts_with: String + transactionID_ends_with: String + transactionID_not_starts_with: String + transactionID_not_ends_with: String + transactionID_gt: String + transactionID_lt: String + transactionID_gte: String + transactionID_lte: String + resolverId: String + resolverId_not: String + resolverId_in: [String] + resolverId_not_in: [String] + resolverId_contains: String + resolverId_not_contains: String + resolverId_starts_with: String + resolverId_ends_with: String + resolverId_not_starts_with: String + resolverId_not_ends_with: String + resolverId_gt: String + resolverId_lt: String + resolverId_gte: String + resolverId_lte: String + resolver: String + resolver_not: String +} + +enum ResolverEvent_orderBy { + id + blockNumber + transactionID + resolverId +} + +input Account_filter { + and: [Account_filter] + or: [Account_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String +} + +enum Account_orderBy { + id +} + +input Resolver_filter { + and: [Resolver_filter] + or: [Resolver_filter] + id: String + id_not: String + id_in: [String] + id_not_in: [String] + id_contains: String + id_not_contains: String + id_starts_with: String + id_ends_with: String + id_not_starts_with: String + id_not_ends_with: String + id_gt: String + id_lt: String + id_gte: String + id_lte: String + domainId: String + domainId_not: String + domainId_in: [String] + domainId_not_in: [String] + domainId_contains: String + domainId_not_contains: String + domainId_starts_with: String + domainId_ends_with: String + domainId_not_starts_with: String + domainId_not_ends_with: String + domainId_gt: String + domainId_lt: String + domainId_gte: String + domainId_lte: String + address: String + address_not: String + address_in: [String] + address_not_in: [String] + address_contains: String + address_not_contains: String + address_starts_with: String + address_ends_with: String + address_not_starts_with: String + address_not_ends_with: String + address_gt: String + address_lt: String + address_gte: String + address_lte: String + addrId: String + addrId_not: String + addrId_in: [String] + addrId_not_in: [String] + addrId_contains: String + addrId_not_contains: String + addrId_starts_with: String + addrId_ends_with: String + addrId_not_starts_with: String + addrId_not_ends_with: String + addrId_gt: String + addrId_lt: String + addrId_gte: String + addrId_lte: String + contentHash: String + contentHash_not: String + contentHash_in: [String] + contentHash_not_in: [String] + contentHash_contains: String + contentHash_not_contains: String + contentHash_starts_with: String + contentHash_ends_with: String + contentHash_not_starts_with: String + contentHash_not_ends_with: String + contentHash_gt: String + contentHash_lt: String + contentHash_gte: String + contentHash_lte: String + texts: [String] + texts_not: [String] + texts_has: String + texts_not_has: String + coinTypes: [BigInt] + coinTypes_not: [BigInt] + coinTypes_has: BigInt + coinTypes_not_has: BigInt + addr: String + addr_not: String + domain: String + domain_not: String +} + +enum Resolver_orderBy { + id + domainId + address + addrId + contentHash + texts + coinTypes +} + +type _Meta_ { + block: _Block_! + + """ + An ID representing this instance of ENSNode. It is composed of the ENSNode version (https://github.com/namehash/ensnode/releases) and the Ponder build_id (https://ponder.sh/docs/api-reference/database#instance-lifecycle). + """ + deployment: String! + + """ + If true, ENSIndexer has reported an indexing error and is not actively indexing blocks. + """ + hasIndexingErrors: Boolean! +} + +""" +Information about a specific block. +""" +type _Block_ { + hash: String + parentHash: String + number: Int! + timestamp: Int! +} diff --git a/docs/ensnode.io/src/pages/examples.astro b/docs/ensnode.io/src/pages/examples.astro deleted file mode 100644 index 7bb7fd044b..0000000000 --- a/docs/ensnode.io/src/pages/examples.astro +++ /dev/null @@ -1,247 +0,0 @@ ---- -import { getCollection } from "astro:content"; -import { Icon } from "astro-icon/components"; -import StaticHeader from "@components/molecules/StaticHeader.astro"; -import ExampleCard from "@components/organisms/ExampleCard.astro"; -import Layout from "../layouts/Layout.astro"; - -const examples = await getCollection("examples"); -const examplesData = examples.map((example) => example.data); - -const categoryMap = new Map(); -examplesData.forEach((example) => { - categoryMap.set(example.category, (categoryMap.get(example.category) || 0) + 1); -}); - -const categories = Array.from(categoryMap.entries()) - .map(([name, count]) => ({ name, count })) - .sort((a, b) => a.name.localeCompare(b.name)); ---- - - - - -
-
-

- Example ENS Data Requests -

-

- Explore our collection of example ENSNode GraphQL queries. -

- -
-
- -
-
- -
- -
-
-
- -
-
-
-

Categories

- - - -
- {categories.map(({ name, count }) => ( - - ))} -
- - -
-
- -
-
-

- Showing all {examplesData.length} {examplesData.length === 1 ? 'example' : 'examples'} -

-
- -
- {examplesData.map((example) => ( - - ))} -
- - -
-
-
-
- - -
diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/index.ts b/packages/ensdb-sdk/src/ensindexer-abstract/index.ts index 4b91db39ce..3e2d08a61d 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/index.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/index.ts @@ -4,9 +4,9 @@ * for ENSDb, which is then used to build the ENSDb Schema for a Drizzle client for ENSDb. */ -export * from "./ensv2.schema"; export * from "./migrated-nodes.schema"; export * from "./protocol-acceleration.schema"; export * from "./registrars.schema"; export * from "./subgraph.schema"; export * from "./tokenscope.schema"; +export * from "./unigraph.schema"; diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts b/packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts similarity index 98% rename from packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts rename to packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts index 02db93746e..1d049c8f70 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts @@ -22,7 +22,7 @@ import type { BlockNumber, Hash } from "viem"; import type { EncodedReferrer } from "@ensnode/ensnode-sdk"; /** - * The ENSv2 Schema + * The Unigraph Schema * * While the initial approach was a highly materialized view of the ENS protocol, abstracting away * as many on-chain details as possible, in practice—due to the sheer complexity of the protocol at @@ -67,8 +67,9 @@ import type { EncodedReferrer } from "@ensnode/ensnode-sdk"; * 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 subgraph, which is reflected via `Registry.canonical` / - * `Domain.canonical` boolean flags on the rows themselves. The bidirectional canonical edge is + * exception of the **Canonical Nametree** — the set of Domains that have an inferrable Canonical + * Name — which _is_ materialized inline: the `Registry.canonical` / `Domain.canonical` membership + * flags plus the `Domain.canonical*` name/path/depth fields on the rows themselves. The bidirectional canonical edge is * NOT materialized in a parallel table; it is derived on demand by checking that the two * unidirectional pointers agree (`Registry.canonicalDomainId = Domain.id` * ↔ `Domain.subregistryId = Registry.id`). Cascading canonicality flips through the subgraph @@ -80,7 +81,7 @@ import type { EncodedReferrer } from "@ensnode/ensnode-sdk"; * 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 ensv2.schema.ts file. + * 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 diff --git a/packages/ensnode-sdk/src/omnigraph-api/example-queries.ts b/packages/ensnode-sdk/src/omnigraph-api/example-queries.ts index c34e3f17ef..9797f541e0 100644 --- a/packages/ensnode-sdk/src/omnigraph-api/example-queries.ts +++ b/packages/ensnode-sdk/src/omnigraph-api/example-queries.ts @@ -3,28 +3,28 @@ import { asInterpretedName, toNormalizedAddress } from "enssdk"; import { DatasourceNames, ENSNamespaceIds } from "@ensnode/datasources"; import { accounts } from "@ensnode/datasources/devnet"; -import { maybeGetDatasourceContract } from "../shared/datasource-contract"; +import { getDatasourceContract } from "../shared/datasource-contract"; import type { NamespaceSpecificValue } from "../shared/namespace-specific-value"; -const SEPOLIA_V2_V2_ETH_REGISTRY = maybeGetDatasourceContract( +const SEPOLIA_V2_V2_ETH_REGISTRY = getDatasourceContract( ENSNamespaceIds.SepoliaV2, DatasourceNames.ENSv2Root, "ETHRegistry", ); -const SEPOLIA_V2_V2_ETH_REGISTRAR = maybeGetDatasourceContract( +const SEPOLIA_V2_V2_ETH_REGISTRAR = getDatasourceContract( ENSNamespaceIds.SepoliaV2, DatasourceNames.ENSv2Root, "ETHRegistrar", ); -const ENS_TEST_ENV_V2_ETH_REGISTRY = maybeGetDatasourceContract( +const ENS_TEST_ENV_V2_ETH_REGISTRY = getDatasourceContract( ENSNamespaceIds.EnsTestEnv, DatasourceNames.ENSv2Root, "ETHRegistry", ); -const ENS_TEST_ENV_V2_ETH_REGISTRAR = maybeGetDatasourceContract( +const ENS_TEST_ENV_V2_ETH_REGISTRAR = getDatasourceContract( ENSNamespaceIds.EnsTestEnv, DatasourceNames.ENSv2Root, "ETHRegistrar", @@ -45,6 +45,18 @@ const SEPOLIA_V2_NAME_WITH_OWNED_RESOLVER = asInterpretedName("sfmonicdebmig.eth const SEPOLIA_V2_TEST_NAME = asInterpretedName("test-name.eth"); +const MAINNET_PUBLIC_RESOLVER = getDatasourceContract( + ENSNamespaceIds.Mainnet, + DatasourceNames.ReverseResolverRoot, + "DefaultPublicResolver5", +); + +const SEPOLIA_V2_PUBLIC_RESOLVER = getDatasourceContract( + ENSNamespaceIds.SepoliaV2, + DatasourceNames.ReverseResolverRoot, + "DefaultPublicResolver5", +); + export type GraphqlApiExampleQuery = { id: string; query: string; @@ -74,7 +86,7 @@ export const GRAPHQL_API_EXAMPLE_QUERIES: GraphqlApiExampleQuery[] = [ # # There are also example queries in the tabs above ☝️ query HelloWorld { - domain(by: { name: "eth" }) { canonical { name { interpreted } } owner { address } } + domain(by: { name: "eth" }) { canonical { name { interpreted beautified } } owner { address } } }`, variables: { default: {} }, }, @@ -99,7 +111,7 @@ query FindDomains( __typename id label { interpreted hash } - canonical { name { interpreted } } + canonical { name { interpreted beautified } } registration { expiry event { timestamp } } } @@ -145,6 +157,52 @@ query DomainByName($name: InterpretedName!) { }, }, + /////////////////////// + // Domain Registration + /////////////////////// + { + id: "domain-registration", + query: ` +query DomainRegistration($name: InterpretedName!) { + domain(by: { name: $name }) { + canonical { name { interpreted } } + + registration { + __typename + id + start + expiry + expired + referrer + registrar { chainId address } + registrant { address } + renewals(first: 5) { + totalCount + edges { node { duration base premium referrer } } + } + + # ENSv1 .eth registrations (also Basenames & Lineanames) + ... on BaseRegistrarRegistration { + baseCost + premium + isInGracePeriod + # present when the .eth name is wrapped by the NameWrapper + wrapped { fuses tokenId } + } + + # names held natively in the NameWrapper + ... on NameWrapperRegistration { + fuses + } + } + } +}`, + variables: { + default: { name: "vitalik.eth" }, + [ENSNamespaceIds.SepoliaV2]: { name: SEPOLIA_V2_NAME_WITH_OWNED_RESOLVER }, + }, + }, + ////////////////////// // Domain Subdomains ////////////////////// @@ -153,11 +211,11 @@ query DomainByName($name: InterpretedName!) { query: ` query DomainSubdomains($name: InterpretedName!) { domain(by: {name: $name}) { - canonical { name { interpreted } } + canonical { name { interpreted beautified } } subdomains(first: 10) { edges { node { - canonical { name { interpreted } } + canonical { name { interpreted beautified } } } } } @@ -166,6 +224,32 @@ query DomainSubdomains($name: InterpretedName!) { variables: { default: { name: "eth" } }, }, + //////////////////////// + // Subdomains Pagination + //////////////////////// + { + id: "subdomains-pagination", + query: ` +query SubdomainsPagination($first: Int!, $after: String) { + domain(by: { name: "eth" }) { + canonical { name { interpreted } } + + # paginate child names: pass pageInfo.endCursor back as $after for the next page + subdomains(first: $first, after: $after) { + totalCount + pageInfo { hasNextPage endCursor } + edges { + cursor + node { + canonical { name { interpreted } } + } + } + } + } +}`, + variables: { default: { first: 10, after: null } }, + }, + ///////////////// // Domain Events ///////////////// @@ -209,7 +293,7 @@ query AccountDomains( edges { node { label { interpreted } - canonical { name { interpreted } } + canonical { name { interpreted beautified } } } } } @@ -256,7 +340,7 @@ query RegistryDomains( edges { node { label { interpreted } - canonical { name { interpreted } } + canonical { name { interpreted beautified } } } } } @@ -382,6 +466,39 @@ query DomainResolver($name: InterpretedName!) { }, }, + //////////////////////// + // Resolver By Address + //////////////////////// + { + id: "resolver-by-address", + query: ` +query ResolverByAddress($contract: AccountIdInput!) { + resolver(by: { contract: $contract }) { + id + contract { chainId address } + + # records this resolver stores, keyed by node + records(first: 5) { + totalCount + edges { + node { + node + name + keys + coinTypes + } + } + } + + events { totalCount edges { node { topics data timestamp } } } + } +}`, + variables: { + default: { contract: MAINNET_PUBLIC_RESOLVER }, + [ENSNamespaceIds.SepoliaV2]: { contract: SEPOLIA_V2_PUBLIC_RESOLVER }, + }, + }, + ////////////// // Namegraph ////////////// @@ -394,17 +511,17 @@ query Namegraph { domains { edges { node { - canonical { name { interpreted } } + canonical { name { interpreted beautified } } subdomains { edges { node { - canonical { name { interpreted } } + canonical { name { interpreted beautified } } subdomains { edges { node { - canonical { name { interpreted } } + canonical { name { interpreted beautified } } } } }