diff --git a/.changeset/busy-poems-bow.md b/.changeset/busy-poems-bow.md new file mode 100644 index 000000000..e45795a25 --- /dev/null +++ b/.changeset/busy-poems-bow.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-schema": minor +--- + +Update `registrars` schema to enable storing at most one metadata record. diff --git a/.changeset/social-colts-smell.md b/.changeset/social-colts-smell.md new file mode 100644 index 000000000..97a2863a6 --- /dev/null +++ b/.changeset/social-colts-smell.md @@ -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". diff --git a/apps/ensindexer/src/lib/managed-names.ts b/apps/ensindexer/src/lib/managed-names.ts index 79bf5f7f4..9470b7983 100644 --- a/apps/ensindexer/src/lib/managed-names.ts +++ b/apps/ensindexer/src/lib/managed-names.ts @@ -70,6 +70,11 @@ const CONTRACTS_BY_MANAGED_NAME: Record = { DatasourceNames.ENSRoot, "UnwrappedEthRegistrarController", ), + getDatasourceContract( + config.namespace, + DatasourceNames.ENSRoot, + "UniversalRegistrarRenewalWithReferrer", + ), ethnamesNameWrapper, ], "base.eth": [ diff --git a/apps/ensindexer/src/plugins/registrars/basenames/handlers/Basenames_RegistrarController.ts b/apps/ensindexer/src/plugins/registrars/basenames/handlers/Basenames_RegistrarController.ts index ab54593d7..70141f260 100644 --- a/apps/ensindexer/src/plugins/registrars/basenames/handlers/Basenames_RegistrarController.ts +++ b/apps/ensindexer/src/plugins/registrars/basenames/handlers/Basenames_RegistrarController.ts @@ -63,7 +63,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -94,7 +93,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -121,7 +119,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -152,7 +149,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -179,7 +175,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, diff --git a/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_RegistrarController.ts b/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_RegistrarController.ts index 0af1e320f..4d1fbfdd1 100644 --- a/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_RegistrarController.ts +++ b/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_RegistrarController.ts @@ -67,7 +67,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -118,7 +117,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -170,7 +168,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -220,7 +217,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -275,7 +271,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -328,7 +323,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, diff --git a/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_UniversalRegistrarRenewalWithReferrer.ts b/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_UniversalRegistrarRenewalWithReferrer.ts index 18c7345af..5b6273515 100644 --- a/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_UniversalRegistrarRenewalWithReferrer.ts +++ b/apps/ensindexer/src/plugins/registrars/ethnames/handlers/Ethnames_UniversalRegistrarRenewalWithReferrer.ts @@ -45,7 +45,6 @@ export default function () { await handleUniversalRegistrarRenewalEvent(context, { id, - subregistryId, node, referral, transactionHash, diff --git a/apps/ensindexer/src/plugins/registrars/lineanames/handlers/Lineanames_RegistrarController.ts b/apps/ensindexer/src/plugins/registrars/lineanames/handlers/Lineanames_RegistrarController.ts index 19a181a1c..16b8c36c2 100644 --- a/apps/ensindexer/src/plugins/registrars/lineanames/handlers/Lineanames_RegistrarController.ts +++ b/apps/ensindexer/src/plugins/registrars/lineanames/handlers/Lineanames_RegistrarController.ts @@ -63,7 +63,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -101,7 +100,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -137,7 +135,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, @@ -173,7 +170,6 @@ export default function () { await handleRegistrarControllerEvent(context, { id, - subregistryId, node, pricing, referral, diff --git a/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-action.ts b/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-action.ts index 5ae6fc4df..c4d6f3fa7 100644 --- a/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-action.ts +++ b/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-action.ts @@ -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 @@ -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(); } /** @@ -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({ diff --git a/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-controller-events.ts b/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-controller-events.ts index 402c34b55..75f65e370 100644 --- a/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-controller-events.ts +++ b/apps/ensindexer/src/plugins/registrars/shared/lib/registrar-controller-events.ts @@ -3,7 +3,6 @@ import schema from "ponder:schema"; import type { Address, Hash } from "viem"; import { - type AccountId, type EncodedReferrer, isRegistrarActionPricingAvailable, isRegistrarActionReferralAvailable, @@ -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; @@ -40,7 +37,6 @@ export async function handleRegistrarControllerEvent( ): Promise { // 1. Make Logical Event Key const logicalEventKey = makeLogicalEventKey({ - subregistryId, node, transactionHash, }); @@ -48,29 +44,34 @@ export async function handleRegistrarControllerEvent( // 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}'.`, ); } diff --git a/apps/ensindexer/src/plugins/registrars/shared/lib/universal-registrar-renewal-with-referrer-events.ts b/apps/ensindexer/src/plugins/registrars/shared/lib/universal-registrar-renewal-with-referrer-events.ts index ebd646ef1..fe3e92b85 100644 --- a/apps/ensindexer/src/plugins/registrars/shared/lib/universal-registrar-renewal-with-referrer-events.ts +++ b/apps/ensindexer/src/plugins/registrars/shared/lib/universal-registrar-renewal-with-referrer-events.ts @@ -3,7 +3,6 @@ import schema from "ponder:schema"; import type { Address, Hash } from "viem"; import { - type AccountId, type EncodedReferrer, isRegistrarActionReferralAvailable, type Node, @@ -21,13 +20,11 @@ export async function handleUniversalRegistrarRenewalEvent( context: Context, { id, - subregistryId, node, referral, transactionHash, }: { id: Event["id"]; - subregistryId: AccountId; node: Node; referral: RegistrarActionReferral; transactionHash: Hash; @@ -35,7 +32,6 @@ export async function handleUniversalRegistrarRenewalEvent( ): Promise { // 1. Make Logical Event Key const logicalEventKey = makeLogicalEventKey({ - subregistryId, node, transactionHash, }); @@ -43,29 +39,34 @@ export async function handleUniversalRegistrarRenewalEvent( // 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}'.`, ); } diff --git a/packages/ensnode-schema/src/schemas/registrars.schema.ts b/packages/ensnode-schema/src/schemas/registrars.schema.ts index 001a537fa..3212da71b 100644 --- a/packages/ensnode-schema/src/schemas/registrars.schema.ts +++ b/packages/ensnode-schema/src/schemas/registrars.schema.ts @@ -400,6 +400,19 @@ export const registrarActions = onchainTable( }), ); +/** + * Logical Registrar Action Metadata Type Enum + * + * Types of internal registrar action metadata. + * + * NOTE: This enum is an internal implementation detail of ENSIndexer and + * should not be used outside of ENSIndexer. + */ +export const internal_registrarActionMetadataType = onchainEnum( + "_ensindexer_registrar_action_metadata_type", + ["CURRENT_LOGICAL_REGISTRAR_ACTION"], +); + /** * Logical Registrar Action Metadata * @@ -422,13 +435,20 @@ export const registrarActions = onchainTable( export const internal_registrarActionMetadata = onchainTable( "_ensindexer_registrar_action_metadata", (t) => ({ + /** + * Registrar Action Metadata Type + * + * The type of internal registrar action metadata being stored. + */ + metadataType: internal_registrarActionMetadataType().primaryKey(), + /** * Logical Event Key * * A fully lowercase string formatted as: - * `{chainId}:{subregistryAddress}:{node}:{transactionHash}` + * `{domainId}:{transactionHash}` */ - logicalEventKey: t.text().primaryKey(), + logicalEventKey: t.text().notNull(), /** * Logical Event ID