import { ethers } from 'ethers'; import { ProviderConfig } from '../utils/types'; /** * Per-chain fee data, returned in the shape ethers v5 expects on a transaction. * EIP-1559 chains return maxFeePerGas/maxPriorityFeePerGas. * Legacy chains return gasPrice. */ export interface ChainFeeData { maxFeePerGas?: ethers.BigNumber; maxPriorityFeePerGas?: ethers.BigNumber; gasPrice?: ethers.BigNumber; } /** * Optional per-chain knobs that root config can override without editing code. * Each subclass reads only what it cares about. */ export interface ChainPolicy { minPriorityFeeGwei?: number; maxFeeMultiplier?: number; maxFeePerGasGwei?: number; minGasPriceGwei?: number; maxGasPriceGwei?: number; gasLimitMultiplier?: number; } /** * Extended provider config that may carry per-chain policy + a private RPC URL. * The plain `ProviderConfig` from utils/types stays the wire/storage shape; * `ChainConfig` is what the Chain object actually holds in memory. */ export interface ChainConfig extends ProviderConfig { publicRpc?: string; privateRpc?: string; policy?: ChainPolicy; /** * Short names a user may type instead of the chainId — `epistery * initialize --chain polygon`. Lowercase, no spaces. Must be unique across * registered chains; the first exact alias match wins. */ aliases?: string[]; } /** * Base class for an EVM chain. Subclasses override only the policy hooks * that are actually different from the EIP-1559 default. * * The Chain object owns: * - the JsonRpcProvider (with explicit network info, fixing "could not detect network") * - per-chain fee policy (getFeeData) * - the contract Proxy that injects fee data into write calls * - gas-limit estimation with a per-chain safety multiplier * * The Chain object does NOT own: * - wallets / private keys * - contract ABIs * - domain config storage */ export declare class Chain { /** * Subclasses override `defaults` to carry the canonical network details. * `chainFor({chainId: 137})` merges caller config on top of these defaults * so only the chainId is required — name, public RPC, currency etc. are * all built in. */ static defaults: Partial; readonly chainId: number; readonly name: string; readonly rpc: string; readonly publicRpc: string | undefined; readonly currency: { name: string; symbol: string; decimals: number; }; readonly policy: ChainPolicy; private _provider; constructor(config: ChainConfig); /** * Lazily-built provider with explicit network info. * * Passing `{ name, chainId }` to the constructor avoids ethers' "could not * detect network" error when the RPC is briefly unreachable at startup, but * it does NOT stop the per-read probe: ethers v5 runs * getNetwork() -> detectNetwork() before EVERY read (eth_call, getBalance, * etc.), and detectNetwork re-sends eth_chainId each time (its memo only * survives a single event-loop tick, so sequentially-awaited reads each fire * their own). That doubled reads into "eth_chainId + " pairs. * * Overriding detectNetwork to return the known static network collapses each * pair into one RPC call — roughly halving read volume on hot paths. This is * pure liveness plumbing: it's independent of which RPC endpoint is chosen, * so it stays correct behind an owned node or a fallback provider. */ get provider(): ethers.providers.JsonRpcProvider; /** EIP-1559 by default. Subclasses override for legacy gasPrice chains. */ supportsEIP1559(): boolean; /** * Default fee policy: pass through whatever the network reports via * eth_feeHistory / eth_gasPrice. Subclasses override to apply per-chain * floors (Polygon's 25 gwei priority floor, JOC's gasPrice floor, etc.). */ getFeeData(): Promise; /** * Estimate gas limit with this chain's safety multiplier. * Used by callers that need to populate gasLimit explicitly (e.g. for * pre-funding calculations). Most write calls will let ethers estimate * automatically; this is for the cases where ethers' estimate is unsafe * (Polygon Amoy, JOC) and a multiplier is required. */ estimateGas(populated: ethers.providers.TransactionRequest): Promise; /** * Wrap an ethers.Contract so every state-mutating method automatically * receives this chain's fee data as the transaction overrides argument. * * Uses Object.create (prototype chain) — the wrapper object gets its own * writable properties for the write methods while reads of everything else * (.address, .signer, view functions, .interface, etc.) fall through to * the original contract via the prototype. * * Why not a Proxy: ethers v5 defines ABI methods with defineReadOnly * (non-writable, non-configurable). V8's proxy invariant requires get * traps to return the *original* value for such properties — returning a * wrapped function throws TypeError. Object.create avoids this because * the own properties on the child shadow the prototype's frozen ones. * * NOTE: epistery-host's DomainChain does NOT use this because all its * write call sites already pass feeData explicitly. This method exists * for other consumers (e.g. CLI tools, agents) that want automatic fee * injection without threading overrides through every call. * * @param contract - the ethers.Contract instance * @param abi - the same ABI used to construct the contract; needed to * identify which methods are state-mutating. */ wrapContract(contract: T, abi: ReadonlyArray): T; /** * Recognize a transaction-overrides object so we don't mistake it for a * positional argument. Excludes BigNumbers and arrays explicitly. */ static isOverridesObject(x: any): boolean; /** Convenience: gwei → BigNumber wei */ protected gwei(n: number): ethers.BigNumber; } //# sourceMappingURL=Chain.d.ts.map