diff --git a/.changeset/green-webs-see.md b/.changeset/green-webs-see.md new file mode 100644 index 0000000000..f070479013 --- /dev/null +++ b/.changeset/green-webs-see.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ponder-sdk": minor +--- + +Extended `PonderAppContext` data model with `localPonderAppUrl` field. diff --git a/.changeset/late-jobs-lay.md b/.changeset/late-jobs-lay.md new file mode 100644 index 0000000000..784df53346 --- /dev/null +++ b/.changeset/late-jobs-lay.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ponder-sdk": minor +--- + +Replaced `localPonderAppUrl` reference with `ponderAppContext.localPonderAppUrl` in the constructor for `LocalPonderClient`. diff --git a/.changeset/loose-waves-slide.md b/.changeset/loose-waves-slide.md new file mode 100644 index 0000000000..d7c81e45a5 --- /dev/null +++ b/.changeset/loose-waves-slide.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-sdk": minor +--- + +Removed the `EnsIndexerUrlEnvironment` interface as it was unused. diff --git a/.changeset/social-bottles-double.md b/.changeset/social-bottles-double.md new file mode 100644 index 0000000000..cc3da4e634 --- /dev/null +++ b/.changeset/social-bottles-double.md @@ -0,0 +1,5 @@ +--- +"ensindexer": minor +--- + +Cleaned up all references to `config.ensIndexerUrl`. The local Ponder app URL is now automatically derived from the Ponder app runtime context, eliminating the need for manual configuration. diff --git a/.github/scripts/run_ensindexer_healthcheck.sh b/.github/scripts/run_ensindexer_healthcheck.sh index ca7a358609..eb891a1cba 100755 --- a/.github/scripts/run_ensindexer_healthcheck.sh +++ b/.github/scripts/run_ensindexer_healthcheck.sh @@ -38,18 +38,10 @@ PID=$! echo "ENSIndexer started with PID: $PID" -# Require ENSINDEXER_URL to be set -if [ -z "$ENSINDEXER_URL" ]; then - echo "Error: ENSINDEXER_URL environment variable must be set" - kill -9 $PID 2>/dev/null || true - wait $PID 2>/dev/null || true - rm -f "$LOG_FILE" - [ -f "$ENV_FILE" ] && rm -f "$ENV_FILE" - exit 1 -fi +ENSINDEXER_HEALTHCHECK="http://localhost:42069/health" # Wait for health check to pass -echo "Waiting for health check to pass at ${ENSINDEXER_URL}/health (up to $HEALTH_CHECK_TIMEOUT seconds)..." +echo "Waiting for health check to pass at ${ENSINDEXER_HEALTHCHECK} (up to $HEALTH_CHECK_TIMEOUT seconds)..." health_check_start=$(date +%s) last_log_check=0 @@ -76,7 +68,7 @@ while true; do fi # Check health endpoint - if curl -sf "${ENSINDEXER_URL}/health" >/dev/null 2>&1; then + if curl -sf "${ENSINDEXER_HEALTHCHECK}" >/dev/null 2>&1; then echo "Health check passed! ENSIndexer is up and running." echo "Test successful - terminating ENSIndexer" # Force kill the ENSIndexer process diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 8564258c22..57c3eef201 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -133,7 +133,6 @@ jobs: DATABASE_SCHEMA: public PLUGINS: subgraph,basenames,lineanames,threedns,protocol-acceleration,registrars,tokenscope ENSRAINBOW_URL: https://api.ensrainbow.io - ENSINDEXER_URL: http://localhost:42069 ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} QUICKNODE_API_KEY: ${{ secrets.QUICKNODE_API_KEY }} QUICKNODE_ENDPOINT_NAME: ${{ secrets.QUICKNODE_ENDPOINT_NAME}} diff --git a/apps/ensindexer/.env.local.example b/apps/ensindexer/.env.local.example index 0d2c9ec33a..73e819b9ba 100644 --- a/apps/ensindexer/.env.local.example +++ b/apps/ensindexer/.env.local.example @@ -226,13 +226,6 @@ LABEL_SET_ID=subgraph # LABEL_SET_VERSION: see https://ensnode.io/ensrainbow/concepts/glossary#label-set-version. LABEL_SET_VERSION=0 -# The "primary" ENSIndexer service URL -# Required. This must be an instance of ENSIndexer using either `ponder start` or `ponder dev`, -# (not `ponder serve`). This URL is used to read Ponder's internal indexing state to power the Config -# and Indexing Status APIs. This should be configured so that ENSIndexer refers back to itself. -# ex: http://localhost:{port}. -ENSINDEXER_URL=http://localhost:42069 - # A feature flag to enable/disable ENSIndexer's Subgraph Compatible Indexing Behavior # Optional. If this is not set, the default value is set to `DEFAULT_SUBGRAPH_COMPAT` (false). # diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index ef14defcd8..950a8950e3 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -6,7 +6,6 @@ import { buildRpcConfigsFromEnv, DatabaseSchemaNameSchema, ENSNamespaceSchema, - EnsIndexerUrlSchema, invariant_isSubgraphCompatibleRequirements, invariant_rpcConfigsSpecifiedForRootChain, makeFullyPinnedLabelSetSchema, @@ -110,7 +109,6 @@ const ENSIndexerConfigSchema = z databaseUrl: DatabaseUrlSchema, databaseSchemaName: DatabaseSchemaNameSchema, rpcConfigs: RpcConfigsSchema, - ensIndexerUrl: EnsIndexerUrlSchema, namespace: ENSNamespaceSchema, plugins: PluginsSchema, @@ -188,7 +186,6 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde return ENSIndexerConfigSchema.parse({ databaseUrl: env.DATABASE_URL, databaseSchemaName: env.DATABASE_SCHEMA, - ensIndexerUrl: env.ENSINDEXER_URL, namespace: env.NAMESPACE, rpcConfigs, diff --git a/apps/ensindexer/src/config/config.test.ts b/apps/ensindexer/src/config/config.test.ts index 7deb8a22f2..538bf16371 100644 --- a/apps/ensindexer/src/config/config.test.ts +++ b/apps/ensindexer/src/config/config.test.ts @@ -25,7 +25,6 @@ const BASE_ENV: ENSIndexerEnvironment = { PLUGINS: "subgraph", DATABASE_SCHEMA: "ensnode", DATABASE_URL: "postgresql://user:password@localhost:5432/mydb", - ENSINDEXER_URL: "http://localhost:42069", ENSRAINBOW_URL: "http://localhost:3223", LABEL_SET_ID: "ens-test-env", LABEL_SET_VERSION: "0", @@ -144,34 +143,6 @@ describe("config (with base env)", () => { }); }); - describe(".ensIndexerUrl", () => { - it("throws an error if ENSINDEXER_URL is not a valid URL", async () => { - vi.stubEnv("ENSINDEXER_URL", "invalid url"); - await expect(getConfig()).rejects.toThrow(/ENSINDEXER_URL must be a valid URL string/i); - }); - - it("throws an error if ENSINDEXER_URL is empty", async () => { - vi.stubEnv("ENSINDEXER_URL", ""); - await expect(getConfig()).rejects.toThrow(/ENSINDEXER_URL must be a valid URL string/i); - }); - - it("throws an error if ENSINDEXER_URL is undefined", async () => { - vi.stubEnv("ENSINDEXER_URL", undefined); - await expect(getConfig()).rejects.toThrow(/ENSINDEXER_URL must be a valid URL string/i); - }); - - it("returns the ENSINDEXER_URL if it is a valid URL", async () => { - const config = await getConfig(); - expect(config.ensIndexerUrl).toStrictEqual(new URL("http://localhost:42069")); - }); - - it("returns a different valid ENSINDEXER_URL if set", async () => { - vi.stubEnv("ENSINDEXER_URL", "https://someotherurl.com"); - const config = await getConfig(); - expect(config.ensIndexerUrl).toStrictEqual(new URL("https://someotherurl.com")); - }); - }); - describe(".ensRainbowUrl", () => { it("throws an error if ENSRAINBOW_URL is not a valid URL", async () => { vi.stubEnv("ENSRAINBOW_URL", "invalid url"); @@ -676,11 +647,9 @@ describe("config (with base env)", () => { */ describe("config (minimal base env)", () => { beforeEach(() => { - const { NAMESPACE, ENSINDEXER_URL, ENSRAINBOW_URL, DATABASE_URL, DATABASE_SCHEMA, RPC_URL_1 } = - BASE_ENV; + const { NAMESPACE, ENSRAINBOW_URL, DATABASE_URL, DATABASE_SCHEMA, RPC_URL_1 } = BASE_ENV; stubEnv({ NAMESPACE, - ENSINDEXER_URL, ENSRAINBOW_URL, DATABASE_URL, DATABASE_SCHEMA, diff --git a/apps/ensindexer/src/config/environment.ts b/apps/ensindexer/src/config/environment.ts index 33092d21fb..db7338b80a 100644 --- a/apps/ensindexer/src/config/environment.ts +++ b/apps/ensindexer/src/config/environment.ts @@ -1,8 +1,4 @@ -import type { - EnsIndexerDatabaseEnvironment, - EnsIndexerUrlEnvironment, - RpcEnvironment, -} from "@ensnode/ensnode-sdk/internal"; +import type { EnsIndexerDatabaseEnvironment, RpcEnvironment } from "@ensnode/ensnode-sdk/internal"; /** * Represents the raw, unvalidated environment variables for the ENSIndexer application. @@ -12,7 +8,6 @@ import type { * mapped/parsed into a structured configuration object like `ENSIndexerConfig`. */ export type ENSIndexerEnvironment = EnsIndexerDatabaseEnvironment & - EnsIndexerUrlEnvironment & RpcEnvironment & { NAMESPACE?: string; PLUGINS?: string; diff --git a/apps/ensindexer/src/config/serialize.ts b/apps/ensindexer/src/config/serialize.ts index 3324d9d2dd..56ab5c6aaa 100644 --- a/apps/ensindexer/src/config/serialize.ts +++ b/apps/ensindexer/src/config/serialize.ts @@ -46,7 +46,6 @@ export function serializeRedactedENSIndexerConfig( return { databaseSchemaName: redactedConfig.databaseSchemaName, databaseUrl: redactedConfig.databaseUrl, - ensIndexerUrl: serializeUrl(redactedConfig.ensIndexerUrl), ensRainbowUrl: serializeUrl(redactedConfig.ensRainbowUrl), labelSet: redactedConfig.labelSet, globalBlockrange: redactedConfig.globalBlockrange, diff --git a/apps/ensindexer/src/config/serialized-types.ts b/apps/ensindexer/src/config/serialized-types.ts index d3aed44e67..4fb0a5bf60 100644 --- a/apps/ensindexer/src/config/serialized-types.ts +++ b/apps/ensindexer/src/config/serialized-types.ts @@ -25,15 +25,7 @@ export interface SerializedRpcConfig extends Omit { - /** - * Serialized representation of {@link ENSIndexerConfig.ensIndexerUrl}. - */ - ensIndexerUrl: UrlString; - + extends Omit { /** * Serialized representation of {@link ENSIndexerConfig.ensRainbowUrl}. */ diff --git a/apps/ensindexer/src/config/types.ts b/apps/ensindexer/src/config/types.ts index 279ec90184..d086591743 100644 --- a/apps/ensindexer/src/config/types.ts +++ b/apps/ensindexer/src/config/types.ts @@ -98,22 +98,6 @@ export interface EnsIndexerConfig { */ databaseUrl: DatabaseUrl; - /** - * The "primary" ENSIndexer service URL - * This must be an instance of ENSIndexer using either `ponder start` - * or `ponder dev`, and not `ponder serve`. - * This URL is used to read Ponder's internal indexing state using - * the `/status` and `/metrics` endpoints that are served directly by Ponder - * within the specified ENSIndexer. - * For ENSIndexer instances started using `ponder start` or `ponder dev`, - * this should be configured so that ENSIndexer refers back to itself, ex: - * http://localhost:{port}. For ENSIndexer instances started using - * `ponder serve`, this should be set to the hostname of - * the related ENSIndexer instance started using `ponder start` or - * `ponder dev` that is writing to the same ENSDb. - */ - ensIndexerUrl: URL; - /** * Constrains the global blockrange for indexing, useful for testing purposes. * diff --git a/apps/ensindexer/src/lib/__test__/mockConfig.ts b/apps/ensindexer/src/lib/__test__/mockConfig.ts index 8bd2acb4c4..5640cd6e6b 100644 --- a/apps/ensindexer/src/lib/__test__/mockConfig.ts +++ b/apps/ensindexer/src/lib/__test__/mockConfig.ts @@ -12,7 +12,6 @@ const _defaultMockConfig = buildConfigFromEnvironment({ DATABASE_SCHEMA: "test_schema", NAMESPACE: "mainnet", PLUGINS: "subgraph", - ENSINDEXER_URL: "http://localhost:42069", ENSRAINBOW_URL: "http://localhost:3223", LABEL_SET_ID: "ens-test-env", LABEL_SET_VERSION: "0", diff --git a/apps/ensindexer/src/lib/local-ponder-client.ts b/apps/ensindexer/src/lib/local-ponder-client.ts index 139d85f221..e30fc6e477 100644 --- a/apps/ensindexer/src/lib/local-ponder-client.ts +++ b/apps/ensindexer/src/lib/local-ponder-client.ts @@ -16,7 +16,6 @@ const pluginsAllDatasourceNames = getPluginsAllDatasourceNames(config.plugins); const indexedBlockranges = buildIndexedBlockranges(config.namespace, pluginsAllDatasourceNames); export const localPonderClient = new LocalPonderClient( - config.ensIndexerUrl, config.indexedChainIds, indexedBlockranges, publicClients, diff --git a/docker-compose.yml b/docker-compose.yml index 7239f80665..fe37eb55e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,6 @@ services: DATABASE_URL: postgresql://postgres:password@postgres:5432/postgres DATABASE_SCHEMA: ensindexer_0 ENSRAINBOW_URL: http://ensrainbow:3223 - ENSINDEXER_URL: http://ensindexer:42069 env_file: # NOTE: must define apps/ensindexer/.env.local (see apps/ensindexer/.env.local.example) # Copy .env.local.example to .env.local and configure all required values diff --git a/packages/ensnode-sdk/src/shared/config/environments.ts b/packages/ensnode-sdk/src/shared/config/environments.ts index 02f24b10ec..0a3a9c8091 100644 --- a/packages/ensnode-sdk/src/shared/config/environments.ts +++ b/packages/ensnode-sdk/src/shared/config/environments.ts @@ -26,13 +26,6 @@ export interface RpcEnvironment { RPC_AUTO_GEN_MODE?: string; } -/** - * Environment variables for ENSIndexer URL configuration. - */ -export interface EnsIndexerUrlEnvironment { - ENSINDEXER_URL?: string; -} - /** * Environment variables for port configuration. */ diff --git a/packages/ensnode-sdk/src/shared/config/types.ts b/packages/ensnode-sdk/src/shared/config/types.ts index 1248870a79..d0b8f54a24 100644 --- a/packages/ensnode-sdk/src/shared/config/types.ts +++ b/packages/ensnode-sdk/src/shared/config/types.ts @@ -4,7 +4,6 @@ import type { UrlString } from "../serialized-types"; import type { ChainId } from "../types"; import type { DatabaseSchemaNameSchema, - EnsIndexerUrlSchema, PortNumberSchema, TheGraphApiKeySchema, } from "./zod-schemas"; @@ -47,7 +46,6 @@ export type RpcConfigs = Map; export type DatabaseUrl = UrlString; export type DatabaseSchemaName = z.infer; -export type EnsIndexerUrl = z.infer; export type TheGraphApiKey = z.infer; export type PortNumber = z.infer; diff --git a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts index 91c9b9b83c..3d9db6de69 100644 --- a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts +++ b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts @@ -51,8 +51,6 @@ export const RpcConfigsSchema = z return rpcConfigs; }); -export const EnsIndexerUrlSchema = makeUrlSchema("ENSINDEXER_URL"); - export const ENSNamespaceSchema = z.enum(ENSNamespaceIds, { error: ({ input }) => `Invalid NAMESPACE. Got '${input}', but supported ENS namespaces are: ${Object.keys(ENSNamespaceIds).join(", ")}`, diff --git a/packages/integration-test-env/src/orchestrator.ts b/packages/integration-test-env/src/orchestrator.ts index 29bd5a9ada..6596792612 100644 --- a/packages/integration-test-env/src/orchestrator.ts +++ b/packages/integration-test-env/src/orchestrator.ts @@ -317,7 +317,6 @@ async function main() { await waitForHealth(`http://localhost:${ENSRAINBOW_PORT}/health`, 30_000, "ENSRainbow"); // Phase 3: Start ENSIndexer - const ENSINDEXER_URL = `http://localhost:${ENSINDEXER_PORT}`; const ENSINDEXER_SCHEMA_NAME = "ensindexer_0"; log("Starting ENSIndexer..."); @@ -331,7 +330,6 @@ async function main() { DATABASE_SCHEMA: ENSINDEXER_SCHEMA_NAME, PLUGINS: "ensv2,protocol-acceleration", ENSRAINBOW_URL, - ENSINDEXER_URL, LABEL_SET_ID, LABEL_SET_VERSION, }, diff --git a/packages/ponder-sdk/src/deserialize/ponder-app-context.ts b/packages/ponder-sdk/src/deserialize/ponder-app-context.ts index 3a53ce1a0b..2939185e1f 100644 --- a/packages/ponder-sdk/src/deserialize/ponder-app-context.ts +++ b/packages/ponder-sdk/src/deserialize/ponder-app-context.ts @@ -17,12 +17,22 @@ import { } from "../ponder-app-context"; import type { Unvalidated } from "./utils"; +/** + * Schema representing a valid port number for the Ponder app to listen on. + */ +export const schemaPortNumber = z + .number({ error: "Port must be a number." }) + .int({ error: "Port must be an integer." }) + .min(1, { error: "Port must be greater than or equal to 1." }) + .max(65535, { error: "Port must be less than or equal to 65535." }); + /** * Type representing the "raw" context of a local Ponder app. */ const schemaRawPonderAppContext = z.object({ options: z.object({ - command: z.string(), + command: z.enum(PonderAppCommands), + port: schemaPortNumber, }), }); @@ -36,6 +46,7 @@ export type RawPonderAppContext = z.infer; */ const schemaPonderAppContext = z.object({ command: z.enum(PonderAppCommands), + localPonderAppUrl: z.instanceof(URL, { error: "localPonderAppUrl must be a valid URL." }), }); /** @@ -50,6 +61,7 @@ function buildUnvalidatedPonderAppContext( ): Unvalidated { return { command: rawPonderAppContext.options.command as Unvalidated, + localPonderAppUrl: new URL(`http://localhost:${rawPonderAppContext.options.port}`), }; } diff --git a/packages/ponder-sdk/src/local-ponder-client.mock.ts b/packages/ponder-sdk/src/local-ponder-client.mock.ts index 90e49c315b..a1e6daa88e 100644 --- a/packages/ponder-sdk/src/local-ponder-client.mock.ts +++ b/packages/ponder-sdk/src/local-ponder-client.mock.ts @@ -14,7 +14,7 @@ export function createLocalPonderClientMock(overrides?: { indexedChainIds?: Set; indexedBlockranges?: Map; cachedPublicClients?: Record; - ponderAppContext?: PonderAppContext; + ponderAppContext?: Pick; }): LocalPonderClient { const indexedChainIds = overrides?.indexedChainIds ?? new Set([chainIds.Mainnet, chainIds.Optimism]); @@ -35,14 +35,12 @@ export function createLocalPonderClientMock(overrides?: { [`${chainIds.Base}`]: {} as CachedPublicClient, } satisfies Record); - const ponderAppContext = - overrides?.ponderAppContext ?? - ({ - command: PonderAppCommands.Start, - } satisfies PonderAppContext); + const ponderAppContext = { + command: overrides?.ponderAppContext?.command ?? PonderAppCommands.Start, + localPonderAppUrl: new URL("http://localhost:3000"), + } satisfies PonderAppContext; return new LocalPonderClient( - new URL("http://localhost:3000"), indexedChainIds, indexedBlockranges, cachedPublicClients, diff --git a/packages/ponder-sdk/src/local-ponder-client.ts b/packages/ponder-sdk/src/local-ponder-client.ts index 35a1b246a4..0e8e786c5e 100644 --- a/packages/ponder-sdk/src/local-ponder-client.ts +++ b/packages/ponder-sdk/src/local-ponder-client.ts @@ -77,7 +77,6 @@ export class LocalPonderClient extends PonderClient { private ponderAppContext: PonderAppContext; /** - * @param localPonderAppUrl URL of the local Ponder app to connect to. * @param indexedChainIds Configured indexed chain IDs which are used to validate and filter the Ponder app metadata to only include entries for indexed chains. * @param indexedBlockranges Configured indexing blockrange for each indexed chain. * @param ponderPublicClients All cached public clients provided by the local Ponder app @@ -85,13 +84,12 @@ export class LocalPonderClient extends PonderClient { * @param ponderAppContext The internal context of the local Ponder app. */ constructor( - localPonderAppUrl: URL, indexedChainIds: Set, indexedBlockranges: Map, ponderPublicClients: Record, ponderAppContext: PonderAppContext, ) { - super(localPonderAppUrl); + super(ponderAppContext.localPonderAppUrl); this.indexedChainIds = indexedChainIds; diff --git a/packages/ponder-sdk/src/ponder-app-context.ts b/packages/ponder-sdk/src/ponder-app-context.ts index 9964efdee9..0afb487455 100644 --- a/packages/ponder-sdk/src/ponder-app-context.ts +++ b/packages/ponder-sdk/src/ponder-app-context.ts @@ -20,4 +20,9 @@ export interface PonderAppContext { * Command used to start the Ponder app. */ command: PonderAppCommand; + + /** + * URL of the local Ponder app. + */ + localPonderAppUrl: URL; } diff --git a/terraform/modules/ensindexer/main.tf b/terraform/modules/ensindexer/main.tf index 847645f96f..7b9778a571 100644 --- a/terraform/modules/ensindexer/main.tf +++ b/terraform/modules/ensindexer/main.tf @@ -34,8 +34,7 @@ resource "render_web_service" "ensindexer" { "LABEL_SET_VERSION" = { value = var.ensindexer_label_set_version }, "PLUGINS" = { value = var.plugins }, "NAMESPACE" = { value = var.namespace }, - "SUBGRAPH_COMPAT" = { value = var.subgraph_compat }, - "ENSINDEXER_URL" = { value = "http://ensindexer-${var.ensnode_indexer_type}:10000" } + "SUBGRAPH_COMPAT" = { value = var.subgraph_compat } }) # See https://render.com/docs/custom-domains