From b96d0639fde6ee8da988876484dc38390c1b362d Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 11:18:10 -0500 Subject: [PATCH 1/6] feat(ensindexer): re-classify Resolver.extended on EIP-1967 proxy upgrade Resolver `isExtended` (ENSIP-10 IExtendedResolver support) was classified once at first visibility. Proxy Resolvers that activate IExtendedResolver via a post-assignment upgrade (e.g. the 3DNS Resolver behind `.box`) were stuck `false` forever, silently breaking wildcard resolution. The Protocol Acceleration plugin now watches the 3DNS Resolver's EIP-1967 `Upgraded` events and re-runs the supportsInterface probe against the new implementation, updating the stored row. Scoped to the `.box` case. Fixes #2275. --- .../reclassify-extended-on-proxy-upgrade.md | 6 ++++ .../resolver-db-helpers.ts | 30 +++++++++++++++++++ .../protocol-acceleration/event-handlers.ts | 2 ++ .../handlers/ThreeDNSResolver.ts | 25 ++++++++++++++++ .../plugins/protocol-acceleration/plugin.ts | 23 ++++++++++++++ .../src/abis/shared/UpgradeableProxy.ts | 18 +++++++++++ packages/datasources/src/index.ts | 1 + packages/datasources/src/mainnet.ts | 11 +++++++ 8 files changed, 116 insertions(+) create mode 100644 .changeset/reclassify-extended-on-proxy-upgrade.md create mode 100644 apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts create mode 100644 packages/datasources/src/abis/shared/UpgradeableProxy.ts diff --git a/.changeset/reclassify-extended-on-proxy-upgrade.md b/.changeset/reclassify-extended-on-proxy-upgrade.md new file mode 100644 index 0000000000..5d736a334a --- /dev/null +++ b/.changeset/reclassify-extended-on-proxy-upgrade.md @@ -0,0 +1,6 @@ +--- +"@ensnode/datasources": patch +"ensindexer": patch +--- + +ENSIndexer now re-classifies a Resolver's `extended` (ENSIP-10 `IExtendedResolver`) support when a known proxy Resolver emits an EIP-1967 `Upgraded` event, instead of fixing the value once at first visibility. Proxy Resolvers that activate `IExtendedResolver` via a post-assignment upgrade (e.g. the 3DNS Resolver behind `.box`) were stuck `extended = false` forever, silently breaking wildcard resolution for affected names (`mystery.box` returned `null`). The Protocol Acceleration plugin now watches the 3DNS Resolver's `Upgraded` events and re-runs the `supportsInterface` probe against the new implementation. Fixes #2275. diff --git a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts index 4b4fcd40ed..b3923c465b 100644 --- a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts +++ b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts @@ -57,6 +57,36 @@ export async function upsertResolver( return row; } +/** + * Re-classifies a Resolver's `supportsInterface`-derived metadata (currently `isExtended`) by + * re-running the eip-165 probe and overwriting the stored row. + * + * The initial `isExtended` classification in {@link upsertResolver} is computed once, at the block + * the Resolver is first seen. For EIP-1967 proxy Resolvers that activate `IExtendedResolver` via an + * `Upgraded` _after_ assignment (e.g. the 3DNS Resolver behind `.box`), that snapshot is stale + * `false` forever. Call this from the proxy's `Upgraded` handler so the probe re-runs against the + * new implementation (at the upgrade block) and `Resolver.extended` reflects current support. + * + * @dev performs a single `supportsInterface` RPC (via Ponder's cached `context.client`). Upserts so + * an `Upgraded` observed before the Resolver has emitted any other event still creates the row. + */ +export async function reclassifyResolverExtendedSupport( + context: IndexingEngineContext, + resolver: AccountId, +): Promise { + const id = makeResolverId(resolver); + + const isExtended = await isExtendedResolver({ + publicClient: context.client, + address: resolver.address, + }); + + await context.ensDb + .insert(ensIndexerSchema.resolver) + .values({ id, ...resolver, isExtended }) + .onConflictDoUpdate({ isExtended }); +} + /** * Ensures the Resolver + ResolverRecords entities exist for the given Resolver event, and returns * the ResolverRecords key for further per-record updates. diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts index 8c1b91ffd2..20dad0dad7 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts @@ -2,6 +2,7 @@ import attach_ENSv1RegistryHandlers from "./handlers/ENSv1Registry"; import attach_ENSv2RegistryHandlers from "./handlers/ENSv2Registry"; import attach_ResolverHandlers from "./handlers/Resolver"; import attach_StandaloneReverseRegistrarHandlers from "./handlers/StandaloneReverseRegistrar"; +import attach_ThreeDNSResolverHandlers from "./handlers/ThreeDNSResolver"; import attach_ThreeDNSTokenHandlers from "./handlers/ThreeDNSToken"; export default function () { @@ -9,5 +10,6 @@ export default function () { attach_ENSv2RegistryHandlers(); attach_ResolverHandlers(); attach_StandaloneReverseRegistrarHandlers(); + attach_ThreeDNSResolverHandlers(); attach_ThreeDNSTokenHandlers(); } diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts new file mode 100644 index 0000000000..90cd9da099 --- /dev/null +++ b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts @@ -0,0 +1,25 @@ +import { PluginName } from "@ensnode/ensnode-sdk"; + +import { getThisAccountId } from "@/lib/get-this-account-id"; +import { addOnchainEventListener } from "@/lib/indexing-engines/ponder"; +import { namespaceContract } from "@/lib/plugin-helpers"; +import { reclassifyResolverExtendedSupport } from "@/lib/protocol-acceleration/resolver-db-helpers"; + +const pluginName = PluginName.ProtocolAcceleration; + +/** + * Handlers for the 3DNS Resolver, an EIP-1967 upgradeable proxy (resolves `.box` and other 3DNS + * TLDs on Mainnet). + * + * Its `IExtendedResolver` (ENSIP-10) support was activated by an `Upgraded` after it was assigned + * as a Resolver, so the first-visibility `isExtended` classification is a stale `false`. We re-run + * the eip-165 probe on each `Upgraded` to keep `Resolver.extended` correct. See issue #2275. + */ +export default function () { + addOnchainEventListener( + namespaceContract(pluginName, "ThreeDNSResolver:Upgraded"), + async ({ context, event }) => { + await reclassifyResolverExtendedSupport(context, getThisAccountId(context, event)); + }, + ); +} diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts index 67291ce4d9..10dd84122d 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts @@ -1,11 +1,13 @@ import { createConfig } from "ponder"; import { + type ContractConfig, DatasourceNames, RegistryABI, ResolverABI, StandaloneReverseRegistrarABI, ThreeDNSTokenABI, + UpgradeableProxyABI, } from "@ensnode/datasources"; import { buildBlockNumberRange, PluginName } from "@ensnode/ensnode-sdk"; import { @@ -74,6 +76,11 @@ export default createPlugin({ rrScroll, } = maybeGetDatasources(config.namespace, ALL_DATASOURCE_NAMES); + // The 3DNS Resolver is defined only on the Mainnet ENS namespace's ENSRoot Datasource; access + // it loosely since ENSRoot's contract set is otherwise uniform across namespaces. + const threeDNSResolver = (ensroot.contracts as Record) + .ThreeDNSResolver; + return createConfig({ chains: chainsConnectionConfigForDatasources( config.namespace, @@ -101,6 +108,22 @@ export default createPlugin({ ), }, + /////////////////////////////////// + // Upgradeable (proxy) Resolvers + /////////////////////////////////// + // Watch known EIP-1967 proxy Resolvers for `Upgraded` events to re-classify their + // `supportsInterface`-derived metadata (currently `Resolver.extended`), which is otherwise + // fixed at first-visibility and goes stale when a proxy activates an interface post-assignment. + // Scoped to the 3DNS Resolver (fixes `.box`); see issue #2275. + [namespaceContract(pluginName, "ThreeDNSResolver")]: { + abi: UpgradeableProxyABI, + chain: { + // only the Mainnet ENS namespace defines the 3DNS Resolver (resolves `.box`) + ...(threeDNSResolver && + chainConfigForContract(config.globalBlockrange, ensroot.chain.id, threeDNSResolver)), + }, + }, + ///////////////////// // ENSv1 RegistryOld ///////////////////// diff --git a/packages/datasources/src/abis/shared/UpgradeableProxy.ts b/packages/datasources/src/abis/shared/UpgradeableProxy.ts new file mode 100644 index 0000000000..62dbc01848 --- /dev/null +++ b/packages/datasources/src/abis/shared/UpgradeableProxy.ts @@ -0,0 +1,18 @@ +/** + * Minimal ABI for EIP-1967 upgradeable proxies. + * + * Captures only the `Upgraded(address indexed implementation)` event, emitted by transparent/UUPS + * proxies whenever their implementation slot changes. ENSIndexer watches this on known proxy + * Resolver contracts to re-classify `supportsInterface`-derived metadata (e.g. `Resolver.extended`) + * after a post-assignment upgrade activates an interface. + * + * @see https://eips.ethereum.org/EIPS/eip-1967 + */ +export const UpgradeableProxy = [ + { + type: "event", + name: "Upgraded", + inputs: [{ name: "implementation", type: "address", indexed: true, internalType: "address" }], + anonymous: false, + }, +] as const; diff --git a/packages/datasources/src/index.ts b/packages/datasources/src/index.ts index f94714fb49..c0c6a15279 100644 --- a/packages/datasources/src/index.ts +++ b/packages/datasources/src/index.ts @@ -4,6 +4,7 @@ export { Registry as RegistryABI } from "./abis/ensv2/Registry"; export { L2ReverseRegistrar as L2ReverseRegistrarABI } from "./abis/root/L2ReverseRegistrar"; export { StandaloneReverseRegistrar as StandaloneReverseRegistrarABI } from "./abis/shared/StandaloneReverseRegistrar"; export { UniversalResolverABI } from "./abis/shared/UniversalResolver"; +export { UpgradeableProxy as UpgradeableProxyABI } from "./abis/shared/UpgradeableProxy"; export { ThreeDNSToken as ThreeDNSTokenABI } from "./abis/threedns/ThreeDNSToken"; export * from "./identify-contracts"; export { AnyRegistrarABI } from "./lib/AnyRegistrarABI"; diff --git a/packages/datasources/src/mainnet.ts b/packages/datasources/src/mainnet.ts index f3e2a4925d..fd6fa1082a 100644 --- a/packages/datasources/src/mainnet.ts +++ b/packages/datasources/src/mainnet.ts @@ -23,6 +23,7 @@ import { Seaport as Seaport1_5 } from "./abis/seaport/Seaport1.5"; // Shared ABIs import { StandaloneReverseRegistrar } from "./abis/shared/StandaloneReverseRegistrar"; import { UniversalResolverABI } from "./abis/shared/UniversalResolver"; +import { UpgradeableProxy } from "./abis/shared/UpgradeableProxy"; import { ThreeDNSToken } from "./abis/threedns/ThreeDNSToken"; import { ResolverABI } from "./lib/ResolverABI"; // Types @@ -55,6 +56,16 @@ export default { abi: ResolverABI, startBlock: 3327417, // ignores any Resolver events prior to `startBlock` of ENSv1RegistryOld on Mainnet }, + + // The 3DNS protocol-wide Resolver, an EIP-1967 upgradeable proxy that resolves 3DNS TLDs + // (e.g. `.box`) on Mainnet via ENSIP-10 + CCIP-Read. It activated `IExtendedResolver` support + // via an `Upgraded` ~1.3h after assignment, so its first-visibility classification is stale + // `false`; we watch its `Upgraded` events to re-classify `Resolver.extended`. See issue #2275. + ThreeDNSResolver: { + abi: UpgradeableProxy, + address: "0xf97aac6c8dbaebcb54ff166d79706e3af7a813c8", + startBlock: 19128555, // proxy deploy + assignment as `box`'s resolver + }, BaseRegistrar: { abi: root_BaseRegistrar, address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", From 41afc5066b66b86ecaf2fff4cb5e2ab04b1f5bff Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 15:07:07 -0500 Subject: [PATCH 2/6] checkpoint: self review --- .../reclassify-extended-on-proxy-upgrade.md | 3 +- .../resolver-db-helpers.ts | 62 +++++++++---------- .../protocol-acceleration/event-handlers.ts | 4 +- .../handlers/ThreeDNSResolver.ts | 25 -------- .../handlers/UpgradeableProxy.ts | 21 +++++++ .../plugins/protocol-acceleration/plugin.ts | 36 +++++------ .../src/abis/shared/UpgradeableProxy.ts | 8 +-- packages/datasources/src/mainnet.ts | 11 ---- 8 files changed, 74 insertions(+), 96 deletions(-) delete mode 100644 apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts create mode 100644 apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts diff --git a/.changeset/reclassify-extended-on-proxy-upgrade.md b/.changeset/reclassify-extended-on-proxy-upgrade.md index 5d736a334a..d3d409aa60 100644 --- a/.changeset/reclassify-extended-on-proxy-upgrade.md +++ b/.changeset/reclassify-extended-on-proxy-upgrade.md @@ -1,6 +1,5 @@ --- -"@ensnode/datasources": patch "ensindexer": patch --- -ENSIndexer now re-classifies a Resolver's `extended` (ENSIP-10 `IExtendedResolver`) support when a known proxy Resolver emits an EIP-1967 `Upgraded` event, instead of fixing the value once at first visibility. Proxy Resolvers that activate `IExtendedResolver` via a post-assignment upgrade (e.g. the 3DNS Resolver behind `.box`) were stuck `extended = false` forever, silently breaking wildcard resolution for affected names (`mystery.box` returned `null`). The Protocol Acceleration plugin now watches the 3DNS Resolver's `Upgraded` events and re-runs the `supportsInterface` probe against the new implementation. Fixes #2275. +ENSIndexer now re-derives a Resolver's ENSIP-10 `IExtendedResolver` support when a known proxy Resolver emits an EIP-1967 `Upgraded` event, instead of fixing the value once at first visibility. Proxy Resolvers that activate `IExtendedResolver` via a post-assignment upgrade (e.g. the 3DNS Resolver behind `.box`) were stuck `extended = false` forever, silently breaking wildcard resolution for affected names. diff --git a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts index b3923c465b..1c08557f30 100644 --- a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts +++ b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts @@ -33,58 +33,54 @@ type ResolverRecordsCompositeKey = Pick< >; /** - * Ensures a Resolver entity exists for `resolver`, capturing additional metadata. - * - * @dev performs a single `supportsInterface` RPC (via Ponder's cached `context.client`) to determine - * `isExtended` support. + * Ensures a Resolver entity exists for `resolver`, updating its Supported Interfaces. */ -export async function upsertResolver( - context: IndexingEngineContext, - resolver: AccountId, -): Promise { +export async function upsertResolver(context: IndexingEngineContext, resolver: AccountId) { const id = makeResolverId(resolver); const existing = await context.ensDb.find(ensIndexerSchema.resolver, { id }); + + // if already exists, no-op if (existing) return existing; + // insert the new Resolver record + await context.ensDb.insert(ensIndexerSchema.resolver).values({ id, ...resolver }); + + // update its Supported Interfaces + await updateResolverInterfaces(context, resolver); +} + +/** + * Updates a Resolver's Supported Interfaces. + */ +async function updateResolverInterfaces(context: IndexingEngineContext, resolver: AccountId) { + const id = makeResolverId(resolver); + const isExtended = await isExtendedResolver({ - publicClient: context.client, address: resolver.address, + publicClient: context.client, }); - const row = { id, ...resolver, isExtended }; - await context.ensDb.insert(ensIndexerSchema.resolver).values(row).onConflictDoNothing(); - return row; + await context.ensDb.update(ensIndexerSchema.resolver, { id }).set({ isExtended }); } /** - * Re-classifies a Resolver's `supportsInterface`-derived metadata (currently `isExtended`) by - * re-running the eip-165 probe and overwriting the stored row. - * - * The initial `isExtended` classification in {@link upsertResolver} is computed once, at the block - * the Resolver is first seen. For EIP-1967 proxy Resolvers that activate `IExtendedResolver` via an - * `Upgraded` _after_ assignment (e.g. the 3DNS Resolver behind `.box`), that snapshot is stale - * `false` forever. Call this from the proxy's `Upgraded` handler so the probe re-runs against the - * new implementation (at the upgrade block) and `Resolver.extended` reflects current support. + * Handles a Resolver's implementation changing by updating its Supported Interfaces, if the Resolver + * is already indexed. * - * @dev performs a single `supportsInterface` RPC (via Ponder's cached `context.client`). Upserts so - * an `Upgraded` observed before the Resolver has emitted any other event still creates the row. + * @dev intentionally avoids upserting a Resolver entity for contracts not already an indexed Resolver. + * If a contract were to emit Upgraded and then emit a Resolver event, `upsertResolver` above would + * ensure that the Supported Interfaces are correctly indexed. */ -export async function reclassifyResolverExtendedSupport( +export async function handleResolverImplementationChange( context: IndexingEngineContext, resolver: AccountId, -): Promise { +) { const id = makeResolverId(resolver); + const existing = await context.ensDb.find(ensIndexerSchema.resolver, { id }); + if (!existing) return; - const isExtended = await isExtendedResolver({ - publicClient: context.client, - address: resolver.address, - }); - - await context.ensDb - .insert(ensIndexerSchema.resolver) - .values({ id, ...resolver, isExtended }) - .onConflictDoUpdate({ isExtended }); + await updateResolverInterfaces(context, resolver); } /** diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts index 20dad0dad7..0f06a31a83 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts @@ -2,14 +2,14 @@ import attach_ENSv1RegistryHandlers from "./handlers/ENSv1Registry"; import attach_ENSv2RegistryHandlers from "./handlers/ENSv2Registry"; import attach_ResolverHandlers from "./handlers/Resolver"; import attach_StandaloneReverseRegistrarHandlers from "./handlers/StandaloneReverseRegistrar"; -import attach_ThreeDNSResolverHandlers from "./handlers/ThreeDNSResolver"; import attach_ThreeDNSTokenHandlers from "./handlers/ThreeDNSToken"; +import attach_UpgradableProxyHandlers from "./handlers/UpgradeableProxy"; export default function () { attach_ENSv1RegistryHandlers(); attach_ENSv2RegistryHandlers(); attach_ResolverHandlers(); attach_StandaloneReverseRegistrarHandlers(); - attach_ThreeDNSResolverHandlers(); + attach_UpgradableProxyHandlers(); attach_ThreeDNSTokenHandlers(); } diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts deleted file mode 100644 index 90cd9da099..0000000000 --- a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/ThreeDNSResolver.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { PluginName } from "@ensnode/ensnode-sdk"; - -import { getThisAccountId } from "@/lib/get-this-account-id"; -import { addOnchainEventListener } from "@/lib/indexing-engines/ponder"; -import { namespaceContract } from "@/lib/plugin-helpers"; -import { reclassifyResolverExtendedSupport } from "@/lib/protocol-acceleration/resolver-db-helpers"; - -const pluginName = PluginName.ProtocolAcceleration; - -/** - * Handlers for the 3DNS Resolver, an EIP-1967 upgradeable proxy (resolves `.box` and other 3DNS - * TLDs on Mainnet). - * - * Its `IExtendedResolver` (ENSIP-10) support was activated by an `Upgraded` after it was assigned - * as a Resolver, so the first-visibility `isExtended` classification is a stale `false`. We re-run - * the eip-165 probe on each `Upgraded` to keep `Resolver.extended` correct. See issue #2275. - */ -export default function () { - addOnchainEventListener( - namespaceContract(pluginName, "ThreeDNSResolver:Upgraded"), - async ({ context, event }) => { - await reclassifyResolverExtendedSupport(context, getThisAccountId(context, event)); - }, - ); -} diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts new file mode 100644 index 0000000000..b1faac55aa --- /dev/null +++ b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts @@ -0,0 +1,21 @@ +import { PluginName } from "@ensnode/ensnode-sdk"; + +import { getThisAccountId } from "@/lib/get-this-account-id"; +import { addOnchainEventListener } from "@/lib/indexing-engines/ponder"; +import { namespaceContract } from "@/lib/plugin-helpers"; +import { handleResolverImplementationChange } from "@/lib/protocol-acceleration/resolver-db-helpers"; + +const pluginName = PluginName.ProtocolAcceleration; + +/** + * Handlers for UpgradeableProxy, necessary for tracking potential implementation updates to Resolvers. + */ +export default function () { + addOnchainEventListener( + namespaceContract(pluginName, "ThreeDNSResolver:Upgraded"), + async ({ context, event }) => { + const resolver = getThisAccountId(context, event); + await handleResolverImplementationChange(context, resolver); + }, + ); +} diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts index 10dd84122d..df40489ad8 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts @@ -1,7 +1,6 @@ import { createConfig } from "ponder"; import { - type ContractConfig, DatasourceNames, RegistryABI, ResolverABI, @@ -76,11 +75,6 @@ export default createPlugin({ rrScroll, } = maybeGetDatasources(config.namespace, ALL_DATASOURCE_NAMES); - // The 3DNS Resolver is defined only on the Mainnet ENS namespace's ENSRoot Datasource; access - // it loosely since ENSRoot's contract set is otherwise uniform across namespaces. - const threeDNSResolver = (ensroot.contracts as Record) - .ThreeDNSResolver; - return createConfig({ chains: chainsConnectionConfigForDatasources( config.namespace, @@ -108,20 +102,24 @@ export default createPlugin({ ), }, - /////////////////////////////////// - // Upgradeable (proxy) Resolvers - /////////////////////////////////// - // Watch known EIP-1967 proxy Resolvers for `Upgraded` events to re-classify their - // `supportsInterface`-derived metadata (currently `Resolver.extended`), which is otherwise - // fixed at first-visibility and goes stale when a proxy activates an interface post-assignment. - // Scoped to the 3DNS Resolver (fixes `.box`); see issue #2275. - [namespaceContract(pluginName, "ThreeDNSResolver")]: { + //////////////////// + // UpgradeableProxy + //////////////////// + [namespaceContract(pluginName, "UpgradeableProxy")]: { abi: UpgradeableProxyABI, - chain: { - // only the Mainnet ENS namespace defines the 3DNS Resolver (resolves `.box`) - ...(threeDNSResolver && - chainConfigForContract(config.globalBlockrange, ensroot.chain.id, threeDNSResolver)), - }, + chain: getDatasourcesWithResolvers(config.namespace).reduce( + (memo, datasource) => ({ + ...memo, + [datasource.chain.id.toString()]: constrainBlockrange( + config.globalBlockrange, + buildBlockNumberRange( + datasource.contracts.Resolver.startBlock, + datasource.contracts.Resolver.endBlock, + ), + ), + }), + {}, + ), }, ///////////////////// diff --git a/packages/datasources/src/abis/shared/UpgradeableProxy.ts b/packages/datasources/src/abis/shared/UpgradeableProxy.ts index 62dbc01848..6978ea3d90 100644 --- a/packages/datasources/src/abis/shared/UpgradeableProxy.ts +++ b/packages/datasources/src/abis/shared/UpgradeableProxy.ts @@ -1,10 +1,10 @@ +import type { Abi } from "viem"; + /** * Minimal ABI for EIP-1967 upgradeable proxies. * * Captures only the `Upgraded(address indexed implementation)` event, emitted by transparent/UUPS - * proxies whenever their implementation slot changes. ENSIndexer watches this on known proxy - * Resolver contracts to re-classify `supportsInterface`-derived metadata (e.g. `Resolver.extended`) - * after a post-assignment upgrade activates an interface. + * proxies whenever their implementation slot changes. * * @see https://eips.ethereum.org/EIPS/eip-1967 */ @@ -15,4 +15,4 @@ export const UpgradeableProxy = [ inputs: [{ name: "implementation", type: "address", indexed: true, internalType: "address" }], anonymous: false, }, -] as const; +] as const satisfies Abi; diff --git a/packages/datasources/src/mainnet.ts b/packages/datasources/src/mainnet.ts index fd6fa1082a..f3e2a4925d 100644 --- a/packages/datasources/src/mainnet.ts +++ b/packages/datasources/src/mainnet.ts @@ -23,7 +23,6 @@ import { Seaport as Seaport1_5 } from "./abis/seaport/Seaport1.5"; // Shared ABIs import { StandaloneReverseRegistrar } from "./abis/shared/StandaloneReverseRegistrar"; import { UniversalResolverABI } from "./abis/shared/UniversalResolver"; -import { UpgradeableProxy } from "./abis/shared/UpgradeableProxy"; import { ThreeDNSToken } from "./abis/threedns/ThreeDNSToken"; import { ResolverABI } from "./lib/ResolverABI"; // Types @@ -56,16 +55,6 @@ export default { abi: ResolverABI, startBlock: 3327417, // ignores any Resolver events prior to `startBlock` of ENSv1RegistryOld on Mainnet }, - - // The 3DNS protocol-wide Resolver, an EIP-1967 upgradeable proxy that resolves 3DNS TLDs - // (e.g. `.box`) on Mainnet via ENSIP-10 + CCIP-Read. It activated `IExtendedResolver` support - // via an `Upgraded` ~1.3h after assignment, so its first-visibility classification is stale - // `false`; we watch its `Upgraded` events to re-classify `Resolver.extended`. See issue #2275. - ThreeDNSResolver: { - abi: UpgradeableProxy, - address: "0xf97aac6c8dbaebcb54ff166d79706e3af7a813c8", - startBlock: 19128555, // proxy deploy + assignment as `box`'s resolver - }, BaseRegistrar: { abi: root_BaseRegistrar, address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", From 32e200d7723c61b4620b8de1d6e4cdb651663449 Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 15:14:20 -0500 Subject: [PATCH 3/6] fix: typechec --- .../src/lib/protocol-acceleration/resolver-db-helpers.ts | 2 +- .../plugins/protocol-acceleration/handlers/UpgradeableProxy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts index 1c08557f30..0194f20f69 100644 --- a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts +++ b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts @@ -41,7 +41,7 @@ export async function upsertResolver(context: IndexingEngineContext, resolver: A const existing = await context.ensDb.find(ensIndexerSchema.resolver, { id }); // if already exists, no-op - if (existing) return existing; + if (existing) return; // insert the new Resolver record await context.ensDb.insert(ensIndexerSchema.resolver).values({ id, ...resolver }); diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts index b1faac55aa..6d304d285c 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts @@ -12,7 +12,7 @@ const pluginName = PluginName.ProtocolAcceleration; */ export default function () { addOnchainEventListener( - namespaceContract(pluginName, "ThreeDNSResolver:Upgraded"), + namespaceContract(pluginName, "UpgradeableProxy:Upgraded"), async ({ context, event }) => { const resolver = getThisAccountId(context, event); await handleResolverImplementationChange(context, resolver); From a80133243374b4f71e342cf7c39ce3f0d13274f5 Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 15:37:52 -0500 Subject: [PATCH 4/6] fix: revert back to single-address checking --- .../protocol-acceleration/event-handlers.ts | 4 +-- ...bleProxy.ts => UpgradableProxyResolver.ts} | 4 +-- .../plugins/protocol-acceleration/plugin.ts | 28 ++++++++----------- packages/datasources/src/mainnet.ts | 5 ++++ 4 files changed, 21 insertions(+), 20 deletions(-) rename apps/ensindexer/src/plugins/protocol-acceleration/handlers/{UpgradeableProxy.ts => UpgradableProxyResolver.ts} (78%) diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts index 0f06a31a83..5c56cf4399 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts @@ -3,13 +3,13 @@ import attach_ENSv2RegistryHandlers from "./handlers/ENSv2Registry"; import attach_ResolverHandlers from "./handlers/Resolver"; import attach_StandaloneReverseRegistrarHandlers from "./handlers/StandaloneReverseRegistrar"; import attach_ThreeDNSTokenHandlers from "./handlers/ThreeDNSToken"; -import attach_UpgradableProxyHandlers from "./handlers/UpgradeableProxy"; +import attach_UpgradableProxyResolverHandlers from "./handlers/UpgradableProxyResolver"; export default function () { attach_ENSv1RegistryHandlers(); attach_ENSv2RegistryHandlers(); attach_ResolverHandlers(); attach_StandaloneReverseRegistrarHandlers(); - attach_UpgradableProxyHandlers(); + attach_UpgradableProxyResolverHandlers(); attach_ThreeDNSTokenHandlers(); } diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts similarity index 78% rename from apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts rename to apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts index 6d304d285c..b074f18550 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxy.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts @@ -8,11 +8,11 @@ import { handleResolverImplementationChange } from "@/lib/protocol-acceleration/ const pluginName = PluginName.ProtocolAcceleration; /** - * Handlers for UpgradeableProxy, necessary for tracking potential implementation updates to Resolvers. + * Handlers for any Resolvers that are UpgradableProxies, necessary for tracking implementation updates. */ export default function () { addOnchainEventListener( - namespaceContract(pluginName, "UpgradeableProxy:Upgraded"), + namespaceContract(pluginName, "UpgradableProxyResolver:Upgraded"), async ({ context, event }) => { const resolver = getThisAccountId(context, event); await handleResolverImplementationChange(context, resolver); diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts index df40489ad8..46450efb48 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts @@ -102,24 +102,20 @@ export default createPlugin({ ), }, - //////////////////// - // UpgradeableProxy - //////////////////// - [namespaceContract(pluginName, "UpgradeableProxy")]: { + //////////////////////////// + // UpgradableProxyResolver + //////////////////////////// + [namespaceContract(pluginName, "UpgradableProxyResolver")]: { abi: UpgradeableProxyABI, - chain: getDatasourcesWithResolvers(config.namespace).reduce( - (memo, datasource) => ({ - ...memo, - [datasource.chain.id.toString()]: constrainBlockrange( + chain: { + // the DotBoxL1Resolver is an UpgradableProxy + ...("DotBoxL1Resolver" in ensroot.contracts && + chainConfigForContract( config.globalBlockrange, - buildBlockNumberRange( - datasource.contracts.Resolver.startBlock, - datasource.contracts.Resolver.endBlock, - ), - ), - }), - {}, - ), + ensroot.chain.id, + ensroot.contracts.DotBoxL1Resolver, + )), + }, }, ///////////////////// diff --git a/packages/datasources/src/mainnet.ts b/packages/datasources/src/mainnet.ts index f3e2a4925d..642ddf3a29 100644 --- a/packages/datasources/src/mainnet.ts +++ b/packages/datasources/src/mainnet.ts @@ -100,6 +100,11 @@ export default { address: "0x1507ce9421232fdbd302f5ebe4590f8d77febbff", startBlock: 24640923, }, + DotBoxL1Resolver: { + abi: ResolverABI, + address: "0xf97aac6c8dbaebcb54ff166d79706e3af7a813c8", + startBlock: 19128555, + }, // the Resolver for *.argent.xyz names ArgentResolver: { From c0f8b8c44a3343b1380399d5dd117d7d3910a379 Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 16:38:54 -0500 Subject: [PATCH 5/6] docs: clarify upsertResolver derives interfaces on first insert (loop 1) --- .../src/lib/protocol-acceleration/resolver-db-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts index 0194f20f69..64b366a42c 100644 --- a/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts +++ b/apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts @@ -33,7 +33,7 @@ type ResolverRecordsCompositeKey = Pick< >; /** - * Ensures a Resolver entity exists for `resolver`, updating its Supported Interfaces. + * Ensures a Resolver entity exists for `resolver`, deriving its Supported Interfaces on first insert. */ export async function upsertResolver(context: IndexingEngineContext, resolver: AccountId) { const id = makeResolverId(resolver); From 212e450a234cd82dec279f7df345c7e6d28ae576 Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 9 Jun 2026 16:49:10 -0500 Subject: [PATCH 6/6] refactor: spell UpgradeableProxyResolver consistently (loop 2) --- .../src/plugins/protocol-acceleration/event-handlers.ts | 4 ++-- ...gradableProxyResolver.ts => UpgradeableProxyResolver.ts} | 4 ++-- apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename apps/ensindexer/src/plugins/protocol-acceleration/handlers/{UpgradableProxyResolver.ts => UpgradeableProxyResolver.ts} (78%) diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts index 5c56cf4399..7930d58d1c 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/event-handlers.ts @@ -3,13 +3,13 @@ import attach_ENSv2RegistryHandlers from "./handlers/ENSv2Registry"; import attach_ResolverHandlers from "./handlers/Resolver"; import attach_StandaloneReverseRegistrarHandlers from "./handlers/StandaloneReverseRegistrar"; import attach_ThreeDNSTokenHandlers from "./handlers/ThreeDNSToken"; -import attach_UpgradableProxyResolverHandlers from "./handlers/UpgradableProxyResolver"; +import attach_UpgradeableProxyResolverHandlers from "./handlers/UpgradeableProxyResolver"; export default function () { attach_ENSv1RegistryHandlers(); attach_ENSv2RegistryHandlers(); attach_ResolverHandlers(); attach_StandaloneReverseRegistrarHandlers(); - attach_UpgradableProxyResolverHandlers(); + attach_UpgradeableProxyResolverHandlers(); attach_ThreeDNSTokenHandlers(); } diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxyResolver.ts similarity index 78% rename from apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts rename to apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxyResolver.ts index b074f18550..abdae25449 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradableProxyResolver.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/handlers/UpgradeableProxyResolver.ts @@ -8,11 +8,11 @@ import { handleResolverImplementationChange } from "@/lib/protocol-acceleration/ const pluginName = PluginName.ProtocolAcceleration; /** - * Handlers for any Resolvers that are UpgradableProxies, necessary for tracking implementation updates. + * Handlers for any Resolvers that are UpgradeableProxies, necessary for tracking implementation updates. */ export default function () { addOnchainEventListener( - namespaceContract(pluginName, "UpgradableProxyResolver:Upgraded"), + namespaceContract(pluginName, "UpgradeableProxyResolver:Upgraded"), async ({ context, event }) => { const resolver = getThisAccountId(context, event); await handleResolverImplementationChange(context, resolver); diff --git a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts index 46450efb48..6e36342d9e 100644 --- a/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts +++ b/apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts @@ -103,12 +103,12 @@ export default createPlugin({ }, //////////////////////////// - // UpgradableProxyResolver + // UpgradeableProxyResolver //////////////////////////// - [namespaceContract(pluginName, "UpgradableProxyResolver")]: { + [namespaceContract(pluginName, "UpgradeableProxyResolver")]: { abi: UpgradeableProxyABI, chain: { - // the DotBoxL1Resolver is an UpgradableProxy + // the DotBoxL1Resolver is an UpgradeableProxy ...("DotBoxL1Resolver" in ensroot.contracts && chainConfigForContract( config.globalBlockrange,