@@ -21,20 +21,78 @@ import { privateKeyToAccount } from "viem/accounts";
2121import { Web3 } from "web3" ;
2222import { WebSocketProvider } from "web3-providers-ws" ;
2323import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat" ;
24+ import * as fs from "node:fs" ;
25+ import * as path from "node:path" ;
2426const logger = createLogger ( { name : "providers" } ) ;
2527const debug = logger . debug . bind ( logger ) ;
2628
29+ /**
30+ * Load cached metadata if available, returns { genesisHash: metadataHex } or undefined
31+ */
32+ const loadCachedMetadata = ( ) : Record < string , `0x${string } `> | undefined => {
33+ const cacheDir = process . env . MOONWALL_CACHE_DIR ;
34+ if ( ! cacheDir ) return undefined ;
35+
36+ const metadataPath = path . join ( cacheDir , "metadata-cache.json" ) ;
37+ try {
38+ const data = fs . readFileSync ( metadataPath , "utf-8" ) ;
39+ const cached = JSON . parse ( data ) as Record < string , `0x${string } `> ;
40+ debug ( `Loaded cached metadata for genesis: ${ Object . keys ( cached ) . join ( ", " ) } ` ) ;
41+ return cached ;
42+ } catch {
43+ return undefined ;
44+ }
45+ } ;
46+
47+ /**
48+ * Save metadata to cache for future connections
49+ */
50+ const saveCachedMetadata = ( genesisHash : string , metadataHex : string ) : void => {
51+ const cacheDir = process . env . MOONWALL_CACHE_DIR ;
52+ if ( ! cacheDir ) return ;
53+
54+ const metadataPath = path . join ( cacheDir , "metadata-cache.json" ) ;
55+ const lockPath = `${ metadataPath } .lock` ;
56+
57+ try {
58+ // Simple lock to prevent concurrent writes
59+ try {
60+ fs . openSync ( lockPath , fs . constants . O_CREAT | fs . constants . O_EXCL ) ;
61+ } catch {
62+ // Another process is writing, skip
63+ return ;
64+ }
65+
66+ const data = JSON . stringify ( { [ genesisHash ] : metadataHex } ) ;
67+ fs . writeFileSync ( metadataPath , data , "utf-8" ) ;
68+ debug ( `Saved metadata cache for genesis: ${ genesisHash } ` ) ;
69+ } catch ( e ) {
70+ debug ( `Failed to save metadata cache: ${ e } ` ) ;
71+ } finally {
72+ try {
73+ fs . unlinkSync ( lockPath ) ;
74+ } catch {
75+ /* ignore */
76+ }
77+ }
78+ } ;
79+
2780export class ProviderFactory {
2881 private url : string ;
2982 private privateKey : string ;
3083
3184 constructor ( private providerConfig : ProviderConfig ) {
32- this . url = providerConfig . endpoints . includes ( "ENV_VAR" )
33- ? process . env . WSS_URL || "error_missing_WSS_URL_env_var"
34- : providerConfig . endpoints [ 0 ] ;
35- debug (
36- `Constructor - providerConfig.endpoints[0]: ${ providerConfig . endpoints [ 0 ] } , this.url: ${ this . url } `
37- ) ;
85+ const endpoint = providerConfig . endpoints [ 0 ] ;
86+ // Support "AUTO" endpoint that uses dynamic MOONWALL_RPC_PORT
87+ if ( endpoint === "AUTO" || endpoint . includes ( "ENV_VAR" ) ) {
88+ this . url =
89+ endpoint === "AUTO"
90+ ? vitestAutoUrl ( )
91+ : process . env . WSS_URL || "error_missing_WSS_URL_env_var" ;
92+ } else {
93+ this . url = endpoint ;
94+ }
95+ debug ( `Constructor - providerConfig.endpoints[0]: ${ endpoint } , this.url: ${ this . url } ` ) ;
3896 this . privateKey = process . env . MOON_PRIV_KEY || ALITH_PRIVATE_KEY ;
3997 }
4098
@@ -63,6 +121,9 @@ export class ProviderFactory {
63121 name : this . providerConfig . name ,
64122 type : this . providerConfig . type ,
65123 connect : async ( ) => {
124+ const cachedMetadata = loadCachedMetadata ( ) ;
125+ const startTime = Date . now ( ) ;
126+
66127 const options : ApiOptions = {
67128 provider : new WsProvider ( this . url ) ,
68129 initWasm : false ,
@@ -72,10 +133,22 @@ export class ProviderFactory {
72133 typesBundle : this . providerConfig . additionalTypes
73134 ? this . providerConfig . additionalTypes
74135 : undefined ,
136+ metadata : cachedMetadata ,
75137 } ;
76138
77139 const api = await ApiPromise . create ( options ) ;
78140 await api . isReady ;
141+
142+ // Cache metadata for future connections if not already cached
143+ if ( ! cachedMetadata ) {
144+ const genesisHash = api . genesisHash . toHex ( ) ;
145+ const metadataHex = api . runtimeMetadata . toHex ( ) ;
146+ saveCachedMetadata ( genesisHash , metadataHex ) ;
147+ debug ( `PolkadotJs connected in ${ Date . now ( ) - startTime } ms (metadata fetched & cached)` ) ;
148+ } else {
149+ debug ( `PolkadotJs connected in ${ Date . now ( ) - startTime } ms (using cached metadata)` ) ;
150+ }
151+
79152 return api ;
80153 } ,
81154 ws : ( ) => new WsProvider ( this . url ) ,
0 commit comments