Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/efp-example-queries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ensnode/ensnode-sdk": minor
---

Add EFP Omnigraph example queries (`efp-follow-graph`, `efp-who-follows`, `efp-list`) and tag every `GraphqlApiExampleQuery` with the `plugin` (`PluginName`) it requires. Existing queries are tagged `unigraph`; the new EFP queries are tagged `efp`.
5 changes: 5 additions & 0 deletions .changeset/efp-omnigraph-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@docs/ensnode": patch
---

Document EFP in the ENS Omnigraph API: a new "EFP" page covering how to enable the `efp` plugin and its API surface, standalone example-query pages (follow graph, who-follows, list) with an "EFP plugin required" tip, the `efp` plugin in the Existing Plugins table, and the `efp` plugin on the hosted Alpha instance. The Omnigraph examples snapshot is refreshed to v1.16.0.
5 changes: 5 additions & 0 deletions .changeset/efp-skill-examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensskills": patch
---

The `efp-protocol` skill's example queries are now generated from the shared ENSNode SDK example-query collection (an `AUTOGEN:EXAMPLES` region), filtered to the `efp` plugin. The base `omnigraph` skill's examples are now filtered to the `unigraph` plugin, so each skill ships only the examples that run against the plugin it documents.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ export const integrateSidebarTopic = {
label: "Protocol Acceleration",
link: "/docs/integrate/omnigraph/protocol-acceleration",
},
{
label: "EFP",
link: "/docs/integrate/omnigraph/efp",
},
{
label: "FAQ",
link: "/docs/integrate/omnigraph/faq",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ These ENSNode instances do not constrain themselves to exclusively activating th
connectWithENSAdminURL="https://admin.ensnode.io/connection?connection=https%3A%2F%2Fapi.alpha.ensnode.io"
namespace="mainnet"
ensVersions="ENSv1 only (not also ENSv2 yet)"
plugins={`${pluginLink("unigraph")}, ${pluginLink("protocol-acceleration")}, ${pluginLink("subgraph")}, ${pluginLink("basenames")}, ${pluginLink("lineanames")}, ${pluginLink("threedns")}, ${pluginLink("registrars")}, ${pluginLink("tokenscope")}`}
plugins={`${pluginLink("unigraph")}, ${pluginLink("protocol-acceleration")}, ${pluginLink("subgraph")}, ${pluginLink("basenames")}, ${pluginLink("lineanames")}, ${pluginLink("threedns")}, ${pluginLink("registrars")}, ${pluginLink("tokenscope")}, ${pluginLink("efp")}`}
>
<Fragment slot="ens-omnigraph-api-icon" set:text={"✅"} />
<Fragment slot="ens-omnigraph-api" set:html={omnigraphForEnsV1} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,20 @@ When new ENSNode plugins are created, it becomes possible for the [ENS Omnigraph

[ENSIndexer](/docs/services/ensindexer) is a reference implementation of an [ENSDb Writer](/docs/services/ensdb/concepts/glossary#ensdb-writer) that implements multiple existing ENSNode Plugin specifications. The code for each plugin inside ENSIndexer is a reference implementation of that plugin's abstract specification. [The ENSNode Plugins implemented in ENSIndexer today](https://github.com/namehash/ensnode/tree/main/apps/ensindexer/src/plugins) are:

| Plugin | Description |
| ----------------------- | ---------------------------------------------------------- |
| `unigraph` | Unified ENSv1 + ENSv2 data model with polymorphic entities |
| `protocol-acceleration` | Accelerated lookups for ENS resolution |
| `subgraph` | Legacy ENS Subgraph-compatible data model |
| `basenames` | Basenames (`.base.eth`) subname indexing |
| `lineanames` | Lineanames (`.linea.eth`) subname indexing |
| `threedns` | 3DNS (`.box`) name indexing |
| `registrars` | Registration and renewal lifecycle tracking |
| `tokenscope` | NFT tokenization of ENS names |
| Plugin | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unigraph` | Unified ENSv1 + ENSv2 data model with polymorphic entities |
| `protocol-acceleration` | Accelerated lookups for ENS resolution |
| `subgraph` | Legacy ENS Subgraph-compatible data model |
| `basenames` | Basenames (`.base.eth`) subname indexing |
| `lineanames` | Lineanames (`.linea.eth`) subname indexing |
| `threedns` | 3DNS (`.box`) name indexing |
| `registrars` | Registration and renewal lifecycle tracking |
| `tokenscope` | NFT tokenization of ENS names |
| `efp` | [Ethereum Follow Protocol](https://efp.app/) onchain social graph (lists, records, tags) — see [EFP in the Omnigraph](/docs/integrate/omnigraph/efp) |

## New Plugins

### Under Development

An `efp` plugin for the [Ethereum Follow Protocol](https://efp.app/) is [already under active development](https://github.com/Quantumlyy/efpnode/tree/main/packages/ensnode-plugin-efp) by the [EthId team](https://ethid.org/).

### Build Your Own

If you are interested in defining a new ENSNode plugin or implementing an existing one, reach out to the NameHash Labs team — we are happy to provide support and additional info. Join us on [Telegram](https://t.me/ensnode) for any questions.
Expand Down
62 changes: 62 additions & 0 deletions docs/ensnode.io/src/content/docs/docs/integrate/omnigraph/efp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: Ethereum Follow Protocol (EFP)
description: Query the Ethereum Follow Protocol (EFP) onchain social graph through the ENS Omnigraph API by enabling the efp plugin.
sidebar:
label: EFP
---

import { LinkCard } from "@astrojs/starlight/components";
import OmnigraphStaticExampleSet from "@components/organisms/OmnigraphStaticExampleSet.astro";

The [Ethereum Follow Protocol (EFP)](https://efp.app) is an onchain social graph for Ethereum accounts — a decentralized "who follows whom" primitive. Because EFP composes directly with ENS identity, the ENS Omnigraph API exposes it alongside ENS data, so you can read an account's follow graph **and** resolve each followed account's ENS name in a single query.

## Enabling the plugin

EFP is indexed by the **`efp` plugin**. The Omnigraph's `Query.efp` and `Account.efp` fields return data only when the connected ENSIndexer has this plugin enabled; otherwise they resolve to `null` — treat that like any other unsupported-instance case, not a query error.

To enable it on your own instance, add `efp` to the ENSIndexer `PLUGINS` environment variable (a comma-separated list), for example:

```bash
PLUGINS=unigraph,efp
```

The `efp` plugin's datasources are defined for the **mainnet** namespace (the EFP contracts on Base, OP Mainnet, and Ethereum Mainnet) and the local **ens-test-env** devnet.

:::note[Already enabled on the Alpha instance]
The hosted [ENSNode 'Alpha'](/docs/hosted-instances#ensnode-alpha) instance has the `efp` plugin enabled, so you can run every query on this page against it without hosting anything yourself.
:::

## What it indexes

The plugin indexes the full EFP onchain state into ENSDb:

- **Lists** — the ERC-721 NFTs that hold follow relationships, with their `owner` / `manager` / `user` roles and storage location.
- **List records** — the individual follows (address records), each with its **tags** (`block`, `mute`, `top8`, and custom tags).
- **Account metadata** — the onchain `(address, key) → value` store, including the `primary-list` key used to validate an account's primary list.

## The API surface

EFP is exposed through two entry points:

- **`Account.efp`** — an account's EFP presence: validated, block/mute-filtered `following` / `followers` connections (whose edges are full `Account`s you can resolve straight into ENS names), the validated `primaryList`, and raw account `metadata`.
- **`Query.efp`** — list-centric queries: `list(by: { tokenId })`, `lists(where:)` by role address, and `listRecords(where:)` (the raw record set, including `block`/`mute`).

Prefer `Account.efp.following` / `followers` for the social-graph answer — they apply EFP's primary-list validation and block/mute filtering for you. Drop to the raw `Query.efp.listRecords` / `EfpList.records` only when you specifically need tags, storage location, or non-primary lists.

## Example

<OmnigraphStaticExampleSet id="efp-follow-graph" hideBackToExamples />

## Learn more

<LinkCard
title="EFP examples"
description="Runnable Omnigraph queries for the EFP social graph, lists, and records."
href="/docs/integrate/omnigraph/examples"
/>

<LinkCard
title="EFP protocol docs"
description="The data model and validity rules behind EFP, at docs.efp.app."
href="https://docs.efp.app"
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: EFP Follow Graph
---

import OmnigraphStaticExampleSet from "@components/organisms/OmnigraphStaticExampleSet.astro";

:::tip[EFP plugin required]
This query reads [Ethereum Follow Protocol (EFP)](/docs/integrate/omnigraph/efp) data, which requires the [`efp` plugin](/docs/integrate/omnigraph/efp) to be enabled on the connected ENSIndexer. It is enabled on the hosted [ENSNode 'Alpha'](/docs/hosted-instances#ensnode-alpha) instance.
:::

<OmnigraphStaticExampleSet id="efp-follow-graph" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: EFP List
---

import OmnigraphStaticExampleSet from "@components/organisms/OmnigraphStaticExampleSet.astro";

:::tip[EFP plugin required]
This query reads [Ethereum Follow Protocol (EFP)](/docs/integrate/omnigraph/efp) data, which requires the [`efp` plugin](/docs/integrate/omnigraph/efp) to be enabled on the connected ENSIndexer. It is enabled on the hosted [ENSNode 'Alpha'](/docs/hosted-instances#ensnode-alpha) instance.
:::

<OmnigraphStaticExampleSet id="efp-list" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: EFP Who Follows
---

import OmnigraphStaticExampleSet from "@components/organisms/OmnigraphStaticExampleSet.astro";

:::tip[EFP plugin required]
This query reads [Ethereum Follow Protocol (EFP)](/docs/integrate/omnigraph/efp) data, which requires the [`efp` plugin](/docs/integrate/omnigraph/efp) to be enabled on the connected ENSIndexer. It is enabled on the hosted [ENSNode 'Alpha'](/docs/hosted-instances#ensnode-alpha) instance.
:::

<OmnigraphStaticExampleSet id="efp-who-follows" />
27 changes: 27 additions & 0 deletions docs/ensnode.io/src/data/omnigraph-examples/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,33 @@ export const OMNIGRAPH_EXAMPLES_CONFIG: OmnigraphExampleConfig[] = [
namespace: ENSNamespaceIds.Mainnet,
hostSeparatePage: false,
},
{
id: "efp-follow-graph",
title: "EFP Follow Graph",
description:
"This query loads an account's validated EFP social graph — its primary list, <code>following</code>, and <code>followers</code> — and resolves each followed account's ENS primary name. Requires the <code>efp</code> plugin.",
category: "EFP",
namespace: ENSNamespaceIds.Mainnet,
hostSeparatePage: true,
},
{
id: "efp-who-follows",
title: "EFP Who Follows",
description:
'This query finds the raw EFP list records pointing at an address (any list, including <code>block</code>/<code>mute</code> tags), to answer "which lists follow this address?". Requires the <code>efp</code> plugin.',
category: "EFP",
namespace: ENSNamespaceIds.Mainnet,
hostSeparatePage: true,
},
{
id: "efp-list",
title: "EFP List",
description:
"This query loads an EFP list by <code>tokenId</code> — its roles, storage location, and raw records with tags. Requires the <code>efp</code> plugin.",
category: "EFP",
namespace: ENSNamespaceIds.Mainnet,
hostSeparatePage: true,
},
];

export const OMNIGRAPH_EXAMPLES_INDEX_PATH = "/docs/integrate/omnigraph/examples" as const;
Expand Down
25 changes: 23 additions & 2 deletions docs/ensnode.io/src/data/omnigraph-examples/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"variables": {
"registry": {
"chainId": 11155111,
"address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1"
"address": "0xdedb92913a25abe1f7bcdd85d8a344a43b398b67"
}
}
},
Expand All @@ -119,7 +119,7 @@
"variables": {
"contract": {
"chainId": 11155111,
"address": "0x64c81210d0e580cfc7746f3fb910bf0e8f6378e1"
"address": "0xdedb92913a25abe1f7bcdd85d8a344a43b398b67"
}
}
},
Expand Down Expand Up @@ -167,5 +167,26 @@
"variables": {
"address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
}
},
{
"id": "efp-follow-graph",
"query": "query EfpFollowGraph($address: Address!) {\n account(by: { address: $address }) {\n efp {\n # The validated primary list, or null if unset/unvalidated.\n primaryList { tokenId }\n # following/followers are validated and block/mute-filtered, so the\n # edges are full Accounts you can walk straight into ENS names.\n following(first: 10) {\n totalCount\n edges {\n node {\n address\n resolve {\n primaryName(by: { chainName: ETHEREUM }) {\n name { beautified }\n }\n }\n }\n }\n }\n followers(first: 10) {\n totalCount\n edges { node { address } }\n }\n }\n }\n}",
"variables": {
"address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
}
},
{
"id": "efp-who-follows",
"query": "# Raw record view: includes block/mute and non-primary lists — inspect `tags`.\n# For the validated social graph, prefer Account.efp.followers instead.\nquery EfpWhoFollows($address: Address!) {\n efp {\n listRecords(where: { recordData: $address }, first: 25) {\n totalCount\n edges {\n node {\n tags\n list { tokenId user }\n }\n }\n }\n }\n}",
"variables": {
"address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
}
},
{
"id": "efp-list",
"query": "query EfpList($tokenId: TokenId!) {\n efp {\n list(by: { tokenId: $tokenId }) {\n tokenId\n owner\n user\n manager\n storageLocation { chainId address slot }\n # Raw records (any tag, no primary-list validation).\n records(first: 25) {\n totalCount\n edges { node { recordData tags } }\n }\n }\n }\n}",
"variables": {
"tokenId": "1"
}
}
]
Loading
Loading