Skip to content

update ensv2 indexing for latest devnet: reservations, event renames, resolver.dedicated removal#1845

Merged
shrugs merged 13 commits intomainfrom
feat/devnet-compat
Mar 31, 2026
Merged

update ensv2 indexing for latest devnet: reservations, event renames, resolver.dedicated removal#1845
shrugs merged 13 commits intomainfrom
feat/devnet-compat

Conversation

@shrugs
Copy link
Copy Markdown
Collaborator

@shrugs shrugs commented Mar 30, 2026

Reviewer Focus (Read This First)

reviewers should focus on:

  1. ENSv2Registry.ts handler refactor — the unified handleRegistrationOrReservation function and the new LabelUnregistered handler
  2. RegistrationType enum change — ENSv2Registry was split into ENSv2RegistryRegistration and ENSv2RegistryReservation
  3. integration test env refactor — switched from standalone testcontainers to DockerComposeEnvironment sourced from root docker-compose.yml.

Problem & Motivation

  • the ENSv2 devnet contracts have changed significantly: NameRegistered → LabelRegistered, new LabelReserved and LabelUnregistered events, updated ABIs, and new contract addresses.
  • ENSv2 registries now support "reservations": registrations without an owner
  • Resolver.dedicated was built against an earlier contract design; it's removed
  • the integration test environment was duplicating docker image configuration between the orchestrator and docker-compose.yml, creating drift risk.

What Changed (Concrete)

  1. ensindexer: unified registration/reservation handler — extracted handleRegistrationOrReservation to handle both LabelRegistered and LabelReserved events. a reservation is a registration with no owner.
  2. ensindexer: new LabelUnregistered handler — sets expiry to event.block.timestamp, effectively removing the name from resolution.
  3. ensindexer: event renames — NameRegistered → LabelRegistered
  4. ensdb-sdk: RegistrationType enum — "ENSv2Registry" split into "ENSv2RegistryRegistration" and "ENSv2RegistryReservation".
  5. ensapi: removed Resolver.dedicated field and DedicatedResolverMetadata type. removed isDedicatedResolver from ensnode-sdk/rpc.
  6. datasources: updated ABIs for ETHRegistrar, Registry, EnhancedAccessControl, UniversalResolverV2 to match latest devnet.
  7. datasources: updated all contract addresses in ens-test-env.ts for latest devnet deployment.
  8. integration-test-env: refactored orchestrator to use DockerComposeEnvironment from root docker-compose.yml instead of standalone PostgreSqlContainer + GenericContainer. removed @testcontainers/postgresql dependency.
  9. docker-compose.yml: added devnet service definition with health check.

Design & Planning

  • reservation as "registration without owner" is a direct mapping of the contract semantics
  • unifying the handler avoids duplicating sanity checks, label discovery, and domain upsert logic.
  • LabelUnregistered sets expiry rather than deleting, consistent with how expired names are already treated (expired = non-existent for resolution), preserving the consistency of the indexed schema
  • docker-compose as single source of truth for devnet/postgres images eliminates version drift between manual runs and CI.
  • Planning artifacts: none
  • Reviewed / approved by: n/a

Self-Review

  • Dead or unnecessary code removed: Resolver.dedicated, DedicatedResolverMetadata, isDedicatedResolver, standalone container config in orchestrator

Cross-Codebase Alignment

  • Search terms used: ENSv2Registry, RegistrationType, dedicated, isDedicatedResolver
  • Reviewed but unchanged: ensapi permissions schema (still used by other resolvers)
  • Deferred alignment: none

Downstream & Consumer Impact

  • API consumers: Resolver.dedicated field is removed (breaking). queries referencing it will fail. a new ENSv2RegistryReservation type appears in the Registration union.
  • SDK consumers: isDedicatedResolver export is removed from @ensnode/ensnode-sdk/rpc.
  • Operators: devnet deployments must use the updated contract addresses. docker-compose.yml now includes a devnet service.
  • Public APIs affected: Resolver.dedicated removed, ENSv2RegistryReservation added
  • Docs updated: integration-test-env/README.md updated to reference docker-compose.yml
  • Naming decisions worth calling out: ENSv2RegistryRegistration / ENSv2RegistryReservation distinguish the two registration types at schema, indexer, and API layers

Testing Evidence

  • integration tests updated (account.integration.test.ts uses new owner address from latest devnet).
  • full integration test suite validates indexing against the updated devnet.
  • Testing performed: integration tests against latest devnet
  • Known gaps: no dedicated unit tests for LabelUnregistered handler or reservation-over-expired-registration path
  • What reviewers have to reason about manually: correctness of invariant conditions in handleRegistrationOrReservation (reservation requires expired-or-none, registration allows reservation-or-expired-or-none)

Risk Analysis

  • Risk areas: schema enum change, API field removal
  • Mitigations or rollback options: this is all devnet-only; production ENSv1 indexing is unaffected. revert the PR to restore previous behavior.
  • Named owner if this causes problems: @shrugs

Pre-Review Checklist (Blocking)

  • I reviewed every line of this diff and understand it end-to-end
  • I'm prepared to defend this PR line-by-line in review
  • I'm comfortable being the on-call owner for this change
  • Relevant changesets are included (or explicitly not required)

Copilot AI review requested due to automatic review settings March 30, 2026 18:59
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Mar 31, 2026 5:26pm
ensnode.io Ready Ready Preview, Comment Mar 31, 2026 5:26pm
ensrainbow.io Ready Ready Preview, Comment Mar 31, 2026 5:26pm

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 30, 2026

🦋 Changeset detected

Latest commit: a9c73b9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
ensapi Major
ensindexer Major
ensadmin Major
ensrainbow Major
fallback-ensapi Major
@ensnode/datasources Major
@ensnode/ensrainbow-sdk Major
@ensnode/ensdb-sdk Major
@ensnode/ensnode-react Major
@ensnode/ensnode-sdk Major
@ensnode/ponder-sdk Major
@ensnode/ponder-subgraph Major
@ensnode/shared-configs Major
@docs/ensnode Major
@docs/ensrainbow Major
@docs/mintlify Major
@namehash/ens-referrals Major
@namehash/namehash-ui Major
@ensnode/integration-test-env Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7063cb71-3e38-454f-8d30-852371e9e3d6

📥 Commits

Reviewing files that changed from the base of the PR and between 19a467e and a9c73b9.

📒 Files selected for processing (3)
  • docker-compose.yml
  • packages/integration-test-env/README.md
  • packages/integration-test-env/src/orchestrator.ts

📝 Walkthrough

Walkthrough

Removes the Resolver.dedicated GraphQL field and dedicated-resolver SDK export; introduces distinct ENSv2 registration vs reservation types across indexer, DB schema, and GraphQL; centralizes ENSv2 label event handling; reshapes multiple ENSv2 ABIs; and migrates integration test orchestration to docker-compose with a devnet service.

Changes

Cohort / File(s) Summary
Changeset & Docs
\.changeset/tricky-clouds-dress.md, AGENTS.md, packages/integration-test-env/README.md
Adds a changeset noting breaking change; updates AGENTS build/typecheck guidance; expands integration-test README and manual devnet workflow.
GraphQL — Registration
apps/ensapi/src/graphql-api/schema/registration.ts
Adds unregistrantId, unregistrant relation fields; introduces ENSv2RegistryReservation type and ENSv2RegistryReservationRef; fixes ENSv2RegistryRegistrationRef discriminator and fields.
GraphQL — Resolver & Tests
apps/ensapi/src/graphql-api/schema/resolver.ts, apps/ensapi/src/graphql-api/schema/account.integration.test.ts
Removes Resolver.dedicated field and DedicatedResolverMetadataRef; updates test constant/address usage.
Indexer — ENSv2 Handlers
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts, .../ETHRegistrar.ts
Adds unified handler for LabelRegistered/LabelReserved with reservation vs registration branching; adjusts invariants and registration-type checks to ENSv2RegistryRegistration/ENSv2RegistryReservation.
Database Schema
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
Replaces ENSv2Registry enum member with ENSv2RegistryRegistration and ENSv2RegistryReservation; adds nullable unregistrantId column and unregistrant relation.
SDK — RPC exports
packages/ensnode-sdk/src/rpc/index.ts, packages/ensnode-sdk/src/rpc/is-dedicated-resolver.ts
Removes is-dedicated-resolver module and its re-export (dedicated-resolver support removed).
Contract ABIs
packages/datasources/src/abis/ensv2/...
Rewrites ABI objects for ETHRegistrar, EnhancedAccessControl, Registry, UniversalResolverV2 to explicit type, stateMutability, internalType, indexed, and normalized inputs/outputs.
Test Env — Devnet & Orchestration
docker-compose.yml, package.json, packages/integration-test-env/src/orchestrator.ts, packages/integration-test-env/package.json, packages/integration-test-env/tsconfig.json
Adds devnet service + healthcheck and devnet npm script; switches integration orchestrator to DockerComposeEnvironment with compose-level waits; adds typecheck script and tsconfig.
Test Addresses & Examples
packages/datasources/src/ens-test-env.ts, packages/ensnode-sdk/src/graphql-api/example-queries.ts
Updates pinned devnet contract addresses and hardcoded DEVNET_USER address constant.

Sequence Diagram

sequenceDiagram
    participant Blockchain
    participant Handler
    participant DB
    participant GraphQL
    participant Client

    Blockchain->>Handler: LabelRegistered / LabelReserved event
    Handler->>Handler: Validate label / canonicalId
    alt owner present
        Handler->>DB: Insert registration (type=ENSv2RegistryRegistration)
        Handler->>DB: Upsert v2Domain owner/update, emit domain event
    else owner absent
        Handler->>DB: Insert registration (type=ENSv2RegistryReservation)
    end

    Blockchain->>Handler: LabelUnregistered event
    Handler->>DB: Set registration.unregistrantId and expiry, emit domain event

    Client->>GraphQL: Query registration
    GraphQL->>DB: Fetch registration by id
    alt type == ENSv2RegistryRegistration
        GraphQL-->>Client: Return Registration shape
    else type == ENSv2RegistryReservation
        GraphQL-->>Client: Return Reservation shape
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

ensnode-sdk

🐰
Registrations hop, reservations wait,
Handlers split the leaping fate,
Devnet hums and ABIs shine,
Dedicated fields decline,
A tiny rabbit cheers: "Code, iterate!"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main changes: updating ENSv2 indexing for the latest devnet with three key elements (reservations, event renames, resolver.dedicated removal).
Description check ✅ Passed The PR description provides comprehensive coverage of changes, motivation, design decisions, testing, and impact. It exceeds the template requirements with detailed 'Reviewer Focus', 'Problem & Motivation', 'What Changed (Concrete)', 'Design & Planning', and 'Risk Analysis' sections.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/devnet-compat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates ENSv2 devnet indexing + schema/API to match the latest contracts, including the new reservation/unregistration event model, plus refactors the integration test environment to run Postgres+devnet via the root docker-compose.

Changes:

  • Refactors ENSv2Registry event handling to unify LabelRegistered/LabelReserved logic and adds LabelUnregistered handling.
  • Splits RegistrationType for ENSv2 into ENSv2RegistryRegistration vs ENSv2RegistryReservation, updating indexer + GraphQL API types accordingly.
  • Updates devnet ABIs/addresses and moves integration-test-env orchestration to DockerComposeEnvironment; removes Resolver.dedicated / dedicated resolver helpers.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pnpm-lock.yaml Removes @testcontainers/postgresql, adds shared-configs workspace dep for integration-test-env.
packages/integration-test-env/tsconfig.json Aligns integration-test-env TS config with shared monorepo config.
packages/integration-test-env/src/orchestrator.ts Starts Postgres+devnet via docker-compose/testcontainers; keeps remaining services running from source.
packages/integration-test-env/package.json Adds shared-configs dependency and a typecheck script.
packages/integration-test-env/README.md Updates devnet pin/reference to docker-compose as source of truth.
packages/ensnode-sdk/src/rpc/is-dedicated-resolver.ts Removes dedicated resolver interface helper.
packages/ensnode-sdk/src/rpc/index.ts Stops exporting isDedicatedResolver.
packages/ensnode-sdk/src/graphql-api/example-queries.ts Updates devnet test user address.
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts Splits ENSv2 registry registration enum into Registration vs Reservation.
packages/datasources/src/ens-test-env.ts Updates devnet contract addresses for latest deployment.
packages/datasources/src/abis/ensv2/UniversalResolverV2.ts Updates UniversalResolverV2 ABI to latest devnet version.
packages/datasources/src/abis/ensv2/Registry.ts Updates Registry ABI (event renames + new events).
packages/datasources/src/abis/ensv2/EnhancedAccessControl.ts Updates EnhancedAccessControl ABI to latest devnet version.
packages/datasources/src/abis/ensv2/ETHRegistrar.ts Updates ETHRegistrar ABI to latest devnet version.
package.json Adds devnet helper script for docker compose.
docker-compose.yml Adds devnet service + healthcheck; adjusts env wiring for schema names.
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts Updates invariants to the new ENSv2 registration type value.
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts Introduces unified registration/reservation handler + LabelUnregistered expiry behavior.
apps/ensindexer/src/lib/ensv2/registry-db-helpers.ts Extracts ensureRegistry helper for registry upserts.
apps/ensapi/src/graphql-api/schema/resolver.ts Removes Resolver.dedicated and dedicated resolver metadata types.
apps/ensapi/src/graphql-api/schema/registration.ts Adds ENSv2RegistryReservation GraphQL object type + discriminator updates.
apps/ensapi/src/graphql-api/schema/account.integration.test.ts Updates devnet owner address used by integration test.
AGENTS.md Updates guidance to prefer workspace typecheck scripts.
.changeset/tricky-clouds-dress.md Changeset documenting breaking removal of Resolver.dedicated.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts`:
- Around line 337-340: The TODO notes a temporary invariant that early-returns
non-Registry ERC1155 transfers; create a formal issue/ticket to track removing
this check and link it from the code by replacing the TODO with the issue ID (or
a brief "tracked in ISSUE-<id>") so future work isn't lost. Update the comment
near makeRegistryId / registryId / context.ensDb.find /
ensIndexerSchema.registry to reference that issue and include expected follow-up
steps (remove the guard and rely on only indexing Registry contracts), then push
that comment change with the issue created in your project tracker.

In `@docker-compose.yml`:
- Line 10: Update the example env so it matches the new schema name: in
apps/ensapi/.env.local.example replace the value currently set to "ensindexer_0"
(the entry around line 16) with "a_unique_schema_name" so it matches
DATABASE_SCHEMA in docker-compose.yml; also scan apps/ensapi/.env.local.example
for any other occurrences of "ensindexer_0" and update them to
"a_unique_schema_name" to keep examples consistent.

In `@packages/integration-test-env/src/orchestrator.ts`:
- Around line 241-250: The orchestrator is referencing auto-generated container
names ("postgres-1", "devnet-1") while docker-compose.yml sets explicit
container_name (e.g. "postgres"), causing getContainer to fail; update the
DockerComposeEnvironment usage to match the explicit names: change the
withWaitStrategy calls that reference "devnet-1" and "postgres-1" to "devnet"
and "postgres", change composeEnvironment.getContainer("postgres-1") to
composeEnvironment.getContainer("postgres"), and ensure any other references
(mapped port, DATABASE_URL construction) use the same explicit container/service
names so names align with docker-compose.yml.
- Line 56: The constant ENSINDEXER_SCHEMA_NAME in orchestrator.ts is set to
"ensindexer_0" but Docker Compose uses "a_unique_schema_name"; change the
ENSINDEXER_SCHEMA_NAME value to "a_unique_schema_name" so the spawned ensindexer
and ensapi processes use the same Postgres schema as the docker-compose service
(look for the ENSINDEXER_SCHEMA_NAME constant in orchestrator.ts and update its
string literal accordingly).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0bfcf860-2819-4647-ad6a-9050247a3540

📥 Commits

Reviewing files that changed from the base of the PR and between 81db674 and 3d12851.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • .changeset/tricky-clouds-dress.md
  • AGENTS.md
  • apps/ensapi/src/graphql-api/schema/account.integration.test.ts
  • apps/ensapi/src/graphql-api/schema/registration.ts
  • apps/ensapi/src/graphql-api/schema/resolver.ts
  • apps/ensindexer/src/lib/ensv2/registry-db-helpers.ts
  • apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts
  • apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts
  • docker-compose.yml
  • package.json
  • packages/datasources/src/abis/ensv2/ETHRegistrar.ts
  • packages/datasources/src/abis/ensv2/EnhancedAccessControl.ts
  • packages/datasources/src/abis/ensv2/Registry.ts
  • packages/datasources/src/abis/ensv2/UniversalResolverV2.ts
  • packages/datasources/src/ens-test-env.ts
  • packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
  • packages/ensnode-sdk/src/graphql-api/example-queries.ts
  • packages/ensnode-sdk/src/rpc/index.ts
  • packages/ensnode-sdk/src/rpc/is-dedicated-resolver.ts
  • packages/integration-test-env/README.md
  • packages/integration-test-env/package.json
  • packages/integration-test-env/src/orchestrator.ts
  • packages/integration-test-env/tsconfig.json
💤 Files with no reviewable changes (2)
  • packages/ensnode-sdk/src/rpc/is-dedicated-resolver.ts
  • packages/ensnode-sdk/src/rpc/index.ts

@shrugs shrugs marked this pull request as ready for review March 30, 2026 19:18
@shrugs shrugs requested a review from a team as a code owner March 30, 2026 19:18
Copilot AI review requested due to automatic review settings March 30, 2026 19:18
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker-compose.yml`:
- Around line 106-120: Add an explicit container_name for the devnet service so
it matches the other services; inside the devnet service block (the service
named "devnet") add the key container_name: devnet at the same indentation level
as image/command/ports, ensuring no duplicate container name conflicts with
other services and updating any external references if necessary.
- Around line 114-115: The healthcheck for the devnet service is incorrect (it
probes http://localhost:8000) and will always fail because the image
ghcr.io/ensdomains/contracts-v2 (Anvil node) exposes a JSON-RPC on port 8545;
either remove the healthcheck block for the devnet service entirely, or change
the healthcheck test to target the JSON-RPC port (e.g., use healthcheck/test
pointing to http://localhost:8545 or a TCP check against port 8545) so the check
actually verifies the Anvil JSON-RPC is responsive.

In `@packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts`:
- Around line 356-364: The schema adds an unregistrantId field but
registration_relations lacks a matching relation; update the
registration_relations definition to include an unregistrant relation mirroring
the existing registrant relation (use unregistrantId as the foreign key, point
to the same target entity/type used by registrant, and name the relation
"unregistrant") so relation-based queries can resolve unregistrant links (refer
to registration_relations, registrantId, and unregistrantId to implement).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0df35386-e07c-498c-95c1-820908d9d85b

📥 Commits

Reviewing files that changed from the base of the PR and between 3d12851 and 8784e46.

📒 Files selected for processing (5)
  • apps/ensapi/src/graphql-api/schema/registration.ts
  • apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts
  • docker-compose.yml
  • packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
  • packages/integration-test-env/src/orchestrator.ts

@vercel vercel bot temporarily deployed to Preview – ensrainbow.io March 30, 2026 19:23 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io March 30, 2026 19:23 Inactive
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io March 30, 2026 19:23 Inactive
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR updates ENSv2 indexing to align with the latest devnet contract changes: event renames (NameRegisteredLabelRegistered), new LabelReserved and LabelUnregistered events, ABI refreshes, and updated contract addresses. It also splits the ENSv2Registry registration type into ENSv2RegistryRegistration / ENSv2RegistryReservation, removes the Resolver.dedicated API field, and refactors the integration test environment to use DockerComposeEnvironment from the root docker-compose.yml.

Key changes:

  • ENSv2Registry.ts: unified handleRegistrationOrReservation correctly distinguishes reservation (owner === undefined) from registration; invariant guards are well-structured and cover the reservation-over-expired-registration path
  • LabelUnregistered handler sets expiry = event.block.timestamp to soft-delete the name from resolution; v2Domain.ownerId is not explicitly cleared (expected to be handled by a subsequent TransferSingle burn, but worth confirming)
  • Schema registrationType enum split and new nullable unregistrantId column are correctly reflected across the indexer, API, and SDK layers
  • ensureRegistry helper correctly omits the now-absent type field from the registry table
  • DockerComposeEnvironment refactor eliminates image version drift between manual runs and CI

Confidence Score: 5/5

Safe to merge — all changes are scoped to devnet; production ENSv1 indexing is unaffected. All findings are P2 style or documentation issues.

The core indexer logic (unified handler, invariant guards, schema split, API removal) is well-structured and internally consistent. No P0/P1 issues were found. The four P2 observations are: a variable name typo in a test, stale error message text in ETHRegistrar, an unconfirmed assumption about TransferSingle clearing ownerId on unregistration, and a removed build-command doc line. None of these block merge.

ENSv2Registry.ts (LabelUnregistered ownerId gap) and ETHRegistrar.ts (stale error messages) are worth a second glance, but neither is blocking.

Important Files Changed

Filename Overview
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts Core logic refactor: unified handleRegistrationOrReservation for LabelRegistered/LabelReserved, new LabelUnregistered handler, event renames. Logic is sound; minor gap: v2Domain.ownerId is not explicitly cleared on unregistration (P2).
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts Updated type guard from "ENSv2Registry" to "ENSv2RegistryRegistration"; stale error message text still references old type name.
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts Splits ENSv2Registry enum value into ENSv2RegistryRegistration / ENSv2RegistryReservation; adds nullable unregistrantId column to the registration table. Schema changes look correct.
apps/ensapi/src/graphql-api/schema/registration.ts Adds ENSv2RegistryReservationRef, exposes nullable registrant and unregistrant fields on the RegistrationInterface, and updates isTypeOf discriminators. Correct alignment with schema enum split.
apps/ensapi/src/graphql-api/schema/resolver.ts Removes Resolver.dedicated field and DedicatedResolverMetadata type as documented. Clean removal with no orphaned references.
packages/integration-test-env/src/orchestrator.ts Switches from standalone PostgreSqlContainer + GenericContainer to DockerComposeEnvironment, sourcing docker-compose.yml as the single source of truth. Database URL construction from mapped port is correct.
docker-compose.yml Adds devnet service with health check, updates schema name to docker_compose_ensindexer_schema for clarity. Health check target port (8000) matches expected devnet health endpoint.
apps/ensapi/src/graphql-api/schema/account.integration.test.ts Updates owner address to match latest devnet deployment; introduces minor NEW_OWNER_OWNER typo in variable name (P2).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ENSv2 Registry Contract Event] --> B{Event Type}
    B -->|LabelRegistered| C[handleRegistrationOrReservation
owner defined → isReservation=false]
    B -->|LabelReserved| D[handleRegistrationOrReservation
owner undefined → isReservation=true]
    B -->|LabelUnregistered| E[LabelUnregistered Handler]
    B -->|ExpiryUpdated| F[ExpiryUpdated Handler]
    C --> G{Existing Registration?}
    D --> G
    G -->|None or Expired| H[Upsert v2Domain
Insert Registration
type=ENSv2RegistryRegistration or ENSv2RegistryReservation]
    G -->|Unexpired + not a Reservation| I[Invariant Error]
    E --> J{Registration Exists
and Not Expired?}
    J -->|Yes| K[Set expiry=block.timestamp
Set unregistrantId]
    J -->|No| L[Invariant Error]
    H --> M[ensureDomainEvent]
    K --> M
Loading

Comments Outside Diff (4)

  1. apps/ensapi/src/graphql-api/schema/account.integration.test.ts, line 34 (link)

    P2 Likely typo: NEW_OWNER_OWNER

    The variable name NEW_OWNER_OWNER looks like an accidental duplication. Based on context (it's the address used in the "returns domains owned by the new owner" test case), it should just be NEW_OWNER.

  2. apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ETHRegistrar.ts, line 542-547 (link)

    P2 Stale error message text references old type name

    The invariant comment and the thrown error message still reference the old type name "ENSv2Registry Registration", but the type has been renamed to "ENSv2RegistryRegistration". This appears in both the NameRegistered and NameRenewed handlers. Stale messages can be confusing when debugging.

    The same applies at the NameRenewed handler (around line 553–556).

  3. apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts, line 486-497 (link)

    P2 v2Domain.ownerId not cleared on LabelUnregistered

    The handler correctly marks the registration as expired by setting expiry = event.block.timestamp, but v2Domain.ownerId is not explicitly cleared. If the contract emits a TransferSingle burn (to the zero address) alongside LabelUnregistered, the existing TransferSingle handler will handle this. But if there is any scenario where the burn is not emitted (e.g., a reserved label with no owner), the domain could still show a stale ownerId after unregistration.

    The inline comment acknowledges missing details about eacVersionId / tokenVersionId, but the ownerId gap is worth explicitly noting or tracking. Consider whether you need to also clear ownerId here for the no-prior-owner (reservation) case, or add an assertion that a TransferSingle burn always follows.

  4. AGENTS.md, line 21 (link)

    P2 Build command documentation removed

    The previous line described how to validate tsup/tsx bundling with pnpm --filter <package-name> build. That guidance has been replaced by a typecheck sub-bullet, but the build validation tip is now completely absent from the developer runbook. Consider keeping both, or confirm this was intentional.

Reviews (1): Last reviewed commit: "nit: add relation" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

The example address for EnsTestEnv "user" is outdated and doesn't match the new devnet configuration used in other test files

Fix on Vercel

@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io March 30, 2026 22:27 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io March 30, 2026 22:27 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io March 30, 2026 22:27 Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
docker-compose.yml (1)

106-121: ⚠️ Potential issue | 🟠 Major

Healthcheck targets incorrect port - devnet (Anvil) does not expose /health on port 8000.

The ghcr.io/ensdomains/contracts-v2 image runs an Anvil blockchain node that exposes JSON-RPC on port 8545, not an HTTP health endpoint on port 8000. This healthcheck will fail indefinitely, causing Wait.forHealthCheck() in the orchestrator to hang.

Replace with a check against the JSON-RPC endpoint:

🔧 Proposed fix
     healthcheck:
-      test: ["CMD", "curl", "--fail", "-s", "http://localhost:8000/health"]
+      test: ["CMD", "curl", "--fail", "-s", "-X", "POST", "-H", "Content-Type: application/json", "-d", "{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}", "http://localhost:8545"]
       interval: 10s
       timeout: 5s
       retries: 5
       start_period: 30s
       start_interval: 1s

Alternatively, if curl is not available in the container, use a TCP check or wget:

test: ["CMD-SHELL", "wget -q --spider http://localhost:8545 || exit 1"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 106 - 121, The devnet service healthcheck is
targeting port 8000 which Anvil does not expose; update the devnet healthcheck
(service name "devnet", healthcheck block) to probe the JSON-RPC endpoint on
port 8545 instead (e.g., curl or wget against http://localhost:8545) or switch
to a TCP check against port 8545 if curl/wget may be missing; ensure the test
array and command form (e.g., CMD or CMD-SHELL) is adjusted accordingly so
Wait.forHealthCheck() can succeed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/integration-test-env/README.md`:
- Around line 36-121: The README's "Start Postgres" subsection has a missing
blank line immediately before the fenced code block that contains the shell
command "docker compose up postgres"; add a single blank line between the
explanatory paragraph ("or with the local docker compose:") and the subsequent
```sh fenced code block so the Markdown renders correctly; locate the "Start
Postgres" heading and the code block with "docker compose up postgres" to make
this change.

---

Duplicate comments:
In `@docker-compose.yml`:
- Around line 106-121: The devnet service healthcheck is targeting port 8000
which Anvil does not expose; update the devnet healthcheck (service name
"devnet", healthcheck block) to probe the JSON-RPC endpoint on port 8545 instead
(e.g., curl or wget against http://localhost:8545) or switch to a TCP check
against port 8545 if curl/wget may be missing; ensure the test array and command
form (e.g., CMD or CMD-SHELL) is adjusted accordingly so Wait.forHealthCheck()
can succeed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f1893894-89a5-4800-b73c-87e84d3a69e3

📥 Commits

Reviewing files that changed from the base of the PR and between 8784e46 and 19a467e.

📒 Files selected for processing (4)
  • apps/ensindexer/src/plugins/ensv2/handlers/ensv2/ENSv2Registry.ts
  • docker-compose.yml
  • packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
  • packages/integration-test-env/README.md

Copy link
Copy Markdown
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@shrugs Great work 🙌 Shared a few small suggestions. Please take the lead to merge when ready 👍

Copilot AI review requested due to automatic review settings March 31, 2026 17:25
@shrugs shrugs merged commit a983e4d into main Mar 31, 2026
14 of 18 checks passed
@shrugs shrugs deleted the feat/devnet-compat branch March 31, 2026 17:26
@github-actions github-actions bot mentioned this pull request Mar 31, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (composeEnvironment) {
try {
await container.stop();
await composeEnvironment.down();
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.

composeEnvironment.down() will stop containers but (like docker compose down) typically keeps named volumes (e.g. postgres_data in the root compose file). That can leak DB state between integration test runs (schemas/data persist), which may cause flakiness locally and unbounded disk growth. Consider removing volumes on teardown (if the testcontainers API supports it) or switching Postgres to an anonymous/ephemeral volume for orchestrated runs.

Suggested change
await composeEnvironment.down();
await composeEnvironment.down({ removeVolumes: true });

Copilot uses AI. Check for mistakes.
pnpm start
```

Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
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 docs list required ports as (8545, 8000, 3223, 42069, 4334), but the current docker-compose config also publishes Postgres on 5432, and devnet's 8000 health port isn't published to the host. Please update this list to reflect the actual ports that must be free when running the orchestrator locally.

Suggested change
Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
Works both in CI and locally — just make sure the required ports are available (8545, 5432, 3223, 42069, 4334).

Copilot uses AI. Check for mistakes.
with environment variables:

```env
DATABASE_URL=postgresql://ensnode:ensnode@localhost:5432/ensnode
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 manual ENSApi env example uses DATABASE_URL=postgresql://ensnode:ensnode@localhost:5432/ensnode, which doesn't match the repo's docker-compose Postgres defaults (postgres:password / db postgres) nor the orchestrator's DATABASE_URL. Either update the example to match the compose/orchestrator defaults, or explicitly note that this is only an example and must be aligned with how Postgres is started.

Suggested change
DATABASE_URL=postgresql://ensnode:ensnode@localhost:5432/ensnode
DATABASE_URL=postgresql://postgres:password@localhost:5432/postgres

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 30
const DEVNET_DEPLOYER: Address = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266";
const DEFAULT_OWNER: Address = "0x70997970c51812dc3a010c7d01b50e0d17dc79c8";
const NEW_OWNER: Address = "0x90f79bf6eb2c4f870365e785982e1f101e93b906";
const NEW_OWNER_OWNER: Address = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC";

describe("Account.domains", () => {
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants