Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ponder-ens): introduce plugin architecture #3

Merged
merged 31 commits into from
Jan 8, 2025

Conversation

tk-o
Copy link
Contributor

@tk-o tk-o commented Dec 31, 2024

This change allows defining simple ponder plugins per supported subnames. Only one plugin can be active during indexing, and to select it, please set the ACTIVE_PLUGIN env var to the name of one of the available plugins (see src/plugins directory). For example:

ACTIVE_PLUGIN=eth
ACTIVE_PLUGIN=base.eth

Also, please make sure to know how to use the DATABASE_SCHEMA env var.

Examples

Running indexer with the requested subname plugin for ponder:

ACTIVE_PLUGIN=base.eth DATABASE_SCHEMA=my_base_eth pnpm start 

This change allows defining simple ponder plugins per supported ENS blockchain which can be loaded conditionally in the runtime.

Requesting a chain to be indexed requires passing a chain ID in the `CHAINS_TO_INDEX` env var and comma-separated chain ID values.
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Hey thanks for all the efforts on this 🙌 Excited to see the "plugins" start to come to life! Will defer to @shrugs on more detailed review for the proposed plugin architecture.

A more general feedback from my side. For each PR we make, let's please also consider what documentation updates might be relevant and include them in the PR. Appreciate this takes time, but good documentation for other devs in the ENS community will be key to our success.

For example, ideas that would be great to include as documentation in the README.md with this PR might include:

  1. Overall introduction and framing for what we're working to do here. For example: enable any permutation of chain-specific ENS indexers to be dynamically activated via environment variables.
  2. Communicate known oversimplifications in our current approach and identify that our roadmap will remove these oversimplifications in time. For example:
    1. At the moment we're making oversimplifications where indexing "mainnet" represents indexing the legacy ENS Subgraph, indexing "base" represents only indexing subnames of "base.eth" on Base, and indexing "linea" represents only indexing subnames of "linea.eth" on Linea. Etc.
    2. At the moment we're making oversimplifications where we assume that all the delegation relationships of subname issuance from an L1 resolver (using ENSIP-10 and EIP-3668) to an offchain gateway server that reads state from a L2 subname registry is immutable. In other words, we're currently assuming that the current L2 subname registry for subnames of "base.eth" on Base is guaranteed to continue serving in that role.
  3. Perhaps an .env.example file that includes comments documenting the environment variables we use and different example values that might be set. Am I correct to assume that there's also some ponder-related environment variables that we should be documenting in an .env.example file too? For example: RPC endpoints for different chains? Let's write our documentation under the assumption that the reader has never used Ponder before and should not need to familiarize themselves with Ponder if they just want to get started and run their own instance.
  4. A "getting started" section that explains step-by-step how to go from cloning the repo to having a running instance of our indexer.
  5. Any other ideas you suggest will be useful 👍 😄

Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Hey nice updates! I like the direction you're moving in 🚀 Reviewed and shared feedback 👍

Copy link
Contributor

@shrugs shrugs left a comment

Choose a reason for hiding this comment

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

an abi diff between mainnet and base would be helpful for identifying any logic discrepancies

please configure your editor to use biome to format files — the columnWidth being changed from 80 to 100 is causing a lot of formatting-related changes

tk-o added 2 commits January 5, 2025 19:53
This one includes a fix for duplicated log entries error
Make the root domain name based of the env var
@tk-o tk-o force-pushed the feat/ponder-ens-plugins branch from 8c25e59 to 75cd66b Compare January 6, 2025 11:04
@tk-o tk-o force-pushed the feat/ponder-ens-plugins branch from 75cd66b to 0e35dd8 Compare January 6, 2025 11:08
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Hey great to see the updates here. 👍 Reviewed and shared feedback 😄

export const ns = createNs(nestedNamespace);
export type NsType<T extends string> = NsReturnType<T, typeof nestedNamespace>;

const START_BLOCK = undefined; // 17607350;
Copy link
Member

Choose a reason for hiding this comment

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

Can we please document the idea here? Why setting these to undefined?

@shrugs
Copy link
Contributor

shrugs commented Jan 6, 2025

i much prefer the root argument passed to makeRegistryHandlers() because it makes clear that the registry handlers are universal except for root that nodes in those handlers are relative to. it also means that in the future v2 this shared code should be relatively identical. using the constant is less clear and requires more mental overhead for the reader

The generic handlers have now the inlined args types to avoid importing subname-specifc event types. Also, we now use the `INDEX_SUBNAME` env var to specify the subname to index, i.e. `eth` or `base.eth` (note: no starting dot character).
Copy link
Contributor

@shrugs shrugs left a comment

Choose a reason for hiding this comment

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

excellent documentation & typescript type work! i only have nitpicks in here, i think, so it's looking very close to merge

/**
* A factory function that returns Ponder indexing handlers for a specified index name/subname.
*/
export const makeRegistryHandlers = (indexedSubname: `${string}eth`) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

now that i understand the nomenclature better, let's call this indexedSubname variable managedSubname (and managedSubnameNode) so that people know that these Registrar handlers are in the context of a registrar that manages the provided subname

}

await context.db.update(registrations, { id: label }).set({ labelName: name, cost });
}

return {
get baseNameNode() {
return baseNameNode;
get indexedSubnameNode() {
Copy link
Contributor

Choose a reason for hiding this comment

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

why provide this back, it will be known to the caller because they provided it as an input agument

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, hmm. i don't love this but it's ok for the moment 👍 we'll refactor this in v2

@@ -114,12 +119,16 @@ export const makeRegistryHandlers = (baseName: `.${`${string}`}eth`) => {
args: { tokenId, from, to },
}: {
context: Context;
args: Event<NsType<"BaseRegistrar:Transfer">>["args"];
args: {
Copy link
Contributor

Choose a reason for hiding this comment

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

and i think for each of these functions in Registrar.ts we should standardize the api to be like a ponder handler: it always accepts context and event, and args is within the event. we need not do this in this pr, i can manage it once we've merged if you'd like

/**
* Initialize the ENS root node with the zeroAddress as the owner.
*/
export async function handleRootNodeCreation({ context }: { context: Context }) {
Copy link
Contributor

Choose a reason for hiding this comment

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

agreed with lightwalker's naming concern, and apologies for the nitpick, but how about calling this setupRootNode


type PonderNsPath<T extends PonderNsPath = "/"> = `` | `/${string}` | `/${string}${T}`;

type EthSubname = `${string}eth`;
Copy link
Contributor

Choose a reason for hiding this comment

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

very nice type work in this file 🫡

Copy link
Member

Choose a reason for hiding this comment

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

@tk-o Yeah nice work here! 💪

@shrugs
Copy link
Contributor

shrugs commented Jan 7, 2025

looks like biome got misconfigured or something — some line width formatting related changes in the recent commits

tk-o added 2 commits January 8, 2025 07:45
Allows to automatically catch code-related issues on the CI
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Thanks for your updates here! Very happy to see docs improving. Let's continue to push hard on making our docs and use of language properly aligned.

Reviewed and shared feedback 👍

PONDER_RPC_URL_8453=https://base-rpc.publicnode.com
PONDER_RPC_URL_59144=https://linea-rpc.publicnode.com

# ponder indexer ens deployment configuration
Copy link
Member

Choose a reason for hiding this comment

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

@tk-o This is still an open feedback

# ponder indexer database configuration

## one schema name per chain ID, i.e. ponder_ens_${INDEX_BASENAME}
DATABASE_SCHEMA=ponder_ens_base.eth
Copy link
Member

Choose a reason for hiding this comment

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

@tk-o This remains an open feedback item.

I'm also concerned to read "chain id" here. I'm very worried we're getting key ideas confused. In my understanding, "chain id" should not be THE key consideration in our configurations. The INDEX_SUBNAME should be THE number 1 consideration, and then everything else should flow downstream from that.

For example, the chain to index is determined by the subnames to be indexed. Not the other way around.

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 baseName = ".base.eth" as const;
export const managedSubname = "base.eth" as const;
Copy link
Member

Choose a reason for hiding this comment

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

Could you help me understand the use of the word "managed" here? Is there a special reason to use that and not "indexed"?

What does it mean to be "managed"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@shrugs could you share your view here?

Copy link
Contributor

Choose a reason for hiding this comment

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

@lightwalker-eth it got lost in the github comments but: the registrar handlers are in the context of the subname that the registrar 'manages' — the ETH Registrar manages .eth, base's l2 registrar deployment manages .base.eth subnames, etc. perhaps there's a better word but i don't like 'indexed' in this case, since the argument is specifying which parent node all of the events in this registrar are relative to


const START_BLOCK = undefined; // 17607350;
const END_BLOCK = undefined; // 17607351;
export const ponderNamespace = createPonderNamespace(managedSubname);
Copy link
Member

Choose a reason for hiding this comment

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

Is there a special reason for us to put lots of emphasis on "ponder" when we name things?

It's true that we're building on the Ponder framework, but I feel attracted to more general naming of these ideas. For example:

  • "ponderNamespace" -> "pluginNamespace"
  • "createPonderNamespace" -> "createPluginNamespace"

Very open to other suggestions. Perhaps I'm missing something?

Copy link
Contributor

Choose a reason for hiding this comment

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

I might suggest contractNamespace

@@ -12,11 +12,9 @@ import { Resolver } from "./abis/Resolver";

const RESOLVER_ABI = mergeAbis([LegacyPublicResolver, Resolver]);

export const baseName = ".eth" as const;
export const managedSubname = "eth";
Copy link
Member

Choose a reason for hiding this comment

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

Please see my other comment about the use of the word "managed" here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cc @shrugs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've reverted back to indexedSubname

export function createPonderNamespace<Subname extends EthSubname>(subname: Subname) {
const path = transformDomain(subname) satisfies PonderNsPath;

/** Creates a name-spaced contract name */
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/** Creates a name-spaced contract name */
/** Creates a namespaced contract name */

namespace / namespaced is one word, no hyphen.

Please search for other instances of "name-spaced" and replace them. Thanks.

*
* However, in some cases, we may want to create a name-spaced 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 the `eth` name, and the other handles the `base.eth` subname. We need to create a name-spaced contract name to avoid conflicts.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Let's say we have two contracts named `Registry`. One handles the `eth` name, and the other handles the `base.eth` subname. We need to create a name-spaced contract name to avoid conflicts.
* 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.

@@ -0,0 +1,64 @@
/**
Copy link
Member

Choose a reason for hiding this comment

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

@tk-o Great job on the docs here! 🚀 🚀 This makes a big impact for our goals! Docs like this do a lot to help encourage other devs to adopt our indexer because they can more easily make sense of what we're doing and why 😄

One small suggestion is to break to a new line around approximately 80ish characters. This helps with line wrapping when viewing code on platforms such as GitHub.

Copy link
Member

Choose a reason for hiding this comment

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

What do you think about renaming the ponder-ens-plugins directory to just plugins? Seems more clean 😄

Assuming we do rename, please be sure to do it in the way that it's properly tracked by git as just renames, and not a bunch of file deletes / creations.

@@ -0,0 +1,31 @@
name: Static Analysis
Copy link
Member

Choose a reason for hiding this comment

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

Great to see this!

Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Hey super awesome updates! 🚀 🚀 🚀

@tk-o tk-o merged commit 7bdc380 into main Jan 8, 2025
1 check passed
@shrugs shrugs deleted the feat/ponder-ens-plugins branch January 9, 2025 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants