UNPKG

@ensnode/ensnode-schema

Version:
1,512 lines (1,505 loc) 61.4 kB
// src/schemas/ensnode-metadata.schema.ts import { onchainTable } from "ponder"; var ensNodeMetadata = onchainTable("ensnode_metadata", (t) => ({ /** * Key * * Allowed keys: * - `EnsNodeMetadataEnsDbVersion['key']` * - `EnsNodeMetadataEnsIndexerPublicConfig['key']` * - `EnsNodeMetadataEnsIndexerIndexingStatus['key']` */ key: t.text().primaryKey(), /** * Value * * Allowed values: * - `EnsNodeMetadataEnsDbVersion['value']` * - `EnsNodeMetadataEnsIndexerPublicConfig['value']` * - `EnsNodeMetadataEnsIndexerIndexingStatus['value']` * * Guaranteed to be a serialized representation of JSON object. */ value: t.jsonb().notNull() })); // src/schemas/ensv2.schema.ts import { index, onchainEnum, onchainTable as onchainTable2, primaryKey, relations, sql, uniqueIndex } from "ponder"; var event = onchainTable2("events", (t) => ({ // Ponder's event.id id: t.text().primaryKey(), // Event Log Metadata // chain chainId: t.integer().notNull().$type(), // block blockHash: t.hex().notNull().$type(), timestamp: t.bigint().notNull(), // transaction transactionHash: t.hex().notNull().$type(), from: t.hex().notNull().$type(), // log address: t.hex().notNull().$type(), logIndex: t.integer().notNull().$type() })); var account = onchainTable2("accounts", (t) => ({ id: t.hex().primaryKey().$type() })); var account_relations = relations(account, ({ many }) => ({ registrations: many(registration, { relationName: "registrant" }), domains: many(v2Domain), permissions: many(permissionsUser) })); var registry = onchainTable2( "registries", (t) => ({ // see RegistryId for guarantees id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type() }), (t) => ({ byId: uniqueIndex().on(t.chainId, t.address) }) ); var relations_registry = relations(registry, ({ one, many }) => ({ domain: one(v2Domain, { relationName: "subregistry", fields: [registry.id], references: [v2Domain.registryId] }), domains: many(v2Domain, { relationName: "registry" }), permissions: one(permissions, { relationName: "permissions", fields: [registry.chainId, registry.address], references: [permissions.chainId, permissions.address] }) })); var v1Domain = onchainTable2( "v1_domains", (t) => ({ // keyed by node, see ENSv1DomainId for guarantees. id: t.text().primaryKey().$type(), // must have a parent v1Domain (note: root node does not exist in index) parentId: t.text().notNull().$type(), // may have an owner ownerId: t.hex().$type(), // represents a labelHash labelHash: t.hex().notNull().$type() // NOTE: Domain-Resolver Relations tracked via Protocol Acceleration plugin }), (t) => ({ byParent: index().on(t.parentId), byOwner: index().on(t.ownerId), byLabelHash: index().on(t.labelHash) }) ); var relations_v1Domain = relations(v1Domain, ({ one, many }) => ({ // v1Domain parent: one(v1Domain, { fields: [v1Domain.parentId], references: [v1Domain.id] }), children: many(v1Domain, { relationName: "parent" }), // shared owner: one(account, { relationName: "owner", fields: [v1Domain.ownerId], references: [account.id] }), label: one(label, { relationName: "label", fields: [v1Domain.labelHash], references: [label.labelHash] }), registrations: many(registration) })); var v2Domain = onchainTable2( "v2_domains", (t) => ({ // see ENSv2DomainId for guarantees id: t.text().primaryKey().$type(), // has a tokenId tokenId: t.bigint().notNull(), // belongs to registry registryId: t.text().notNull().$type(), // may have one subregistry subregistryId: t.text().$type(), // may have an owner ownerId: t.hex().$type(), // represents a labelHash labelHash: t.hex().notNull().$type() // NOTE: Domain-Resolver Relations tracked via Protocol Acceleration plugin }), (t) => ({ byRegistry: index().on(t.registryId), bySubregistry: index().on(t.subregistryId).where(sql`${t.subregistryId} IS NOT NULL`), byOwner: index().on(t.ownerId), byLabelHash: index().on(t.labelHash) }) ); var relations_v2Domain = relations(v2Domain, ({ one, many }) => ({ // v2Domain registry: one(registry, { relationName: "registry", fields: [v2Domain.registryId], references: [registry.id] }), subregistry: one(registry, { relationName: "subregistry", fields: [v2Domain.subregistryId], references: [registry.id] }), // shared owner: one(account, { relationName: "owner", fields: [v2Domain.ownerId], references: [account.id] }), label: one(label, { relationName: "label", fields: [v2Domain.labelHash], references: [label.labelHash] }), registrations: many(registration) })); var registrationType = onchainEnum("RegistrationType", [ // TODO: prefix these with ENSv1, maybe excluding ThreeDNS "NameWrapper", "BaseRegistrar", "ThreeDNS", "ENSv2Registry" ]); var registration = onchainTable2( "registrations", (t) => ({ // keyed by (domainId, index) id: t.text().primaryKey().$type(), domainId: t.text().notNull().$type(), index: t.integer().notNull(), // has a type type: registrationType().notNull(), // may have an expiry expiry: t.bigint(), // maybe have a grace period (BaseRegistrar) gracePeriod: t.bigint(), // registrar AccountId registrarChainId: t.integer().notNull().$type(), registrarAddress: t.hex().notNull().$type(), // references registrant registrantId: t.hex().$type(), // may have a referrer referrer: t.hex().$type(), // may have fuses (NameWrapper, Wrapped BaseRegistrar) fuses: t.integer(), // TODO(paymentToken): add payment token tracking here // may have base cost (BaseRegistrar, ENSv2Registrar) base: t.bigint(), // may have a premium (BaseRegistrar) premium: t.bigint(), // may be Wrapped (BaseRegistrar) wrapped: t.boolean().default(false), // has an event eventId: t.text().notNull() }), (t) => ({ byId: uniqueIndex().on(t.domainId, t.index) }) ); var latestRegistrationIndex = onchainTable2("latest_registration_indexes", (t) => ({ domainId: t.text().primaryKey().$type(), index: t.integer().notNull() })); var registration_relations = relations(registration, ({ one, many }) => ({ // belongs to either v1Domain or v2Domain v1Domain: one(v1Domain, { fields: [registration.domainId], references: [v1Domain.id] }), v2Domain: one(v2Domain, { fields: [registration.domainId], references: [v2Domain.id] }), // has one registrant registrant: one(account, { fields: [registration.registrantId], references: [account.id], relationName: "registrant" }), // has many renewals renewals: many(renewal), // has an event event: one(event, { fields: [registration.eventId], references: [event.id] }) })); var renewal = onchainTable2( "renewals", (t) => ({ // keyed by (registrationId, index) id: t.text().primaryKey().$type(), domainId: t.text().notNull().$type(), registrationIndex: t.integer().notNull(), index: t.integer().notNull(), // all renewals have a duration duration: t.bigint().notNull(), // may have a referrer referrer: t.hex().$type(), // TODO(paymentToken): add payment token tracking here // may have base cost base: t.bigint(), // may have a premium (ENSv1 RegistrarControllers) premium: t.bigint(), // has an event eventId: t.text().notNull() }), (t) => ({ byId: uniqueIndex().on(t.domainId, t.registrationIndex, t.index) }) ); var renewal_relations = relations(renewal, ({ one }) => ({ // belongs to registration registration: one(registration, { fields: [renewal.domainId, renewal.registrationIndex], references: [registration.domainId, registration.index] }), // has an event event: one(event, { fields: [renewal.eventId], references: [event.id] }) })); var latestRenewalIndex = onchainTable2( "latest_renewal_indexes", (t) => ({ domainId: t.text().notNull().$type(), registrationIndex: t.integer().notNull(), index: t.integer().notNull() }), (t) => ({ pk: primaryKey({ columns: [t.domainId, t.registrationIndex] }) }) ); var permissions = onchainTable2( "permissions", (t) => ({ id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type() }), (t) => ({ byId: uniqueIndex().on(t.chainId, t.address) }) ); var relations_permissions = relations(permissions, ({ many }) => ({ resources: many(permissionsResource), users: many(permissionsUser) })); var permissionsResource = onchainTable2( "permissions_resources", (t) => ({ id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), resource: t.bigint().notNull() }), (t) => ({ byId: uniqueIndex().on(t.chainId, t.address, t.resource) }) ); var relations_permissionsResource = relations(permissionsResource, ({ one }) => ({ permissions: one(permissions, { fields: [permissionsResource.chainId, permissionsResource.address], references: [permissions.chainId, permissions.address] }) })); var permissionsUser = onchainTable2( "permissions_users", (t) => ({ id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), resource: t.bigint().notNull(), user: t.hex().notNull().$type(), // has one roles bitmap roles: t.bigint().notNull() }), (t) => ({ byId: uniqueIndex().on(t.chainId, t.address, t.resource, t.user) }) ); var relations_permissionsUser = relations(permissionsUser, ({ one }) => ({ account: one(account, { fields: [permissionsUser.user], references: [account.id] }), permissions: one(permissions, { fields: [permissionsUser.chainId, permissionsUser.address], references: [permissions.chainId, permissions.address] }), resource: one(permissionsResource, { fields: [permissionsUser.chainId, permissionsUser.address, permissionsUser.resource], references: [ permissionsResource.chainId, permissionsResource.address, permissionsResource.resource ] }) })); var label = onchainTable2( "labels", (t) => ({ labelHash: t.hex().primaryKey().$type(), interpreted: t.text().notNull().$type() }), (t) => ({ byInterpreted: index().on(t.interpreted) }) ); var label_relations = relations(label, ({ many }) => ({ domains: many(v2Domain) })); var registryCanonicalDomain = onchainTable2("registry_canonical_domains", (t) => ({ registryId: t.text().primaryKey().$type(), domainId: t.text().notNull().$type() })); // src/schemas/protocol-acceleration.schema.ts import { onchainTable as onchainTable3, primaryKey as primaryKey2, relations as relations2, uniqueIndex as uniqueIndex2 } from "ponder"; var reverseNameRecord = onchainTable3( "reverse_name_records", (t) => ({ // keyed by (address, coinType) address: t.hex().notNull().$type(), coinType: t.bigint().notNull(), /** * Represents the ENSIP-19 Reverse Name Record for a given (address, coinType). * * The value of this field is guaranteed to be a non-empty-string normalized ENS name (see * `interpretNameRecordValue` for additional context and specific guarantees). Unnormalized * names and empty string values are interpreted as a deletion of the associated Reverse Name * Record entity (represented in the schema as the _absence_ of a relevant Reverse Name Record * entity). */ value: t.text().notNull() }), (t) => ({ pk: primaryKey2({ columns: [t.address, t.coinType] }) }) ); var domainResolverRelation = onchainTable3( "domain_resolver_relations", (t) => ({ // keyed by (chainId, registry, node) chainId: t.integer().notNull().$type(), // The Registry (ENSv1Registry or ENSv2Registry)'s AccountId. address: t.hex().notNull().$type(), domainId: t.hex().notNull().$type(), // The Domain's assigned Resolver's address (NOTE: always scoped to chainId) resolver: t.hex().notNull().$type() }), (t) => ({ pk: primaryKey2({ columns: [t.chainId, t.address, t.domainId] }) }) ); var domainResolverRelation_relations = relations2(domainResolverRelation, ({ one }) => ({ resolver: one(resolver, { fields: [domainResolverRelation.chainId, domainResolverRelation.resolver], references: [resolver.chainId, resolver.address] }) })); var resolver = onchainTable3( "resolvers", (t) => ({ // keyed by (chainId, address) id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type() }), (t) => ({ byId: uniqueIndex2().on(t.chainId, t.address) }) ); var resolver_relations = relations2(resolver, ({ many }) => ({ records: many(resolverRecords) })); var resolverRecords = onchainTable3( "resolver_records", (t) => ({ // keyed by (chainId, resolver, node) id: t.text().primaryKey().$type(), chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), node: t.hex().notNull().$type(), /** * Represents the value of the reverse-resolution (ENSIP-3) name() record, used for Reverse Resolution. * * The emitted record values are interpreted according to `interpretNameRecordValue` — unnormalized * names and empty string values are interpreted as a deletion of the associated record (represented * here as `null`). * * If set, the value of this field is guaranteed to be a non-empty-string normalized ENS name * (see `interpretNameRecordValue` for additional context and specific guarantees). */ name: t.text() }), (t) => ({ byId: uniqueIndex2().on(t.chainId, t.address, t.node) }) ); var resolverRecords_relations = relations2(resolverRecords, ({ one, many }) => ({ // belongs to resolver resolver: one(resolver, { fields: [resolverRecords.chainId, resolverRecords.address], references: [resolver.chainId, resolver.address] }), // resolverRecord has many address records addressRecords: many(resolverAddressRecord), // resolverRecord has many text records textRecords: many(resolverTextRecord) })); var resolverAddressRecord = onchainTable3( "resolver_address_records", (t) => ({ // keyed by ((chainId, resolver, node), coinType) chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), node: t.hex().notNull().$type(), // NOTE: all well-known CoinTypes fit into javascript number but NOT postgres .integer, must be // stored as BigInt coinType: t.bigint().notNull(), /** * Represents the value of the Addresss Record specified by ((chainId, resolver, node), coinType). * * The value of this field is interpreted by `interpretAddressRecordValue` — see its implementation * for additional context and specific guarantees. */ value: t.text().notNull() }), (t) => ({ pk: primaryKey2({ columns: [t.chainId, t.address, t.node, t.coinType] }) }) ); var resolverAddressRecordRelations = relations2(resolverAddressRecord, ({ one }) => ({ // belongs to resolverRecord resolver: one(resolverRecords, { fields: [ resolverAddressRecord.chainId, resolverAddressRecord.address, resolverAddressRecord.node ], references: [resolverRecords.chainId, resolverRecords.address, resolverRecords.node] }) })); var resolverTextRecord = onchainTable3( "resolver_text_records", (t) => ({ // keyed by ((chainId, resolver, node), key) chainId: t.integer().notNull().$type(), address: t.hex().notNull().$type(), node: t.hex().notNull().$type(), key: t.text().notNull(), /** * Represents the value of the Text Record specified by ((chainId, resolver, node), key). * * The value of this field is interpreted by `interpretTextRecordValue` — see its implementation * for additional context and specific guarantees. */ value: t.text().notNull() }), (t) => ({ pk: primaryKey2({ columns: [t.chainId, t.address, t.node, t.key] }) }) ); var resolverTextRecordRelations = relations2(resolverTextRecord, ({ one }) => ({ // belongs to resolverRecord resolver: one(resolverRecords, { fields: [resolverTextRecord.chainId, resolverTextRecord.address, resolverTextRecord.node], references: [resolverRecords.chainId, resolverRecords.address, resolverRecords.node] }) })); var migratedNode = onchainTable3("migrated_nodes", (t) => ({ node: t.hex().primaryKey() })); // src/schemas/registrars.schema.ts import { index as index2, onchainEnum as onchainEnum2, onchainTable as onchainTable4, relations as relations3, uniqueIndex as uniqueIndex3 } from "ponder"; var subregistries = onchainTable4( "subregistries", (t) => ({ /** * Subregistry ID * * Identifies the chainId and address of the smart contract associated * with the subregistry. * * Guaranteed to be a fully lowercase string formatted according to * the CAIP-10 standard. * * @see https://chainagnostic.org/CAIPs/caip-10 */ subregistryId: t.text().primaryKey(), /** * The node (namehash) of the name the subregistry manages subnames of. * Example subregistry managed names: * - `eth` * - `base.eth` * - `linea.eth` * * Guaranteed to be a fully lowercase hex string representation of 32-bytes. */ node: t.hex().notNull() }), (t) => ({ uniqueNode: uniqueIndex3().on(t.node) }) ); var registrationLifecycles = onchainTable4( "registration_lifecycles", (t) => ({ /** * The node (namehash) of the FQDN of the domain the registration lifecycle * is associated with. * * Guaranteed to be a subname of the node (namehash) of the subregistry * identified by `subregistryId`. * * Guaranteed to be a fully lowercase hex string representation of 32-bytes. */ node: t.hex().primaryKey(), /** * Subregistry ID * * Identifies the chainId and address of the subregistry smart contract * that manages the registration lifecycle. * * Guaranteed to be a fully lowercase string formatted according to * the CAIP-10 standard. * * @see https://chainagnostic.org/CAIPs/caip-10 */ subregistryId: t.text().notNull(), /** * Expires at * * Unix timestamp when the Registration Lifecycle is scheduled to expire. */ expiresAt: t.bigint().notNull() }), (t) => ({ bySubregistry: index2().on(t.subregistryId) }) ); var registrarActionType = onchainEnum2("registrar_action_type", [ "registration", "renewal" ]); var registrarActions = onchainTable4( "registrar_actions", (t) => ({ /** * "Logical registrar action" ID * * The `id` value is a deterministic and globally unique identifier for * the "logical registrar action". * * The `id` value represents the *initial* onchain event associated with * the "logical registrar action", but the full state of * the "logical registrar action" is an aggregate across each of * the onchain events referenced in the `eventIds` field. * * Guaranteed to be the very first element in `eventIds` array. */ id: t.text().primaryKey(), /** * The type of the "logical registrar action". */ type: registrarActionType().notNull(), /** * Subregistry ID * * The ID of the subregistry the "logical registrar action" was taken on. * * Identifies the chainId and address of the associated subregistry smart * contract. * * Guaranteed to be a fully lowercase string formatted according to * the CAIP-10 standard. * * @see https://chainagnostic.org/CAIPs/caip-10 */ subregistryId: t.text().notNull(), /** * The node (namehash) of the FQDN of the domain associated with * the "logical registrar action". * * Guaranteed to be a fully lowercase hex string representation of 32-bytes. */ node: t.hex().notNull(), /** * Incremental Duration * * If `type` is "registration": * - Represents the duration between `blockTimestamp` and * the initial `expiresAt` value that the associated * "registration lifecycle" will be initialized with. * If `type` is "renewal": * - Represents the incremental increase in duration made to * the `expiresAt` value in the associated "registration lifecycle". * * A "registration lifecycle" may be extended via renewal even after it * expires if it is still within its grace period. * * Consider the following scenario: * * The "registration lifecycle" of a direct subname of .eth is scheduled to * expire on Jan 1, midnight UTC. It is currently 30 days after this * expiration time. Therefore, there are currently another 60 days of grace * period remaining for this name. Anyone can still make a renewal to * extend the "registration lifecycle" of this name. * * Given this scenario, consider the following examples: * * 1. If a renewal is made with 10 days incremental duration, * the "registration lifecycle" for this name will remain in * an "expired" state, but it will now have another 70 days of * grace period remaining. * * 2. If a renewal is made with 50 days incremental duration, * the "registration lifecycle" for this name will no longer be * "expired" and will become "active", but the "registration lifecycle" * will now be scheduled to expire again in 20 days. * * After the "registration lifecycle" for a name becomes expired by more * than its grace period, it can no longer be renewed by anyone and is * considered "released". The name must first be registered again, starting * a new "registration lifecycle" of * active / expired / grace period / released. * * May be 0. * * Guaranteed to be a non-negative bigint value. */ incrementalDuration: t.bigint().notNull(), /** * Base cost * * Base cost (before any `premium`) of Ether measured in units of Wei * paid to execute the "logical registrar action". * * May be 0. * * Guaranteed to be: * 1) null if and only if `total` is null. * 2) Otherwise, a non-negative bigint value. */ baseCost: t.bigint(), /** * Premium * * "premium" cost (in excesses of the `baseCost`) of Ether measured in * units of Wei paid to execute the "logical registrar action". * * May be 0. * * Guaranteed to be: * 1) null if and only if `total` is null. * 2) Otherwise, zero when `type` is `renewal`. * 3) Otherwise, a non-negative bigint value. */ premium: t.bigint(), /** * Total * * Total cost of Ether measured in units of Wei paid to execute * the "logical registrar action". * * May be 0. * * Guaranteed to be: * 1) null if and only if both `baseCost` and `premium` are null. * 2) Otherwise, a non-negative bigint value, equal to the sum of * `baseCost` and `premium`. */ total: t.bigint(), /** * Registrant * * Identifies the address that initiated the "logical registrar action" and * is paying the `total` cost (if applicable). * * It may not be the owner of the name: * 1. When a name is registered, the initial owner of the name may be * distinct from the registrant. * 2. There are no restrictions on who may renew a name. * Therefore the owner of the name may be distinct from the registrant. * * * The "chainId" of this address is the same as is referenced in `subregistryId`. * * Guaranteed to be a fully lowercase address */ registrant: t.hex().notNull(), /** * Encoded Referrer * * Represents the "raw" 32-byte "referrer" value emitted onchain in * association with the registrar action. * * Guaranteed to be: * 1) null if the emitted `eventIds` contain no information about a referrer. * 2) Otherwise, a fully lowercase hex string representation of 32-bytes. */ encodedReferrer: t.hex(), /** * Decoded referrer * * The referrer address decoded from `encodedReferrer` using strict * left-zero-padding validation. * * Identifies the interpreted address of the referrer. * The "chainId" of this address is the same as is referenced in * `subregistryId`. * * Guaranteed to be: * 1) null if `encodedReferrer` is null. * 2) Otherwise, a fully lowercase address. * 3) May be the "zero address" to represent that an `encodedReferrer` is * defined but that it is interpreted as no referrer. */ decodedReferrer: t.hex(), /** * Number of the block that includes the "logical registrar action". * * The "chainId" of this block is the same as is referenced in * `subregistryId`. * * Guaranteed to be a non-negative bigint value. */ blockNumber: t.bigint().notNull(), /** * Unix timestamp of the block referenced by `blockNumber` that includes * the "logical registrar action". */ timestamp: t.bigint().notNull(), /** * Transaction hash of the transaction associated with * the "logical registrar action". * * The "chainId" of this transaction is the same as is referenced in * `subregistryId`. * * Note that a single transaction may be associated with any number of * "logical registrar actions". * * Guaranteed to be a fully lowercase hex string representation of 32-bytes. */ transactionHash: t.hex().notNull(), /** * Event IDs * * Array of the eventIds that have contributed to the state of * the "logical registrar action" record. * * Each eventId is a deterministic and globally unique onchain event * identifier. * * Guarantees: * - Each eventId is of events that occurred within the block * referenced by `blockNumber`. * - At least 1 eventId. * - Ordered chronologically (ascending) by logIndex within `blockNumber`. * - The first element in the array is equal to the `id` of * the overall "logical registrar action" record. * * The following ideas are not generalized for ENS overall but happen to * be a characteristic of the scope of our current indexing logic: * 1. These id's always reference events emitted by * a related "BaseRegistrar" contract. * 2. These id's optionally reference events emitted by * a related "Registrar Controller" contract. This is because our * current indexing logic doesn't guarantee to index * all "Registrar Controller" contracts. */ eventIds: t.text().array().notNull() }), (t) => ({ byDecodedReferrer: index2().on(t.decodedReferrer), byTimestamp: index2().on(t.timestamp) }) ); var internal_registrarActionMetadataType = onchainEnum2( "_ensindexer_registrar_action_metadata_type", ["CURRENT_LOGICAL_REGISTRAR_ACTION"] ); var internal_registrarActionMetadata = onchainTable4( "_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: * `{domainId}:{transactionHash}` */ logicalEventKey: t.text().notNull(), /** * Logical Event ID * * A string holding the `id` value of the existing "logical registrar action" * record that is currently being built as an aggregation of onchain events. * * May be used by subsequent event handlers to identify which * "logical registrar action" to aggregate additional indexed state into. */ logicalEventId: t.text().notNull() }) ); var subregistryRelations = relations3(subregistries, ({ many }) => ({ registrationLifecycle: many(registrationLifecycles) })); var registrationLifecycleRelations = relations3( registrationLifecycles, ({ one, many }) => ({ subregistry: one(subregistries, { fields: [registrationLifecycles.subregistryId], references: [subregistries.subregistryId] }), registrarAction: many(registrarActions) }) ); var registrarActionRelations = relations3(registrarActions, ({ one }) => ({ registrationLifecycle: one(registrationLifecycles, { fields: [registrarActions.node], references: [registrationLifecycles.node] }) })); // src/schemas/subgraph.schema.ts import { index as index3, onchainTable as onchainTable5, relations as relations4 } from "ponder"; // src/lib/collate.ts function monkeypatchCollate(col, collation) { col.getSQLType = function() { return `${Object.getPrototypeOf(this).getSQLType.call(this)} COLLATE ${collation}`; }; return col; } // src/schemas/subgraph.schema.ts var subgraph_domain = onchainTable5( "subgraph_domains", (t) => ({ // The namehash of the name id: t.hex().primaryKey(), /** * The ENS Name that this Domain represents. * * If {@link ENSIndexerConfig#isSubgraphCompatible}, this value is guaranteed to be either: * a) null (in the case of the root node), or * b) a Subgraph Interpreted Name. * * @see https://ensnode.io/docs/reference/terminology#subgraph-indexability--labelname-interpretation * * Otherwise, this value is guaranteed to be an Interpreted Name, which is either: * a) a normalized Name, or * b) a Name entirely consisting of Interpreted Labels. * * Note that the type of the column will remain string | null, for legacy subgraph compatibility, * but in practice will never be null. The Root node's name will be '' (empty string). * * @see https://ensnode.io/docs/reference/terminology#interpreted-name */ name: t.text(), /** * The Label associated with the Domain. * * If {@link ENSIndexerConfig#isSubgraphCompatible}, this value is guaranteed to be either: * a) null, in the case of the root Node or a name whose childmost label is subgraph-unindexable, or * b) a subgraph-indexable Subgraph Interpreted Label (i.e. a Literal Label of undefined normalization). * * @see https://ensnode.io/docs/reference/terminology#subgraph-indexability--labelname-interpretation * * Otherwise, this value is guaranteed to be an Interpreted Label which is either: * a) null, exclusively in the case of the root Node, * b) a normalized Label, or * c) an Encoded LabelHash, which encodes either * i. in the case of an Unknown Label, the LabelHash emitted onchain, or * ii. in the case of an Unnormalized Label, the LabelHash of the Literal Label value found onchain. * * @see https://ensnode.io/docs/reference/terminology#interpreted-label */ labelName: t.text(), // keccak256(labelName) labelhash: t.hex(), // The namehash (id) of the parent name parentId: t.hex(), // The number of subdomains subdomainCount: t.integer().notNull().default(0), // Address logged from current resolver, if any resolvedAddressId: t.hex(), // The resolver that controls the domain's settings resolverId: t.text(), // The time-to-live (TTL) value of the domain's records ttl: t.bigint(), // Indicates whether the domain has been migrated to a new registrar isMigrated: t.boolean().notNull().default(false), // The time when the domain was created createdAt: t.bigint().notNull(), // The account that owns the domain ownerId: t.hex().notNull(), // The account that owns the ERC721 NFT for the domain registrantId: t.hex(), // The account that owns the wrapped domain wrappedOwnerId: t.hex(), // The expiry date for the domain, from either the registration, or the wrapped domain if PCC is burned expiryDate: t.bigint() }), (t) => ({ byLabelhash: index3().on(t.labelhash), byParentId: index3().on(t.parentId), byOwnerId: index3().on(t.ownerId), byRegistrantId: index3().on(t.registrantId), byWrappedOwnerId: index3().on(t.wrappedOwnerId) }) ); monkeypatchCollate(subgraph_domain.name, '"C"'); monkeypatchCollate(subgraph_domain.labelName, '"C"'); var subgraph_domainRelations = relations4(subgraph_domain, ({ one, many }) => ({ resolvedAddress: one(subgraph_account, { fields: [subgraph_domain.resolvedAddressId], references: [subgraph_account.id] }), owner: one(subgraph_account, { fields: [subgraph_domain.ownerId], references: [subgraph_account.id] }), parent: one(subgraph_domain, { fields: [subgraph_domain.parentId], references: [subgraph_domain.id] }), resolver: one(subgraph_resolver, { fields: [subgraph_domain.resolverId], references: [subgraph_resolver.id] }), subdomains: many(subgraph_domain, { relationName: "parent" }), registrant: one(subgraph_account, { fields: [subgraph_domain.registrantId], references: [subgraph_account.id] }), wrappedOwner: one(subgraph_account, { fields: [subgraph_domain.wrappedOwnerId], references: [subgraph_account.id] }), wrappedDomain: one(subgraph_wrappedDomain, { fields: [subgraph_domain.id], references: [subgraph_wrappedDomain.domainId] }), registration: one(subgraph_registration, { fields: [subgraph_domain.id], references: [subgraph_registration.domainId] }), // event relations transfers: many(subgraph_transfer), newOwners: many(subgraph_newOwner), newResolvers: many(subgraph_newResolver), newTTLs: many(subgraph_newTTL), wrappedTransfers: many(subgraph_wrappedTransfer), nameWrappeds: many(subgraph_nameWrapped), nameUnwrappeds: many(subgraph_nameUnwrapped), fusesSets: many(subgraph_fusesSet), expiryExtendeds: many(subgraph_expiryExtended) })); var subgraph_account = onchainTable5("subgraph_accounts", (t) => ({ id: t.hex().primaryKey() })); var subgraph_accountRelations = relations4(subgraph_account, ({ many }) => ({ domains: many(subgraph_domain), wrappedDomains: many(subgraph_wrappedDomain), registrations: many(subgraph_registration) })); var subgraph_resolver = onchainTable5( "subgraph_resolvers", (t) => ({ // The unique identifier for this resolver, which is a concatenation of the domain namehash and the resolver address id: t.text().primaryKey(), // The domain that this resolver is associated with domainId: t.hex().notNull(), // The address of the resolver contract address: t.hex().notNull().$type(), // The current value of the 'addr' record for this resolver, as determined by the associated events addrId: t.hex(), // The content hash for this resolver, in binary format contentHash: t.text(), // The set of observed text record keys for this resolver // NOTE: we avoid .notNull.default([]) to match subgraph behavior texts: t.text().array(), // The set of observed SLIP-44 coin types for this resolver // NOTE: we avoid .notNull.default([]) to match subgraph behavior coinTypes: t.bigint().array() }), (t) => ({ byDomainId: index3().on(t.domainId) }) ); var subgraph_resolverRelations = relations4(subgraph_resolver, ({ one, many }) => ({ addr: one(subgraph_account, { fields: [subgraph_resolver.addrId], references: [subgraph_account.id] }), domain: one(subgraph_domain, { fields: [subgraph_resolver.domainId], references: [subgraph_domain.id] }), // event relations addrChangeds: many(subgraph_addrChanged), multicoinAddrChangeds: many(subgraph_multicoinAddrChanged), nameChangeds: many(subgraph_nameChanged), abiChangeds: many(subgraph_abiChanged), pubkeyChangeds: many(subgraph_pubkeyChanged), textChangeds: many(subgraph_textChanged), contenthashChangeds: many(subgraph_contenthashChanged), interfaceChangeds: many(subgraph_interfaceChanged), authorisationChangeds: many(subgraph_authorisationChanged), versionChangeds: many(subgraph_versionChanged) })); var subgraph_registration = onchainTable5( "subgraph_registrations", (t) => ({ // The unique identifier of the registration id: t.hex().primaryKey(), // The domain name associated with the registration domainId: t.hex().notNull(), // The registration date of the domain registrationDate: t.bigint().notNull(), // The expiry date of the domain expiryDate: t.bigint().notNull(), // The cost associated with the domain registration cost: t.bigint(), // The account that registered the domain registrantId: t.hex().notNull(), /** * The Label associated with the domain registration. * * If {@link ENSIndexerConfig#isSubgraphCompatible}, this value is guaranteed to be either: * a) null, in the case of the root Node or a Domain whose label is subgraph-unindexable, or * b) a subgraph-indexable Subgraph Interpreted Label (i.e. a Literal Label of undefined normalization). * * @see https://ensnode.io/docs/reference/terminology#subgraph-indexability--labelname-interpretation * * Otherwise, this value is guaranteed to be an Interpreted Label which is either: * a) a normalized Label, or * b) in the case of an Unnormalized Label, an Encoded LabelHash of the Literal Label value found onchain. * * Note that the type of the column will remain string | null, for legacy subgraph compatibility. * In practice however, because there is no Registration entity for the root Node (the only Node * with a null labelName) this field will never be null. * * @see https://ensnode.io/docs/reference/terminology#interpreted-label */ labelName: t.text() }), (t) => ({ byDomainId: index3().on(t.domainId), byRegistrationDate: index3().on(t.registrationDate) }) ); var subgraph_registrationRelations = relations4(subgraph_registration, ({ one, many }) => ({ domain: one(subgraph_domain, { fields: [subgraph_registration.domainId], references: [subgraph_domain.id] }), registrant: one(subgraph_account, { fields: [subgraph_registration.registrantId], references: [subgraph_account.id] }), // event relations nameRegistereds: many(subgraph_nameRegistered), nameReneweds: many(subgraph_nameRenewed), nameTransferreds: many(subgraph_nameTransferred) })); var subgraph_wrappedDomain = onchainTable5( "subgraph_wrapped_domains", (t) => ({ // The unique identifier for each instance of the WrappedDomain entity id: t.hex().primaryKey(), // The domain that is wrapped by this WrappedDomain domainId: t.hex().notNull(), // The expiry date of the wrapped domain expiryDate: t.bigint().notNull(), // The number of fuses remaining on the wrapped domain fuses: t.integer().notNull(), // The account that owns this WrappedDomain ownerId: t.hex().notNull(), /** * The Name that this WrappedDomain represents. Names are emitted by the NameWrapper contract as * DNS-Encoded Names which may be malformed, which will result in this field being `null`. * * If {@link ENSIndexerConfig#isSubgraphCompatible}, this value is guaranteed to be either: * a) null (in the case of a DNS-Encoded Name that is malformed or contains subgraph-unindexable labels), or * b) a subgraph-indexable Subgraph Interpreted Label (i.e. a Literal Label of undefined normalization). * * @see https://ensnode.io/docs/reference/terminology#subgraph-indexability--labelname-interpretation * * Otherwise, this value is guaranteed to be either: * a) null (in the case of a malformed DNS-Encoded Name), * b) an Interpreted Name. * * @see https://ensnode.io/docs/reference/terminology#interpreted-name */ name: t.text() }), (t) => ({ byDomainId: index3().on(t.domainId) }) ); var subgraph_wrappedDomainRelations = relations4(subgraph_wrappedDomain, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_wrappedDomain.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_wrappedDomain.ownerId], references: [subgraph_account.id] }) })); var sharedEventColumns = (t) => ({ id: t.text().primaryKey(), blockNumber: t.integer().notNull(), transactionID: t.hex().notNull() }); var domainEvent = (t) => ({ ...sharedEventColumns(t), domainId: t.hex().notNull() }); var domainEventIndex = (t) => ({ // primary reverse lookup idx: index3().on(t.domainId), // sorting index idx_compound: index3().on(t.domainId, t.id) }); var subgraph_transfer = onchainTable5( "subgraph_transfers", (t) => ({ ...domainEvent(t), ownerId: t.hex().notNull() }), domainEventIndex ); var subgraph_newOwner = onchainTable5( "subgraph_new_owners", (t) => ({ ...domainEvent(t), ownerId: t.hex().notNull(), parentDomainId: t.hex().notNull() }), domainEventIndex ); var subgraph_newResolver = onchainTable5( "subgraph_new_resolvers", (t) => ({ ...domainEvent(t), resolverId: t.text().notNull() }), domainEventIndex ); var subgraph_newTTL = onchainTable5( "subgraph_new_ttls", (t) => ({ ...domainEvent(t), ttl: t.bigint().notNull() }), domainEventIndex ); var subgraph_wrappedTransfer = onchainTable5( "subgraph_wrapped_transfers", (t) => ({ ...domainEvent(t), ownerId: t.hex().notNull() }), domainEventIndex ); var subgraph_nameWrapped = onchainTable5( "subgraph_name_wrapped", (t) => ({ ...domainEvent(t), name: t.text(), fuses: t.integer().notNull(), ownerId: t.hex().notNull(), expiryDate: t.bigint().notNull() }), domainEventIndex ); var subgraph_nameUnwrapped = onchainTable5( "subgraph_name_unwrapped", (t) => ({ ...domainEvent(t), ownerId: t.hex().notNull() }), domainEventIndex ); var subgraph_fusesSet = onchainTable5( "subgraph_fuses_set", (t) => ({ ...domainEvent(t), fuses: t.integer().notNull() }), domainEventIndex ); var subgraph_expiryExtended = onchainTable5( "subgraph_expiry_extended", (t) => ({ ...domainEvent(t), expiryDate: t.bigint().notNull() }), domainEventIndex ); var registrationEvent = (t) => ({ ...sharedEventColumns(t), registrationId: t.hex().notNull() }); var registrationEventIndex = (t) => ({ // primary reverse lookup idx: index3().on(t.registrationId), // sorting index idx_compound: index3().on(t.registrationId, t.id) }); var subgraph_nameRegistered = onchainTable5( "subgraph_name_registered", (t) => ({ ...registrationEvent(t), registrantId: t.hex().notNull(), expiryDate: t.bigint().notNull() }), registrationEventIndex ); var subgraph_nameRenewed = onchainTable5( "subgraph_name_renewed", (t) => ({ ...registrationEvent(t), expiryDate: t.bigint().notNull() }), registrationEventIndex ); var subgraph_nameTransferred = onchainTable5( "subgraph_name_transferred", (t) => ({ ...registrationEvent(t), newOwnerId: t.hex().notNull() }), registrationEventIndex ); var resolverEvent = (t) => ({ ...sharedEventColumns(t), resolverId: t.text().notNull() }); var resolverEventIndex = (t) => ({ // primary reverse lookup idx: index3().on(t.resolverId), // sorting index idx_compound: index3().on(t.resolverId, t.id) }); var subgraph_addrChanged = onchainTable5( "subgraph_addr_changed", (t) => ({ ...resolverEvent(t), addrId: t.hex().notNull() }), resolverEventIndex ); var subgraph_multicoinAddrChanged = onchainTable5( "subgraph_multicoin_addr_changed", (t) => ({ ...resolverEvent(t), coinType: t.bigint().notNull(), addr: t.hex().notNull() }), resolverEventIndex ); var subgraph_nameChanged = onchainTable5( "subgraph_name_changed", (t) => ({ ...resolverEvent(t), name: t.text().notNull() }), resolverEventIndex ); var subgraph_abiChanged = onchainTable5( "subgraph_abi_changed", (t) => ({ ...resolverEvent(t), contentType: t.bigint().notNull() }), resolverEventIndex ); var subgraph_pubkeyChanged = onchainTable5( "subgraph_pubkey_changed", (t) => ({ ...resolverEvent(t), x: t.hex().notNull(), y: t.hex().notNull() }), resolverEventIndex ); var subgraph_textChanged = onchainTable5( "subgraph_text_changed", (t) => ({ ...resolverEvent(t), key: t.text().notNull(), value: t.text() }), resolverEventIndex ); var subgraph_contenthashChanged = onchainTable5( "subgraph_contenthash_changed", (t) => ({ ...resolverEvent(t), hash: t.hex().notNull() }), resolverEventIndex ); var subgraph_interfaceChanged = onchainTable5( "subgraph_interface_changed", (t) => ({ ...resolverEvent(t), interfaceID: t.hex().notNull(), implementer: t.hex().notNull() }), resolverEventIndex ); var subgraph_authorisationChanged = onchainTable5( "subgraph_authorisation_changed", (t) => ({ ...resolverEvent(t), owner: t.hex().notNull(), target: t.hex().notNull(), isAuthorized: t.boolean().notNull() }), resolverEventIndex ); var subgraph_versionChanged = onchainTable5( "subgraph_version_changed", (t) => ({ ...resolverEvent(t), version: t.bigint().notNull() }), resolverEventIndex ); var subgraph_transferRelations = relations4(subgraph_transfer, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_transfer.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_transfer.ownerId], references: [subgraph_account.id] }) })); var subgraph_newOwnerRelations = relations4(subgraph_newOwner, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_newOwner.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_newOwner.ownerId], references: [subgraph_account.id] }), parentDomain: one(subgraph_domain, { fields: [subgraph_newOwner.parentDomainId], references: [subgraph_domain.id] }) })); var subgraph_newResolverRelations = relations4(subgraph_newResolver, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_newResolver.domainId], references: [subgraph_domain.id] }), resolver: one(subgraph_resolver, { fields: [subgraph_newResolver.resolverId], references: [subgraph_resolver.id] }) })); var subgraph_newTTLRelations = relations4(subgraph_newTTL, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_newTTL.domainId], references: [subgraph_domain.id] }) })); var subgraph_wrappedTransferRelations = relations4(subgraph_wrappedTransfer, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_wrappedTransfer.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_wrappedTransfer.ownerId], references: [subgraph_account.id] }) })); var subgraph_nameWrappedRelations = relations4(subgraph_nameWrapped, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_nameWrapped.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_nameWrapped.ownerId], references: [subgraph_account.id] }) })); var subgraph_nameUnwrappedRelations = relations4(subgraph_nameUnwrapped, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_nameUnwrapped.domainId], references: [subgraph_domain.id] }), owner: one(subgraph_account, { fields: [subgraph_nameUnwrapped.ownerId], references: [subgraph_account.id] }) })); var subgraph_fusesSetRelations = relations4(subgraph_fusesSet, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_fusesSet.domainId], references: [subgraph_domain.id] }) })); var subgraph_expiryExtendedRelations = relations4(subgraph_expiryExtended, ({ one }) => ({ domain: one(subgraph_domain, { fields: [subgraph_expiryExtended.domainId], references: [subgraph_domain.id] }) })); var subgraph_nameRegisteredRelations = relations4(subgraph_nameRegistered, ({ one }) => ({ registration: one(subgraph_registration, { fields: [subgraph_nameRegistered.registrationId], references: [subgraph_registration.id] }), registrant: one(subgraph_account, { fields: [subgraph_nameRegistered.registrantId], references: [subgraph_account.id] }) })); var subgraph_nameRenewedRelations = relations4(subgraph_nameRenewed, ({ one }) => ({ registration: one(subgraph_registration, { fields: [subgraph_nameRenewed.registrationId], references: [subgraph_registration.id] }) })); var subgraph_nameTransferredRelations = relations4(subgraph_nameTransferred, ({ one }) => ({ registration: one(subgraph_registration, { fields: [subgraph_nameTransferred.registrationId], references: [subgraph_registration.id] }), newOwner: one(subgraph_account, { fields: [subgraph_nameTransferred.newOwnerId], references: [subgraph_account.id] }) })); var subgraph_addrChangedRelations = relations4(subgraph_addrChanged, ({ one }) => ({ resolver: one(subgraph_resolver, { fields: [subgraph_addrChanged.resolverId], references: [subgraph_resolver.id] }), addr: one(subgraph_account, { fields: [subgraph_addrChanged.addrId], references: [subgraph_account.id] }) })); var subgraph_multicoinAddrChangedRelations = relations4( subgraph_multicoinAddrChanged, ({ one }) => ({