import type { ConnectionOptions } from "node:tls"; import type { Prettify } from "@/types/utils.js"; import type { Abi } from "abitype"; import type { PoolConfig } from "pg"; import type { Narrow, Transport } from "viem"; import type { AddressConfig } from "./address.js"; import type { GetEventFilter } from "./eventFilter.js"; export type Config = { database?: DatabaseConfig; ordering?: "omnichain" | "multichain" | "experimental_isolated"; chains: { [chainName: string]: ChainConfig }; contracts: { [contractName: string]: GetContract }; accounts: { [accountName: string]: AccountConfig }; blocks: { [sourceName: string]: GetBlockFilter; }; }; export type CreateConfigReturnType = { database?: DatabaseConfig; ordering?: "omnichain" | "multichain" | "experimental_isolated"; chains: chains; contracts: contracts; accounts: accounts; blocks: blocks; }; export const createConfig = < const chains, const contracts = {}, const accounts = {}, const blocks = {}, >(config: { database?: DatabaseConfig; ordering?: "omnichain" | "multichain" | "experimental_isolated"; // TODO: add jsdoc to these properties. chains: ChainsConfig>; contracts?: ContractsConfig>; accounts?: AccountsConfig>; blocks?: BlockFiltersConfig; }): CreateConfigReturnType => config as Prettify< CreateConfigReturnType >; // database type DatabaseConfig = | { kind: "pglite"; /** Directory path to use for PGlite database files. Default: `".ponder/pglite"`. */ directory?: string; } | { kind: "postgres"; /** Postgres database connection string. Default: `DATABASE_PRIVATE_URL` > `DATABASE_URL` environment variable. */ connectionString?: string; /** Postgres pool configuration passed to `node-postgres`. */ poolConfig?: { /** Maximum number of clients in the pool. Default: `30`. */ max?: number; /** Enable SSL, or provide a custom SSL configuration. Default: `undefined`. */ ssl?: boolean | Prettify; } & Omit; }; // base type BlockConfig = { /** Block number at which to start indexing events (inclusive). If `undefined`, events will be processed from block 0. Default: `undefined`. */ startBlock?: number | "latest"; /** Block number at which to stop indexing events (inclusive). If `undefined`, events will be processed in real-time. Default: `undefined`. */ endBlock?: number | "latest"; }; type TransactionReceiptConfig = { includeTransactionReceipts?: boolean; }; type FunctionCallConfig = { /* * Enable call trace indexing for this contract. * * - Docs: https://ponder.sh/docs/guides/call-traces */ includeCallTraces?: boolean; }; // chain type ChainConfig = { /** Chain ID of the chain. */ id: chain extends { id: infer id extends number } ? id | number : number; /** RPC url. */ rpc: string | string[] | Transport | undefined; ws?: string; /** Polling interval (in ms). Default: `1_000`. */ pollingInterval?: number; /** * Maximum number of RPC requests per second. * @deprecated Handled automatically instead. */ maxRequestsPerSecond?: number; /** Disable RPC request caching. Default: `false`. */ disableCache?: boolean; /** * Maximum block range for eth_getLogs. If undefined, Ponder will * attempt to determine the block range automatically based on error messages. */ ethGetLogsBlockRange?: number; /** * Use "pending" block tag instead of "latest" when polling for new blocks. * Enables reading pre-confirmation data such as Base flashblocks. * Default: `false`. */ readPending?: boolean; }; type ChainsConfig = {} extends chains ? {} : { [chainName in keyof chains]: ChainConfig; }; // contracts type AbiConfig = { /** Contract application byte interface. */ abi: abi; }; type GetContractChain< chains, abi extends Abi, /// allChainNames extends string = [keyof chains] extends [never] ? string : keyof chains & string, > = { /** * Chain that this contract is deployed to. Must match a chain name in `chains`. * Any filter information overrides the values in the higher level "contracts" property. */ chain: | allChainNames | { [name in allChainNames]?: Prettify< AddressConfig & GetEventFilter & TransactionReceiptConfig & FunctionCallConfig & BlockConfig >; }; }; type ContractConfig = Prettify< AbiConfig & GetContractChain & AddressConfig & GetEventFilter & TransactionReceiptConfig & FunctionCallConfig & BlockConfig >; type GetContract = contract extends { abi: infer abi extends Abi; } ? // 1. Contract has a valid abi ContractConfig : // 2. Contract has an invalid abi ContractConfig; type ContractsConfig = {} extends contracts ? // contracts empty, return empty {} : { [name in keyof contracts]: GetContract; }; // accounts type GetAccountChain< chains, /// allChainNames extends string = [keyof chains] extends [never] ? string : keyof chains & string, > = { /** * Chain that this account is deployed to. Must match a chain name in `chains`. * Any filter information overrides the values in the higher level "accounts" property. */ chain: | allChainNames | { [name in allChainNames]?: Prettify< AddressConfig & TransactionReceiptConfig & BlockConfig >; }; }; type AccountConfig = Prettify< GetAccountChain & Required & TransactionReceiptConfig & BlockConfig >; type AccountsConfig = {} extends accounts ? {} : { [name in keyof accounts]: AccountConfig; }; // blocks type BlockFilterConfig = { /** Block number at which to start indexing events (inclusive). If `undefined`, events will be processed from block 0. Default: `undefined`. */ startBlock?: number | "latest"; /** Block number at which to stop indexing events (inclusive). If `undefined`, events will be processed in real-time. Default: `undefined`. */ endBlock?: number | "latest"; interval?: number; }; type GetBlockFilter< chains, /// allChainNames extends string = [keyof chains] extends [never] ? string : keyof chains & string, > = BlockFilterConfig & { chain: | allChainNames | { [name in allChainNames]?: BlockFilterConfig; }; }; type BlockFiltersConfig = {} extends blocks ? {} : { [name in keyof blocks]: GetBlockFilter; };