/** * Which implementation VERSION is actually behind a module proxy. * * Every proxy resolves to an implementation address, and ImplementationRegistry emits * `ImplementationRegistered(typeId, typeName, versionId, version, implementation, latest)` when * that address is registered — carrying the human version string ("v4", "v11", …). Matching the * two gives the deployed version per module, which nothing else exposes: the contracts have no * `version()` getter, and the registry's own `getImplementation(type, version)` needs the * version string you are trying to discover. * * This exists because guessing is genuinely dangerous. Two tempting shortcuts both give wrong * answers, and cost real time when the deployment is heterogeneous: * * - Probing for a function that "should" be new: many upgrades change only internal behaviour * or add custom errors, leaving the external surface byte-identical. A probe also proves * nothing unless you first confirm the symbol exists in the reference build — probing a name * that never existed in ANY version reports "absent" for every version. * - Comparing deployed bytecode against a local `forge build`: this repo's default profile has * the optimizer OFF (so vm.roll tests stay honest) while broadcasts use * FOUNDRY_PROFILE=production. Optimizer-off code is ~2.2x larger, so the naive comparison * mismatches on every contract and looks like universal drift. * * SUBGRAPH FIRST (2026-07): the eth_getLogs(fromBlock 0 → latest) scan below is a genuine * liability — many RPC providers reject a range that wide, and the catch returns an empty map, * which silently renders EVERY module version "unknown". The subgraph indexes the same facts * (Beacon.currentImplementation/version + BeaconUpgradeEvent.newImplementation/version, both * verified fully populated on live Gnosis and Arbitrum), so `loadImplementationVersions` reads * those first and only touches the log scan when the subgraph cannot name an implementation. * The log scan is NOT removed: an implementation that was registered but never promoted onto a * global beacon (reachable only by pinning) exists in the registry log and nowhere else. */ import { ethers } from 'ethers'; export interface RegisteredImplementation { typeName: string; version: string; implementation: string; /** True when this was the registry's `latest` for its type at registration time. */ latest: boolean; } /** Resolve the implementation a proxy currently delegates to (beacon or direct). */ export declare function resolveImplementation(provider: ethers.providers.Provider, proxy: string): Promise; /** * Every implementation the registry has ever registered, keyed by lowercased address. * * One `eth_getLogs` over a single global contract. Callers should fetch once and reuse across * modules rather than per-module. */ export declare function fetchRegisteredImplementations(provider: ethers.providers.Provider, registryAddress: string): Promise>; /** Where a version index's records came from, for diagnostics. */ export type VersionIndexSource = 'subgraph' | 'rpc-logs' | 'subgraph+rpc-logs' | 'none'; /** * Implementation → version lookup that prefers the subgraph and keeps the * registry log scan as an explicitly-triggered fallback. */ export interface VersionIndex { /** Version record for an implementation address, or undefined. */ get(implementation: string | null | undefined): RegisteredImplementation | undefined; /** Records currently held. */ readonly size: number; readonly source: VersionIndexSource; /** * Guarantee an answer was attempted for every listed implementation: if the * subgraph did not know one of them, run the registry eth_getLogs scan ONCE * and merge whatever it adds. Never throws. */ ensure(implementations: Array): Promise; } /** * Build a VersionIndex: subgraph first, registry logs on demand. * * `skipSubgraph` exists for offline/unit paths; omitting `provider` or * `registryAddress` simply means the log fallback is unavailable and unknown * implementations stay unknown (which is the honest answer, not "old"). */ export declare function loadImplementationVersions(opts: { provider?: ethers.providers.Provider | null; registryAddress?: string | null; chainId?: number; skipSubgraph?: boolean; }): Promise; /** * Deployed version string for a proxy, or null when it cannot be determined. * * Null is meaningful and must not be rendered as "old" or "current": it means the registry log * was unreadable, or the implementation predates registry tracking. */ export declare function resolveModuleVersion(provider: ethers.providers.Provider, proxy: string, registered: Map): Promise<{ implementation: string | null; version: string | null; latest: boolean | null; }>;