From e2523e14244c3d989e408e88fc862dcbb42b86d4 Mon Sep 17 00:00:00 2001 From: shrugs Date: Tue, 28 Apr 2026 17:37:22 -0500 Subject: [PATCH 1/8] checkpoint: add optional sender override for hca-aware events --- .../src/lib/ensv2/event-db-helpers.ts | 10 +++++- .../src/ensindexer-abstract/ensv2.schema.ts | 33 ++++++++++--------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts b/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts index ac7c99aa2..6c3eb7a9d 100644 --- a/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts +++ b/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts @@ -3,6 +3,7 @@ import { type DomainId, makePermissionsId, makeResolverId, + type NormalizedAddress, type PermissionsUserId, } from "enssdk"; import type { Hash } from "viem"; @@ -24,7 +25,11 @@ const hasTopics = (topics: LogEventBase["log"]["topics"]): topics is Topics => * * @returns event.id */ -export async function ensureEvent(context: IndexingEngineContext, event: LogEventBase) { +export async function ensureEvent( + context: IndexingEngineContext, + event: LogEventBase, + sender?: NormalizedAddress, +) { // all relevant ENS events obviously have a topic, so we can safely constrain the type of this data if (!hasTopics(event.log.topics)) { throw new Error(`Invariant: All events indexed via ensureEvent must have at least one topic.`); @@ -39,6 +44,9 @@ export async function ensureEvent(context: IndexingEngineContext, event: LogEven .values({ id: event.id, + // sender override if provided, otherwise transaction.from + sender: sender ?? event.transaction.from, + // chain chainId: context.chain.id, diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts index 543f7232e..fe98dd14b 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts @@ -1,10 +1,10 @@ import type { - Address, ChainId, DomainId, InterpretedLabel, LabelHash, Node, + NormalizedAddress, PermissionsId, PermissionsResourceId, PermissionsUserId, @@ -96,6 +96,9 @@ export const event = onchainTable( // Ponder's event.id id: t.text().primaryKey(), + // sender of the transaction (optionally HCA-aware for supported events) + sender: t.hex().notNull().$type(), + // Event Log Metadata // chain @@ -109,11 +112,11 @@ export const event = onchainTable( // transaction transactionHash: t.hex().notNull().$type(), transactionIndex: t.integer().notNull(), - from: t.hex().notNull().$type
(), - to: t.hex().$type
(), // NOTE: a null `to` means this was a tx that deployed a contract + from: t.hex().notNull().$type(), + to: t.hex().$type(), // NOTE: a null `to` means this was a tx that deployed a contract // log - address: t.hex().notNull().$type
(), + address: t.hex().notNull().$type(), logIndex: t.integer().notNull().$type(), selector: t.hex().notNull().$type(), topics: t.hex().array().notNull().$type<[Hash, ...Hash[]]>(), @@ -167,7 +170,7 @@ export const permissionsUserEvent = onchainTable( /////////// export const account = onchainTable("accounts", (t) => ({ - id: t.hex().primaryKey().$type
(), + id: t.hex().primaryKey().$type(), })); export const account_relations = relations(account, ({ many }) => ({ @@ -196,7 +199,7 @@ export const registry = onchainTable( type: registryType().notNull(), chainId: t.integer().notNull().$type(), - address: t.hex().notNull().$type
(), + address: t.hex().notNull().$type(), // If this is an ENSv1VirtualRegistry, `node` is the namehash of the parent ENSv1 domain that // owns it, otherwise null. @@ -251,10 +254,10 @@ export const domain = onchainTable( labelHash: t.hex().notNull().$type(), // may have an owner - ownerId: t.hex().$type
(), + ownerId: t.hex().$type(), // If this is an ENSv1Domain, may have a `rootRegistryOwner`, otherwise null. - rootRegistryOwnerId: t.hex().$type
(), + rootRegistryOwnerId: t.hex().$type(), // NOTE: Domain-Resolver Relations tracked via Protocol Acceleration plugin // NOTE: parent is derived via registryCanonicalDomain, not stored on the domain row @@ -331,13 +334,13 @@ export const registration = onchainTable( // registrar AccountId registrarChainId: t.integer().notNull().$type(), - registrarAddress: t.hex().notNull().$type
(), + registrarAddress: t.hex().notNull().$type(), // may reference a registrant - registrantId: t.hex().$type
(), + registrantId: t.hex().$type(), // may reference an unregistrant - unregistrantId: t.hex().$type
(), + unregistrantId: t.hex().$type(), // may have referrer data referrer: t.hex().$type(), @@ -492,7 +495,7 @@ export const permissions = onchainTable( id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), - address: t.hex().notNull().$type
(), + address: t.hex().notNull().$type(), }), (t) => ({ byId: uniqueIndex().on(t.chainId, t.address), @@ -510,7 +513,7 @@ export const permissionsResource = onchainTable( id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), - address: t.hex().notNull().$type
(), + address: t.hex().notNull().$type(), resource: t.bigint().notNull(), }), (t) => ({ @@ -531,9 +534,9 @@ export const permissionsUser = onchainTable( id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), - address: t.hex().notNull().$type
(), + address: t.hex().notNull().$type(), resource: t.bigint().notNull(), - user: t.hex().notNull().$type
(), + user: t.hex().notNull().$type(), // has one roles bitmap roles: t.bigint().notNull(), From 89d3e5e8e3065477a596de112a8f648d845e7a14 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 00:07:14 -0500 Subject: [PATCH 2/8] checkpoint --- .../src/lib/ensv2/account-db-helpers.ts | 18 +++++----- .../src/lib/ensv2/event-db-helpers.ts | 2 +- .../ensv2/handlers/ensv2/ENSv2Registry.ts | 34 ++++++++++--------- .../ensv2/handlers/ensv2/ETHRegistrar.ts | 7 ++-- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts b/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts index 0209813ac..e8942d6e8 100644 --- a/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts +++ b/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts @@ -1,4 +1,4 @@ -import type { Address } from "enssdk"; +import type { Address, NormalizedAddress } from "enssdk"; import { interpretAddress } from "@ensnode/ensnode-sdk"; @@ -8,12 +8,14 @@ import { ensIndexerSchema, type IndexingEngineContext } from "@/lib/indexing-eng * Ensures that the account identified by `address` exists. * If `address` is the zeroAddress, no-op. */ -export async function ensureAccount(context: IndexingEngineContext, address: Address) { - const interpreted = interpretAddress(address); - if (interpreted === null) return; +export async function ensureAccount( + context: IndexingEngineContext, + address: Address, +): Promise { + const id = interpretAddress(address); + if (id === null) return null; - await context.ensDb - .insert(ensIndexerSchema.account) - .values({ id: interpreted }) - .onConflictDoNothing(); + await context.ensDb.insert(ensIndexerSchema.account).values({ id: id }).onConflictDoNothing(); + + return id; } diff --git a/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts b/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts index 6c3eb7a9d..21d5abfbd 100644 --- a/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts +++ b/apps/ensindexer/src/lib/ensv2/event-db-helpers.ts @@ -28,7 +28,7 @@ const hasTopics = (topics: LogEventBase["log"]["topics"]): topics is Topics => export async function ensureEvent( context: IndexingEngineContext, event: LogEventBase, - sender?: NormalizedAddress, + sender?: NormalizedAddress | null, ) { // all relevant ENS events obviously have a topic, so we can safely constrain the type of this data if (!hasTopics(event.log.topics)) { diff --git a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts index 170826f2f..480091007 100644 --- a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts +++ b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts @@ -128,14 +128,14 @@ export default function () { .onConflictDoUpdate({ tokenId }); // insert Registration - const eventId = await ensureEvent(context, event); - await ensureAccount(context, registrant); + const registrantId = await ensureAccount(context, registrant); + const eventId = await ensureEvent(context, event, registrantId); await insertLatestRegistration(context, { domainId, type: isReservation ? "ENSv2RegistryReservation" : "ENSv2RegistryRegistration", registrarChainId: registry.chainId, registrarAddress: registry.address, - registrantId: interpretAddress(registrant), + registrantId, start: event.block.timestamp, expiry, eventId, @@ -189,17 +189,17 @@ export default function () { // unregistering a label just immediately sets its expiration to event.block.timestamp, which // effectively removes it from resolution (which interprets expired names as non-existent) - await ensureAccount(context, unregistrant); + const unregistrantId = await ensureAccount(context, unregistrant); await context.ensDb.update(ensIndexerSchema.registration, { id: registration.id }).set({ expiry: event.block.timestamp, - unregistrantId: interpretAddress(unregistrant), + unregistrantId, }); // NOTE(shrugs): PermissionedRegistry also increments eacVersionId and tokenVersionId if there was a // previous owner, but i'm not sure if we need to handle that detail here // push event to domain history - const eventId = await ensureEvent(context, event); + const eventId = await ensureEvent(context, event, unregistrantId); await ensureDomainEvent(context, domainId, eventId); }, ); @@ -217,7 +217,6 @@ export default function () { sender: NormalizedAddress; }>; }) => { - // biome-ignore lint/correctness/noUnusedVariables: not sure if we care to index sender const { tokenId, newExpiry: expiry, sender } = event.args; const registry = getThisAccountId(context, event); @@ -244,7 +243,8 @@ export default function () { .set({ expiry }); // push event to domain history - const eventId = await ensureEvent(context, event); + const senderId = await ensureAccount(context, sender); + const eventId = await ensureEvent(context, event, senderId); await ensureDomainEvent(context, domainId, eventId); }, ); @@ -259,9 +259,10 @@ export default function () { event: EventWithArgs<{ tokenId: TokenId; subregistry: NormalizedAddress; + sender: NormalizedAddress; }>; }) => { - const { tokenId, subregistry: _subregistry } = event.args; + const { tokenId, subregistry: _subregistry, sender } = event.args; const subregistry = interpretAddress(_subregistry); const registryAccountId = getThisAccountId(context, event); @@ -301,7 +302,8 @@ export default function () { } // push event to domain history - const eventId = await ensureEvent(context, event); + const senderId = await ensureAccount(context, sender); + const eventId = await ensureEvent(context, event, senderId); await ensureDomainEvent(context, domainId, eventId); }, ); @@ -344,9 +346,9 @@ export default function () { event, }: { context: IndexingEngineContext; - event: EventWithArgs<{ id: TokenId; to: NormalizedAddress }>; + event: EventWithArgs<{ id: TokenId; to: NormalizedAddress; operator: NormalizedAddress }>; }) { - const { id: tokenId, to: owner } = event.args; + const { id: tokenId, to: owner, operator } = event.args; const storageId = makeStorageId(tokenId); const registry = getThisAccountId(context, event); @@ -358,12 +360,12 @@ export default function () { if (!exists) return; // no-op non-Registry ERC1155 Transfers // update the Domain's ownerId - await context.ensDb - .update(ensIndexerSchema.domain, { id: domainId }) - .set({ ownerId: interpretAddress(owner) }); + const ownerId = await ensureAccount(context, owner); + await context.ensDb.update(ensIndexerSchema.domain, { id: domainId }).set({ ownerId }); // push event to domain history - const eventId = await ensureEvent(context, event); + const operatorId = await ensureAccount(context, operator); + const eventId = await ensureEvent(context, event, operatorId); await ensureDomainEvent(context, domainId, eventId); } diff --git a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts index 1a8ec7ded..ea42ec341 100644 --- a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts +++ b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts @@ -103,12 +103,11 @@ export default function () { } // upsert registrant - await ensureAccount(context, owner); + const registrantId = await ensureAccount(context, owner); // update latest Registration await context.ensDb.update(ensIndexerSchema.registration, { id: registration.id }).set({ - // TODO: reconsider 'Registration.registrant' if ENSv2 doesn't provide explicit 'registrant' - registrantId: interpretAddress(owner), + registrantId, // we now know the correct registrar to attribute to, so overwrite registrarChainId: registrar.chainId, @@ -122,7 +121,7 @@ export default function () { }); // push event to domain history - const eventId = await ensureEvent(context, event); + const eventId = await ensureEvent(context, event, registrantId); await ensureDomainEvent(context, domainId, eventId); }, ); From 0afeb0b24e7c95e62964ad592f64311b92a680d3 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 12:36:30 -0500 Subject: [PATCH 3/8] feat: hca-aware events + docs --- .../lib/find-events/find-events-resolver.ts | 5 +++- .../src/omnigraph-api/schema/account.ts | 5 ++-- .../ensapi/src/omnigraph-api/schema/domain.ts | 3 +- apps/ensapi/src/omnigraph-api/schema/event.ts | 28 +++++++++++++++---- .../src/omnigraph-api/schema/permissions.ts | 3 +- .../src/omnigraph-api/schema/registration.ts | 6 ++-- .../schema/registry-permissions-user.ts | 3 +- .../schema/resolver-permissions-user.ts | 3 +- docker/docker-compose.devnet.yml | 11 +++----- docker/envs/.env.docker.devnet | 2 -- .../src/ensindexer-abstract/ensv2.schema.ts | 17 ++++++++--- 11 files changed, 59 insertions(+), 27 deletions(-) diff --git a/apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts b/apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts index bec3513c8..f14bb692a 100644 --- a/apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts +++ b/apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts @@ -26,8 +26,10 @@ interface EventsWhere { timestamp_gte?: bigint | null; /** Filter to events at or before this timestamp. */ timestamp_lte?: bigint | null; - /** Filter to events sent by this address. */ + /** Filter to events whose `tx.from` matches. Not HCA-aware. */ from?: Address | null; + /** Filter to events whose HCA-aware `sender` matches. */ + sender?: Address | null; } /** @@ -49,6 +51,7 @@ function eventsWhereConditions(where?: EventsWhere | null): SQL | undefined { ? lte(ensIndexerSchema.event.timestamp, where.timestamp_lte) : undefined, where.from ? eq(ensIndexerSchema.event.from, where.from) : undefined, + where.sender ? eq(ensIndexerSchema.event.sender, where.sender) : undefined, ); } diff --git a/apps/ensapi/src/omnigraph-api/schema/account.ts b/apps/ensapi/src/omnigraph-api/schema/account.ts index 55a4d0736..08c83d6d2 100644 --- a/apps/ensapi/src/omnigraph-api/schema/account.ts +++ b/apps/ensapi/src/omnigraph-api/schema/account.ts @@ -90,13 +90,14 @@ AccountRef.implement({ // Account.events ////////////////// events: t.connection({ - description: "All Events for which this Account is the sender (i.e. `Transaction.from`).", + description: + "All Events for which this Account is the HCA-aware `sender` (i.e. `Event.sender`).", type: EventRef, args: { where: t.arg({ type: AccountEventsWhereInput }), }, resolve: (parent, args) => - resolveFindEvents({ ...args, where: { ...args.where, from: parent.id } }), + resolveFindEvents({ ...args, where: { ...args.where, sender: parent.id } }), }), /////////////////////// diff --git a/apps/ensapi/src/omnigraph-api/schema/domain.ts b/apps/ensapi/src/omnigraph-api/schema/domain.ts index ea0e147fc..77c8dfafd 100644 --- a/apps/ensapi/src/omnigraph-api/schema/domain.ts +++ b/apps/ensapi/src/omnigraph-api/schema/domain.ts @@ -177,7 +177,8 @@ DomainInterfaceRef.implement({ //////////////// owner: t.field({ type: AccountRef, - description: "The owner of this Domain.", + description: + "If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA-aware owner of the Domain.", nullable: true, resolve: (parent) => parent.ownerId, }), diff --git a/apps/ensapi/src/omnigraph-api/schema/event.ts b/apps/ensapi/src/omnigraph-api/schema/event.ts index 99a9702c7..61713ea98 100644 --- a/apps/ensapi/src/omnigraph-api/schema/event.ts +++ b/apps/ensapi/src/omnigraph-api/schema/event.ts @@ -95,12 +95,24 @@ EventRef.implement({ // Event.from ////////////// from: t.field({ - description: "Identifies the sender of the Transaction within which this Event was emitted.", + description: + "Identifies the sender of the Transaction within which this Event was emitted (`tx.from`). Never HCA-aware — always the EOA/relayer that submitted the transaction. Use `Event.sender` for the HCA-aware actor.", type: "Address", nullable: false, resolve: (parent) => parent.from, }), + //////////////// + // Event.sender + //////////////// + sender: t.field({ + description: + "The HCA-aware sender of the Event. For ENSv2 events that emit an explicit sender/owner/account argument, this is the HCA account address (if used). For all other events (including all ENSv1 events), falls back to `from` (i.e. `tx.from`).", + type: "Address", + nullable: false, + resolve: (parent) => parent.sender, + }), + //////////// // Event.to //////////// @@ -160,7 +172,7 @@ EventRef.implement({ /** * Shared filter for events connections. Used by Domain.events, Resolver.events, Permissions.events, - * and Account.events (which excludes `from` since it's implied). + * and Account.events (which excludes `sender` since it's implied). */ export const EventsWhereInput = builder.inputType("EventsWhereInput", { description: "Filter conditions for an events connection.", @@ -180,16 +192,22 @@ export const EventsWhereInput = builder.inputType("EventsWhereInput", { }), from: t.field({ type: "Address", - description: "Filter to events sent by this address.", + description: + "Filter to events whose `tx.from` matches. Not HCA-aware — use `sender` to filter by the HCA account address.", + }), + sender: t.field({ + type: "Address", + description: + "Filter to events whose HCA-aware `sender` matches. For ENSv2 events with an explicit sender/owner/account argument, this matches the HCA account address (if used). For other events, falls back to `tx.from`.", }), }), }); /** - * Like EventsWhereInput but without `from` (used where `from` is implied, e.g. Account.events). + * Like EventsWhereInput but without `sender` (used where `sender` is implied, e.g. Account.events). */ export const AccountEventsWhereInput = builder.inputType("AccountEventsWhereInput", { - description: "Filter conditions for Account.events (where `from` is implied by the Account).", + description: "Filter conditions for Account.events (where `sender` is implied by the Account).", fields: (t) => ({ selector_in: t.field({ type: ["Hex"], diff --git a/apps/ensapi/src/omnigraph-api/schema/permissions.ts b/apps/ensapi/src/omnigraph-api/schema/permissions.ts index fda7f12a3..93be36e86 100644 --- a/apps/ensapi/src/omnigraph-api/schema/permissions.ts +++ b/apps/ensapi/src/omnigraph-api/schema/permissions.ts @@ -260,7 +260,8 @@ PermissionsUserRef.implement({ // PermissionsUser.user //////////////////////// user: t.field({ - description: "The User for whom these Roles are granted.", + description: + "The User for whom these Roles are granted, the HCA account address, if used.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/registration.ts b/apps/ensapi/src/omnigraph-api/schema/registration.ts index a0a53b436..067070503 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registration.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registration.ts @@ -140,7 +140,8 @@ RegistrationInterfaceRef.implement({ // Registration.registrant /////////////////////////// registrant: t.field({ - description: "The Registrant of a Registration, if exists.", + description: + "The Registrant of a Registration, if exists. For ENSv2 Registrations the address is HCA-aware, the HCA account address, if used.", type: AccountRef, nullable: true, resolve: (parent) => parent.registrantId, @@ -150,7 +151,8 @@ RegistrationInterfaceRef.implement({ // Registration.unregistrant ///////////////////////////// unregistrant: t.field({ - description: "The Unregistrant of a Registration, if exists.", + description: + "The Unregistrant of a Registration, if exists. For ENSv2 Registrations the address is HCA-aware, the HCA account address, if used.", type: AccountRef, nullable: true, resolve: (parent) => parent.unregistrantId, diff --git a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts index 1edf94f81..b2083c620 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts @@ -49,7 +49,8 @@ RegistryPermissionsUserRef.implement({ // RegistryPermissionsUser.user ///////////////////////////////// user: t.field({ - description: "The User for whom these Roles are granted.", + description: + "The User for whom these Roles are granted, the HCA account address, if used.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts index 71a6bd81d..509e5afdf 100644 --- a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts @@ -49,7 +49,8 @@ ResolverPermissionsUserRef.implement({ // ResolverPermissionsUser.user ////////////////////////////////// user: t.field({ - description: "The User for whom these Roles are granted.", + description: + "The User for whom these Roles are granted, the HCA account address, if used.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index 03bfb243c..e8684532c 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -4,13 +4,11 @@ services: file: services/ensindexer.yml service: ensindexer environment: - # TODO: in future we will migrate devnet to chain_id=1 - # need to remove `RPC_URL_15658733` in that case RPC_URL_15658733: http://devnet:8545 - RPC_URL_1: http://devnet:8545 ENSINDEXER_SCHEMA_NAME: docker_devnet_v1 - LABEL_SET_ID: ens-test-env NAMESPACE: ens-test-env + LABEL_SET_ID: ens-test-env + LABEL_SET_VERSION: 0 env_file: - path: envs/.env.docker.common required: true @@ -31,10 +29,7 @@ services: file: services/ensapi.yml service: ensapi environment: - # TODO: in future we will migrate devnet to chain_id=1 - # need to remove `RPC_URL_15658733` in that case RPC_URL_15658733: http://devnet:8545 - RPC_URL_1: http://devnet:8545 ENSINDEXER_SCHEMA_NAME: docker_devnet_v1 depends_on: postgres: @@ -53,6 +48,8 @@ services: service: ensrainbow environment: LABEL_SET_ID: ens-test-env + LABEL_SET_VERSION: 0 + DB_SCHEMA_VERSION: 3 env_file: - path: envs/.env.docker.common required: true diff --git a/docker/envs/.env.docker.devnet b/docker/envs/.env.docker.devnet index 7c63beef8..1ad54974f 100644 --- a/docker/envs/.env.docker.devnet +++ b/docker/envs/.env.docker.devnet @@ -2,5 +2,3 @@ # These values work out of the box — override by creating .env.docker.local. PLUGINS=subgraph,ensv2 -LABEL_SET_VERSION=0 -DB_SCHEMA_VERSION=3 diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts index fe98dd14b..afa3ac78d 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts @@ -96,7 +96,9 @@ export const event = onchainTable( // Ponder's event.id id: t.text().primaryKey(), - // sender of the transaction (optionally HCA-aware for supported events) + // The HCA-aware sender of the event. For ENSv2 events that emit an explicit + // `sender`/`owner`/`account` arg, this is set from that arg (the HCA account address, if used). + // For all other events (and all ENSv1 events), this falls back to `event.from` (i.e. `tx.from`). sender: t.hex().notNull().$type(), // Event Log Metadata @@ -112,6 +114,8 @@ export const event = onchainTable( // transaction transactionHash: t.hex().notNull().$type(), transactionIndex: t.integer().notNull(), + // `tx.from` — never HCA-aware. Always the EOA/relayer that submitted the transaction. + // Use `event.sender` for the HCA-aware actor. from: t.hex().notNull().$type(), to: t.hex().$type(), // NOTE: a null `to` means this was a tx that deployed a contract @@ -125,6 +129,7 @@ export const event = onchainTable( (t) => ({ bySelector: index().on(t.selector), byFrom: index().on(t.from), + bySender: index().on(t.sender), byTimestamp: index().on(t.timestamp), }), ); @@ -253,7 +258,8 @@ export const domain = onchainTable( // represents a labelHash labelHash: t.hex().notNull().$type(), - // may have an owner + // If this is an ENSv1Domain, this is the effective owner of the Domain. + // If this is an ENSv2Domain, this is the HCA-aware owner of the Domain. ownerId: t.hex().$type(), // If this is an ENSv1Domain, may have a `rootRegistryOwner`, otherwise null. @@ -336,10 +342,12 @@ export const registration = onchainTable( registrarChainId: t.integer().notNull().$type(), registrarAddress: t.hex().notNull().$type(), - // may reference a registrant + // may reference a registrant. If this is an ENSv2 Registration, the registrant is HCA-aware, + // the HCA account address, if used. registrantId: t.hex().$type(), - // may reference an unregistrant + // may reference an unregistrant. If this is an ENSv2 Registration, the unregistrant is + // HCA-aware, the HCA account address, if used. unregistrantId: t.hex().$type(), // may have referrer data @@ -536,6 +544,7 @@ export const permissionsUser = onchainTable( chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), resource: t.bigint().notNull(), + // The User this Permission is granted to, the HCA account address, if used. user: t.hex().notNull().$type(), // has one roles bitmap From ca35d9387dbe648ed8b981958ba8ccd317e8ce8f Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 12:40:23 -0500 Subject: [PATCH 4/8] chore: changeset for hca-aware events Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/hca-aware-events.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hca-aware-events.md diff --git a/.changeset/hca-aware-events.md b/.changeset/hca-aware-events.md new file mode 100644 index 000000000..4364097b0 --- /dev/null +++ b/.changeset/hca-aware-events.md @@ -0,0 +1,5 @@ +--- +"ensapi": minor +--- + +Adds HCA-aware `Event.sender` to the Omnigraph API alongside the existing `Event.from`. For ENSv2 events that emit an explicit `sender`/`owner`/`account`/ERC1155 `operator` argument, `Event.sender` is set from that argument (the HCA Smart Account address, if used) and falls back to `tx.from` otherwise. Adds a `sender` filter to `EventsWhereInput`. `Account.events` now filters by `sender` (HCA-aware) instead of `tx.from`. Documents HCA-aware semantics on `Domain.owner`, `Registration.registrant`/`unregistrant`, and `*.PermissionsUser.user`. From 47e405ba90c4c25fc2a7b616ec295e3a1ca8d710 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 13:01:31 -0500 Subject: [PATCH 5/8] docs: align HCA-aware wording + tighten interpretAddress to NormalizedAddress Update HCA-aware Address descriptions across schema and GraphQL to a unified pattern: "the HCA account address if used, otherwise Transaction.from". Tighten interpretAddress and ensureAccount inputs to NormalizedAddress. Regenerate omnigraph schema.graphql and introspection.ts. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/lib/resolution/execute-operations.ts | 3 +- .../ensapi/src/omnigraph-api/schema/domain.ts | 2 +- apps/ensapi/src/omnigraph-api/schema/event.ts | 5 +- .../src/omnigraph-api/schema/permissions.ts | 2 +- .../src/omnigraph-api/schema/registration.ts | 4 +- .../schema/registry-permissions-user.ts | 2 +- .../schema/resolver-permissions-user.ts | 2 +- .../src/lib/ensv2/account-db-helpers.ts | 4 +- .../ensv2/handlers/ensv2/ETHRegistrar.ts | 1 - .../src/ensindexer-abstract/ensv2.schema.ts | 16 ++-- .../interpretation/interpret-address.ts | 6 +- .../src/omnigraph/generated/introspection.ts | 19 ++++ .../src/omnigraph/generated/schema.graphql | 90 ++++++++++++++----- 13 files changed, 109 insertions(+), 47 deletions(-) diff --git a/apps/ensapi/src/lib/resolution/execute-operations.ts b/apps/ensapi/src/lib/resolution/execute-operations.ts index 9d4381624..8c87e82a3 100644 --- a/apps/ensapi/src/lib/resolution/execute-operations.ts +++ b/apps/ensapi/src/lib/resolution/execute-operations.ts @@ -6,6 +6,7 @@ import { type Hex, type Name, type RecordVersion, + toNormalizedAddress, } from "enssdk"; import { ContractFunctionExecutionError, @@ -161,6 +162,6 @@ export function interpretOperationWithRawResult(call: Operation, raw: unknown): return { ...call, result: size(data) === 0 ? null : { contentType, data } }; } case "interfaceImplementer": - return { ...call, result: interpretAddress(raw as Address) }; + return { ...call, result: interpretAddress(toNormalizedAddress(raw as string)) }; } } diff --git a/apps/ensapi/src/omnigraph-api/schema/domain.ts b/apps/ensapi/src/omnigraph-api/schema/domain.ts index 77c8dfafd..23cbfffa0 100644 --- a/apps/ensapi/src/omnigraph-api/schema/domain.ts +++ b/apps/ensapi/src/omnigraph-api/schema/domain.ts @@ -178,7 +178,7 @@ DomainInterfaceRef.implement({ owner: t.field({ type: AccountRef, description: - "If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA-aware owner of the Domain.", + "If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from.", nullable: true, resolve: (parent) => parent.ownerId, }), diff --git a/apps/ensapi/src/omnigraph-api/schema/event.ts b/apps/ensapi/src/omnigraph-api/schema/event.ts index 61713ea98..2fd271f13 100644 --- a/apps/ensapi/src/omnigraph-api/schema/event.ts +++ b/apps/ensapi/src/omnigraph-api/schema/event.ts @@ -106,8 +106,7 @@ EventRef.implement({ // Event.sender //////////////// sender: t.field({ - description: - "The HCA-aware sender of the Event. For ENSv2 events that emit an explicit sender/owner/account argument, this is the HCA account address (if used). For all other events (including all ENSv1 events), falls back to `from` (i.e. `tx.from`).", + description: "The HCA account address if used, otherwise Transaction.from.", type: "Address", nullable: false, resolve: (parent) => parent.sender, @@ -198,7 +197,7 @@ export const EventsWhereInput = builder.inputType("EventsWhereInput", { sender: t.field({ type: "Address", description: - "Filter to events whose HCA-aware `sender` matches. For ENSv2 events with an explicit sender/owner/account argument, this matches the HCA account address (if used). For other events, falls back to `tx.from`.", + "Filter to events whose `sender` matches: the HCA account address if used, otherwise Transaction.from.", }), }), }); diff --git a/apps/ensapi/src/omnigraph-api/schema/permissions.ts b/apps/ensapi/src/omnigraph-api/schema/permissions.ts index 93be36e86..f21ab9746 100644 --- a/apps/ensapi/src/omnigraph-api/schema/permissions.ts +++ b/apps/ensapi/src/omnigraph-api/schema/permissions.ts @@ -261,7 +261,7 @@ PermissionsUserRef.implement({ //////////////////////// user: t.field({ description: - "The User for whom these Roles are granted, the HCA account address, if used.", + "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/registration.ts b/apps/ensapi/src/omnigraph-api/schema/registration.ts index 067070503..250b8444f 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registration.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registration.ts @@ -141,7 +141,7 @@ RegistrationInterfaceRef.implement({ /////////////////////////// registrant: t.field({ description: - "The Registrant of a Registration, if exists. For ENSv2 Registrations the address is HCA-aware, the HCA account address, if used.", + "The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from.", type: AccountRef, nullable: true, resolve: (parent) => parent.registrantId, @@ -152,7 +152,7 @@ RegistrationInterfaceRef.implement({ ///////////////////////////// unregistrant: t.field({ description: - "The Unregistrant of a Registration, if exists. For ENSv2 Registrations the address is HCA-aware, the HCA account address, if used.", + "The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from.", type: AccountRef, nullable: true, resolve: (parent) => parent.unregistrantId, diff --git a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts index b2083c620..88cd7d8e6 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts @@ -50,7 +50,7 @@ RegistryPermissionsUserRef.implement({ ///////////////////////////////// user: t.field({ description: - "The User for whom these Roles are granted, the HCA account address, if used.", + "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts index 509e5afdf..63fe4f418 100644 --- a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts @@ -50,7 +50,7 @@ ResolverPermissionsUserRef.implement({ ////////////////////////////////// user: t.field({ description: - "The User for whom these Roles are granted, the HCA account address, if used.", + "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts b/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts index e8942d6e8..ac55983c4 100644 --- a/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts +++ b/apps/ensindexer/src/lib/ensv2/account-db-helpers.ts @@ -1,4 +1,4 @@ -import type { Address, NormalizedAddress } from "enssdk"; +import type { NormalizedAddress } from "enssdk"; import { interpretAddress } from "@ensnode/ensnode-sdk"; @@ -10,7 +10,7 @@ import { ensIndexerSchema, type IndexingEngineContext } from "@/lib/indexing-eng */ export async function ensureAccount( context: IndexingEngineContext, - address: Address, + address: NormalizedAddress, ): Promise { const id = interpretAddress(address); if (id === null) return null; diff --git a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts index ea42ec341..171a368b9 100644 --- a/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts +++ b/apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts @@ -11,7 +11,6 @@ import { import { type EncodedReferrer, - interpretAddress, isRegistrationFullyExpired, PluginName, toJson, diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts index afa3ac78d..703a8fdea 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts @@ -96,9 +96,7 @@ export const event = onchainTable( // Ponder's event.id id: t.text().primaryKey(), - // The HCA-aware sender of the event. For ENSv2 events that emit an explicit - // `sender`/`owner`/`account` arg, this is set from that arg (the HCA account address, if used). - // For all other events (and all ENSv1 events), this falls back to `event.from` (i.e. `tx.from`). + // The HCA account address if used, otherwise Transaction.from. sender: t.hex().notNull().$type(), // Event Log Metadata @@ -259,7 +257,7 @@ export const domain = onchainTable( labelHash: t.hex().notNull().$type(), // If this is an ENSv1Domain, this is the effective owner of the Domain. - // If this is an ENSv2Domain, this is the HCA-aware owner of the Domain. + // If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. ownerId: t.hex().$type(), // If this is an ENSv1Domain, may have a `rootRegistryOwner`, otherwise null. @@ -342,12 +340,12 @@ export const registration = onchainTable( registrarChainId: t.integer().notNull().$type(), registrarAddress: t.hex().notNull().$type(), - // may reference a registrant. If this is an ENSv2 Registration, the registrant is HCA-aware, - // the HCA account address, if used. + // may reference a registrant. If this is an ENSv2 Registration, the HCA account address + // if used, otherwise Transaction.from. registrantId: t.hex().$type(), - // may reference an unregistrant. If this is an ENSv2 Registration, the unregistrant is - // HCA-aware, the HCA account address, if used. + // may reference an unregistrant. If this is an ENSv2 Registration, the HCA account address + // if used, otherwise Transaction.from. unregistrantId: t.hex().$type(), // may have referrer data @@ -544,7 +542,7 @@ export const permissionsUser = onchainTable( chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), resource: t.bigint().notNull(), - // The User this Permission is granted to, the HCA account address, if used. + // The User this Permission is granted to, the HCA account address if used, otherwise Transaction.from. user: t.hex().notNull().$type(), // has one roles bitmap diff --git a/packages/ensnode-sdk/src/shared/interpretation/interpret-address.ts b/packages/ensnode-sdk/src/shared/interpretation/interpret-address.ts index 9fd9068a1..6f2737928 100644 --- a/packages/ensnode-sdk/src/shared/interpretation/interpret-address.ts +++ b/packages/ensnode-sdk/src/shared/interpretation/interpret-address.ts @@ -1,8 +1,8 @@ -import type { Address } from "enssdk"; +import type { NormalizedAddress } from "enssdk"; import { isAddressEqual, zeroAddress } from "viem"; /** - * Interprets a viem#Address. zeroAddress is interpreted as null, otherwise Address. + * Interprets a NormalizedAddress. zeroAddress is interpreted as null, otherwise as-is. */ -export const interpretAddress = (owner: Address) => +export const interpretAddress = (owner: NormalizedAddress): NormalizedAddress | null => isAddressEqual(zeroAddress, owner) ? null : owner; diff --git a/packages/enssdk/src/omnigraph/generated/introspection.ts b/packages/enssdk/src/omnigraph/generated/introspection.ts index c6763e36a..e95f195db 100644 --- a/packages/enssdk/src/omnigraph/generated/introspection.ts +++ b/packages/enssdk/src/omnigraph/generated/introspection.ts @@ -3088,6 +3088,18 @@ const introspection = { "args": [], "isDeprecated": false }, + { + "name": "sender", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "Address" + } + }, + "args": [], + "isDeprecated": false + }, { "name": "timestamp", "type": { @@ -3178,6 +3190,13 @@ const introspection = { } } }, + { + "name": "sender", + "type": { + "kind": "SCALAR", + "name": "Address" + } + }, { "name": "timestamp_gte", "type": { diff --git a/packages/enssdk/src/omnigraph/generated/schema.graphql b/packages/enssdk/src/omnigraph/generated/schema.graphql index ac546669a..36e1e3396 100644 --- a/packages/enssdk/src/omnigraph/generated/schema.graphql +++ b/packages/enssdk/src/omnigraph/generated/schema.graphql @@ -7,7 +7,7 @@ type Account { domains(after: String, before: String, first: Int, last: Int, order: DomainsOrderInput, where: AccountDomainsWhereInput): AccountDomainsConnection """ - All Events for which this Account is the sender (i.e. `Transaction.from`). + All Events for which this Account is the HCA-aware `sender` (i.e. `Event.sender`). """ events(after: String, before: String, first: Int, last: Int, where: AccountEventsWhereInput): AccountEventsConnection @@ -68,7 +68,7 @@ type AccountEventsConnectionEdge { } """ -Filter conditions for Account.events (where `from` is implied by the Account). +Filter conditions for Account.events (where `sender` is implied by the Account). """ input AccountEventsWhereInput { """ @@ -166,7 +166,9 @@ type BaseRegistrarRegistration implements Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -180,7 +182,9 @@ type BaseRegistrarRegistration implements Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account """ @@ -216,7 +220,9 @@ interface Domain { """ name: InterpretedName - """The owner of this Domain.""" + """ + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + """ owner: Account """ @@ -340,7 +346,9 @@ type ENSv1Domain implements Domain { """The namehash of this ENSv1 Domain.""" node: Node! - """The owner of this Domain.""" + """ + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + """ owner: Account """ @@ -440,7 +448,9 @@ type ENSv2Domain implements Domain { """ name: InterpretedName - """The owner of this Domain.""" + """ + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + """ owner: Account """ @@ -539,7 +549,9 @@ type ENSv2RegistryRegistration implements Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -553,7 +565,9 @@ type ENSv2RegistryRegistration implements Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account } @@ -581,7 +595,9 @@ type ENSv2RegistryReservation implements Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -595,7 +611,9 @@ type ENSv2RegistryReservation implements Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account } @@ -619,7 +637,7 @@ type Event { data: Hex! """ - Identifies the sender of the Transaction within which this Event was emitted. + Identifies the sender of the Transaction within which this Event was emitted (`tx.from`). Never HCA-aware — always the EOA/relayer that submitted the transaction. Use `Event.sender` for the HCA-aware actor. """ from: Address! @@ -629,6 +647,9 @@ type Event { """The index of this Event's log within the Block.""" logIndex: Int! + """The HCA account address if used, otherwise Transaction.from.""" + sender: Address! + """ The UnixTimestamp indicating the moment in which this Event was emitted. """ @@ -651,7 +672,9 @@ type Event { """Filter conditions for an events connection.""" input EventsWhereInput { - """Filter to events sent by this address.""" + """ + Filter to events whose `tx.from` matches. Not HCA-aware — use `sender` to filter by the HCA account address. + """ from: Address """ @@ -659,6 +682,11 @@ input EventsWhereInput { """ selector_in: [Hex!] + """ + Filter to events whose `sender` matches: the HCA account address if used, otherwise Transaction.from. + """ + sender: Address + """Filter to events at or after this UnixTimestamp.""" timestamp_gte: BigInt @@ -725,7 +753,9 @@ type NameWrapperRegistration implements Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -739,7 +769,9 @@ type NameWrapperRegistration implements Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account } @@ -857,7 +889,9 @@ type PermissionsUser { """The Roles this User has been granted within this Resource.""" roles: BigInt! - """The User for whom these Roles are granted.""" + """ + The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + """ user: Account! } @@ -979,7 +1013,9 @@ interface Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -993,7 +1029,9 @@ interface Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account } @@ -1085,7 +1123,9 @@ type RegistryPermissionsUser { """The Roles that this Permission grants.""" roles: BigInt! - """The User for whom these Roles are granted.""" + """ + The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + """ user: Account! } @@ -1172,7 +1212,9 @@ type ResolverPermissionsUser { """The Roles that this Permission grants.""" roles: BigInt! - """The User for whom these Roles are granted.""" + """ + The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + """ user: Account! } @@ -1236,7 +1278,9 @@ type ThreeDNSRegistration implements Registration { """The extra `referrer` data provided with a Registration, if exists.""" referrer: Hex - """The Registrant of a Registration, if exists.""" + """ + The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ registrant: Account """The Registrar contract under which this Registration is managed.""" @@ -1250,7 +1294,9 @@ type ThreeDNSRegistration implements Registration { """A UnixTimestamp indicating when this Registration was created.""" start: BigInt! - """The Unregistrant of a Registration, if exists.""" + """ + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + """ unregistrant: Account } From 609cb902b424a1f33b737e23f28ac6156a292963 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 14:12:46 -0500 Subject: [PATCH 6/8] fix: address Copilot/Vercel review notes (loop 1) Reword HCA-aware Address descriptions for fields that aren't actually populated from tx.from (Domain.owner, Registration.registrant, Registration.unregistrant, PermissionsUser.user). These read from protocol event args (TransferSingle.to, LabelRegistered.sender, EACRolesChanged.account), not Transaction.from. Keep "otherwise Transaction.from" only on Event.sender and the sender filter, where it is literally accurate. Update Account.events integration test to assert event.sender (now HCA-aware) instead of event.from. Add sender to the shared EventFragment and EventResult type. Regenerate omnigraph schema.graphql. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../schema/account.integration.test.ts | 2 +- .../ensapi/src/omnigraph-api/schema/domain.ts | 2 +- .../src/omnigraph-api/schema/permissions.ts | 2 +- .../src/omnigraph-api/schema/registration.ts | 4 +-- .../schema/registry-permissions-user.ts | 2 +- .../schema/resolver-permissions-user.ts | 2 +- .../find-events/event-pagination-queries.ts | 2 ++ .../src/ensindexer-abstract/ensv2.schema.ts | 12 +++---- .../src/omnigraph/generated/schema.graphql | 36 +++++++++---------- 9 files changed, 33 insertions(+), 31 deletions(-) diff --git a/apps/ensapi/src/omnigraph-api/schema/account.integration.test.ts b/apps/ensapi/src/omnigraph-api/schema/account.integration.test.ts index b3f14f8c9..0e35b228e 100644 --- a/apps/ensapi/src/omnigraph-api/schema/account.integration.test.ts +++ b/apps/ensapi/src/omnigraph-api/schema/account.integration.test.ts @@ -99,7 +99,7 @@ describe("Account.events", () => { expect(events.length).toBeGreaterThan(0); for (const event of events) { - expect(event.from).toBe(DEVNET_DEPLOYER); + expect(event.sender).toBe(DEVNET_DEPLOYER); } }); }); diff --git a/apps/ensapi/src/omnigraph-api/schema/domain.ts b/apps/ensapi/src/omnigraph-api/schema/domain.ts index 23cbfffa0..915ec9c1b 100644 --- a/apps/ensapi/src/omnigraph-api/schema/domain.ts +++ b/apps/ensapi/src/omnigraph-api/schema/domain.ts @@ -178,7 +178,7 @@ DomainInterfaceRef.implement({ owner: t.field({ type: AccountRef, description: - "If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from.", + "If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the on-chain owner address (the HCA account address if used).", nullable: true, resolve: (parent) => parent.ownerId, }), diff --git a/apps/ensapi/src/omnigraph-api/schema/permissions.ts b/apps/ensapi/src/omnigraph-api/schema/permissions.ts index f21ab9746..de6a5bd90 100644 --- a/apps/ensapi/src/omnigraph-api/schema/permissions.ts +++ b/apps/ensapi/src/omnigraph-api/schema/permissions.ts @@ -261,7 +261,7 @@ PermissionsUserRef.implement({ //////////////////////// user: t.field({ description: - "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", + "The user/grantee address this Permission is granted to (the HCA account address if used).", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/registration.ts b/apps/ensapi/src/omnigraph-api/schema/registration.ts index 250b8444f..588e1c4a6 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registration.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registration.ts @@ -141,7 +141,7 @@ RegistrationInterfaceRef.implement({ /////////////////////////// registrant: t.field({ description: - "The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from.", + "The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used).", type: AccountRef, nullable: true, resolve: (parent) => parent.registrantId, @@ -152,7 +152,7 @@ RegistrationInterfaceRef.implement({ ///////////////////////////// unregistrant: t.field({ description: - "The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from.", + "The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used).", type: AccountRef, nullable: true, resolve: (parent) => parent.unregistrantId, diff --git a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts index 88cd7d8e6..b5d7f0bc6 100644 --- a/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/registry-permissions-user.ts @@ -50,7 +50,7 @@ RegistryPermissionsUserRef.implement({ ///////////////////////////////// user: t.field({ description: - "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", + "The user/grantee address this Permission is granted to (the HCA account address if used).", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts index 63fe4f418..3221d7ec5 100644 --- a/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts +++ b/apps/ensapi/src/omnigraph-api/schema/resolver-permissions-user.ts @@ -50,7 +50,7 @@ ResolverPermissionsUserRef.implement({ ////////////////////////////////// user: t.field({ description: - "The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from.", + "The user/grantee address this Permission is granted to (the HCA account address if used).", type: AccountRef, nullable: false, resolve: (parent) => parent.user, diff --git a/apps/ensapi/src/test/integration/find-events/event-pagination-queries.ts b/apps/ensapi/src/test/integration/find-events/event-pagination-queries.ts index 265515ec7..e7fc735cf 100644 --- a/apps/ensapi/src/test/integration/find-events/event-pagination-queries.ts +++ b/apps/ensapi/src/test/integration/find-events/event-pagination-queries.ts @@ -21,6 +21,7 @@ export const EventFragment = gql` transactionHash transactionIndex from + sender to address logIndex @@ -38,6 +39,7 @@ export type EventResult = { transactionHash: Hex; transactionIndex: number; from: NormalizedAddress; + sender: NormalizedAddress; to: NormalizedAddress | null; address: NormalizedAddress; logIndex: number; diff --git a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts index 703a8fdea..0308fdeab 100644 --- a/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts +++ b/packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts @@ -257,7 +257,7 @@ export const domain = onchainTable( labelHash: t.hex().notNull().$type(), // If this is an ENSv1Domain, this is the effective owner of the Domain. - // If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + // If this is an ENSv2Domain, this is the on-chain owner address (the HCA account address if used). ownerId: t.hex().$type(), // If this is an ENSv1Domain, may have a `rootRegistryOwner`, otherwise null. @@ -340,12 +340,12 @@ export const registration = onchainTable( registrarChainId: t.integer().notNull().$type(), registrarAddress: t.hex().notNull().$type(), - // may reference a registrant. If this is an ENSv2 Registration, the HCA account address - // if used, otherwise Transaction.from. + // may reference a registrant. If this is an ENSv2 Registration, the protocol-emitted + // registrant address (the HCA account address if used). registrantId: t.hex().$type(), - // may reference an unregistrant. If this is an ENSv2 Registration, the HCA account address - // if used, otherwise Transaction.from. + // may reference an unregistrant. If this is an ENSv2 Registration, the protocol-emitted + // unregistrant address (the HCA account address if used). unregistrantId: t.hex().$type(), // may have referrer data @@ -542,7 +542,7 @@ export const permissionsUser = onchainTable( chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), resource: t.bigint().notNull(), - // The User this Permission is granted to, the HCA account address if used, otherwise Transaction.from. + // The user/grantee address this Permission is granted to (the HCA account address if used). user: t.hex().notNull().$type(), // has one roles bitmap diff --git a/packages/enssdk/src/omnigraph/generated/schema.graphql b/packages/enssdk/src/omnigraph/generated/schema.graphql index 36e1e3396..4b15d5404 100644 --- a/packages/enssdk/src/omnigraph/generated/schema.graphql +++ b/packages/enssdk/src/omnigraph/generated/schema.graphql @@ -167,7 +167,7 @@ type BaseRegistrarRegistration implements Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -183,7 +183,7 @@ type BaseRegistrarRegistration implements Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account @@ -221,7 +221,7 @@ interface Domain { name: InterpretedName """ - If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the on-chain owner address (the HCA account address if used). """ owner: Account @@ -347,7 +347,7 @@ type ENSv1Domain implements Domain { node: Node! """ - If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the on-chain owner address (the HCA account address if used). """ owner: Account @@ -449,7 +449,7 @@ type ENSv2Domain implements Domain { name: InterpretedName """ - If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the HCA account address if used, otherwise Transaction.from. + If this is an ENSv1Domain, this is the effective owner of the Domain. If this is an ENSv2Domain, this is the on-chain owner address (the HCA account address if used). """ owner: Account @@ -550,7 +550,7 @@ type ENSv2RegistryRegistration implements Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -566,7 +566,7 @@ type ENSv2RegistryRegistration implements Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account } @@ -596,7 +596,7 @@ type ENSv2RegistryReservation implements Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -612,7 +612,7 @@ type ENSv2RegistryReservation implements Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account } @@ -754,7 +754,7 @@ type NameWrapperRegistration implements Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -770,7 +770,7 @@ type NameWrapperRegistration implements Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account } @@ -890,7 +890,7 @@ type PermissionsUser { roles: BigInt! """ - The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + The user/grantee address this Permission is granted to (the HCA account address if used). """ user: Account! } @@ -1014,7 +1014,7 @@ interface Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -1030,7 +1030,7 @@ interface Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account } @@ -1124,7 +1124,7 @@ type RegistryPermissionsUser { roles: BigInt! """ - The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + The user/grantee address this Permission is granted to (the HCA account address if used). """ user: Account! } @@ -1213,7 +1213,7 @@ type ResolverPermissionsUser { roles: BigInt! """ - The User for whom these Roles are granted: the HCA account address if used, otherwise Transaction.from. + The user/grantee address this Permission is granted to (the HCA account address if used). """ user: Account! } @@ -1279,7 +1279,7 @@ type ThreeDNSRegistration implements Registration { referrer: Hex """ - The Registrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Registrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). """ registrant: Account @@ -1295,7 +1295,7 @@ type ThreeDNSRegistration implements Registration { start: BigInt! """ - The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the HCA account address if used, otherwise Transaction.from. + The Unregistrant of a Registration, if exists. For ENSv2 Registrations, the protocol-emitted unregistrant address (the HCA account address if used). """ unregistrant: Account } From a913f345a448fa80073555d19fddeb096d7517b5 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 17:49:40 -0500 Subject: [PATCH 7/8] docs: sync ENSv2 schema changes to ensdb database-schemas.mdx - events: add sender column row, update from description to flag never HCA-aware, add sender to indexes list - permissions_user_events: document join table (was missing from initial ENSDb docs in #2007) - domains.ownerId: polymorphic ENSv1 effective-owner / ENSv2 on-chain owner (HCA-aware) wording - registrations.registrantId / unregistrantId: ENSv2 HCA-aware note - permissions_users.user: HCA-aware grantee description Per https://github.com/namehash/ensnode/pull/2014#issuecomment review. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../docs/ensdb/concepts/database-schemas.mdx | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/ensnode.io/src/content/docs/ensdb/concepts/database-schemas.mdx b/docs/ensnode.io/src/content/docs/ensdb/concepts/database-schemas.mdx index 965a031ce..1720691ba 100644 --- a/docs/ensnode.io/src/content/docs/ensdb/concepts/database-schemas.mdx +++ b/docs/ensnode.io/src/content/docs/ensdb/concepts/database-schemas.mdx @@ -160,7 +160,8 @@ the Event responsible for its existence. | `timestamp` | `numeric(78)` | no | Block timestamp. | | `transactionHash` | `text` | no | Transaction hash. | | `transactionIndex` | `integer` | no | Index of the transaction within the block. | -| `from` | `text` | no | Transaction sender address. | +| `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. | | `logIndex` | `integer` | no | Index of the log within the transaction. | @@ -168,7 +169,7 @@ the Event responsible for its existence. | `topics` | `text[]` | no | All log topics. | | `data` | `text` | no | Log data. | -**Indexes:** `selector`, `from`, `timestamp`. +**Indexes:** `selector`, `from`, `sender`, `timestamp`. #### `domain_events` @@ -203,6 +204,17 @@ Join table linking a `permissions` record to its associated `events`. **Primary key:** `(permissionsId, eventId)`. +#### `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 | +|--------|------|----------| +| `permissionsUserId` | `text` | no | +| `eventId` | `text` | no | + +**Primary key:** `(permissionsUserId, eventId)`. + #### `accounts` | Column | Type | Nullable | Description | @@ -244,7 +256,7 @@ Domain-Resolver relations are tracked via the Protocol Acceleration plugin, not | `tokenId` | `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. | | `labelHash` | `text` | no | Represents a labelHash. References `labels.labelHash`. | -| `ownerId` | `text` | yes | Materialized effective owner address. | +| `ownerId` | `text` | yes | If `ENSv1Domain`, the materialized effective owner address. If `ENSv2Domain`, the on-chain owner address (the HCA account address if used). | | `rootRegistryOwnerId` | `text` | yes | ENSv1 only: the owner recorded in the root ENSv1 registry. `null` for ENSv2 domains. | **Indexes:** `type`, `registryId`, `subregistryId` (partial: non-null only), `ownerId`, `labelHash`. @@ -279,8 +291,8 @@ A registration is keyed by `id`. | `gracePeriod` | `numeric(78)` | yes | Grace period duration in seconds. `BaseRegistrar` only. | | `registrarChainId` | `integer` | no | Chain of the registrar contract. | | `registrarAddress` | `text` | no | Address of the registrar contract. | -| `registrantId` | `text` | yes | Account that initiated the registration. | -| `unregistrantId` | `text` | yes | Account that triggered an unregistration, if applicable. | +| `registrantId` | `text` | yes | Account that initiated the registration. For ENSv2 Registrations, the protocol-emitted registrant address (the HCA account address if used). | +| `unregistrantId` | `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. | @@ -374,7 +386,7 @@ A user's role bitmap for a specific resource within a `permissions` contract. | `chainId` | `integer` | 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's Ethereum address. | +| `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 `(chainId, address, resource, user)`. From 21e87e033f3582ba9583bc9d58a94dcbbeef3fc7 Mon Sep 17 00:00:00 2001 From: shrugs Date: Wed, 29 Apr 2026 18:04:51 -0500 Subject: [PATCH 8/8] fix: add from filter to AccountEventsWhereInput Per Copilot review: the description says "without sender" but the input omitted both sender and from. Add a from filter so callers can narrow by tx.from while sender is implied by the Account. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/ensapi/src/omnigraph-api/schema/event.ts | 5 +++++ packages/enssdk/src/omnigraph/generated/introspection.ts | 7 +++++++ packages/enssdk/src/omnigraph/generated/schema.graphql | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/apps/ensapi/src/omnigraph-api/schema/event.ts b/apps/ensapi/src/omnigraph-api/schema/event.ts index 2fd271f13..7e20923bf 100644 --- a/apps/ensapi/src/omnigraph-api/schema/event.ts +++ b/apps/ensapi/src/omnigraph-api/schema/event.ts @@ -221,5 +221,10 @@ export const AccountEventsWhereInput = builder.inputType("AccountEventsWhereInpu type: "BigInt", description: "Filter to events at or before this UnixTimestamp.", }), + from: t.field({ + type: "Address", + description: + "Filter to events whose `tx.from` matches. Not HCA-aware — the Account's HCA-aware filter is applied via `sender = Account.id`.", + }), }), }); diff --git a/packages/enssdk/src/omnigraph/generated/introspection.ts b/packages/enssdk/src/omnigraph/generated/introspection.ts index e95f195db..b018ad6b8 100644 --- a/packages/enssdk/src/omnigraph/generated/introspection.ts +++ b/packages/enssdk/src/omnigraph/generated/introspection.ts @@ -486,6 +486,13 @@ const introspection = { "kind": "INPUT_OBJECT", "name": "AccountEventsWhereInput", "inputFields": [ + { + "name": "from", + "type": { + "kind": "SCALAR", + "name": "Address" + } + }, { "name": "selector_in", "type": { diff --git a/packages/enssdk/src/omnigraph/generated/schema.graphql b/packages/enssdk/src/omnigraph/generated/schema.graphql index 4b15d5404..dcb12e7d1 100644 --- a/packages/enssdk/src/omnigraph/generated/schema.graphql +++ b/packages/enssdk/src/omnigraph/generated/schema.graphql @@ -71,6 +71,11 @@ type AccountEventsConnectionEdge { Filter conditions for Account.events (where `sender` is implied by the Account). """ input AccountEventsWhereInput { + """ + Filter to events whose `tx.from` matches. Not HCA-aware — the Account's HCA-aware filter is applied via `sender = Account.id`. + """ + from: Address + """ Filter to events whose selector (event signature) is one of the provided values. """