diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 00000000..6252eb77 --- /dev/null +++ b/.env.local.example @@ -0,0 +1,19 @@ +# RPC configuration +# Follow the format: RPC_URL_{chainId}={rpcUrl} + +RPC_URL_1=https://ethereum-rpc.publicnode.com +RPC_URL_8453=https://base-rpc.publicnode.com +RPC_URL_59144=https://linea-rpc.publicnode.com + +# Identify which indexer plugin to activate (see `src/plugins` for available plugins) + +ACTIVE_PLUGIN=base.eth + +# Database configuration + +# This is where the indexer will create the tables defined in ponder.schema.ts +# No two indexer instances can use the same database schema at the same time. This prevents data corruption. +# @link https://ponder.sh/docs/api-reference/database#database-schema-rules +DATABASE_SCHEMA=subname_index_base.eth +# The indexer will use Postgres with that as the connection string. If not defined, the indexer will use PSlite. +DATABASE_URL=postgresql://dbuser:abcd1234@localhost:5432/my_database diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 00000000..14e879d3 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,31 @@ +name: Static Analysis + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + biome-ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install pnpm + run: npm install -g pnpm + + - name: Install dependencies + run: pnpm install + + - name: Run Biome CI + run: pnpm biome ci diff --git a/README.md b/README.md index 08be0e8e..d9b3ba79 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ various resources use both null and zeroAddress to indicate emptiness, this is h ### ens indexing plugin -l2 ens deployments are very similar — write plugin to make configuring source addresses easy and pass node that domains in these handlers are implicitly parented to (assuming that l2 deployments make nodes against the NAMEHASH_ZERO i.e. every name is basically a 2LD) +l2 ens deployments are very similar — write plugin to make configuring source addresses easy and pass node that domains in these handlers are implicitly parented to (assuming that l2 deployments make nodes against the ROOT_NODE i.e. every name is basically a 2LD) ### registry diff --git a/biome.json b/biome.json index 86c37ba0..b08a5735 100644 --- a/biome.json +++ b/biome.json @@ -7,7 +7,7 @@ }, "files": { "ignoreUnknown": false, - "ignore": [] + "ignore": [".ponder", "generated"] }, "formatter": { "enabled": true, diff --git a/package.json b/package.json index bfa13d0f..d052dd6a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "hono": "^4.6.14", - "ponder": "^0.8.8", + "ponder": "^0.8.17", "viem": "^2.21.57" }, "devDependencies": { @@ -23,5 +23,8 @@ }, "engines": { "node": ">=18.14" + }, + "resolutions": { + "vite": "5.1.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f073c5f..9daa9d03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + vite: 5.1.8 + importers: .: @@ -12,8 +15,8 @@ importers: specifier: ^4.6.14 version: 4.6.14 ponder: - specifier: ^0.8.8 - version: 0.8.8(@opentelemetry/api@1.9.0)(@types/node@20.17.10)(hono@4.6.14)(typescript@5.7.2)(viem@2.21.57(typescript@5.7.2)) + specifier: ^0.8.17 + version: 0.8.17(@opentelemetry/api@1.9.0)(@types/node@20.17.10)(hono@4.6.14)(typescript@5.7.2)(viem@2.21.57(typescript@5.7.2)) viem: specifier: ^2.21.57 version: 2.21.57(typescript@5.7.2) @@ -103,8 +106,8 @@ packages: peerDependencies: commander: ~12.1.0 - '@electric-sql/pglite@0.2.15': - resolution: {integrity: sha512-Jiq31Dnk+rg8rMhcSxs4lQvHTyizNo5b269c1gCC3ldQ0sCLrNVPGzy+KnmonKy1ZArTUuXZf23/UamzFMKVaA==} + '@electric-sql/pglite@0.2.13': + resolution: {integrity: sha512-YRY806NnScVqa21/1L1vaysSQ+0/cAva50z7vlwzaGiBOTS9JhdzIRHN0KfgMhobFAphbznZJ7urMso4RtMBIQ==} '@envelop/core@5.0.2': resolution: {integrity: sha512-tVL6OrMe6UjqLosiE+EH9uxh2TQC0469GwF4tE014ugRaDDKKVWwFwZe0TBMlcyHKh5MD4ZxktWo/1hqUxIuhw==} @@ -1381,8 +1384,8 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true - ponder@0.8.8: - resolution: {integrity: sha512-dz1PJ3a8m4/TZbB4/CwyHYxS6ky8HxDvvl4n4uctKa81ZFjR+YjHU3hfQFimTt2GpNi0vq4ZxCr9bzc20aSafw==} + ponder@0.8.17: + resolution: {integrity: sha512-p0gvs0CJpdJ6sf5OOQYXaIfmIeUVoTMkCbPVAJ1jK1O2m62ZnTlxpnGrPp5ZAWYxdlCSQQCpZpNhdsYGejGK+g==} engines: {node: '>=18.14'} hasBin: true peerDependencies: @@ -1698,13 +1701,13 @@ packages: vite-tsconfig-paths@4.3.1: resolution: {integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw==} peerDependencies: - vite: '*' + vite: 5.1.8 peerDependenciesMeta: vite: optional: true - vite@5.0.7: - resolution: {integrity: sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==} + vite@5.1.8: + resolution: {integrity: sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1839,7 +1842,7 @@ snapshots: dependencies: commander: 12.1.0 - '@electric-sql/pglite@0.2.15': {} + '@electric-sql/pglite@0.2.13': {} '@envelop/core@5.0.2': dependencies: @@ -2392,9 +2395,9 @@ snapshots: dotenv@16.4.7: {} - drizzle-orm@0.36.4(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(kysely@0.26.3)(pg@8.13.1)(react@18.3.1): + drizzle-orm@0.36.4(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0)(kysely@0.26.3)(pg@8.13.1)(react@18.3.1): optionalDependencies: - '@electric-sql/pglite': 0.2.15 + '@electric-sql/pglite': 0.2.13 '@opentelemetry/api': 1.9.0 kysely: 0.26.3 pg: 8.13.1 @@ -2742,9 +2745,9 @@ snapshots: optionalDependencies: pg: 8.13.1 - kysely-pglite@0.6.1(@electric-sql/pglite@0.2.15)(kysely@0.26.3)(pg@8.13.1): + kysely-pglite@0.6.1(@electric-sql/pglite@0.2.13)(kysely@0.26.3)(pg@8.13.1): dependencies: - '@electric-sql/pglite': 0.2.15 + '@electric-sql/pglite': 0.2.13 '@oclif/core': 4.2.0 '@repeaterjs/repeater': 3.0.6 '@sindresorhus/is': 7.0.1 @@ -2934,11 +2937,11 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - ponder@0.8.8(@opentelemetry/api@1.9.0)(@types/node@20.17.10)(hono@4.6.14)(typescript@5.7.2)(viem@2.21.57(typescript@5.7.2)): + ponder@0.8.17(@opentelemetry/api@1.9.0)(@types/node@20.17.10)(hono@4.6.14)(typescript@5.7.2)(viem@2.21.57(typescript@5.7.2)): dependencies: '@babel/code-frame': 7.26.2 '@commander-js/extra-typings': 12.1.0(commander@12.1.0) - '@electric-sql/pglite': 0.2.15 + '@electric-sql/pglite': 0.2.13 '@escape.tech/graphql-armor-max-aliases': 2.6.0 '@escape.tech/graphql-armor-max-depth': 2.4.0 '@escape.tech/graphql-armor-max-tokens': 2.5.0 @@ -2950,7 +2953,7 @@ snapshots: dataloader: 2.2.3 detect-package-manager: 3.0.2 dotenv: 16.4.7 - drizzle-orm: 0.36.4(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(kysely@0.26.3)(pg@8.13.1)(react@18.3.1) + drizzle-orm: 0.36.4(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0)(kysely@0.26.3)(pg@8.13.1)(react@18.3.1) glob: 10.4.5 graphql: 16.10.0 graphql-yoga: 5.10.8(graphql@16.10.0) @@ -2958,7 +2961,7 @@ snapshots: http-terminator: 3.2.0 ink: 4.4.1(react@18.3.1) kysely: 0.26.3 - kysely-pglite: 0.6.1(@electric-sql/pglite@0.2.15)(kysely@0.26.3)(pg@8.13.1) + kysely-pglite: 0.6.1(@electric-sql/pglite@0.2.13)(kysely@0.26.3)(pg@8.13.1) pg: 8.13.1 pg-connection-string: 2.7.0 picocolors: 1.1.1 @@ -2967,9 +2970,9 @@ snapshots: react: 18.3.1 stacktrace-parser: 0.1.10 viem: 2.21.57(typescript@5.7.2) - vite: 5.0.7(@types/node@20.17.10) + vite: 5.1.8(@types/node@20.17.10) vite-node: 1.0.2(@types/node@20.17.10) - vite-tsconfig-paths: 4.3.1(typescript@5.7.2)(vite@5.0.7(@types/node@20.17.10)) + vite-tsconfig-paths: 4.3.1(typescript@5.7.2)(vite@5.1.8(@types/node@20.17.10)) optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: @@ -3296,7 +3299,7 @@ snapshots: debug: 4.4.0(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.0.7(@types/node@20.17.10) + vite: 5.1.8(@types/node@20.17.10) transitivePeerDependencies: - '@types/node' - less @@ -3307,18 +3310,18 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.1(typescript@5.7.2)(vite@5.0.7(@types/node@20.17.10)): + vite-tsconfig-paths@4.3.1(typescript@5.7.2)(vite@5.1.8(@types/node@20.17.10)): dependencies: debug: 4.4.0(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.4(typescript@5.7.2) optionalDependencies: - vite: 5.0.7(@types/node@20.17.10) + vite: 5.1.8(@types/node@20.17.10) transitivePeerDependencies: - supports-color - typescript - vite@5.0.7(@types/node@20.17.10): + vite@5.1.8(@types/node@20.17.10): dependencies: esbuild: 0.19.12 postcss: 8.4.49 diff --git a/ponder.config.ts b/ponder.config.ts index bca2b37c..771964df 100644 --- a/ponder.config.ts +++ b/ponder.config.ts @@ -1,98 +1,29 @@ -import { createConfig, factory, mergeAbis } from "ponder"; -import { http, getAbiItem } from "viem"; +import { ACTIVE_PLUGIN } from "./src/lib/plugin-helpers"; +import { + activate as activateEthBase, + config as ethBaseConfig, + ownedName as ethBaseOwnedName, +} from "./src/plugins/base.eth/ponder.config"; +import { + activate as activateEth, + config as ethConfig, + ownedName as ethOwnedName, +} from "./src/plugins/eth/ponder.config"; -import { BaseRegistrar } from "./abis/BaseRegistrar"; -import { EthRegistrarController } from "./abis/EthRegistrarController"; -import { EthRegistrarControllerOld } from "./abis/EthRegistrarControllerOld"; -import { LegacyPublicResolver } from "./abis/LegacyPublicResolver"; -import { NameWrapper } from "./abis/NameWrapper"; -import { Registry } from "./abis/Registry"; -import { Resolver } from "./abis/Resolver"; +type AllConfigs = typeof ethConfig & typeof ethBaseConfig; -// just for testing... -const END_BLOCK = 12_000_000; - -const RESOLVER_ABI = mergeAbis([LegacyPublicResolver, Resolver]); - -const REGISTRY_OLD_ADDRESS = "0x314159265dd8dbb310642f98f50c066173c1259b"; -const REGISTRY_ADDRESS = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; - -const BASE_REGISTRAR_ADDRESS = "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85"; -const ETH_REGISTRAR_CONTROLLER_OLD_ADDRESS = "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5"; -const ETH_REGISTRAR_CONTROLLER_ADDRESS = "0x253553366Da8546fC250F225fe3d25d0C782303b"; -const NAME_WRAPPER_ADDRESS = "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401"; - -export default createConfig({ - networks: { - mainnet: { - chainId: 1, - transport: http(process.env.PONDER_RPC_URL_1), - }, - }, - contracts: { - RegistryOld: { - network: "mainnet", - abi: Registry, - address: REGISTRY_OLD_ADDRESS, - startBlock: 3327417, - endBlock: END_BLOCK, - }, - Registry: { - network: "mainnet", - abi: Registry, - address: REGISTRY_ADDRESS, - startBlock: 9380380, - endBlock: END_BLOCK, - }, - OldRegistryResolvers: { - network: "mainnet", - abi: RESOLVER_ABI, - address: factory({ - address: REGISTRY_OLD_ADDRESS, - event: getAbiItem({ abi: Registry, name: "NewResolver" }), - parameter: "resolver", - }), - startBlock: 9380380, - endBlock: END_BLOCK, - }, - Resolver: { - network: "mainnet", - abi: RESOLVER_ABI, - address: factory({ - address: REGISTRY_ADDRESS, - event: getAbiItem({ abi: Registry, name: "NewResolver" }), - parameter: "resolver", - }), - startBlock: 9380380, - endBlock: END_BLOCK, - }, - BaseRegistrar: { - network: "mainnet", - abi: BaseRegistrar, - address: BASE_REGISTRAR_ADDRESS, - startBlock: 9380410, - endBlock: END_BLOCK, - }, - EthRegistrarControllerOld: { - network: "mainnet", - abi: EthRegistrarControllerOld, - address: ETH_REGISTRAR_CONTROLLER_OLD_ADDRESS, - startBlock: 9380471, - endBlock: END_BLOCK, - }, - EthRegistrarController: { - network: "mainnet", - abi: EthRegistrarController, - address: ETH_REGISTRAR_CONTROLLER_ADDRESS, - startBlock: Math.min(16925618, END_BLOCK), - endBlock: END_BLOCK, - }, - NameWrapper: { - network: "mainnet", - abi: NameWrapper, - address: NAME_WRAPPER_ADDRESS, - startBlock: Math.min(16925608, END_BLOCK), - endBlock: END_BLOCK, - }, - }, -}); +// here we export only a single 'plugin's config, by type it as every config +// this makes all of the mapping types happy at typecheck-time, but only the relevant +// config is run at runtime +export default ((): AllConfigs => { + switch (ACTIVE_PLUGIN) { + case ethOwnedName: + activateEth(); + return ethConfig as AllConfigs; + case ethBaseOwnedName: + activateEthBase(); + return ethBaseConfig as AllConfigs; + default: + throw new Error(`Unsupported ACTIVE_PLUGIN: ${ACTIVE_PLUGIN}`); + } +})(); diff --git a/src/EthRegistrar.ts b/src/EthRegistrar.ts deleted file mode 100644 index 742a380f..00000000 --- a/src/EthRegistrar.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { type Context, type Event, ponder } from "ponder:registry"; -import { domains, registrations } from "ponder:schema"; -import type { Hex } from "viem"; -import { NAMEHASH_ETH, isLabelValid, makeSubnodeNamehash, tokenIdToLabel } from "./lib/ens-helpers"; -import { upsertAccount, upsertRegistration } from "./lib/upserts"; - -// all nodes referenced by EthRegistrar are parented to .eth -const ROOT_NODE = NAMEHASH_ETH; -const GRACE_PERIOD_SECONDS = 7776000n; // 90 days in seconds - -async function handleNameRegistered({ - context, - event, -}: { context: Context; event: Event<"BaseRegistrar:NameRegistered"> }) { - const { id, owner, expires } = event.args; - - await upsertAccount(context, owner); - - const label = tokenIdToLabel(id); - const node = makeSubnodeNamehash(ROOT_NODE, label); - - // TODO: materialze labelName via rainbow tables ala Registry.ts - const labelName = undefined; - - await upsertRegistration(context, { - id: label, - domainId: node, - registrationDate: event.block.timestamp, - expiryDate: expires, - registrantId: owner, - labelName, - }); - - await context.db.update(domains, { id: node }).set({ - registrantId: owner, - expiryDate: expires + GRACE_PERIOD_SECONDS, - labelName, - }); - - // TODO: log Event -} - -async function handleNameRegisteredByControllerOld({ - context, - event, -}: { - context: Context; - event: Event<"EthRegistrarControllerOld:NameRegistered">; -}) { - return await setNamePreimage(context, event.args.name, event.args.label, event.args.cost); -} - -async function handleNameRegisteredByController({ - context, - event, -}: { - context: Context; - event: Event<"EthRegistrarController:NameRegistered">; -}) { - return await setNamePreimage( - context, - event.args.name, - event.args.label, - event.args.baseCost + event.args.premium, - ); -} - -async function handleNameRenewedByController({ - context, - event, -}: { - context: Context; - event: - | Event<"EthRegistrarController:NameRenewed"> - | Event<"EthRegistrarControllerOld:NameRenewed">; -}) { - return await setNamePreimage(context, event.args.name, event.args.label, event.args.cost); -} - -async function setNamePreimage(context: Context, name: string, label: Hex, cost: bigint) { - if (!isLabelValid(name)) return; - - const node = makeSubnodeNamehash(ROOT_NODE, label); - const domain = await context.db.find(domains, { id: node }); - if (!domain) throw new Error("domain expected"); - - if (domain.labelName !== name) { - await context.db.update(domains, { id: node }).set({ labelName: name, name: `${name}.eth` }); - } - - await context.db.update(registrations, { id: label }).set({ labelName: name, cost }); -} - -async function handleNameRenewed({ - context, - event, -}: { context: Context; event: Event<"BaseRegistrar:NameRenewed"> }) { - const { id, expires } = event.args; - - const label = tokenIdToLabel(id); - const node = makeSubnodeNamehash(ROOT_NODE, label); - - await context.db.update(registrations, { id: label }).set({ expiryDate: expires }); - - await context.db - .update(domains, { id: node }) - .set({ expiryDate: expires + GRACE_PERIOD_SECONDS }); - - // TODO: log Event -} - -async function handleNameTransferred({ - context, - event, -}: { context: Context; event: Event<"BaseRegistrar:Transfer"> }) { - const { tokenId, from, to } = event.args; - - await upsertAccount(context, to); - - const label = tokenIdToLabel(tokenId); - const node = makeSubnodeNamehash(ROOT_NODE, label); - - const registration = await context.db.find(registrations, { id: label }); - if (!registration) return; - - await context.db.update(registrations, { id: label }).set({ registrantId: to }); - - await context.db.update(domains, { id: node }).set({ registrantId: to }); - - // TODO: log Event -} - -ponder.on("BaseRegistrar:NameRegistered", handleNameRegistered); -ponder.on("BaseRegistrar:NameRenewed", handleNameRenewed); -ponder.on("BaseRegistrar:Transfer", handleNameTransferred); - -ponder.on("EthRegistrarControllerOld:NameRegistered", handleNameRegisteredByControllerOld); -ponder.on("EthRegistrarControllerOld:NameRenewed", handleNameRenewedByController); - -ponder.on("EthRegistrarController:NameRegistered", handleNameRegisteredByController); -ponder.on("EthRegistrarController:NameRenewed", handleNameRenewedByController); diff --git a/src/NameWrapper.ts b/src/NameWrapper.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Resolver.ts b/src/Resolver.ts deleted file mode 100644 index 912c998c..00000000 --- a/src/Resolver.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { type Context, type Event, ponder } from "ponder:registry"; -import { domains, resolvers } from "ponder:schema"; -import { hasNullByte, uniq } from "./lib/helpers"; -import { makeResolverId } from "./lib/ids"; -import { upsertAccount, upsertResolver } from "./lib/upserts"; - -// there is a legacy resolver abi with different TextChanged events. -// luckily the subgraph doesn't care about the value parameter so we can use a union -// to unify the codepath -type AnyTextChangedEvent = - | Event<"Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)"> - | Event<"Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)"> - | Event<"OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)"> - | Event<"OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)">; - -async function _handleAddrChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:AddrChanged"> }) { - const { a: address, node } = event.args; - await upsertAccount(context, address); - - const id = makeResolverId(node, event.log.address); - await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - addrId: address, - }); - - // materialize the resolved add to the domain iff this resolver is active - const domain = await context.db.find(domains, { id: node }); - if (domain?.resolverId === id) { - await context.db.update(domains, { id: node }).set({ resolvedAddress: address }); - } - - // TODO: log ResolverEvent -} - -async function _handleAddressChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:AddressChanged"> }) { - const { node, coinType, newAddress } = event.args; - await upsertAccount(context, newAddress); - - const id = makeResolverId(node, event.log.address); - const resolver = await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // upsert the new coinType - await context.db - .update(resolvers, { id }) - .set({ coinTypes: uniq([...resolver.coinTypes, coinType]) }); - - // TODO: log ResolverEvent -} - -async function _handleNameChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:NameChanged"> }) { - const { node, name } = event.args; - if (hasNullByte(name)) return; - - const id = makeResolverId(node, event.log.address); - await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // TODO: log ResolverEvent -} - -async function _handleABIChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:ABIChanged"> }) { - const { node } = event.args; - const id = makeResolverId(node, event.log.address); - const resolver = await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // TODO: log ResolverEvent -} - -async function _handlePubkeyChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:PubkeyChanged"> }) { - const { node } = event.args; - const id = makeResolverId(node, event.log.address); - const resolver = await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // TODO: log ResolverEvent -} - -async function _handleTextChanged({ - context, - event, -}: { - context: Context; - event: AnyTextChangedEvent; -}) { - const { node, key } = event.args; - const id = makeResolverId(node, event.log.address); - const resolver = await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // upsert new key - await context.db.update(resolvers, { id }).set({ texts: uniq([...resolver.texts, key]) }); - - // TODO: log ResolverEvent -} - -async function _handleContenthashChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:ContenthashChanged"> }) { - const { node, hash } = event.args; - const id = makeResolverId(node, event.log.address); - await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - contentHash: hash, - }); - - // TODO: log ResolverEvent -} - -async function _handleInterfaceChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:InterfaceChanged"> }) { - const { node } = event.args; - const id = makeResolverId(node, event.log.address); - await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // TODO: log ResolverEvent -} - -async function _handleAuthorisationChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:AuthorisationChanged"> }) { - const { node } = event.args; - const id = makeResolverId(node, event.log.address); - await upsertResolver(context, { - id, - domainId: node, - address: event.log.address, - }); - - // TODO: log ResolverEvent -} - -async function _handleVersionChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:VersionChanged"> }) { - // a version change nulls out the resolver - const { node } = event.args; - const id = makeResolverId(node, event.log.address); - const domain = await context.db.find(domains, { id: node }); - if (!domain) throw new Error("domain expected"); - - // materialize the Domain's resolvedAddress field - if (domain.resolverId === id) { - await context.db.update(domains, { id: node }).set({ resolvedAddress: null }); - } - - // clear out the resolver's info - await context.db.update(resolvers, { id }).set({ - addrId: null, - contentHash: null, - coinTypes: [], - texts: [], - }); - - // TODO: log ResolverEvent -} - -async function _handleDNSRecordChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:DNSRecordChanged"> }) { - // subgraph ignores -} - -async function _handleDNSRecordDeleted({ - context, - event, -}: { context: Context; event: Event<"Resolver:DNSRecordDeleted"> }) { - // subgraph ignores -} - -async function _handleDNSZonehashChanged({ - context, - event, -}: { context: Context; event: Event<"Resolver:DNSZonehashChanged"> }) { - // subgraph ignores -} - -// Old registry handlers -ponder.on("OldRegistryResolvers:AddrChanged", _handleAddrChanged); -ponder.on("OldRegistryResolvers:AddressChanged", _handleAddressChanged); -ponder.on("OldRegistryResolvers:NameChanged", _handleNameChanged); -ponder.on("OldRegistryResolvers:ABIChanged", _handleABIChanged); -ponder.on("OldRegistryResolvers:PubkeyChanged", _handlePubkeyChanged); -ponder.on( - "OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)", - _handleTextChanged, -); -ponder.on( - "OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)", - _handleTextChanged, -); -ponder.on("OldRegistryResolvers:ContenthashChanged", _handleContenthashChanged); -ponder.on("OldRegistryResolvers:InterfaceChanged", _handleInterfaceChanged); -ponder.on("OldRegistryResolvers:AuthorisationChanged", _handleAuthorisationChanged); -ponder.on("OldRegistryResolvers:VersionChanged", _handleVersionChanged); -ponder.on("OldRegistryResolvers:DNSRecordChanged", _handleDNSRecordChanged); -ponder.on("OldRegistryResolvers:DNSRecordDeleted", _handleDNSRecordDeleted); -ponder.on("OldRegistryResolvers:DNSZonehashChanged", _handleDNSZonehashChanged); - -// New registry handlers -ponder.on("Resolver:AddrChanged", _handleAddrChanged); -ponder.on("Resolver:AddressChanged", _handleAddressChanged); -ponder.on("Resolver:NameChanged", _handleNameChanged); -ponder.on("Resolver:ABIChanged", _handleABIChanged); -ponder.on("Resolver:PubkeyChanged", _handlePubkeyChanged); -ponder.on( - "Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)", - _handleTextChanged, -); -ponder.on( - "Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)", - _handleTextChanged, -); -ponder.on("Resolver:ContenthashChanged", _handleContenthashChanged); -ponder.on("Resolver:InterfaceChanged", _handleInterfaceChanged); -ponder.on("Resolver:AuthorisationChanged", _handleAuthorisationChanged); -ponder.on("Resolver:VersionChanged", _handleVersionChanged); -ponder.on("Resolver:DNSRecordChanged", _handleDNSRecordChanged); -ponder.on("Resolver:DNSRecordDeleted", _handleDNSRecordDeleted); -ponder.on("Resolver:DNSZonehashChanged", _handleDNSZonehashChanged); diff --git a/src/handlers/Registrar.ts b/src/handlers/Registrar.ts new file mode 100644 index 00000000..2031c0cc --- /dev/null +++ b/src/handlers/Registrar.ts @@ -0,0 +1,144 @@ +import { type Context } from "ponder:registry"; +import { domains, registrations } from "ponder:schema"; +import { Block } from "ponder"; +import { type Hex, namehash } from "viem"; +import { isLabelValid, makeSubnodeNamehash, tokenIdToLabel } from "../lib/subname-helpers"; +import { upsertAccount, upsertRegistration } from "../lib/upserts"; + +const GRACE_PERIOD_SECONDS = 7776000n; // 90 days in seconds + +/** + * A factory function that returns Ponder indexing handlers for a specified subname. + */ +export const makeRegistryHandlers = (ownedName: `${string}eth`) => { + const ownedSubnameNode = namehash(ownedName); + + async function setNamePreimage(context: Context, name: string, label: Hex, cost: bigint) { + if (!isLabelValid(name)) return; + + const node = makeSubnodeNamehash(ownedSubnameNode, label); + const domain = await context.db.find(domains, { id: node }); + if (!domain) throw new Error("domain expected"); + + if (domain.labelName !== name) { + await context.db + .update(domains, { id: node }) + .set({ labelName: name, name: `${name}${ownedName}` }); + } + + await context.db.update(registrations, { id: label }).set({ labelName: name, cost }); + } + + return { + get ownedSubnameNode() { + return ownedSubnameNode; + }, + + async handleNameRegistered({ + context, + event, + }: { + context: Context; + event: { + block: Block; + args: { id: bigint; owner: Hex; expires: bigint }; + }; + }) { + const { id, owner, expires } = event.args; + + await upsertAccount(context, owner); + + const label = tokenIdToLabel(id); + const node = makeSubnodeNamehash(ownedSubnameNode, label); + + // TODO: materialze labelName via rainbow tables ala Registry.ts + const labelName = undefined; + + await upsertRegistration(context, { + id: label, + domainId: node, + registrationDate: event.block.timestamp, + expiryDate: expires, + registrantId: owner, + labelName, + }); + + await context.db.update(domains, { id: node }).set({ + registrantId: owner, + expiryDate: expires + GRACE_PERIOD_SECONDS, + labelName, + }); + + // TODO: log Event + }, + + async handleNameRegisteredByController({ + context, + args: { name, label, cost }, + }: { + context: Context; + args: { name: string; label: Hex; cost: bigint }; + }) { + return await setNamePreimage(context, name, label, cost); + }, + + async handleNameRenewedByController({ + context, + args: { name, label, cost }, + }: { + context: Context; + args: { name: string; label: Hex; cost: bigint }; + }) { + return await setNamePreimage(context, name, label, cost); + }, + + async handleNameRenewed({ + context, + event, + }: { + context: Context; + event: { + args: { id: bigint; expires: bigint }; + }; + }) { + const { id, expires } = event.args; + + const label = tokenIdToLabel(id); + const node = makeSubnodeNamehash(ownedSubnameNode, label); + + await context.db.update(registrations, { id: label }).set({ expiryDate: expires }); + + await context.db + .update(domains, { id: node }) + .set({ expiryDate: expires + GRACE_PERIOD_SECONDS }); + + // TODO: log Event + }, + + async handleNameTransferred({ + context, + args: { tokenId, from, to }, + }: { + context: Context; + args: { + tokenId: bigint; + from: Hex; + to: Hex; + }; + }) { + await upsertAccount(context, to); + + const label = tokenIdToLabel(tokenId); + const node = makeSubnodeNamehash(ownedSubnameNode, label); + + const registration = await context.db.find(registrations, { id: label }); + if (!registration) return; + + await context.db.update(registrations, { id: label }).set({ registrantId: to }); + + await context.db.update(domains, { id: node }).set({ registrantId: to }); + + // TODO: log Event + }, + }; +}; diff --git a/src/Registry.ts b/src/handlers/Registry.ts similarity index 65% rename from src/Registry.ts rename to src/handlers/Registry.ts index 9654e8f3..36de3a79 100644 --- a/src/Registry.ts +++ b/src/handlers/Registry.ts @@ -1,15 +1,25 @@ -import { type Context, type Event, ponder } from "ponder:registry"; -import { resolvers } from "ponder:schema"; -import { domains } from "ponder:schema"; +import { Context } from "ponder:registry"; +import { domains, resolvers } from "ponder:schema"; +import { Block } from "ponder"; import { type Hex, zeroAddress } from "viem"; -import { NAMEHASH_ZERO, encodeLabelhash, makeSubnodeNamehash } from "./lib/ens-helpers"; -import { makeResolverId } from "./lib/ids"; -import { upsertAccount } from "./lib/upserts"; +import { makeResolverId } from "../lib/ids"; +import { ROOT_NODE, encodeLabelhash, makeSubnodeNamehash } from "../lib/subname-helpers"; +import { upsertAccount } from "../lib/upserts"; + +/** + * Initialize the ENS root node with the zeroAddress as the owner. + */ +export async function setupRootNode({ context }: { context: Context }) { + // ensure we have an account for the zeroAddress + await upsertAccount(context, zeroAddress); -// a domain is migrated iff it exists and isMigrated is set to true, otherwise it is not -async function isDomainMigrated(context: Context, node: Hex) { - const domain = await context.db.find(domains, { id: node }); - return domain?.isMigrated ?? false; + // initialize the ENS root to be owned by the zeroAddress and not migrated + await context.db.insert(domains).values({ + id: ROOT_NODE, + ownerId: zeroAddress, + createdAt: 0n, + isMigrated: false, + }); } function isDomainEmpty(domain: typeof domains.$inferSelect) { @@ -35,12 +45,15 @@ async function recursivelyRemoveEmptyDomainFromParentSubdomainCount(context: Con } } -async function _handleTransfer({ +export async function handleTransfer({ context, event, }: { context: Context; - event: Event<"Registry:Transfer">; + event: { + args: { node: Hex; owner: Hex }; + block: Block; + }; }) { const { node, owner } = event.args; @@ -61,14 +74,17 @@ async function _handleTransfer({ // TODO: log DomainEvent } -const _handleNewOwner = +export const handleNewOwner = (isMigrated: boolean) => async ({ context, event, }: { context: Context; - event: Event<"Registry:NewOwner">; + event: { + args: { node: Hex; label: Hex; owner: Hex }; + block: Block; + }; }) => { const { label, node, owner } = event.args; @@ -116,12 +132,14 @@ const _handleNewOwner = } }; -async function _handleNewTTL({ +export async function handleNewTTL({ context, event, }: { context: Context; - event: Event<"Registry:NewTTL">; + event: { + args: { node: Hex; ttl: bigint }; + }; }) { const { node, ttl } = event.args; @@ -133,12 +151,14 @@ async function _handleNewTTL({ // TODO: log DomainEvent } -async function _handleNewResolver({ +export async function handleNewResolver({ context, event, }: { context: Context; - event: Event<"Registry:NewResolver">; + event: { + args: { node: Hex; resolver: Hex }; + }; }) { const { node, resolver: resolverAddress } = event.args; @@ -170,56 +190,3 @@ async function _handleNewResolver({ // TODO: log DomainEvent } - -// setup on old registry -ponder.on("RegistryOld:setup", async ({ context }) => { - // ensure we have an account for the zeroAddress - await upsertAccount(context, zeroAddress); - - // ensure we have a root Domain, owned by the zeroAddress - await context.db.insert(domains).values({ - id: NAMEHASH_ZERO, - ownerId: zeroAddress, - createdAt: 0n, - isMigrated: false, - }); -}); - -// old registry functions are proxied to the current handlers -// iff the domain has not yet been migrated -ponder.on("RegistryOld:NewOwner", async ({ context, event }) => { - const node = makeSubnodeNamehash(event.args.node, event.args.label); - const isMigrated = await isDomainMigrated(context, node); - if (isMigrated) return; - return _handleNewOwner(false)({ context, event }); -}); - -ponder.on("RegistryOld:NewResolver", async ({ context, event }) => { - // NOTE: the subgraph makes an exception for the root node here - // but i don't know that that's necessary, as in ponder our root node starts out - // unmigrated and once the NewOwner event is emitted by the new registry, - // the root will be considered migrated - // https://github.com/ensdomains/ens-subgraph/blob/master/src/ensRegistry.ts#L246 - - // otherwise, only handle iff not migrated - const isMigrated = await isDomainMigrated(context, event.args.node); - if (isMigrated) return; - return _handleNewResolver({ context, event }); -}); - -ponder.on("RegistryOld:NewTTL", async ({ context, event }) => { - const isMigrated = await isDomainMigrated(context, event.args.node); - if (isMigrated) return; - return _handleNewTTL({ context, event }); -}); - -ponder.on("RegistryOld:Transfer", async ({ context, event }) => { - const isMigrated = await isDomainMigrated(context, event.args.node); - if (isMigrated) return; - return _handleTransfer({ context, event }); -}); - -ponder.on("Registry:NewOwner", _handleNewOwner(true)); -ponder.on("Registry:NewResolver", _handleNewResolver); -ponder.on("Registry:NewTTL", _handleNewTTL); -ponder.on("Registry:Transfer", _handleTransfer); diff --git a/src/handlers/Resolver.ts b/src/handlers/Resolver.ts new file mode 100644 index 00000000..1b0708e6 --- /dev/null +++ b/src/handlers/Resolver.ts @@ -0,0 +1,311 @@ +import { type Context } from "ponder:registry"; +import { domains, resolvers } from "ponder:schema"; +import { Log } from "ponder"; +import { Hex } from "viem"; +import { hasNullByte, uniq } from "../lib/helpers"; +import { makeResolverId } from "../lib/ids"; +import { upsertAccount, upsertResolver } from "../lib/upserts"; + +export async function handleAddrChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex; a: Hex }; + log: Log; + }; +}) { + const { a: address, node } = event.args; + await upsertAccount(context, address); + + const id = makeResolverId(node, event.log.address); + await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + addrId: address, + }); + + // materialize the resolved add to the domain iff this resolver is active + const domain = await context.db.find(domains, { id: node }); + if (domain?.resolverId === id) { + await context.db.update(domains, { id: node }).set({ resolvedAddress: address }); + } + + // TODO: log ResolverEvent +} + +export async function handleAddressChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex; coinType: bigint; newAddress: Hex }; + log: Log; + }; +}) { + const { node, coinType, newAddress } = event.args; + await upsertAccount(context, newAddress); + + const id = makeResolverId(node, event.log.address); + const resolver = await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // upsert the new coinType + await context.db + .update(resolvers, { id }) + .set({ coinTypes: uniq([...resolver.coinTypes, coinType]) }); + + // TODO: log ResolverEvent +} + +export async function handleNameChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex; name: string }; + log: Log; + }; +}) { + const { node, name } = event.args; + if (hasNullByte(name)) return; + + const id = makeResolverId(node, event.log.address); + await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // TODO: log ResolverEvent +} + +export async function handleABIChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex }; + log: Log; + }; +}) { + const { node } = event.args; + const id = makeResolverId(node, event.log.address); + const resolver = await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // TODO: log ResolverEvent +} + +export async function handlePubkeyChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex }; + log: Log; + }; +}) { + const { node } = event.args; + const id = makeResolverId(node, event.log.address); + const resolver = await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // TODO: log ResolverEvent +} + +export async function handleTextChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex; indexedKey: string; key: string; value?: string }; + log: Log; + }; +}) { + const { node, key } = event.args; + const id = makeResolverId(node, event.log.address); + const resolver = await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // upsert new key + await context.db.update(resolvers, { id }).set({ texts: uniq([...resolver.texts, key]) }); + + // TODO: log ResolverEvent +} + +export async function handleContenthashChanged({ + context, + event, +}: { + context: Context; + event: { + args: { node: Hex; hash: Hex }; + log: Log; + }; +}) { + const { node, hash } = event.args; + const id = makeResolverId(node, event.log.address); + await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + contentHash: hash, + }); + + // TODO: log ResolverEvent +} + +export async function handleInterfaceChanged({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + interfaceID: Hex; + implementer: Hex; + }; + log: Log; + }; +}) { + const { node } = event.args; + const id = makeResolverId(node, event.log.address); + await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // TODO: log ResolverEvent +} + +export async function handleAuthorisationChanged({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + owner: Hex; + target: Hex; + isAuthorised: boolean; + }; + log: Log; + }; +}) { + const { node } = event.args; + const id = makeResolverId(node, event.log.address); + await upsertResolver(context, { + id, + domainId: node, + address: event.log.address, + }); + + // TODO: log ResolverEvent +} + +export async function handleVersionChanged({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + newVersion: bigint; + }; + log: Log; + }; +}) { + // a version change nulls out the resolver + const { node } = event.args; + const id = makeResolverId(node, event.log.address); + const domain = await context.db.find(domains, { id: node }); + if (!domain) throw new Error("domain expected"); + + // materialize the Domain's resolvedAddress field + if (domain.resolverId === id) { + await context.db.update(domains, { id: node }).set({ resolvedAddress: null }); + } + + // clear out the resolver's info + await context.db.update(resolvers, { id }).set({ + addrId: null, + contentHash: null, + coinTypes: [], + texts: [], + }); + + // TODO: log ResolverEvent +} + +export async function handleDNSRecordChanged({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + name: Hex; + resource: number; + record: Hex; + }; + }; +}) { + // subgraph ignores +} + +export async function handleDNSRecordDeleted({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + name: Hex; + resource: number; + record?: Hex; + }; + }; +}) { + // subgraph ignores +} + +export async function handleDNSZonehashChanged({ + context, + event, +}: { + context: Context; + event: { + args: { + node: Hex; + zonehash: Hex; + }; + }; +}) { + // subgraph ignores +} diff --git a/src/lib/plugin-helpers.ts b/src/lib/plugin-helpers.ts new file mode 100644 index 00000000..9d0b0ce8 --- /dev/null +++ b/src/lib/plugin-helpers.ts @@ -0,0 +1,83 @@ +/** + * A factory function that returns a function to create a namespaced contract + * name for Ponder indexing handlers. + * + * Ponder config requires a flat dictionary of contract config entires, where + * each entry has its unique name and set of EVM event names derived from + * the contract's ABI. Ponder will use contract names and their respective + * event names to create names for indexing handlers. For example, a contract + * named `Registry` includes events: `NewResolver` and `NewTTL`. Ponder will + * create indexing handlers named `Registry:NewResolver` and `Registry:NewTTL`. + * + * However, in some cases, we may want to create a namespaced contract name to + * distinguish between contracts having the same name, but handling different + * implementations. + * + * Let's say we have two contracts named `Registry`. One handles `eth` subnames + * and the other handles `base.eth` subnames. We need to create a namespaced + * contract name to avoid conflicts. + * We could use the actual name/subname as a prefix, like `eth/Registry` and + * `base.eth/Registry`. We cannot do that, though, as Ponder does not support + * dots and colons in its indexing handler names. + * + * We need to use a different separator, in this case, a forward slash within + * a path-like format. + * + * @param subname + * + * @example + * ```ts + * const boxNs = createPluginNamespace("box"); + * const ethNs = createPluginNamespace("base.eth"); + * const baseEthNs = createPluginNamespace("base.eth"); + * + * boxNs("Registry"); // returns "/box/Registry" + * ethNs("Registry"); // returns "/eth/Registry" + * baseEthNs("Registry"); // returns "/base/eth/Registry" + * ``` + */ +export function createPluginNamespace(subname: Subname) { + const namespacePath = nameIntoPath(subname) satisfies PluginNamespacePath; + + /** Creates a namespaced contract name */ + return function pluginNamespace( + contractName: ContractName, + ): PluginNamespaceReturnType { + return `${namespacePath}/${contractName}`; + }; +} + +type TransformNameIntoPath = Name extends `${infer Sub}.${infer Rest}` + ? `/${TransformNameIntoPath}/${Sub}` + : `/${Name}`; + +/** + * Transforms a name into a path-like format, by reversing the name parts and + * joining them with a forward slash. The name parts are separated by a dot. + * + * @param name is made of dot-separated labels + * @returns path-like format of the reversed domain + * + * @example + * ```ts + * nameIntoPath("base.eth"); // returns "/eth/base" + * nameIntoPath("my.box"); // returns "/box/my" + **/ +function nameIntoPath(name: Name): TransformNameIntoPath { + // TODO: validate the name + return `/${name.split(".").reverse().join("/")}` as TransformNameIntoPath; +} + +/** The return type of the `pluginNamespace` function */ +type PluginNamespaceReturnType< + ContractName extends string, + NamespacePath extends PluginNamespacePath, +> = `${NamespacePath}/${ContractName}`; + +type PluginNamespacePath = + | `` + | `/${string}` + | `/${string}${T}`; + +/** @var the requested active plugin name (see `src/plugins` for available plugins) */ +export const ACTIVE_PLUGIN = process.env.ACTIVE_PLUGIN; diff --git a/src/lib/ens-helpers.ts b/src/lib/subname-helpers.ts similarity index 90% rename from src/lib/ens-helpers.ts rename to src/lib/subname-helpers.ts index 4e256a7b..a00b6edd 100644 --- a/src/lib/ens-helpers.ts +++ b/src/lib/subname-helpers.ts @@ -1,8 +1,7 @@ import { type Hex, concat, keccak256, namehash, toHex } from "viem"; // TODO: pull from ens utils lib or something -export const NAMEHASH_ZERO = namehash(""); -export const NAMEHASH_ETH = namehash("eth"); +export const ROOT_NODE = namehash(""); // TODO: this should probably be a part of some ens util lib export const makeSubnodeNamehash = (node: Hex, label: Hex) => keccak256(concat([node, label])); diff --git a/src/plugins/README.md b/src/plugins/README.md new file mode 100644 index 00000000..991fc8fc --- /dev/null +++ b/src/plugins/README.md @@ -0,0 +1,8 @@ +# Indexer plugins + +This directory contains plugins which allow defining subname-specific processing of blockchain events. +Only one plugin can be active at a time. Use the `ACTIVE_PLUGIN` env variable to select the active plugin, for example: + +``` +ACTIVE_PLUGIN=base.eth +``` \ No newline at end of file diff --git a/src/plugins/base.eth/README.md b/src/plugins/base.eth/README.md new file mode 100644 index 00000000..1f56a46c --- /dev/null +++ b/src/plugins/base.eth/README.md @@ -0,0 +1,9 @@ +# 'base.eth' plugin for Ponder indexer + +This plugin contains configuration required to run blockchain indexing with [the Ponder app](https://ponder.sh/). It includes relevant ABI files, contract addresses and the block numbers those contracts were deployed at on a selected network. + +## Architecture + +The Basenames contracts manage subnames of "base.eth". All contracts and their interactions were described here: +https://github.com/base-org/basenames?tab=readme-ov-file#architecture + diff --git a/src/plugins/base.eth/abis/BaseRegistrar.ts b/src/plugins/base.eth/abis/BaseRegistrar.ts new file mode 100644 index 00000000..0cf2d6ab --- /dev/null +++ b/src/plugins/base.eth/abis/BaseRegistrar.ts @@ -0,0 +1,556 @@ +export const BaseRegistrar = [ + { + inputs: [ + { internalType: "contract ENS", name: "registry_", type: "address" }, + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "bytes32", name: "baseNode_", type: "bytes32" }, + { internalType: "string", name: "baseURI_", type: "string" }, + { internalType: "string", name: "collectionURI_", type: "string" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "AccountBalanceOverflow", type: "error" }, + { inputs: [], name: "AlreadyInitialized", type: "error" }, + { inputs: [], name: "BalanceQueryForZeroAddress", type: "error" }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "Expired", + type: "error", + }, + { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" }, + { inputs: [], name: "NoHandoverRequest", type: "error" }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "NonexistentToken", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "tokenId", type: "uint256" }, + { internalType: "address", name: "sender", type: "address" }, + ], + name: "NotApprovedOwner", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "NotAvailable", + type: "error", + }, + { inputs: [], name: "NotOwnerNorApproved", type: "error" }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "NotRegisteredOrInGrace", + type: "error", + }, + { inputs: [], name: "OnlyController", type: "error" }, + { inputs: [], name: "RegistrarNotLive", type: "error" }, + { inputs: [], name: "TokenAlreadyExists", type: "error" }, + { inputs: [], name: "TokenDoesNotExist", type: "error" }, + { inputs: [], name: "TransferFromIncorrectOwner", type: "error" }, + { inputs: [], name: "TransferToNonERC721ReceiverImplementer", type: "error" }, + { inputs: [], name: "TransferToZeroAddress", type: "error" }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { indexed: true, internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "operator", + type: "address", + }, + { + indexed: false, + internalType: "bool", + name: "isApproved", + type: "bool", + }, + ], + name: "ApprovalForAll", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "_fromTokenId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "_toTokenId", + type: "uint256", + }, + ], + name: "BatchMetadataUpdate", + type: "event", + }, + { anonymous: false, inputs: [], name: "ContractURIUpdated", type: "event" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "controller", + type: "address", + }, + ], + name: "ControllerAdded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "controller", + type: "address", + }, + ], + name: "ControllerRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "id", type: "uint256" }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + ], + name: "NameRegistered", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "id", type: "uint256" }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "resolver", + type: "address", + }, + { indexed: false, internalType: "uint64", name: "ttl", type: "uint64" }, + ], + name: "NameRegisteredWithRecord", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "uint256", name: "id", type: "uint256" }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + ], + name: "NameRenewed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "from", type: "address" }, + { indexed: true, internalType: "address", name: "to", type: "address" }, + { indexed: true, internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [{ internalType: "address", name: "controller", type: "address" }], + name: "addController", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "approve", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "owner", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "baseNode", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "contractURI", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "controller", type: "address" }], + name: "controllers", + outputs: [{ internalType: "bool", name: "isApproved", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "id", type: "uint256" }], + name: "getApproved", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "operator", type: "address" }, + ], + name: "isApprovedForAll", + outputs: [{ internalType: "bool", name: "result", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "id", type: "uint256" }], + name: "isAvailable", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "id", type: "uint256" }], + name: "nameExpires", + outputs: [{ internalType: "uint256", name: "expiry", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "ownerOf", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "ownershipHandoverExpiresAt", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "address", name: "owner", type: "address" }, + ], + name: "reclaim", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "register", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "registerOnly", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "address", name: "resolver", type: "address" }, + { internalType: "uint64", name: "ttl", type: "uint64" }, + ], + name: "registerWithRecord", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "registry", + outputs: [{ internalType: "contract ENS", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "controller", type: "address" }], + name: "removeController", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "renew", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "from", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "from", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "operator", type: "address" }, + { internalType: "bool", name: "isApproved", type: "bool" }, + ], + name: "setApprovalForAll", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "baseURI_", type: "string" }], + name: "setBaseTokenURI", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "collectionURI_", type: "string" }], + name: "setContractURI", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "resolver", type: "address" }], + name: "setResolver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + name: "tokenURI", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "from", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "transferFrom", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newOwner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/EARegistrarController.ts b/src/plugins/base.eth/abis/EARegistrarController.ts new file mode 100644 index 00000000..2923cbc1 --- /dev/null +++ b/src/plugins/base.eth/abis/EARegistrarController.ts @@ -0,0 +1,568 @@ +export const EarlyAccessRegistrarController = [ + { + inputs: [ + { + internalType: "contract BaseRegistrar", + name: "base_", + type: "address", + }, + { + internalType: "contract IPriceOracle", + name: "prices_", + type: "address", + }, + { + internalType: "contract IReverseRegistrar", + name: "reverseRegistrar_", + type: "address", + }, + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "bytes32", name: "rootNode_", type: "bytes32" }, + { internalType: "string", name: "rootName_", type: "string" }, + { internalType: "address", name: "paymentReceiver_", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [{ internalType: "address", name: "target", type: "address" }], + name: "AddressEmptyCode", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "AddressInsufficientBalance", + type: "error", + }, + { inputs: [], name: "AlreadyInitialized", type: "error" }, + { + inputs: [{ internalType: "address", name: "sender", type: "address" }], + name: "AlreadyRegisteredWithDiscount", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "duration", type: "uint256" }], + name: "DurationTooShort", + type: "error", + }, + { inputs: [], name: "FailedInnerCall", type: "error" }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "InactiveDiscount", + type: "error", + }, + { inputs: [], name: "InsufficientValue", type: "error" }, + { + inputs: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "InvalidDiscount", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "InvalidDiscountAmount", + type: "error", + }, + { inputs: [], name: "InvalidPaymentReceiver", type: "error" }, + { + inputs: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "address", name: "validator", type: "address" }, + ], + name: "InvalidValidator", + type: "error", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "NameNotAvailable", + type: "error", + }, + { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" }, + { inputs: [], name: "NoHandoverRequest", type: "error" }, + { inputs: [], name: "ResolverRequiredWhenDataSupplied", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { inputs: [], name: "TransferFailed", type: "error" }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "registrant", + type: "address", + }, + { + indexed: true, + internalType: "bytes32", + name: "discountKey", + type: "bytes32", + }, + ], + name: "DiscountApplied", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "discountKey", + type: "bytes32", + }, + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + indexed: false, + internalType: "struct EARegistrarController.DiscountDetails", + name: "details", + type: "tuple", + }, + ], + name: "DiscountUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "payee", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "price", + type: "uint256", + }, + ], + name: "ETHPaymentProcessed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "string", name: "name", type: "string" }, + { + indexed: true, + internalType: "bytes32", + name: "label", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + ], + name: "NameRegistered", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newPaymentReceiver", + type: "address", + }, + ], + name: "PaymentReceiverUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newPrices", + type: "address", + }, + ], + name: "PriceOracleUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newReverseRegistrar", + type: "address", + }, + ], + name: "ReverseRegistrarUpdated", + type: "event", + }, + { + inputs: [], + name: "MIN_NAME_LENGTH", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MIN_REGISTRATION_DURATION", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "available", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "address", name: "resolver", type: "address" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + { internalType: "bool", name: "reverseRecord", type: "bool" }, + ], + internalType: "struct EARegistrarController.RegisterRequest", + name: "request", + type: "tuple", + }, + { internalType: "bytes32", name: "discountKey", type: "bytes32" }, + { internalType: "bytes", name: "validationData", type: "bytes" }, + ], + name: "discountedRegister", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "bytes32", name: "discountKey", type: "bytes32" }, + ], + name: "discountedRegisterPrice", + outputs: [{ internalType: "uint256", name: "price", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "registrant", type: "address" }], + name: "discountedRegistrants", + outputs: [{ internalType: "bool", name: "hasRegisteredWithDiscount", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "discounts", + outputs: [ + { internalType: "bool", name: "active", type: "bool" }, + { internalType: "address", name: "discountValidator", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getActiveDiscounts", + outputs: [ + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + internalType: "struct EARegistrarController.DiscountDetails[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address[]", name: "addresses", type: "address[]" }], + name: "hasRegisteredWithDiscount", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "ownershipHandoverExpiresAt", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "paymentReceiver", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "prices", + outputs: [{ internalType: "contract IPriceOracle", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_token", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "recoverFunds", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "registerPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "rentPrice", + outputs: [ + { + components: [ + { internalType: "uint256", name: "base", type: "uint256" }, + { internalType: "uint256", name: "premium", type: "uint256" }, + ], + internalType: "struct IPriceOracle.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "reverseRegistrar", + outputs: [{ internalType: "contract IReverseRegistrar", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rootName", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rootNode", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + internalType: "struct EARegistrarController.DiscountDetails", + name: "details", + type: "tuple", + }, + ], + name: "setDiscountDetails", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "paymentReceiver_", type: "address" }], + name: "setPaymentReceiver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "contract IPriceOracle", + name: "prices_", + type: "address", + }, + ], + name: "setPriceOracle", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "contract IReverseRegistrar", + name: "reverse_", + type: "address", + }, + ], + name: "setReverseRegistrar", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newOwner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "valid", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "withdrawETH", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/L1Resolver.ts b/src/plugins/base.eth/abis/L1Resolver.ts new file mode 100644 index 00000000..ba2c1b68 --- /dev/null +++ b/src/plugins/base.eth/abis/L1Resolver.ts @@ -0,0 +1,467 @@ +export const L1Resolver = [ + { + inputs: [ + { + internalType: "string", + name: "url_", + type: "string", + }, + { + internalType: "address[]", + name: "signers_", + type: "address[]", + }, + { + internalType: "address", + name: "owner_", + type: "address", + }, + { + internalType: "address", + name: "rootResolver_", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "AlreadyInitialized", + type: "error", + }, + { + inputs: [], + name: "InvalidSigner", + type: "error", + }, + { + inputs: [], + name: "NewOwnerIsZeroAddress", + type: "error", + }, + { + inputs: [], + name: "NoHandoverRequest", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + { + internalType: "string[]", + name: "urls", + type: "string[]", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + { + internalType: "bytes4", + name: "callbackFunction", + type: "bytes4", + }, + { + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "OffchainLookup", + type: "error", + }, + { + inputs: [], + name: "SignatureExpired", + type: "error", + }, + { + inputs: [], + name: "Unauthorized", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address[]", + name: "signers", + type: "address[]", + }, + ], + name: "AddedSigners", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "signer", + type: "address", + }, + ], + name: "RemovedSigner", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "resolver", + type: "address", + }, + ], + name: "RootResolverChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "newUrl", + type: "string", + }, + ], + name: "UrlChanged", + type: "event", + }, + { + stateMutability: "nonpayable", + type: "fallback", + }, + { + inputs: [ + { + internalType: "address[]", + name: "signers_", + type: "address[]", + }, + ], + name: "addSigners", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "uint64", + name: "expires", + type: "uint64", + }, + { + internalType: "bytes", + name: "request", + type: "bytes", + }, + { + internalType: "bytes", + name: "result", + type: "bytes", + }, + ], + name: "makeSignatureHash", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "result", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "ownershipHandoverExpiresAt", + outputs: [ + { + internalType: "uint256", + name: "result", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "signer", + type: "address", + }, + ], + name: "removeSigner", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes", + name: "name", + type: "bytes", + }, + { + internalType: "bytes", + name: "data", + type: "bytes", + }, + ], + name: "resolve", + outputs: [ + { + internalType: "bytes", + name: "", + type: "bytes", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes", + name: "response", + type: "bytes", + }, + { + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "resolveWithProof", + outputs: [ + { + internalType: "bytes", + name: "", + type: "bytes", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rootResolver", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "rootResolver_", + type: "address", + }, + ], + name: "setRootResolver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "string", + name: "url_", + type: "string", + }, + ], + name: "setUrl", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "signer", + type: "address", + }, + ], + name: "signers", + outputs: [ + { + internalType: "bool", + name: "isApproved", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes4", + name: "interfaceID", + type: "bytes4", + }, + ], + name: "supportsInterface", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "url", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/L2Resolver.ts b/src/plugins/base.eth/abis/L2Resolver.ts new file mode 100644 index 00000000..63e1c09d --- /dev/null +++ b/src/plugins/base.eth/abis/L2Resolver.ts @@ -0,0 +1,693 @@ +export const L2Resolver = [ + { + inputs: [ + { internalType: "contract ENS", name: "ens_", type: "address" }, + { + internalType: "address", + name: "registrarController_", + type: "address", + }, + { internalType: "address", name: "reverseRegistrar_", type: "address" }, + { internalType: "address", name: "owner_", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "AlreadyInitialized", type: "error" }, + { inputs: [], name: "CantSetSelfAsDelegate", type: "error" }, + { inputs: [], name: "CantSetSelfAsOperator", type: "error" }, + { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" }, + { inputs: [], name: "NoHandoverRequest", type: "error" }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: true, + internalType: "uint256", + name: "contentType", + type: "uint256", + }, + ], + name: "ABIChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "address", name: "a", type: "address" }, + ], + name: "AddrChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: false, + internalType: "uint256", + name: "coinType", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "newAddress", + type: "bytes", + }, + ], + name: "AddressChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "operator", + type: "address", + }, + { indexed: false, internalType: "bool", name: "approved", type: "bool" }, + ], + name: "ApprovalForAll", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "owner", + type: "address", + }, + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: true, + internalType: "address", + name: "delegate", + type: "address", + }, + { indexed: true, internalType: "bool", name: "approved", type: "bool" }, + ], + name: "Approved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "bytes", name: "hash", type: "bytes" }, + ], + name: "ContenthashChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "bytes", name: "name", type: "bytes" }, + { + indexed: false, + internalType: "uint16", + name: "resource", + type: "uint16", + }, + { indexed: false, internalType: "bytes", name: "record", type: "bytes" }, + ], + name: "DNSRecordChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "bytes", name: "name", type: "bytes" }, + { + indexed: false, + internalType: "uint16", + name: "resource", + type: "uint16", + }, + ], + name: "DNSRecordDeleted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: false, + internalType: "bytes", + name: "lastzonehash", + type: "bytes", + }, + { + indexed: false, + internalType: "bytes", + name: "zonehash", + type: "bytes", + }, + ], + name: "DNSZonehashChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: true, + internalType: "bytes4", + name: "interfaceID", + type: "bytes4", + }, + { + indexed: false, + internalType: "address", + name: "implementer", + type: "address", + }, + ], + name: "InterfaceChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "string", name: "name", type: "string" }, + ], + name: "NameChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "bytes32", name: "x", type: "bytes32" }, + { indexed: false, internalType: "bytes32", name: "y", type: "bytes32" }, + ], + name: "PubkeyChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "newRegistrarController", + type: "address", + }, + ], + name: "RegistrarControllerUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "newReverseRegistrar", + type: "address", + }, + ], + name: "ReverseRegistrarUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: true, + internalType: "string", + name: "indexedKey", + type: "string", + }, + { indexed: false, internalType: "string", name: "key", type: "string" }, + { indexed: false, internalType: "string", name: "value", type: "string" }, + ], + name: "TextChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: false, + internalType: "uint64", + name: "newVersion", + type: "uint64", + }, + ], + name: "VersionChanged", + type: "event", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "uint256", name: "contentTypes", type: "uint256" }, + ], + name: "ABI", + outputs: [ + { internalType: "uint256", name: "", type: "uint256" }, + { internalType: "bytes", name: "", type: "bytes" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "addr", + outputs: [{ internalType: "address payable", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "uint256", name: "coinType", type: "uint256" }, + ], + name: "addr", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "delegate", type: "address" }, + { internalType: "bool", name: "approved", type: "bool" }, + ], + name: "approve", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "clearRecords", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "contenthash", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes32", name: "name", type: "bytes32" }, + { internalType: "uint16", name: "resource", type: "uint16" }, + ], + name: "dnsRecord", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "ens", + outputs: [{ internalType: "contract ENS", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes32", name: "name", type: "bytes32" }, + ], + name: "hasDNSRecords", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes4", name: "interfaceID", type: "bytes4" }, + ], + name: "interfaceImplementer", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "delegate", type: "address" }, + ], + name: "isApprovedFor", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "address", name: "operator", type: "address" }, + ], + name: "isApprovedForAll", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes[]", name: "data", type: "bytes[]" }], + name: "multicall", + outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "nodehash", type: "bytes32" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + ], + name: "multicallWithNodeCheck", + outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "ownershipHandoverExpiresAt", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "pubkey", + outputs: [ + { internalType: "bytes32", name: "x", type: "bytes32" }, + { internalType: "bytes32", name: "y", type: "bytes32" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + name: "recordVersions", + outputs: [{ internalType: "uint64", name: "", type: "uint64" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "registrarController", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "", type: "bytes" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "resolve", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "reverseRegistrar", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "uint256", name: "contentType", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "setABI", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "uint256", name: "coinType", type: "uint256" }, + { internalType: "bytes", name: "a", type: "bytes" }, + ], + name: "setAddr", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "a", type: "address" }, + ], + name: "setAddr", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "operator", type: "address" }, + { internalType: "bool", name: "approved", type: "bool" }, + ], + name: "setApprovalForAll", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes", name: "hash", type: "bytes" }, + ], + name: "setContenthash", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "setDNSRecords", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes4", name: "interfaceID", type: "bytes4" }, + { internalType: "address", name: "implementer", type: "address" }, + ], + name: "setInterface", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "string", name: "newName", type: "string" }, + ], + name: "setName", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes32", name: "x", type: "bytes32" }, + { internalType: "bytes32", name: "y", type: "bytes32" }, + ], + name: "setPubkey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "registrarController_", + type: "address", + }, + ], + name: "setRegistrarController", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "reverseRegistrar_", type: "address" }], + name: "setReverseRegistrar", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "string", name: "key", type: "string" }, + { internalType: "string", name: "value", type: "string" }, + ], + name: "setText", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes", name: "hash", type: "bytes" }, + ], + name: "setZonehash", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "string", name: "key", type: "string" }, + ], + name: "text", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newOwner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "zonehash", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/RegistrarController.ts b/src/plugins/base.eth/abis/RegistrarController.ts new file mode 100644 index 00000000..eed59ccb --- /dev/null +++ b/src/plugins/base.eth/abis/RegistrarController.ts @@ -0,0 +1,633 @@ +export const RegistrarController = [ + { + inputs: [ + { + internalType: "contract BaseRegistrar", + name: "base_", + type: "address", + }, + { + internalType: "contract IPriceOracle", + name: "prices_", + type: "address", + }, + { + internalType: "contract IReverseRegistrar", + name: "reverseRegistrar_", + type: "address", + }, + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "bytes32", name: "rootNode_", type: "bytes32" }, + { internalType: "string", name: "rootName_", type: "string" }, + { internalType: "address", name: "paymentReceiver_", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [{ internalType: "address", name: "target", type: "address" }], + name: "AddressEmptyCode", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "AddressInsufficientBalance", + type: "error", + }, + { inputs: [], name: "AlreadyInitialized", type: "error" }, + { + inputs: [{ internalType: "address", name: "sender", type: "address" }], + name: "AlreadyRegisteredWithDiscount", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "duration", type: "uint256" }], + name: "DurationTooShort", + type: "error", + }, + { inputs: [], name: "FailedInnerCall", type: "error" }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "InactiveDiscount", + type: "error", + }, + { inputs: [], name: "InsufficientValue", type: "error" }, + { + inputs: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "InvalidDiscount", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "InvalidDiscountAmount", + type: "error", + }, + { inputs: [], name: "InvalidPaymentReceiver", type: "error" }, + { + inputs: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "address", name: "validator", type: "address" }, + ], + name: "InvalidValidator", + type: "error", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "NameNotAvailable", + type: "error", + }, + { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" }, + { inputs: [], name: "NoHandoverRequest", type: "error" }, + { inputs: [], name: "ResolverRequiredWhenDataSupplied", type: "error" }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "SafeERC20FailedOperation", + type: "error", + }, + { inputs: [], name: "TransferFailed", type: "error" }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "registrant", + type: "address", + }, + { + indexed: true, + internalType: "bytes32", + name: "discountKey", + type: "bytes32", + }, + ], + name: "DiscountApplied", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "discountKey", + type: "bytes32", + }, + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + indexed: false, + internalType: "struct RegistrarController.DiscountDetails", + name: "details", + type: "tuple", + }, + ], + name: "DiscountUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "payee", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "price", + type: "uint256", + }, + ], + name: "ETHPaymentProcessed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "string", name: "name", type: "string" }, + { + indexed: true, + internalType: "bytes32", + name: "label", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + ], + name: "NameRegistered", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "string", name: "name", type: "string" }, + { + indexed: true, + internalType: "bytes32", + name: "label", + type: "bytes32", + }, + { + indexed: false, + internalType: "uint256", + name: "expires", + type: "uint256", + }, + ], + name: "NameRenewed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newPaymentReceiver", + type: "address", + }, + ], + name: "PaymentReceiverUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newPrices", + type: "address", + }, + ], + name: "PriceOracleUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newReverseRegistrar", + type: "address", + }, + ], + name: "ReverseRegistrarUpdated", + type: "event", + }, + { + inputs: [], + name: "MIN_NAME_LENGTH", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MIN_REGISTRATION_DURATION", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "available", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "address", name: "resolver", type: "address" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + { internalType: "bool", name: "reverseRecord", type: "bool" }, + ], + internalType: "struct RegistrarController.RegisterRequest", + name: "request", + type: "tuple", + }, + { internalType: "bytes32", name: "discountKey", type: "bytes32" }, + { internalType: "bytes", name: "validationData", type: "bytes" }, + ], + name: "discountedRegister", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "bytes32", name: "discountKey", type: "bytes32" }, + ], + name: "discountedRegisterPrice", + outputs: [{ internalType: "uint256", name: "price", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "registrant", type: "address" }], + name: "discountedRegistrants", + outputs: [{ internalType: "bool", name: "hasRegisteredWithDiscount", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "discounts", + outputs: [ + { internalType: "bool", name: "active", type: "bool" }, + { internalType: "address", name: "discountValidator", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getActiveDiscounts", + outputs: [ + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + internalType: "struct RegistrarController.DiscountDetails[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address[]", name: "addresses", type: "address[]" }], + name: "hasRegisteredWithDiscount", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "launchTime", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "ownershipHandoverExpiresAt", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "paymentReceiver", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "prices", + outputs: [{ internalType: "contract IPriceOracle", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_token", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "recoverFunds", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + { internalType: "address", name: "resolver", type: "address" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + { internalType: "bool", name: "reverseRecord", type: "bool" }, + ], + internalType: "struct RegistrarController.RegisterRequest", + name: "request", + type: "tuple", + }, + ], + name: "register", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "registerPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "renew", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "name", type: "string" }, + { internalType: "uint256", name: "duration", type: "uint256" }, + ], + name: "rentPrice", + outputs: [ + { + components: [ + { internalType: "uint256", name: "base", type: "uint256" }, + { internalType: "uint256", name: "premium", type: "uint256" }, + ], + internalType: "struct IPriceOracle.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "reverseRegistrar", + outputs: [{ internalType: "contract IReverseRegistrar", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rootName", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rootNode", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "bool", name: "active", type: "bool" }, + { + internalType: "address", + name: "discountValidator", + type: "address", + }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "discount", type: "uint256" }, + ], + internalType: "struct RegistrarController.DiscountDetails", + name: "details", + type: "tuple", + }, + ], + name: "setDiscountDetails", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "launchTime_", type: "uint256" }], + name: "setLaunchTime", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "paymentReceiver_", type: "address" }], + name: "setPaymentReceiver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "contract IPriceOracle", + name: "prices_", + type: "address", + }, + ], + name: "setPriceOracle", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "contract IReverseRegistrar", + name: "reverse_", + type: "address", + }, + ], + name: "setReverseRegistrar", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newOwner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "valid", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "withdrawETH", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/Registry.ts b/src/plugins/base.eth/abis/Registry.ts new file mode 100644 index 00000000..c0254788 --- /dev/null +++ b/src/plugins/base.eth/abis/Registry.ts @@ -0,0 +1,199 @@ +export const Registry = [ + { + inputs: [{ internalType: "address", name: "rootOwner", type: "address" }], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "operator", + type: "address", + }, + { indexed: false, internalType: "bool", name: "approved", type: "bool" }, + ], + name: "ApprovalForAll", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: true, + internalType: "bytes32", + name: "label", + type: "bytes32", + }, + { + indexed: false, + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "NewOwner", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: false, + internalType: "address", + name: "resolver", + type: "address", + }, + ], + name: "NewResolver", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { indexed: false, internalType: "uint64", name: "ttl", type: "uint64" }, + ], + name: "NewTTL", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + { + indexed: false, + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [ + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "address", name: "operator", type: "address" }, + ], + name: "isApprovedForAll", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "owner", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "recordExists", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "resolver", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "operator", type: "address" }, + { internalType: "bool", name: "approved", type: "bool" }, + ], + name: "setApprovalForAll", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "owner_", type: "address" }, + ], + name: "setOwner", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "address", name: "resolver_", type: "address" }, + { internalType: "uint64", name: "ttl_", type: "uint64" }, + ], + name: "setRecord", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "resolver_", type: "address" }, + ], + name: "setResolver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes32", name: "label", type: "bytes32" }, + { internalType: "address", name: "owner_", type: "address" }, + ], + name: "setSubnodeOwner", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "bytes32", name: "label", type: "bytes32" }, + { internalType: "address", name: "owner_", type: "address" }, + { internalType: "address", name: "resolver_", type: "address" }, + { internalType: "uint64", name: "ttl_", type: "uint64" }, + ], + name: "setSubnodeRecord", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "uint64", name: "ttl_", type: "uint64" }, + ], + name: "setTTL", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }], + name: "ttl", + outputs: [{ internalType: "uint64", name: "", type: "uint64" }], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/abis/ReverseRegistrar.ts b/src/plugins/base.eth/abis/ReverseRegistrar.ts new file mode 100644 index 00000000..cc67d88c --- /dev/null +++ b/src/plugins/base.eth/abis/ReverseRegistrar.ts @@ -0,0 +1,245 @@ +export const ReverseRegistrar = [ + { + inputs: [ + { internalType: "contract ENS", name: "registry_", type: "address" }, + { internalType: "address", name: "owner_", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "AlreadyInitialized", type: "error" }, + { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" }, + { inputs: [], name: "NoHandoverRequest", type: "error" }, + { inputs: [], name: "NoZeroAddress", type: "error" }, + { + inputs: [ + { internalType: "address", name: "addr", type: "address" }, + { internalType: "address", name: "sender", type: "address" }, + ], + name: "NotAuthorized", + type: "error", + }, + { inputs: [], name: "Unauthorized", type: "error" }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "address", name: "addr", type: "address" }, + { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" }, + ], + name: "BaseReverseClaimed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "controller", + type: "address", + }, + { indexed: false, internalType: "bool", name: "approved", type: "bool" }, + ], + name: "ControllerApprovalChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "contract NameResolver", + name: "resolver", + type: "address", + }, + ], + name: "DefaultResolverChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "pendingOwner", + type: "address", + }, + ], + name: "OwnershipHandoverRequested", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + inputs: [], + name: "cancelOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "owner", type: "address" }], + name: "claim", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "addr", type: "address" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "resolver", type: "address" }, + ], + name: "claimForBaseAddr", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "resolver", type: "address" }, + ], + name: "claimWithResolver", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "completeOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "controller", type: "address" }], + name: "controllers", + outputs: [{ internalType: "bool", name: "approved", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "defaultResolver", + outputs: [{ internalType: "contract NameResolver", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "addr", type: "address" }], + name: "node", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "result", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "pendingOwner", type: "address" }], + name: "ownershipHandoverExpiresAt", + outputs: [{ internalType: "uint256", name: "result", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "registry", + outputs: [{ internalType: "contract ENS", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "requestOwnershipHandover", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "controller", type: "address" }, + { internalType: "bool", name: "approved", type: "bool" }, + ], + name: "setControllerApproval", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "resolver", type: "address" }], + name: "setDefaultResolver", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "string", name: "name", type: "string" }], + name: "setName", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "addr", type: "address" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "resolver", type: "address" }, + { internalType: "string", name: "name", type: "string" }, + ], + name: "setNameForAddr", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newOwner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; diff --git a/src/plugins/base.eth/handlers/Registrar.ts b/src/plugins/base.eth/handlers/Registrar.ts new file mode 100644 index 00000000..7ab0a0e5 --- /dev/null +++ b/src/plugins/base.eth/handlers/Registrar.ts @@ -0,0 +1,79 @@ +import { ponder } from "ponder:registry"; +import { domains } from "ponder:schema"; +import { makeRegistryHandlers } from "../../../handlers/Registrar"; +import { makeSubnodeNamehash, tokenIdToLabel } from "../../../lib/subname-helpers"; +import { upsertAccount } from "../../../lib/upserts"; +import { ownedName, pluginNamespace } from "../ponder.config"; + +const { + handleNameRegistered, + handleNameRegisteredByController, + handleNameRenewedByController, + handleNameRenewed, + handleNameTransferred, + ownedSubnameNode, +} = makeRegistryHandlers(ownedName); + +export default function () { + // support NameRegisteredWithRecord for BaseRegistrar as it used by Base's RegistrarControllers + ponder.on(pluginNamespace("BaseRegistrar:NameRegisteredWithRecord"), async ({ context, event }) => + handleNameRegistered({ context, event }), + ); + + ponder.on(pluginNamespace("BaseRegistrar:NameRegistered"), async ({ context, event }) => { + // base has 'preminted' names via Registrar#registerOnly, which explicitly does not update Registry. + // this breaks a subgraph assumption, as it expects a domain to exist (via Registry:NewOwner) before + // any Registrar:NameRegistered events. in the future we will likely happily upsert domains, but + // in order to avoid prematurely drifting from subgraph equivalancy, we upsert the domain here, + // allowing the base indexer to progress. + const { id, owner } = event.args; + const label = tokenIdToLabel(id); + const node = makeSubnodeNamehash(ownedSubnameNode, label); + await upsertAccount(context, owner); + await context.db + .insert(domains) + .values({ + id: node, + ownerId: owner, + createdAt: event.block.timestamp, + }) + .onConflictDoNothing(); + + // after ensuring the domain exists, continue with the standard handler + return handleNameRegistered({ context, event }); + }); + ponder.on(pluginNamespace("BaseRegistrar:NameRenewed"), handleNameRenewed); + + // Base's BaseRegistrar uses `id` instead of `tokenId` + ponder.on(pluginNamespace("BaseRegistrar:Transfer"), async ({ context, event }) => { + return await handleNameTransferred({ + context, + args: { ...event.args, tokenId: event.args.id }, + }); + }); + + ponder.on(pluginNamespace("EARegistrarController:NameRegistered"), async ({ context, event }) => { + // TODO: registration expected here + + return handleNameRegisteredByController({ + context, + args: { ...event.args, cost: 0n }, + }); + }); + + ponder.on(pluginNamespace("RegistrarController:NameRegistered"), async ({ context, event }) => { + // TODO: registration expected here + + return handleNameRegisteredByController({ + context, + args: { ...event.args, cost: 0n }, + }); + }); + + ponder.on(pluginNamespace("RegistrarController:NameRenewed"), async ({ context, event }) => { + return handleNameRenewedByController({ + context, + args: { ...event.args, cost: 0n }, + }); + }); +} diff --git a/src/plugins/base.eth/handlers/Registry.ts b/src/plugins/base.eth/handlers/Registry.ts new file mode 100644 index 00000000..57f8fd50 --- /dev/null +++ b/src/plugins/base.eth/handlers/Registry.ts @@ -0,0 +1,17 @@ +import { ponder } from "ponder:registry"; +import { + handleNewOwner, + handleNewResolver, + handleNewTTL, + handleTransfer, + setupRootNode, +} from "../../../handlers/Registry"; +import { pluginNamespace } from "../ponder.config"; + +export default function () { + ponder.on(pluginNamespace("Registry:setup"), setupRootNode); + ponder.on(pluginNamespace("Registry:NewOwner"), handleNewOwner(true)); + ponder.on(pluginNamespace("Registry:NewResolver"), handleNewResolver); + ponder.on(pluginNamespace("Registry:NewTTL"), handleNewTTL); + ponder.on(pluginNamespace("Registry:Transfer"), handleTransfer); +} diff --git a/src/plugins/base.eth/handlers/Resolver.ts b/src/plugins/base.eth/handlers/Resolver.ts new file mode 100644 index 00000000..e1a30e0c --- /dev/null +++ b/src/plugins/base.eth/handlers/Resolver.ts @@ -0,0 +1,32 @@ +import { ponder } from "ponder:registry"; +import { + handleABIChanged, + handleAddrChanged, + handleAddressChanged, + handleContenthashChanged, + handleDNSRecordChanged, + handleDNSRecordDeleted, + handleDNSZonehashChanged, + handleInterfaceChanged, + handleNameChanged, + handlePubkeyChanged, + handleTextChanged, + handleVersionChanged, +} from "../../../handlers/Resolver"; +import { pluginNamespace as ns } from "../ponder.config"; + +export default function () { + // New registry handlers + ponder.on(ns("Resolver:AddrChanged"), handleAddrChanged); + ponder.on(ns("Resolver:AddressChanged"), handleAddressChanged); + ponder.on(ns("Resolver:NameChanged"), handleNameChanged); + ponder.on(ns("Resolver:ABIChanged"), handleABIChanged); + ponder.on(ns("Resolver:PubkeyChanged"), handlePubkeyChanged); + ponder.on(ns("Resolver:TextChanged"), handleTextChanged); + ponder.on(ns("Resolver:ContenthashChanged"), handleContenthashChanged); + ponder.on(ns("Resolver:InterfaceChanged"), handleInterfaceChanged); + ponder.on(ns("Resolver:VersionChanged"), handleVersionChanged); + ponder.on(ns("Resolver:DNSRecordChanged"), handleDNSRecordChanged); + ponder.on(ns("Resolver:DNSRecordDeleted"), handleDNSRecordDeleted); + ponder.on(ns("Resolver:DNSZonehashChanged"), handleDNSZonehashChanged); +} diff --git a/src/plugins/base.eth/ponder.config.ts b/src/plugins/base.eth/ponder.config.ts new file mode 100644 index 00000000..6340a2c8 --- /dev/null +++ b/src/plugins/base.eth/ponder.config.ts @@ -0,0 +1,69 @@ +import { createConfig, factory } from "ponder"; +import { http, getAbiItem } from "viem"; +import { base } from "viem/chains"; + +import { createPluginNamespace } from "../../lib/plugin-helpers"; +import { BaseRegistrar } from "./abis/BaseRegistrar"; +import { EarlyAccessRegistrarController } from "./abis/EARegistrarController"; +import { L2Resolver } from "./abis/L2Resolver"; +import { RegistrarController } from "./abis/RegistrarController"; +import { Registry } from "./abis/Registry"; + +export const ownedName = "base.eth" as const; + +export const pluginNamespace = createPluginNamespace(ownedName); + +export const config = createConfig({ + networks: { + base: { + chainId: base.id, + transport: http(process.env[`RPC_URL_${base.id}`]), + }, + }, + contracts: { + [pluginNamespace("Registry")]: { + network: "base", + abi: Registry, + address: "0xb94704422c2a1e396835a571837aa5ae53285a95", + startBlock: 17571480, + }, + [pluginNamespace("Resolver")]: { + network: "base", + abi: L2Resolver, + address: factory({ + address: "0xb94704422c2a1e396835a571837aa5ae53285a95", + event: getAbiItem({ abi: Registry, name: "NewResolver" }), + parameter: "resolver", + }), + startBlock: 17575714, + }, + [pluginNamespace("BaseRegistrar")]: { + network: "base", + abi: BaseRegistrar, + address: "0x03c4738Ee98aE44591e1A4A4F3CaB6641d95DD9a", + startBlock: 17571486, + }, + [pluginNamespace("EARegistrarController")]: { + network: "base", + abi: EarlyAccessRegistrarController, + address: "0xd3e6775ed9b7dc12b205c8e608dc3767b9e5efda", + startBlock: 17575699, + }, + [pluginNamespace("RegistrarController")]: { + network: "base", + abi: RegistrarController, + address: "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5", + startBlock: 18619035, + }, + }, +}); + +export async function activate() { + const ponderIndexingModules = await Promise.all([ + import("./handlers/Registry"), + import("./handlers/Registrar"), + import("./handlers/Resolver"), + ]); + + ponderIndexingModules.map((m) => m.default()); +} diff --git a/abis/BaseRegistrar.ts b/src/plugins/eth/abis/BaseRegistrar.ts similarity index 100% rename from abis/BaseRegistrar.ts rename to src/plugins/eth/abis/BaseRegistrar.ts diff --git a/abis/EthRegistrarController.ts b/src/plugins/eth/abis/EthRegistrarController.ts similarity index 100% rename from abis/EthRegistrarController.ts rename to src/plugins/eth/abis/EthRegistrarController.ts diff --git a/abis/EthRegistrarControllerOld.ts b/src/plugins/eth/abis/EthRegistrarControllerOld.ts similarity index 100% rename from abis/EthRegistrarControllerOld.ts rename to src/plugins/eth/abis/EthRegistrarControllerOld.ts diff --git a/abis/LegacyPublicResolver.ts b/src/plugins/eth/abis/LegacyPublicResolver.ts similarity index 100% rename from abis/LegacyPublicResolver.ts rename to src/plugins/eth/abis/LegacyPublicResolver.ts diff --git a/abis/NameWrapper.ts b/src/plugins/eth/abis/NameWrapper.ts similarity index 100% rename from abis/NameWrapper.ts rename to src/plugins/eth/abis/NameWrapper.ts diff --git a/abis/Registry.ts b/src/plugins/eth/abis/Registry.ts similarity index 100% rename from abis/Registry.ts rename to src/plugins/eth/abis/Registry.ts diff --git a/abis/Resolver.ts b/src/plugins/eth/abis/Resolver.ts similarity index 100% rename from abis/Resolver.ts rename to src/plugins/eth/abis/Resolver.ts diff --git a/src/plugins/eth/handlers/EthRegistrar.ts b/src/plugins/eth/handlers/EthRegistrar.ts new file mode 100644 index 00000000..b4c1249d --- /dev/null +++ b/src/plugins/eth/handlers/EthRegistrar.ts @@ -0,0 +1,51 @@ +import { ponder } from "ponder:registry"; +import { makeRegistryHandlers } from "../../../handlers/Registrar"; +import { ownedName, pluginNamespace } from "../ponder.config"; + +const { + handleNameRegistered, + handleNameRegisteredByController, + handleNameRenewedByController, + handleNameRenewed, + handleNameTransferred, +} = makeRegistryHandlers(ownedName); + +export default function () { + ponder.on(pluginNamespace("BaseRegistrar:NameRegistered"), handleNameRegistered); + ponder.on(pluginNamespace("BaseRegistrar:NameRenewed"), handleNameRenewed); + + ponder.on(pluginNamespace("BaseRegistrar:Transfer"), async ({ context, event }) => { + return await handleNameTransferred({ context, args: event.args }); + }); + + ponder.on( + pluginNamespace("EthRegistrarControllerOld:NameRegistered"), + async ({ context, event }) => { + // the old registrar controller just had `cost` param + return await handleNameRegisteredByController({ context, args: event.args }); + }, + ); + ponder.on( + pluginNamespace("EthRegistrarControllerOld:NameRenewed"), + async ({ context, event }) => { + return await handleNameRenewedByController({ context, args: event.args }); + }, + ); + + ponder.on( + pluginNamespace("EthRegistrarController:NameRegistered"), + async ({ context, event }) => { + // the new registrar controller uses baseCost + premium to compute cost + return await handleNameRegisteredByController({ + context, + args: { + ...event.args, + cost: event.args.baseCost + event.args.premium, + }, + }); + }, + ); + ponder.on(pluginNamespace("EthRegistrarController:NameRenewed"), async ({ context, event }) => { + return await handleNameRenewedByController({ context, args: event.args }); + }); +} diff --git a/src/plugins/eth/handlers/NameWrapper.ts b/src/plugins/eth/handlers/NameWrapper.ts new file mode 100644 index 00000000..8337712e --- /dev/null +++ b/src/plugins/eth/handlers/NameWrapper.ts @@ -0,0 +1 @@ +// diff --git a/src/plugins/eth/handlers/Registry.ts b/src/plugins/eth/handlers/Registry.ts new file mode 100644 index 00000000..b3e2064e --- /dev/null +++ b/src/plugins/eth/handlers/Registry.ts @@ -0,0 +1,61 @@ +import { type Context, ponder } from "ponder:registry"; +import { domains } from "ponder:schema"; +import { type Hex } from "viem"; +import { + handleNewOwner, + handleNewResolver, + handleNewTTL, + handleTransfer, + setupRootNode, +} from "../../../handlers/Registry"; +import { makeSubnodeNamehash } from "../../../lib/subname-helpers"; +import { pluginNamespace } from "../ponder.config"; + +// a domain is migrated iff it exists and isMigrated is set to true, otherwise it is not +async function isDomainMigrated(context: Context, node: Hex) { + const domain = await context.db.find(domains, { id: node }); + return domain?.isMigrated ?? false; +} + +export default function () { + ponder.on(pluginNamespace("RegistryOld:setup"), setupRootNode); + + // old registry functions are proxied to the current handlers + // iff the domain has not yet been migrated + ponder.on(pluginNamespace("RegistryOld:NewOwner"), async ({ context, event }) => { + const node = makeSubnodeNamehash(event.args.node, event.args.label); + const isMigrated = await isDomainMigrated(context, node); + if (isMigrated) return; + return handleNewOwner(false)({ context, event }); + }); + + ponder.on(pluginNamespace("RegistryOld:NewResolver"), async ({ context, event }) => { + // NOTE: the subgraph makes an exception for the root node here + // but i don't know that that's necessary, as in ponder our root node starts out + // unmigrated and once the NewOwner event is emitted by the new registry, + // the root will be considered migrated + // https://github.com/ensdomains/ens-subgraph/blob/master/src/ensRegistry.ts#L246 + + // otherwise, only handle iff not migrated + const isMigrated = await isDomainMigrated(context, event.args.node); + if (isMigrated) return; + return handleNewResolver({ context, event }); + }); + + ponder.on(pluginNamespace("RegistryOld:NewTTL"), async ({ context, event }) => { + const isMigrated = await isDomainMigrated(context, event.args.node); + if (isMigrated) return; + return handleNewTTL({ context, event }); + }); + + ponder.on(pluginNamespace("RegistryOld:Transfer"), async ({ context, event }) => { + const isMigrated = await isDomainMigrated(context, event.args.node); + if (isMigrated) return; + return handleTransfer({ context, event }); + }); + + ponder.on(pluginNamespace("Registry:NewOwner"), handleNewOwner(true)); + ponder.on(pluginNamespace("Registry:NewResolver"), handleNewResolver); + ponder.on(pluginNamespace("Registry:NewTTL"), handleNewTTL); + ponder.on(pluginNamespace("Registry:Transfer"), handleTransfer); +} diff --git a/src/plugins/eth/handlers/Resolver.ts b/src/plugins/eth/handlers/Resolver.ts new file mode 100644 index 00000000..e7e0046a --- /dev/null +++ b/src/plugins/eth/handlers/Resolver.ts @@ -0,0 +1,74 @@ +import { ponder } from "ponder:registry"; +import { + handleABIChanged, + handleAddrChanged, + handleAddressChanged, + handleAuthorisationChanged, + handleContenthashChanged, + handleDNSRecordChanged, + handleDNSRecordDeleted, + handleDNSZonehashChanged, + handleInterfaceChanged, + handleNameChanged, + handlePubkeyChanged, + handleTextChanged, + handleVersionChanged, +} from "../../../handlers/Resolver"; +import { pluginNamespace } from "../ponder.config"; + +export default function () { + // Old registry handlers + ponder.on(pluginNamespace("OldRegistryResolvers:AddrChanged"), handleAddrChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:AddressChanged"), handleAddressChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:NameChanged"), handleNameChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:ABIChanged"), handleABIChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:PubkeyChanged"), handlePubkeyChanged); + ponder.on( + pluginNamespace( + "OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)", + ), + handleTextChanged, + ); + ponder.on( + pluginNamespace( + "OldRegistryResolvers:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)", + ), + handleTextChanged, + ); + ponder.on(pluginNamespace("OldRegistryResolvers:ContenthashChanged"), handleContenthashChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:InterfaceChanged"), handleInterfaceChanged); + ponder.on( + pluginNamespace("OldRegistryResolvers:AuthorisationChanged"), + handleAuthorisationChanged, + ); + ponder.on(pluginNamespace("OldRegistryResolvers:VersionChanged"), handleVersionChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:DNSRecordChanged"), handleDNSRecordChanged); + ponder.on(pluginNamespace("OldRegistryResolvers:DNSRecordDeleted"), handleDNSRecordDeleted); + ponder.on(pluginNamespace("OldRegistryResolvers:DNSZonehashChanged"), handleDNSZonehashChanged); + + // New registry handlers + ponder.on(pluginNamespace("Resolver:AddrChanged"), handleAddrChanged); + ponder.on(pluginNamespace("Resolver:AddressChanged"), handleAddressChanged); + ponder.on(pluginNamespace("Resolver:NameChanged"), handleNameChanged); + ponder.on(pluginNamespace("Resolver:ABIChanged"), handleABIChanged); + ponder.on(pluginNamespace("Resolver:PubkeyChanged"), handlePubkeyChanged); + ponder.on( + pluginNamespace( + "Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key)", + ), + handleTextChanged, + ); + ponder.on( + pluginNamespace( + "Resolver:TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)", + ), + handleTextChanged, + ); + ponder.on(pluginNamespace("Resolver:ContenthashChanged"), handleContenthashChanged); + ponder.on(pluginNamespace("Resolver:InterfaceChanged"), handleInterfaceChanged); + ponder.on(pluginNamespace("Resolver:AuthorisationChanged"), handleAuthorisationChanged); + ponder.on(pluginNamespace("Resolver:VersionChanged"), handleVersionChanged); + ponder.on(pluginNamespace("Resolver:DNSRecordChanged"), handleDNSRecordChanged); + ponder.on(pluginNamespace("Resolver:DNSRecordDeleted"), handleDNSRecordDeleted); + ponder.on(pluginNamespace("Resolver:DNSZonehashChanged"), handleDNSZonehashChanged); +} diff --git a/src/plugins/eth/ponder.config.ts b/src/plugins/eth/ponder.config.ts new file mode 100644 index 00000000..5b0e960b --- /dev/null +++ b/src/plugins/eth/ponder.config.ts @@ -0,0 +1,95 @@ +import { createConfig, factory, mergeAbis } from "ponder"; +import { http, getAbiItem } from "viem"; + +import { mainnet } from "viem/chains"; +import { createPluginNamespace } from "../../lib/plugin-helpers"; +import { BaseRegistrar } from "./abis/BaseRegistrar"; +import { EthRegistrarController } from "./abis/EthRegistrarController"; +import { EthRegistrarControllerOld } from "./abis/EthRegistrarControllerOld"; +import { LegacyPublicResolver } from "./abis/LegacyPublicResolver"; +import { NameWrapper } from "./abis/NameWrapper"; +import { Registry } from "./abis/Registry"; +import { Resolver } from "./abis/Resolver"; + +const RESOLVER_ABI = mergeAbis([LegacyPublicResolver, Resolver]); + +export const ownedName = "eth"; + +export const pluginNamespace = createPluginNamespace(ownedName); + +export const config = createConfig({ + networks: { + mainnet: { + chainId: mainnet.id, + transport: http(process.env[`RPC_URL_${mainnet.id}`]), + }, + }, + contracts: { + [pluginNamespace("RegistryOld")]: { + network: "mainnet", + abi: Registry, + address: "0x314159265dd8dbb310642f98f50c066173c1259b", + startBlock: 3327417, + }, + [pluginNamespace("Registry")]: { + network: "mainnet", + abi: Registry, + address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + startBlock: 9380380, + }, + [pluginNamespace("OldRegistryResolvers")]: { + network: "mainnet", + abi: RESOLVER_ABI, + address: factory({ + address: "0x314159265dd8dbb310642f98f50c066173c1259b", + event: getAbiItem({ abi: Registry, name: "NewResolver" }), + parameter: "resolver", + }), + startBlock: 9380380, + }, + [pluginNamespace("Resolver")]: { + network: "mainnet", + abi: RESOLVER_ABI, + address: factory({ + address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + event: getAbiItem({ abi: Registry, name: "NewResolver" }), + parameter: "resolver", + }), + startBlock: 9380380, + }, + [pluginNamespace("BaseRegistrar")]: { + network: "mainnet", + abi: BaseRegistrar, + address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85", + startBlock: 9380410, + }, + [pluginNamespace("EthRegistrarControllerOld")]: { + network: "mainnet", + abi: EthRegistrarControllerOld, + address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + startBlock: 9380471, + }, + [pluginNamespace("EthRegistrarController")]: { + network: "mainnet", + abi: EthRegistrarController, + address: "0x253553366Da8546fC250F225fe3d25d0C782303b", + startBlock: 16925618, + }, + [pluginNamespace("NameWrapper")]: { + network: "mainnet", + abi: NameWrapper, + address: "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401", + startBlock: 16925608, + }, + }, +}); + +export async function activate() { + const ponderIndexingModules = await Promise.all([ + import("./handlers/Registry"), + import("./handlers/EthRegistrar"), + import("./handlers/Resolver"), + ]); + + ponderIndexingModules.map((m) => m.default()); +}