📄 src/web3/client/registry.ts
D-OPEN SOVEREIGN
1import { EvmClient } from './EvmClient';
2import { EVM_CHAINS } from '../network/chains';
3import type { ReadOnlyDomainAPI } from './types';
4import { addressLoader } from '../registry/loader';
5
6export const readOnlyClients: Record<number, ReadOnlyDomainAPI> = {};
7export const readOnlyStagingClients: Record<number, ReadOnlyDomainAPI> = {};
8
9const evmClients: Record<number, EvmClient> = {};
10const evmStagingClients: Record<number, EvmClient> = {};
11
12for (const [id, config] of Object.entries(EVM_CHAINS)) {
13 const chainId = Number(id);
14 if (config.deployed) {
15 const client = new EvmClient(chainId, 'production');
16 evmClients[chainId] = client;
17 readOnlyClients[chainId] = client;
18 }
19 if (config.stagingDeployed) {
20 const client = new EvmClient(chainId, 'staging');
21 evmStagingClients[chainId] = client;
22 readOnlyStagingClients[chainId] = client;
23 }
24}
25
26// Drop-in replacement for getReadonlyApiFromTech(tech, chainId) / getReadonlyAdapterByBrand(brand, chainId).
27// Returns ReadOnlyDomainAPI — callers never depend on EvmClient specifics.
28export function getReadClient(
29 chainId: number,
30 environment: 'production' | 'staging' = 'production'
31): ReadOnlyDomainAPI {
32 const map = environment === 'staging' ? readOnlyStagingClients : readOnlyClients;
33 const client = map[chainId];
34 if (!client) throw new Error(`No read client for chain ${chainId} (${environment})`);
35 return client;
36}
37
38// Internal — the write path (session/writeClient.ts) needs the concrete EvmClient
39// (chain, publicClient) to construct an EvmWriteClient, not the adapter-agnostic
40// ReadOnlyDomainAPI. Not part of the adapter-agnostic public contract.
41export function getEvmClient(
42 chainId: number,
43 environment: 'production' | 'staging' = 'production'
44): EvmClient {
45 const map = environment === 'staging' ? evmStagingClients : evmClients;
46 const client = map[chainId];
47 if (!client) throw new Error(`No EVM client for chain ${chainId} (${environment})`);
48 return client;
49}
50
51// client.ready only guarantees addresses are resolved — the manifest's ABI fetch
52// runs fire-and-forget in the background (by design, for progressive browser
53// loading via subscribe()/getLoadingStatus()). A one-shot caller that reads/writes
54// immediately (an admin tool, a CLI script) instead of rendering a loading state
55// needs ABIs resolved too before its first call, or it silently gets empty/failed
56// reads. This is the fully-ready equivalent for that use case.
57export async function getReadyEvmClient(
58 chainId: number,
59 environment: 'production' | 'staging' = 'production'
60): Promise<EvmClient> {
61 const client = getEvmClient(chainId, environment);
62 await client.ready;
63 await addressLoader.waitForAbis(chainId, environment);
64 return client;
65}
66