From b1fd060a2a60d4435a66c5ef2205e948cf7e4085 Mon Sep 17 00:00:00 2001 From: Kyle Scott Date: Tue, 7 Nov 2023 15:42:43 -0500 Subject: [PATCH 1/3] start to build ponder app type --- packages/core/src/build/ponderApp.test-d.ts | 75 +++++++++++++++++++++ packages/core/src/build/ponderApp.ts | 74 ++++++++++++++++++++ packages/core/src/config/config.test-d.ts | 4 +- packages/core/src/config/config.ts | 16 ++--- 4 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 packages/core/src/build/ponderApp.test-d.ts create mode 100644 packages/core/src/build/ponderApp.ts diff --git a/packages/core/src/build/ponderApp.test-d.ts b/packages/core/src/build/ponderApp.test-d.ts new file mode 100644 index 000000000..88d8ba0e9 --- /dev/null +++ b/packages/core/src/build/ponderApp.test-d.ts @@ -0,0 +1,75 @@ +import { ParseAbi } from "abitype"; +import { assertType, test } from "vitest"; + +import { PonderApp } from "./ponderApp"; + +type OneAbi = ParseAbi< + ["event Event0(bytes32 indexed arg3)", "event Event1(bytes32 indexed)"] +>; +type TwoAbi = ParseAbi<["event Event(bytes32 indexed)", "event Event()"]>; + +test("PonderApp non intersecting event names", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [{ name: "One"; network: any; abi: OneAbi }]; + }>; + + type name = Parameters[0]; + // ^? + + assertType("" as "One:Event0" | "One:Event1"); +}); + +test("PonderApp intersecting event names", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [{ name: "Two"; network: any; abi: TwoAbi }]; + }>; + + type name = Parameters[0]; + // ^? + + assertType("" as "Two:Event(bytes32 indexed)" | "Two:Event()"); +}); + +test("PonderApp multiple contracts", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [ + { name: "One"; network: any; abi: OneAbi }, + { name: "Two"; network: any; abi: TwoAbi } + ]; + }>; + + // Events should only correspond to their contract + type name = Exclude< + // ^? + Parameters[0], + "One:Event0" | "One:Event1" | "Two:Event(bytes32 indexed)" | "Two:Event()" + >; + + assertType("" as name); +}); + +test("PonderApp event type"), + () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [{ name: "One"; network: any; abi: OneAbi }]; + }>; + + type name = Parameters[1]>[0]["event"]["name"]; + // ^? + + assertType("" as "Event0" | "Event1"); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (({}) as p).on("One:Event1", ({ event }) => {}); + // ^? + }; diff --git a/packages/core/src/build/ponderApp.ts b/packages/core/src/build/ponderApp.ts new file mode 100644 index 000000000..91f06f8d0 --- /dev/null +++ b/packages/core/src/build/ponderApp.ts @@ -0,0 +1,74 @@ +import { Abi, GetEventArgs } from "viem"; + +import { + FilterEvents, + RecoverAbiEvent, + ResolvedConfig, + SafeEventNames, +} from "@/config/config"; +import { Block } from "@/types/block"; +import { Log } from "@/types/log"; +import { Transaction } from "@/types/transaction"; + +export type Name = + `${TContract["name"]}:${SafeEventNames< + FilterEvents, + FilterEvents + >[number]}`; + +export type Names = + TContracts extends readonly [ + infer First extends ResolvedConfig["contracts"][number], + ...infer Rest extends ResolvedConfig["contracts"] + ] + ? [Name, ...Names] + : []; + +type RecoverContract< + TContracts extends ResolvedConfig["contracts"], + TName extends string +> = TContracts extends readonly [ + infer First extends ResolvedConfig["contracts"][number], + ...infer Rest extends ResolvedConfig["contracts"] +] + ? First["name"] extends TName + ? First + : RecoverContract + : never; + +export type PonderApp = { + on: [number]>( + name: TName, + indexingFunction: ({ + event, + context, + }: { + event: { + name: TName extends `${string}:${infer EventName}` ? EventName : string; + params: GetEventArgs< + Abi, + string, + { + EnableUnion: false; + IndexedOnly: false; + Required: true; + }, + TName extends `${infer ContractName}:${infer EventName}` + ? RecoverAbiEvent< + RecoverContract extends { + abi: infer _abi extends Abi; + } + ? FilterEvents<_abi> + : never, + EventName + > + : never + >; + log: Log; + block: Block; + transaction: Transaction; + }; + context: any; + }) => Promise | void + ) => void; +}; diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts index c2a5b11ad..c64d04840 100644 --- a/packages/core/src/config/config.test-d.ts +++ b/packages/core/src/config/config.test-d.ts @@ -124,11 +124,11 @@ test("RecoverAbiEvent", () => { type a = RecoverAbiEvent< // ^? FilterEvents, + "Approve", SafeEventNames< FilterEvents, FilterEvents - >, - "Approve" + > >; assertType(abiSimple[1]); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index c73e5ba9c..ae1aa506a 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -46,8 +46,8 @@ export type SafeEventNames< export type RecoverAbiEvent< TAbi extends readonly AbiEvent[], - TSafeNames extends readonly string[], - TSafeName extends string + TSafeName extends string, + TSafeNames extends readonly string[] = SafeEventNames > = TAbi extends readonly [ infer FirstAbi, ...infer RestAbi extends readonly AbiEvent[] @@ -58,9 +58,9 @@ export type RecoverAbiEvent< ] ? FirstName extends TSafeName ? FirstAbi - : RecoverAbiEvent - : [] - : []; + : RecoverAbiEvent + : never + : never; type ContractRequired< TNetworkNames extends string, @@ -133,8 +133,8 @@ type ContractFilter< }, RecoverAbiEvent< TAbi, - SafeEventNames, FilterEvents>, - TEventName + TEventName, + SafeEventNames, FilterEvents> > extends infer _abiEvent extends AbiEvent ? _abiEvent : AbiEvent @@ -198,7 +198,7 @@ export type ResolvedConfig = { /** List of blockchain networks. */ networks: readonly Network[]; /** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */ - contracts?: readonly Contract[]; + contracts: readonly Contract[]; /** Configuration for Ponder internals. */ options?: Option; }; From 55521de23a492d3c7545b27566abebaed8ddd7b1 Mon Sep 17 00:00:00 2001 From: Kyle Scott Date: Tue, 7 Nov 2023 18:56:48 -0500 Subject: [PATCH 2/3] Add to the PonderApp type --- packages/core/src/Ponder.ts | 4 +- packages/core/src/build/ponderApp.test-d.ts | 94 ++++++++++++++- packages/core/src/build/ponderApp.ts | 127 +++++++++++++++++--- packages/core/src/config/config.test-d.ts | 22 ++-- packages/core/src/config/config.ts | 69 ++++++----- packages/core/src/config/database.ts | 6 +- packages/core/src/config/networks.ts | 4 +- packages/core/src/config/options.ts | 4 +- packages/core/src/config/sources.test.ts | 10 +- packages/core/src/config/sources.ts | 12 +- packages/core/src/indexing/ponderActions.ts | 42 ++++++- 11 files changed, 304 insertions(+), 90 deletions(-) diff --git a/packages/core/src/Ponder.ts b/packages/core/src/Ponder.ts index c1c2382e9..1ec544680 100644 --- a/packages/core/src/Ponder.ts +++ b/packages/core/src/Ponder.ts @@ -3,7 +3,7 @@ import process from "node:process"; import { BuildService } from "@/build/service"; import { CodegenService } from "@/codegen/service"; -import { type ResolvedConfig } from "@/config/config"; +import { type Config } from "@/config/config"; import { buildDatabase } from "@/config/database"; import { type Network, buildNetwork } from "@/config/networks"; import { type Options } from "@/config/options"; @@ -64,7 +64,7 @@ export class Ponder { userStore, }: { options: Options; - config: ResolvedConfig; + config: Config; // These options are only used for testing. eventStore?: EventStore; userStore?: UserStore; diff --git a/packages/core/src/build/ponderApp.test-d.ts b/packages/core/src/build/ponderApp.test-d.ts index 88d8ba0e9..a1d70db9e 100644 --- a/packages/core/src/build/ponderApp.test-d.ts +++ b/packages/core/src/build/ponderApp.test-d.ts @@ -1,13 +1,40 @@ -import { ParseAbi } from "abitype"; +import { AbiEvent, ParseAbi } from "abitype"; import { assertType, test } from "vitest"; -import { PonderApp } from "./ponderApp"; +import { ExtractAddress, ExtractAllAddresses, PonderApp } from "./ponderApp"; type OneAbi = ParseAbi< ["event Event0(bytes32 indexed arg3)", "event Event1(bytes32 indexed)"] >; type TwoAbi = ParseAbi<["event Event(bytes32 indexed)", "event Event()"]>; +test("ExtractAddress", () => { + type a = ExtractAddress<{ address: "0x2" }>; + // ^? + assertType("" as "0x2"); + + type b = ExtractAddress<{ + // ^? + factory: { address: "0x2"; event: AbiEvent; parameter: string }; + }>; + assertType("" as never); +}); + +test("ExtractAllAddress", () => { + type a = ExtractAllAddresses< + // ^? + [ + { name: "optimism"; address: "0x2" }, + { + name: "optimism"; + factory: { address: "0x2"; event: AbiEvent; parameter: string }; + } + ] + >[never]; + // ^? + assertType("" as "0x2"); +}); + test("PonderApp non intersecting event names", () => { type p = PonderApp<{ // ^? @@ -73,3 +100,66 @@ test("PonderApp event type"), (({}) as p).on("One:Event1", ({ event }) => {}); // ^? }; + +test("PonderApp context network type", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [ + { + name: "One"; + network: [{ name: "mainnet" }, { name: "optimism" }]; + abi: OneAbi; + } + ]; + }>; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (({}) as p).on("One:Event1", ({ context }) => {}); + // ^? +}); + +test("PonderApp context client type", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [ + { + name: "One"; + network: [{ name: "mainnet" }, { name: "optimism" }]; + abi: OneAbi; + } + ]; + }>; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (({}) as p).on("One:Event1", ({ context: { client } }) => {}); + // ^? +}); + +test("PonderApp context contracts type", () => { + type p = PonderApp<{ + // ^? + networks: any; + contracts: readonly [ + { + name: "One"; + network: [{ name: "mainnet"; address: "0x1" }, { name: "optimism" }]; + abi: OneAbi; + address: "0x2"; + startBlock: 1; + endBlock: 2; + } + ]; + }>; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (({}) as p).on("One:Event1", ({ context: { contracts } }) => {}); + // ^? +}); diff --git a/packages/core/src/build/ponderApp.ts b/packages/core/src/build/ponderApp.ts index 91f06f8d0..7b1ae09f4 100644 --- a/packages/core/src/build/ponderApp.ts +++ b/packages/core/src/build/ponderApp.ts @@ -1,42 +1,126 @@ +import { AbiEvent } from "abitype"; import { Abi, GetEventArgs } from "viem"; import { - FilterEvents, + Config, + ContractFilter, + ContractRequired, + FilterAbiEvents, RecoverAbiEvent, - ResolvedConfig, SafeEventNames, } from "@/config/config"; +import { ReadOnlyClient } from "@/indexing/ponderActions"; import { Block } from "@/types/block"; import { Log } from "@/types/log"; import { Transaction } from "@/types/transaction"; -export type Name = +/** "{ContractName}:{EventName}". */ +export type Name = `${TContract["name"]}:${SafeEventNames< - FilterEvents, - FilterEvents + FilterAbiEvents >[number]}`; -export type Names = +/** All possible names for a list of contracts. */ +export type Names = TContracts extends readonly [ - infer First extends ResolvedConfig["contracts"][number], - ...infer Rest extends ResolvedConfig["contracts"] + infer First extends Config["contracts"][number], + ...infer Rest extends Config["contracts"] ] ? [Name, ...Names] : []; -type RecoverContract< - TContracts extends ResolvedConfig["contracts"], +/** Recover the `contract` element at the index where {@link TName} is equal to {@link TContracts}[index]. */ +export type RecoverContract< + TContracts extends Config["contracts"], TName extends string > = TContracts extends readonly [ - infer First extends ResolvedConfig["contracts"][number], - ...infer Rest extends ResolvedConfig["contracts"] + infer First extends Config["contracts"][number], + ...infer Rest extends Config["contracts"] ] ? First["name"] extends TName ? First : RecoverContract : never; -export type PonderApp = { +type ContractNetworkOverrides = ContractRequired< + string, + readonly AbiEvent[], + string +>["network"]; + +/** Extract the address type from a Contract. */ +export type ExtractAddress< + TContract extends + | ContractNetworkOverrides + | ContractFilter +> = Extract["address"]; + +/** Extract the startBlock type from a Contract. */ +export type ExtractStartBlock< + TContract extends + | ContractNetworkOverrides + | ContractFilter +> = Extract["startBlock"]; + +/** Extract the endBlock type from a Contract. */ +export type ExtractEndBlock< + TContract extends + | ContractNetworkOverrides + | ContractFilter +> = Extract["endBlock"]; + +/** Extract all address from a list of Contracts. */ +export type ExtractAllAddresses = + TContracts extends readonly [ + infer First extends ContractNetworkOverrides[number], + ...infer Rest extends ContractNetworkOverrides + ] + ? readonly [ExtractAddress, ...ExtractAllAddresses] + : []; + +/** Extract all startBlocks from a list of Contracts. */ +export type ExtractAllStartBlocks = + TContracts extends readonly [ + infer First extends ContractNetworkOverrides[number], + ...infer Rest extends ContractNetworkOverrides + ] + ? readonly [ExtractStartBlock, ...ExtractAllStartBlocks] + : []; + +/** Extract all endBlocks from a list of Contracts. */ +export type ExtractAllEndBlocks = + TContracts extends readonly [ + infer First extends ContractNetworkOverrides[number], + ...infer Rest extends ContractNetworkOverrides + ] + ? readonly [ExtractEndBlock, ...ExtractAllEndBlocks] + : []; + +/** Transform Contracts into the appropriate type for PonderApp. */ +type AppContracts = + TContracts extends readonly [ + infer First extends Config["contracts"][number], + ...infer Rest extends Config["contracts"] + ] + ? Record< + First["name"], + { + abi: First["abi"]; + address: + | ExtractAddress + | ExtractAllAddresses[number]; + startBlock: + | ExtractStartBlock + | ExtractAllStartBlocks[number]; + endBlock: + | ExtractEndBlock + | ExtractAllEndBlocks[number]; + } + > & + AppContracts + : {}; + +export type PonderApp = { on: [number]>( name: TName, indexingFunction: ({ @@ -58,7 +142,7 @@ export type PonderApp = { RecoverContract extends { abi: infer _abi extends Abi; } - ? FilterEvents<_abi> + ? FilterAbiEvents<_abi> : never, EventName > @@ -68,7 +152,20 @@ export type PonderApp = { block: Block; transaction: Transaction; }; - context: any; + context: { + contracts: AppContracts; + network: { + chainId: number; + name: TName extends `${infer ContractName}:${string}` + ? RecoverContract< + TConfig["contracts"], + ContractName + >["network"][number]["name"] + : never; + }; + client: ReadOnlyClient; + models: any; // use ts-schema to infer types + }; }) => Promise | void ) => void; }; diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts index c64d04840..b15528585 100644 --- a/packages/core/src/config/config.test-d.ts +++ b/packages/core/src/config/config.test-d.ts @@ -2,10 +2,10 @@ import { http } from "viem"; import { assertType, test } from "vitest"; import { + Config, createConfig, - FilterEvents, + FilterAbiEvents, RecoverAbiEvent, - ResolvedConfig, SafeEventNames, } from "./config"; @@ -84,7 +84,7 @@ export const abiWithSameEvent = [ ] as const; test("filter events", () => { - type t = FilterEvents; + type t = FilterAbiEvents; // ^? assertType([ @@ -97,15 +97,13 @@ test("filter events", () => { test("safe event names", () => { type a = SafeEventNames< // ^? - FilterEvents, - FilterEvents + FilterAbiEvents >; assertType(["Approve", "Transfer"] as const); type b = SafeEventNames< // ^? - FilterEvents, - FilterEvents + FilterAbiEvents >; assertType([ "Approve(address indexed from, address indexed to, uint256 amount)", @@ -115,7 +113,7 @@ test("safe event names", () => { }); test("ResolvedConfig default values", () => { - type a = NonNullable[number]["filter"]; + type a = NonNullable[number]["filter"]; // ^? assertType({} as { event: string[] } | { event: string } | undefined); }); @@ -123,12 +121,8 @@ test("ResolvedConfig default values", () => { test("RecoverAbiEvent", () => { type a = RecoverAbiEvent< // ^? - FilterEvents, - "Approve", - SafeEventNames< - FilterEvents, - FilterEvents - > + FilterAbiEvents, + "Approve" >; assertType(abiSimple[1]); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index ae1aa506a..7c918955f 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1,20 +1,17 @@ import type { Abi, AbiEvent, FormatAbiItem } from "abitype"; import type { GetEventArgs, Transport } from "viem"; -/** - * Keep only AbiEvents from an Abi - */ -export type FilterEvents = T extends readonly [ +export type FilterAbiEvents = T extends readonly [ infer First, ...infer Rest extends Abi ] ? First extends AbiEvent - ? readonly [First, ...FilterEvents] - : FilterEvents + ? readonly [First, ...FilterAbiEvents] + : FilterAbiEvents : []; /** - * Remove TElement from TArr + * Remove TElement from TArr. */ type FilterElement< TElement, @@ -26,17 +23,17 @@ type FilterElement< : []; /** - * Return an array of safe event names that handle multiple events with the same name + * Return an array of safe event names that handle event overridding. */ export type SafeEventNames< TAbi extends readonly AbiEvent[], - TArr extends readonly AbiEvent[] + TArr extends readonly AbiEvent[] = TAbi > = TAbi extends readonly [ infer First extends AbiEvent, ...infer Rest extends readonly AbiEvent[] ] ? First["name"] extends FilterElement[number]["name"] - ? // Name collisions exist, format long name + ? // Overriding occurs, use full name FormatAbiItem extends `event ${infer LongEvent extends string}` ? readonly [LongEvent, ...SafeEventNames] : never @@ -44,10 +41,13 @@ export type SafeEventNames< readonly [First["name"], ...SafeEventNames] : []; +/** + * Recover the element from {@link TAbi} at the index where {@link TSafeName} is equal to {@link TSafeNames}[index]. + */ export type RecoverAbiEvent< TAbi extends readonly AbiEvent[], TSafeName extends string, - TSafeNames extends readonly string[] = SafeEventNames + TSafeNames extends readonly string[] = SafeEventNames > = TAbi extends readonly [ infer FirstAbi, ...infer RestAbi extends readonly AbiEvent[] @@ -62,29 +62,32 @@ export type RecoverAbiEvent< : never : never; -type ContractRequired< +/** Required fields for a contract. */ +export type ContractRequired< TNetworkNames extends string, TAbi extends readonly AbiEvent[], TEventName extends string > = { /** Contract name. Must be unique across `contracts` and `filters`. */ name: string; + /** Contract application byte interface. */ + abi: Abi; /** * Network that this contract is deployed to. Must match a network name in `networks`. - * Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa. + * Any filter information overrides the values in the higher level "contracts" property. + * Factories cannot override an address and vice versa. */ network: readonly ({ name: TNetworkNames } & Partial< ContractFilter >)[]; - abi: Abi; }; -type ContractFilter< +/** Fields for a contract used to filter down which events indexed. */ +export type ContractFilter< TAbi extends readonly AbiEvent[], TEventName extends string > = ( | { - /** Contract address. */ address?: `0x${string}` | readonly `0x${string}`[]; } | { @@ -112,17 +115,11 @@ type ContractFilter< | { event: string; args?: GetEventArgs } : | { - event: readonly SafeEventNames< - FilterEvents, - FilterEvents - >[number][]; + event: readonly SafeEventNames>[number][]; args?: never; } | { - event: SafeEventNames< - FilterEvents, - FilterEvents - >[number]; + event: SafeEventNames>[number]; args?: GetEventArgs< Abi, string, @@ -134,7 +131,7 @@ type ContractFilter< RecoverAbiEvent< TAbi, TEventName, - SafeEventNames, FilterEvents> + SafeEventNames> > extends infer _abiEvent extends AbiEvent ? _abiEvent : AbiEvent @@ -142,6 +139,14 @@ type ContractFilter< }; }; +/** Contract in Ponder config. */ +export type Contract< + TNetworkNames extends string, + TAbi extends readonly AbiEvent[], + TEventName extends string +> = ContractRequired & + ContractFilter; + type Database = | { kind: "sqlite"; @@ -154,7 +159,8 @@ type Database = connectionString?: string; }; -type Network = { +/** Network in Ponder config. */ +export type Network = { /** Network name. Must be unique across all networks. */ name: string; /** Chain ID of the network. */ @@ -180,19 +186,12 @@ type Network = { maxRpcRequestConcurrency?: number; }; -type Contract< - TNetworkNames extends string, - TAbi extends readonly AbiEvent[], - TEventName extends string -> = ContractRequired & - ContractFilter; - type Option = { /** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */ maxHealthcheckDuration?: number; }; -export type ResolvedConfig = { +export type Config = { /** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */ database?: Database; /** List of blockchain networks. */ @@ -213,7 +212,7 @@ export const createConfig = < contracts: { [key in keyof TConfig["contracts"] & number]: Contract< TConfig["networks"][number]["name"], - FilterEvents, + FilterAbiEvents, TConfig["contracts"][key]["filter"] extends { event: infer _event extends string; } diff --git a/packages/core/src/config/database.ts b/packages/core/src/config/database.ts index 2366bfbeb..c140fe425 100644 --- a/packages/core/src/config/database.ts +++ b/packages/core/src/config/database.ts @@ -2,7 +2,7 @@ import Sqlite from "better-sqlite3"; import path from "node:path"; import pg, { Client, DatabaseError, Pool } from "pg"; -import type { ResolvedConfig } from "@/config/config"; +import type { Config } from "@/config/config"; import type { Options } from "@/config/options"; import { PostgresError } from "@/errors/postgres"; import { SqliteError } from "@/errors/sqlite"; @@ -91,9 +91,9 @@ export const buildDatabase = ({ config, }: { options: Options; - config: ResolvedConfig; + config: Config; }): Database => { - let resolvedDatabaseConfig: NonNullable; + let resolvedDatabaseConfig: NonNullable; const defaultSqliteFilename = path.join(options.ponderDir, "cache.db"); diff --git a/packages/core/src/config/networks.ts b/packages/core/src/config/networks.ts index 8ecd9bd61..c3552e8f2 100644 --- a/packages/core/src/config/networks.ts +++ b/packages/core/src/config/networks.ts @@ -1,7 +1,7 @@ import { type Client, type PublicClient, createPublicClient } from "viem"; import * as chains from "viem/chains"; -import type { ResolvedConfig } from "@/config/config"; +import type { Config } from "@/config/config"; import type { Common } from "@/Ponder"; export type Network = { @@ -18,7 +18,7 @@ export function buildNetwork({ network, common, }: { - network: ResolvedConfig["networks"][0]; + network: Config["networks"][0]; common: Common; }) { const { name, chainId, transport } = network; diff --git a/packages/core/src/config/options.ts b/packages/core/src/config/options.ts index d67114a91..1a62c7598 100644 --- a/packages/core/src/config/options.ts +++ b/packages/core/src/config/options.ts @@ -3,7 +3,7 @@ import type { LevelWithSilent } from "pino"; import type { CliOptions } from "@/bin/ponder"; -import type { ResolvedConfig } from "./config"; +import type { Config } from "./config"; export type Options = { configFile: string; @@ -29,7 +29,7 @@ export const buildOptions = ({ configOptions = {}, }: { cliOptions: CliOptions; - configOptions?: ResolvedConfig["options"]; + configOptions?: Config["options"]; }): Options => { const railwayHealthcheckTimeout = process.env.RAILWAY_HEALTHCHECK_TIMEOUT_SEC ? Math.max(Number(process.env.RAILWAY_HEALTHCHECK_TIMEOUT_SEC) - 5, 0) // Add 5 seconds of buffer. diff --git a/packages/core/src/config/sources.test.ts b/packages/core/src/config/sources.test.ts index 161449038..2ca45008d 100644 --- a/packages/core/src/config/sources.test.ts +++ b/packages/core/src/config/sources.test.ts @@ -1,7 +1,7 @@ import { http } from "viem"; import { expect, test } from "vitest"; -import { createConfig, ResolvedConfig } from "./config"; +import { Config, createConfig } from "./config"; import { abiSimple, abiWithSameEvent } from "./config.test-d"; import { buildSources } from "./sources"; @@ -27,7 +27,7 @@ test("buildSources() builds topics for multiple events", () => { maxBlockRange: 10, }, ], - }) as unknown as ResolvedConfig, + }) as unknown as Config, }); expect(sources[0].criteria.topics).toMatchObject([ @@ -65,7 +65,7 @@ test("buildSources() for duplicate event", () => { maxBlockRange: 10, }, ], - }) as unknown as ResolvedConfig, + }) as unknown as Config, }); expect(sources[0].criteria.topics).toMatchObject([ @@ -103,7 +103,7 @@ test("buildSources() builds topics for event with args", () => { maxBlockRange: 10, }, ], - }) as unknown as ResolvedConfig, + }) as unknown as Config, }); expect(sources[0].criteria.topics).toMatchObject([ @@ -140,7 +140,7 @@ test("buildSources() overrides default values with network values", () => { maxBlockRange: 10, }, ], - }) as unknown as ResolvedConfig, + }) as unknown as Config, }); expect(sources[0].criteria.address).toBe( diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts index 4afad74e4..092136113 100644 --- a/packages/core/src/config/sources.ts +++ b/packages/core/src/config/sources.ts @@ -11,7 +11,7 @@ import { import { toLowerCase } from "@/utils/lowercase"; import { AbiEvents, getEvents } from "./abi"; -import { ResolvedConfig } from "./config"; +import { Config } from "./config"; import { buildFactoryCriteria } from "./factories"; /** @@ -70,11 +70,7 @@ export const sourceIsLogFilter = (source: Source): source is LogFilter => export const sourceIsFactory = (source: Source): source is Factory => source.type === "factory"; -export const buildSources = ({ - config, -}: { - config: ResolvedConfig; -}): Source[] => { +export const buildSources = ({ config }: { config: Config }): Source[] => { const contracts = config.contracts ?? []; return contracts @@ -155,9 +151,7 @@ export const buildSources = ({ const buildTopics = ( abi: Abi, - filter: NonNullable< - NonNullable[number]["filter"] - > + filter: NonNullable[number]["filter"]> ): Topics => { if (Array.isArray(filter.event)) { // List of event signatures diff --git a/packages/core/src/indexing/ponderActions.ts b/packages/core/src/indexing/ponderActions.ts index 722e9d14c..b72adb751 100644 --- a/packages/core/src/indexing/ponderActions.ts +++ b/packages/core/src/indexing/ponderActions.ts @@ -11,6 +11,7 @@ import { GetStorageAtReturnType, MulticallParameters, MulticallReturnType, + PublicRpcSchema, ReadContractParameters, ReadContractReturnType, Transport, @@ -23,11 +24,50 @@ import { readContract as viemReadContract, } from "viem/actions"; +import { Prettify } from "@/types/utils"; + +export type PonderActions = { + getBalance: ( + args: Omit + ) => Promise; + getBytecode: ( + args: Omit + ) => Promise; + getStorageAt: ( + args: Omit + ) => Promise; + multicall: < + TContracts extends ContractFunctionConfig[], + TAllowFailure extends boolean = true + >( + args: Omit< + MulticallParameters, + "blockTag" | "blockNumber" + > + ) => Promise>; + readContract: < + TAbi extends Abi | readonly unknown[], + TFunctionName extends string + >( + args: Omit< + ReadContractParameters, + "blockTag" | "blockNumber" + > + ) => Promise>; +}; + +export type ReadOnlyClient< + transport extends Transport = Transport, + chain extends Chain | undefined = Chain | undefined +> = Prettify< + Client +>; + export const ponderActions = (getCurrentBlockNumber: () => bigint) => ( client: Client - ) => ({ + ): PonderActions => ({ getBalance: ( args: Omit ): Promise => From e5f7d702152077a5f9f5d9f2999593db56c0d5b7 Mon Sep 17 00:00:00 2001 From: Kyle Scott Date: Tue, 7 Nov 2023 19:08:23 -0500 Subject: [PATCH 3/3] fix unsolved merge error --- packages/core/src/build/service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/build/service.ts b/packages/core/src/build/service.ts index 2e1f960f9..57ca1e934 100644 --- a/packages/core/src/build/service.ts +++ b/packages/core/src/build/service.ts @@ -10,7 +10,7 @@ import type { ViteNodeRunner } from "vite-node/client"; // @ts-ignore import type { ViteNodeServer } from "vite-node/server"; -import type { ResolvedConfig } from "@/config/config"; +import type { Config } from "@/config/config"; import { UserError } from "@/errors/user"; import type { Common } from "@/Ponder"; import { buildSchema } from "@/schema/schema"; @@ -22,7 +22,7 @@ import { readGraphqlSchema } from "./schema"; import { parseViteNodeError, ViteNodeError } from "./stacktrace"; type BuildServiceEvents = { - newConfig: { config: ResolvedConfig }; + newConfig: { config: Config }; newIndexingFunctions: { indexingFunctions: RawIndexingFunctions }; newSchema: { schema: Schema; graphqlSchema: GraphQLSchema }; }; @@ -196,7 +196,7 @@ export class BuildService extends Emittery { const rawConfig = result.exports.config; const resolvedConfig = ( typeof rawConfig === "function" ? await rawConfig() : await rawConfig - ) as ResolvedConfig; + ) as Config; // TODO: Validate config lol