Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tricky-clouds-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensapi": minor
---
Comment thread
shrugs marked this conversation as resolved.

Omnigraph API (BREAKING): Removed `Resolver.dedicated` field in advance of PermissionedResolver integration.
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Runnable commands for validating changes; lint and format with Biome.
- Run tests for a single package/app: `pnpm --filter <package-name> test` (e.g. `pnpm --filter ensapi test`)
- Lint and format: `pnpm lint` (fixes where applicable); CI lint: `pnpm lint:ci`
- Type checking: `pnpm typecheck` (runs typecheck in all workspaces)
- Build (validate tsup/tsx bundling for the package you changed): `pnpm --filter <package-name> build`
- Always prefer `pnpm -F <package-name> typecheck` over `tsc`

## Testing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
// via devnet
const DEVNET_DEPLOYER: Address = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266";
const DEFAULT_OWNER: Address = "0x70997970c51812dc3a010c7d01b50e0d17dc79c8";
const NEW_OWNER: Address = "0x90f79bf6eb2c4f870365e785982e1f101e93b906";
const NEW_OWNER_OWNER: Address = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC";

Comment thread
shrugs marked this conversation as resolved.
describe("Account.domains", () => {
Comment on lines 26 to 30
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant name NEW_OWNER_OWNER reads like a typo and makes the test harder to follow. Rename it to something unambiguous (e.g. NEW_OWNER) and update the query variables accordingly.

Copilot uses AI. Check for mistakes.
type AccountDomainsResult = {
Expand Down Expand Up @@ -66,7 +66,9 @@ describe("Account.domains", () => {
});

it("returns domains owned by the new owner", async () => {
const result = await request<AccountDomainsResult>(AccountDomains, { address: NEW_OWNER });
const result = await request<AccountDomainsResult>(AccountDomains, {
address: NEW_OWNER_OWNER,
});
const domains = flattenConnection(result.account.domains);
const names = domains.map((d) => d.name);

Expand Down
38 changes: 37 additions & 1 deletion apps/ensapi/src/graphql-api/schema/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { builder } from "@/graphql-api/builder";
import { orderPaginationBy, paginateByInt } from "@/graphql-api/lib/connection-helpers";
import { getModelId } from "@/graphql-api/lib/get-model-id";
import { lazyConnection } from "@/graphql-api/lib/lazy-connection";
import { AccountRef } from "@/graphql-api/schema/account";
import { AccountIdRef } from "@/graphql-api/schema/account-id";
import { INDEX_PAGINATED_CONNECTION_ARGS } from "@/graphql-api/schema/constants";
import { DomainInterfaceRef } from "@/graphql-api/schema/domain";
Expand Down Expand Up @@ -42,6 +43,7 @@ export type RegistrationInterface = Pick<
| "registrarChainId"
| "registrarAddress"
| "registrantId"
| "unregistrantId"
| "referrer"
>;
export type NameWrapperRegistration = RequiredAndNotNull<Registration, "fuses">;
Expand All @@ -54,6 +56,7 @@ export type BaseRegistrarRegistration = RequiredAndNotNull<
};
export type ThreeDNSRegistration = Registration;
export type ENSv2RegistryRegistration = Registration;
export type ENSv2RegistryReservation = Registration;

RegistrationInterfaceRef.implement({
description:
Expand Down Expand Up @@ -130,6 +133,26 @@ RegistrationInterfaceRef.implement({
resolve: (parent) => parent.referrer,
}),

///////////////////////////
// Registration.registrant
///////////////////////////
registrant: t.field({
description: "The Registrant of a Registration, if exists.",
type: AccountRef,
nullable: true,
resolve: (parent) => parent.registrantId,
}),

/////////////////////////////
// Registration.unregistrant
/////////////////////////////
unregistrant: t.field({
description: "The Unregistrant of a Registration, if exists.",
type: AccountRef,
nullable: true,
resolve: (parent) => parent.unregistrantId,
}),

/////////////////////////
// Registration.renewals
/////////////////////////
Expand Down Expand Up @@ -275,7 +298,20 @@ export const ENSv2RegistryRegistrationRef = builder.objectRef<ENSv2RegistryRegis
ENSv2RegistryRegistrationRef.implement({
description: "ENSv2RegistryRegistration represents a Registration within an ENSv2 Registry.",
interfaces: [RegistrationInterfaceRef],
isTypeOf: (value) => (value as RegistrationInterface).type === "ENSv2Registry",
isTypeOf: (value) => (value as RegistrationInterface).type === "ENSv2RegistryRegistration",
fields: (t) => ({}),
});

////////////////////////////
// ENSv2RegistryReservation
////////////////////////////
export const ENSv2RegistryReservationRef = builder.objectRef<ENSv2RegistryReservation>(
"ENSv2RegistryReservation",
);
ENSv2RegistryReservationRef.implement({
description: "ENSv2RegistryReservation represents a Reservation within an ENSv2 Registry.",
interfaces: [RegistrationInterfaceRef],
isTypeOf: (value) => (value as RegistrationInterface).type === "ENSv2RegistryReservation",
fields: (t) => ({}),
});

Expand Down
72 changes: 2 additions & 70 deletions apps/ensapi/src/graphql-api/schema/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,19 @@ import { type ResolveCursorConnectionArgs, resolveCursorConnection } from "@poth
import { and, eq } from "drizzle-orm";
import { namehash } from "viem";

import {
makePermissionsId,
makeResolverRecordsId,
NODE_ANY,
type ResolverId,
ROOT_RESOURCE,
} from "@ensnode/ensnode-sdk";
import { makePermissionsId, makeResolverRecordsId, type ResolverId } from "@ensnode/ensnode-sdk";
import { isBridgedResolver } from "@ensnode/ensnode-sdk/internal";

import { builder } from "@/graphql-api/builder";
import { orderPaginationBy, paginateBy } from "@/graphql-api/lib/connection-helpers";
import { resolveFindEvents } from "@/graphql-api/lib/find-events/find-events-resolver";
import { getModelId } from "@/graphql-api/lib/get-model-id";
import { lazyConnection } from "@/graphql-api/lib/lazy-connection";
import { AccountRef } from "@/graphql-api/schema/account";
import { AccountIdInput, AccountIdRef } from "@/graphql-api/schema/account-id";
import { ID_PAGINATED_CONNECTION_ARGS } from "@/graphql-api/schema/constants";
import { EventRef, EventsWhereInput } from "@/graphql-api/schema/event";
import { NameOrNodeInput } from "@/graphql-api/schema/name-or-node";
import { PermissionsRef, type PermissionsUserResource } from "@/graphql-api/schema/permissions";
import { PermissionsRef } from "@/graphql-api/schema/permissions";
import { ResolverRecordsRef } from "@/graphql-api/schema/resolver-records";
import { ensDb, ensIndexerSchema } from "@/lib/ensdb/singleton";

Expand Down Expand Up @@ -121,24 +114,6 @@ ResolverRef.implement({
},
}),

//////////////////////
// Resolver.dedicated
//////////////////////
dedicated: t.field({
description: "If Resolver is a DedicatedResolver, additional DedicatedResolverMetadata.",
type: DedicatedResolverMetadataRef,
nullable: true,
resolve: async (parent, args, context) =>
ensDb.query.permissionsUser.findFirst({
where: (t, { eq, and }) =>
and(
eq(t.chainId, parent.chainId),
eq(t.address, parent.address),
eq(t.resource, ROOT_RESOURCE),
),
}),
}),

////////////////////
// Resolver.bridged
////////////////////
Expand Down Expand Up @@ -178,49 +153,6 @@ ResolverRef.implement({
}),
});

/////////////////////////////
// DedicatedResolverMetadata
/////////////////////////////
export const DedicatedResolverMetadataRef = builder.objectRef<PermissionsUserResource>(
"DedicatedResolverMetadataRef",
);
DedicatedResolverMetadataRef.implement({
description: "Represents additional metadata available for DedicatedResolvers.",
fields: (t) => ({
///////////////////////////
// DedicatedResolver.owner
///////////////////////////
owner: t.field({
description: "The Account that owns this DedicatedResolver.",
type: AccountRef,
nullable: false,
resolve: (parent) => parent.user,
}),

/////////////////////////////////
// DedicatedResolver.permissions
/////////////////////////////////
permissions: t.field({
description: "TODO",
type: PermissionsRef,
nullable: false,
// TODO: render a DedicatedResolverPermissions model that parses the backing permissions into dedicated-resolver-semantic roles?
resolve: ({ chainId, address }) => makePermissionsId({ chainId, address }),
}),

/////////////////////////////
// Resolver.dedicatedRecords
/////////////////////////////
records: t.field({
description: "The ResolverRecords issued under `NODE_ANY`.",
type: ResolverRecordsRef,
nullable: true,
resolve: ({ chainId, address }, args) =>
makeResolverRecordsId({ chainId, address }, NODE_ANY),
}),
}),
});

/////////////////////
// Inputs
/////////////////////
Expand Down
17 changes: 17 additions & 0 deletions apps/ensindexer/src/lib/ensv2/registry-db-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { type AccountId, makeRegistryId } from "@ensnode/ensnode-sdk";

import { ensIndexerSchema, type IndexingEngineContext } from "@/lib/indexing-engines/ponder";

export async function ensureRegistry(context: IndexingEngineContext, registry: AccountId) {
const registryId = makeRegistryId(registry);

await context.ensDb
.insert(ensIndexerSchema.registry)
.values({
id: registryId,
...registry,
})
.onConflictDoNothing();

return registryId;
}
Loading
Loading