Skip to content
5 changes: 5 additions & 0 deletions .changeset/busy-poems-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ensnode/ensnode-schema": minor
---

Update `registrars` schema to enable storing at most one metadata record.
5 changes: 5 additions & 0 deletions .changeset/social-colts-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensindexer": patch
---

Update `registrars` plugin indexing logic to store at most one metadata record in ENSDb for current "logical registrar action".
5 changes: 5 additions & 0 deletions apps/ensindexer/src/lib/managed-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const CONTRACTS_BY_MANAGED_NAME: Record<Name, AccountId[]> = {
DatasourceNames.ENSRoot,
"UnwrappedEthRegistrarController",
),
getDatasourceContract(
config.namespace,
DatasourceNames.ENSRoot,
"UniversalRegistrarRenewalWithReferrer",
),
ethnamesNameWrapper,
],
"base.eth": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -94,7 +93,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand All @@ -121,7 +119,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -152,7 +149,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand All @@ -179,7 +175,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -118,7 +117,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -170,7 +168,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -220,7 +217,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -275,7 +271,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -328,7 +323,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export default function () {

await handleUniversalRegistrarRenewalEvent(context, {
id,
subregistryId,
node,
referral,
transactionHash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -101,7 +100,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -137,7 +135,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down Expand Up @@ -173,7 +170,6 @@ export default function () {

await handleRegistrarControllerEvent(context, {
id,
subregistryId,
node,
pricing,
referral,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import type { Context } from "ponder:registry";
import schema from "ponder:schema";
import type { Hash } from "viem";

import {
type AccountId,
formatAccountId,
type Node,
type RegistrarAction,
} from "@ensnode/ensnode-sdk";
import { formatAccountId, type Node, type RegistrarAction } from "@ensnode/ensnode-sdk";

/**
* Logical Event Key
Expand All @@ -24,15 +19,13 @@ export type LogicalEventKey = string;
* Make a logical event key for a "logical registrar action".
*/
export function makeLogicalEventKey({
subregistryId,
node,
transactionHash,
}: {
subregistryId: AccountId;
node: Node;
transactionHash: Hash;
}): LogicalEventKey {
return [formatAccountId(subregistryId), node, transactionHash].join(":").toLowerCase();
return [node, transactionHash].join(":").toLowerCase();
}

/**
Expand All @@ -57,15 +50,25 @@ export async function insertRegistrarAction(
// 1. Create logical event key
const logicalEventKey = makeLogicalEventKey({
node,
subregistryId,
transactionHash,
});

// 2. Store mapping between logical event key and logical event id
await context.db.insert(schema.internal_registrarActionMetadata).values({
logicalEventKey,
logicalEventId: id,
});
// Note: If the metadata record already exists,
// we update it to point to the current logical event key and id.
// This ensures that there is always at most one record in ENSDb
// for the current "logical registrar action".
await context.db
.insert(schema.internal_registrarActionMetadata)
.values({
metadataType: "CURRENT_LOGICAL_REGISTRAR_ACTION",
logicalEventKey,
logicalEventId: id,
})
.onConflictDoUpdate(() => ({
logicalEventKey,
logicalEventId: id,
}));

// 3. Store initial record for the "logical registrar action"
await context.db.insert(schema.registrarActions).values({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import schema from "ponder:schema";
import type { Address, Hash } from "viem";

import {
type AccountId,
type EncodedReferrer,
isRegistrarActionPricingAvailable,
isRegistrarActionReferralAvailable,
Expand All @@ -24,14 +23,12 @@ export async function handleRegistrarControllerEvent(
context: Context,
{
id,
subregistryId,
node,
pricing,
referral,
transactionHash,
}: {
id: Event["id"];
subregistryId: AccountId;
node: Node;
pricing: RegistrarActionPricing;
referral: RegistrarActionReferral;
Expand All @@ -40,37 +37,41 @@ export async function handleRegistrarControllerEvent(
): Promise<void> {
// 1. Make Logical Event Key
const logicalEventKey = makeLogicalEventKey({
subregistryId,
node,
transactionHash,
});

// 2. Use the Logical Event Key to get the "logical registrar action" record
// which needs to be updated.

// 2. a) Find subregistryActionMetadata record by logical event key.
const subregistryActionMetadata = await context.db.find(schema.internal_registrarActionMetadata, {
logicalEventKey,
// 2. a) Find registrarActionMetadata record for the current "logical registrar action".
const registrarActionMetadata = await context.db.find(schema.internal_registrarActionMetadata, {
metadataType: "CURRENT_LOGICAL_REGISTRAR_ACTION",
});

// Invariant: the subregistryActionMetadata record must be available for `logicalEventKey`
if (!subregistryActionMetadata) {
// Invariant: the registrarActionMetadata record must exist
if (!registrarActionMetadata) {
throw new Error(
`The required "logical registrar action" ID could not be found for the following logical event key: '${logicalEventKey}'.`,
);
}

const { logicalEventId } = subregistryActionMetadata;
// Invariant: the stored logical event key must match the current logical event key
if (registrarActionMetadata.logicalEventKey !== logicalEventKey) {
throw new Error(
`The logical event key ('${registrarActionMetadata.logicalEventKey}') for the "logical registrar action" record must be same as the current logical event key ('${logicalEventKey}').`,
);
}

// 2. b) Find "logical registrar action" record by `logicalEventId`.
const logicalRegistrarAction = await context.db.find(schema.registrarActions, {
id: logicalEventId,
id: registrarActionMetadata.logicalEventId,
});

// Invariant: the "logical registrar action" record must be available for `logicalEventId`
if (!logicalRegistrarAction) {
throw new Error(
`The "logical registrar action" record, which could not be found for the following logical event ID: '${logicalEventId}'.`,
`The "logical registrar action" record, which could not be found for the following logical event ID: '${registrarActionMetadata.logicalEventId}'.`,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import schema from "ponder:schema";
import type { Address, Hash } from "viem";

import {
type AccountId,
type EncodedReferrer,
isRegistrarActionReferralAvailable,
type Node,
Expand All @@ -21,51 +20,53 @@ export async function handleUniversalRegistrarRenewalEvent(
context: Context,
{
id,
subregistryId,
node,
referral,
transactionHash,
}: {
id: Event["id"];
subregistryId: AccountId;
node: Node;
referral: RegistrarActionReferral;
transactionHash: Hash;
},
): Promise<void> {
// 1. Make Logical Event Key
const logicalEventKey = makeLogicalEventKey({
subregistryId,
node,
transactionHash,
});

// 2. Use the Logical Event Key to get the "logical registrar action" record
// which needs to be updated.

// 2. a) Find subregistryActionMetadata record by logical event key.
const subregistryActionMetadata = await context.db.find(schema.internal_registrarActionMetadata, {
logicalEventKey,
// 2. a) Find registrarActionMetadata record for the current "logical registrar action".
const registrarActionMetadata = await context.db.find(schema.internal_registrarActionMetadata, {
metadataType: "CURRENT_LOGICAL_REGISTRAR_ACTION",
});

// Invariant: the subregistryActionMetadata record must be available for `logicalEventKey`
if (!subregistryActionMetadata) {
// Invariant: the registrarActionMetadata record must exist
if (!registrarActionMetadata) {
throw new Error(
`The required "logical registrar action" ID could not be found for the following logical event key: '${logicalEventKey}'.`,
);
}

const { logicalEventId } = subregistryActionMetadata;
// Invariant: the stored logical event key must match the current logical event key
if (registrarActionMetadata.logicalEventKey !== logicalEventKey) {
throw new Error(
`The logical event key ('${registrarActionMetadata.logicalEventKey}') for the "logical registrar action" record must be same as the current logical event key ('${logicalEventKey}').`,
);
}

// 2. b) Find "logical registrar action" record by `logicalEventId`.
const logicalRegistrarAction = await context.db.find(schema.registrarActions, {
id: logicalEventId,
id: registrarActionMetadata.logicalEventId,
});

// Invariant: the "logical registrar action" record must be available for `logicalEventId`
if (!logicalRegistrarAction) {
throw new Error(
`The "logical registrar action" record, which could not be found for the following logical event ID: '${logicalEventId}'.`,
`The "logical registrar action" record, which could not be found for the following logical event ID: '${registrarActionMetadata.logicalEventId}'.`,
);
}

Expand Down
Loading