@openzeppelin/ui-react 1.2.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,111 +4,159 @@ import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { Button } from "@openzeppelin/ui-components";
5
5
 
6
6
  //#region src/version.ts
7
- const VERSION = "1.2.0";
7
+ const VERSION = "2.0.1";
8
8
 
9
9
  //#endregion
10
10
  //#region src/hooks/AdapterContext.tsx
11
11
  /**
12
12
  * AdapterContext.tsx
13
13
  *
14
- * This file defines the React Context used for the adapter singleton pattern.
14
+ * This file defines the React Context used for the runtime singleton pattern.
15
15
  * It provides types and the context definition, but the actual implementation
16
- * is in the AdapterProvider component.
16
+ * is in the `RuntimeProvider` component.
17
17
  *
18
- * The adapter singleton pattern ensures that only one adapter instance exists
18
+ * The runtime singleton pattern ensures that only one runtime instance exists
19
19
  * per network configuration, which is critical for consistent wallet connection
20
20
  * state across the application.
21
21
  */
22
22
  /**
23
- * The React Context that provides adapter registry access throughout the app
24
- * Components can access this through the useAdapterContext hook
23
+ * The React Context that provides runtime registry access throughout the app.
24
+ * Components can access this through the `useRuntimeContext` hook.
25
25
  */
26
- const AdapterContext = createContext(null);
26
+ const RuntimeContext = createContext(null);
27
27
 
28
28
  //#endregion
29
29
  //#region src/hooks/AdapterProvider.tsx
30
30
  /**
31
- * AdapterProvider.tsx
31
+ * RuntimeProvider.tsx
32
32
  *
33
- * This file implements the Adapter Provider component which manages a registry of
34
- * adapter instances. It's a key part of the adapter singleton pattern which ensures
35
- * that only one adapter instance exists per network configuration.
33
+ * This file implements the Runtime Provider component which manages a registry of
34
+ * runtime instances. It's a key part of the runtime singleton pattern which ensures
35
+ * that only one runtime instance exists per network configuration.
36
36
  *
37
- * The adapter registry is shared across the application via React Context, allowing
38
- * components to access the same adapter instances and maintain consistent wallet
37
+ * The runtime registry is shared across the application via React Context, allowing
38
+ * components to access the same runtime instances and maintain consistent wallet
39
39
  * connection state.
40
40
  *
41
41
  * IMPORTANT: This implementation needs special care to avoid React state update errors
42
42
  * during component rendering. Direct state updates during render are not allowed, which
43
- * is why adapter loading is controlled carefully.
43
+ * is why runtime loading is controlled carefully.
44
44
  */
45
45
  /**
46
- * Provider component that manages adapter instances centrally
47
- * to avoid creating multiple instances of the same adapter.
46
+ * Provider component that manages runtime instances centrally
47
+ * to avoid creating multiple instances of the same runtime.
48
48
  *
49
49
  * This component:
50
- * 1. Maintains a registry of adapter instances by network ID
51
- * 2. Tracks loading states for adapters being initialized
52
- * 3. Provides a function to get or load adapters for specific networks
53
- * 4. Ensures adapter instances are reused when possible
50
+ * 1. Maintains a registry of runtime instances by network ID
51
+ * 2. Tracks loading states for runtimes being initialized
52
+ * 3. Provides a function to get or load runtimes for specific networks
53
+ * 4. Ensures runtime instances are reused when possible
54
54
  */
55
- function AdapterProvider({ children, resolveAdapter }) {
56
- const [adapterRegistry, setAdapterRegistry] = useState({});
55
+ function RuntimeProvider({ children, resolveRuntime }) {
56
+ const [runtimeRegistry, setRuntimeRegistry] = useState({});
57
57
  const [loadingNetworks, setLoadingNetworks] = useState(/* @__PURE__ */ new Set());
58
+ const runtimeRegistryRef = useRef(runtimeRegistry);
59
+ const failedNetworksRef = useRef(/* @__PURE__ */ new Set());
58
60
  useEffect(() => {
59
- const adapterCount = Object.keys(adapterRegistry).length;
60
- if (adapterCount > 0) logger.info("AdapterProvider", `Registry contains ${adapterCount} adapters:`, {
61
- networkIds: Object.keys(adapterRegistry),
61
+ runtimeRegistryRef.current = runtimeRegistry;
62
+ }, [runtimeRegistry]);
63
+ useEffect(() => {
64
+ failedNetworksRef.current = /* @__PURE__ */ new Set();
65
+ }, [resolveRuntime]);
66
+ useEffect(() => {
67
+ return () => {
68
+ Object.values(runtimeRegistryRef.current).forEach((runtime) => {
69
+ runtime.dispose();
70
+ });
71
+ };
72
+ }, []);
73
+ useEffect(() => {
74
+ const runtimeCount = Object.keys(runtimeRegistry).length;
75
+ if (runtimeCount > 0) logger.info("RuntimeProvider", `Registry contains ${runtimeCount} runtimes:`, {
76
+ networkIds: Object.keys(runtimeRegistry),
62
77
  loadingCount: loadingNetworks.size,
63
78
  loadingNetworkIds: Array.from(loadingNetworks)
64
79
  });
65
- }, [adapterRegistry, loadingNetworks]);
80
+ }, [runtimeRegistry, loadingNetworks]);
81
+ /**
82
+ * Evicts a runtime from the registry by network ID and disposes it.
83
+ * This is the safe counterpart to `getRuntimeForNetwork`: callers should only
84
+ * release a runtime after they have already promoted its replacement.
85
+ *
86
+ * Disposal is deferred to the next macrotask so React's commit phase
87
+ * (including development-mode prop diffing) can finish reading the old
88
+ * runtime's properties without hitting the RuntimeDisposedError proxy trap.
89
+ */
90
+ const releaseRuntime = useCallback((networkId) => {
91
+ const runtime = runtimeRegistryRef.current[networkId];
92
+ if (!runtime) return;
93
+ logger.info("RuntimeProvider", `Releasing runtime for network ${networkId}`);
94
+ setRuntimeRegistry((prev) => {
95
+ const next = { ...prev };
96
+ delete next[networkId];
97
+ return next;
98
+ });
99
+ setTimeout(() => {
100
+ try {
101
+ runtime.dispose();
102
+ } catch (error) {
103
+ logger.error("RuntimeProvider", `Error disposing runtime for network ${networkId}:`, error);
104
+ }
105
+ }, 0);
106
+ }, []);
66
107
  /**
67
- * Function to get or create an adapter for a network
108
+ * Function to get or create a runtime for a network
68
109
  *
69
- * IMPORTANT: The actual adapter loading is handled in the useConfiguredAdapterSingleton hook
70
- * to avoid React state updates during render, which would cause errors.
110
+ * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates
111
+ * during render, which would cause errors.
71
112
  *
72
113
  * This function:
73
- * 1. Returns existing adapters immediately if available
74
- * 2. Reports loading state for adapters being initialized
75
- * 3. Initiates adapter loading when needed
114
+ * 1. Returns existing runtimes immediately if available
115
+ * 2. Reports loading state for runtimes being initialized
116
+ * 3. Initiates runtime loading when needed
76
117
  */
77
- const getAdapterForNetwork = useCallback((networkConfig) => {
118
+ const getRuntimeForNetwork = useCallback((networkConfig) => {
78
119
  if (!networkConfig) return {
79
- adapter: null,
120
+ runtime: null,
80
121
  isLoading: false
81
122
  };
82
123
  const networkId = networkConfig.id;
83
- logger.debug("AdapterProvider", `Adapter requested for network ${networkId}`);
84
- if (adapterRegistry[networkId]) {
85
- logger.debug("AdapterProvider", `Using existing adapter for network ${networkId}`);
124
+ logger.debug("RuntimeProvider", `Runtime requested for network ${networkId}`);
125
+ if (runtimeRegistry[networkId]) {
126
+ logger.debug("RuntimeProvider", `Using existing runtime for network ${networkId}`);
86
127
  return {
87
- adapter: adapterRegistry[networkId],
128
+ runtime: runtimeRegistry[networkId],
88
129
  isLoading: false
89
130
  };
90
131
  }
91
132
  if (loadingNetworks.has(networkId)) {
92
- logger.debug("AdapterProvider", `Adapter for network ${networkId} is currently loading`);
133
+ logger.debug("RuntimeProvider", `Runtime for network ${networkId} is currently loading`);
93
134
  return {
94
- adapter: null,
135
+ runtime: null,
95
136
  isLoading: true
96
137
  };
97
138
  }
139
+ if (failedNetworksRef.current.has(networkId)) {
140
+ logger.debug("RuntimeProvider", `Runtime for network ${networkId} previously failed; skipping retry`);
141
+ return {
142
+ runtime: null,
143
+ isLoading: false
144
+ };
145
+ }
98
146
  setLoadingNetworks((prev) => {
99
147
  const newSet = new Set(prev);
100
148
  newSet.add(networkId);
101
149
  return newSet;
102
150
  });
103
- logger.info("AdapterProvider", `Starting adapter initialization for network ${networkId} (${networkConfig.name})`);
104
- resolveAdapter(networkConfig).then((adapter) => {
105
- logger.info("AdapterProvider", `Adapter for network ${networkId} loaded successfully`, {
106
- type: adapter.constructor.name,
107
- objectId: Object.prototype.toString.call(adapter)
151
+ logger.info("RuntimeProvider", `Starting runtime initialization for network ${networkId} (${networkConfig.name})`);
152
+ resolveRuntime(networkConfig).then((runtime) => {
153
+ logger.info("RuntimeProvider", `Runtime for network ${networkId} loaded successfully`, {
154
+ type: runtime.constructor.name,
155
+ objectId: Object.prototype.toString.call(runtime)
108
156
  });
109
- setAdapterRegistry((prev) => ({
157
+ setRuntimeRegistry((prev) => ({
110
158
  ...prev,
111
- [networkId]: adapter
159
+ [networkId]: runtime
112
160
  }));
113
161
  setLoadingNetworks((prev) => {
114
162
  const newSet = new Set(prev);
@@ -116,7 +164,8 @@ function AdapterProvider({ children, resolveAdapter }) {
116
164
  return newSet;
117
165
  });
118
166
  }).catch((error) => {
119
- logger.error("AdapterProvider", `Error loading adapter for network ${networkId}:`, error);
167
+ logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
168
+ failedNetworksRef.current.add(networkId);
120
169
  setLoadingNetworks((prev) => {
121
170
  const newSet = new Set(prev);
122
171
  newSet.delete(networkId);
@@ -124,16 +173,19 @@ function AdapterProvider({ children, resolveAdapter }) {
124
173
  });
125
174
  });
126
175
  return {
127
- adapter: null,
176
+ runtime: null,
128
177
  isLoading: true
129
178
  };
130
179
  }, [
131
- adapterRegistry,
180
+ runtimeRegistry,
132
181
  loadingNetworks,
133
- resolveAdapter
182
+ resolveRuntime
134
183
  ]);
135
- const contextValue = useMemo(() => ({ getAdapterForNetwork }), [getAdapterForNetwork]);
136
- return /* @__PURE__ */ jsx(AdapterContext.Provider, {
184
+ const contextValue = useMemo(() => ({
185
+ getRuntimeForNetwork,
186
+ releaseRuntime
187
+ }), [getRuntimeForNetwork, releaseRuntime]);
188
+ return /* @__PURE__ */ jsx(RuntimeContext.Provider, {
137
189
  value: contextValue,
138
190
  children
139
191
  });
@@ -151,7 +203,7 @@ function AdapterProvider({ children, resolveAdapter }) {
151
203
  * consuming package's bundle. This creates MULTIPLE instances of this module:
152
204
  *
153
205
  * 1. The app's direct import → packages/react/dist/index.js
154
- * 2. The adapter's inlined copy → .vite/deps/@openzeppelin_ui-builder-adapter-evm.js
206
+ * 2. The adapter package's inlined copy → .vite/deps/@openzeppelin_adapter_evm.js
155
207
  *
156
208
  * Since React contexts use referential identity, these two module instances have
157
209
  * DIFFERENT context objects. When the adapter's components call useWalletState(),
@@ -213,54 +265,88 @@ function useWalletState() {
213
265
  /**
214
266
  * useAdapterContext.ts
215
267
  *
216
- * This file provides a hook to access the AdapterContext throughout the application.
217
- * It's a critical part of the adapter singleton pattern, allowing components to
218
- * access the centralized adapter registry.
268
+ * This file provides a hook to access the runtime context throughout the application.
269
+ * It's a critical part of the runtime singleton pattern, allowing components to
270
+ * access the centralized runtime registry.
219
271
  *
220
- * The adapter singleton pattern ensures:
221
- * - Only one adapter instance exists per network
272
+ * The runtime singleton pattern ensures:
273
+ * - Only one runtime instance exists per network
222
274
  * - Wallet connection state is consistent across the app
223
- * - Better performance by eliminating redundant adapter initialization
275
+ * - Better performance by eliminating redundant runtime initialization
224
276
  */
225
277
  /**
226
- * Hook to access the adapter context
278
+ * Hook to access the runtime context
227
279
  *
228
- * This hook provides access to the getAdapterForNetwork function which
229
- * retrieves or creates adapter instances from the singleton registry.
280
+ * This hook provides access to the `getRuntimeForNetwork` function which
281
+ * retrieves or creates runtime instances from the singleton registry.
230
282
  *
231
- * Components should typically use useConfiguredAdapterSingleton instead
283
+ * Components should typically use the higher-level wallet/runtime hooks instead
232
284
  * of this hook directly, as it handles React state update timing properly.
233
285
  *
234
- * @throws Error if used outside of an AdapterProvider context
235
- * @returns The adapter context value
286
+ * @throws Error if used outside of a RuntimeProvider context
287
+ * @returns The runtime context value
236
288
  */
237
- function useAdapterContext() {
238
- const context = useContext(AdapterContext);
239
- if (!context) throw new Error("useAdapterContext must be used within an AdapterProvider");
289
+ function useRuntimeContext() {
290
+ const context = useContext(RuntimeContext);
291
+ if (!context) throw new Error("useRuntimeContext must be used within a RuntimeProvider");
240
292
  return context;
241
293
  }
242
294
 
295
+ //#endregion
296
+ //#region src/hooks/walletSessionRegistry.ts
297
+ /**
298
+ * Returns the cached wallet session for an ecosystem, if present.
299
+ *
300
+ * @param registry - Current internal wallet session registry.
301
+ * @param ecosystem - Ecosystem key to resolve.
302
+ * @returns The cached session entry or `null` when the ecosystem has not been configured yet.
303
+ */
304
+ function getWalletSession(registry, ecosystem) {
305
+ if (!ecosystem) return null;
306
+ return registry[ecosystem] ?? null;
307
+ }
308
+ /**
309
+ * Inserts or replaces the cached wallet session for a single ecosystem.
310
+ *
311
+ * @param registry - Current internal wallet session registry.
312
+ * @param session - Session entry to cache.
313
+ * @returns A new registry object containing the upserted ecosystem session.
314
+ */
315
+ function upsertWalletSession(registry, session) {
316
+ return {
317
+ ...registry,
318
+ [session.ecosystem]: session
319
+ };
320
+ }
321
+
243
322
  //#endregion
244
323
  //#region src/hooks/WalletStateProvider.tsx
245
324
  /**
246
- * Configures the adapter's UI kit and returns the UI provider component and hooks.
325
+ * Configures the runtime's UI kit capability and returns the ecosystem session artifacts.
247
326
  */
248
- async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOverrides = {}) {
327
+ async function configureRuntimeUiKit(runtime, loadConfigModule, programmaticOverrides = {}) {
328
+ const uiKit = runtime.uiKit;
329
+ if (!uiKit) return {
330
+ providerComponent: null,
331
+ hooks: null
332
+ };
249
333
  try {
250
- if (typeof adapter.configureUiKit === "function") {
251
- logger.info("[WSP configureAdapterUiKit] Calling configureUiKit for adapter:", adapter?.networkConfig?.id);
252
- await adapter.configureUiKit(programmaticOverrides, { loadUiKitNativeConfig: loadConfigModule });
253
- logger.info("[WSP configureAdapterUiKit] configureUiKit completed for adapter:", adapter?.networkConfig?.id);
334
+ const hasUiKitOverride = Object.keys(programmaticOverrides).length > 0;
335
+ if (typeof uiKit.configureUiKit === "function") {
336
+ const nextUiKitConfig = { ...programmaticOverrides };
337
+ logger.info("[WSP configureRuntimeUiKit]", hasUiKitOverride ? `Applying explicit UI kit overrides for runtime: ${runtime.networkConfig.id}` : `Initializing runtime UI kit from adapter/app defaults for runtime: ${runtime.networkConfig.id}`);
338
+ await uiKit.configureUiKit(nextUiKitConfig, { loadUiKitNativeConfig: loadConfigModule });
339
+ logger.info("[WSP configureRuntimeUiKit] configureUiKit completed for runtime:", runtime.networkConfig.id);
254
340
  }
255
- const providerComponent = adapter.getEcosystemReactUiContextProvider?.() || null;
256
- const hooks = adapter.getEcosystemReactHooks?.() || null;
257
- logger.info("[WSP configureAdapterUiKit]", "UI provider and hooks retrieved successfully.");
341
+ const providerComponent = uiKit.getEcosystemReactUiContextProvider?.() || null;
342
+ const hooks = uiKit.getEcosystemReactHooks?.() || null;
343
+ logger.info("[WSP configureRuntimeUiKit]", "UI provider and hooks retrieved successfully.");
258
344
  return {
259
345
  providerComponent,
260
346
  hooks
261
347
  };
262
348
  } catch (error) {
263
- logger.error("[WSP configureAdapterUiKit]", "Error during adapter UI setup:", error);
349
+ logger.error("[WSP configureRuntimeUiKit]", "Error during runtime UI setup:", error);
264
350
  throw error;
265
351
  }
266
352
  }
@@ -270,26 +356,28 @@ async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOver
270
356
  * It is responsible for:
271
357
  * 1. Managing the globally selected active network ID (`activeNetworkId`).
272
358
  * 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.
273
- * 3. Fetching and providing the corresponding `ContractAdapter` instance (`activeAdapter`) for the active network,
274
- * leveraging the `AdapterProvider` to ensure adapter singletons.
275
- * 4. Storing and providing the `EcosystemSpecificReactHooks` (`walletFacadeHooks`) from the active adapter.
276
- * 5. Rendering the adapter-specific UI context provider (e.g., WagmiProvider for EVM) around its children,
277
- * which is essential for the facade hooks to function correctly.
359
+ * 3. Fetching and providing the corresponding `EcosystemRuntime` (`activeRuntime`) for the active network,
360
+ * leveraging the `RuntimeProvider` to ensure runtime singletons.
361
+ * 4. Caching ecosystem-scoped wallet session artifacts (provider roots and facade hooks)
362
+ * independently from the network-scoped runtime.
363
+ * 5. Rendering the active ecosystem wallet provider (e.g., WagmiProvider for EVM) around its
364
+ * children, which is essential for the facade hooks to function correctly.
278
365
  * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.
279
366
  *
280
367
  * Consumers use the `useWalletState()` hook to access this global state.
281
- * It should be placed high in the component tree, inside an `<AdapterProvider>`.
368
+ * It should be placed high in the component tree, inside a `<RuntimeProvider>`.
282
369
  */
283
370
  function WalletStateProvider({ children, initialNetworkId = null, getNetworkConfigById, loadConfigModule }) {
284
371
  const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = useState(initialNetworkId);
285
372
  const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] = useState(null);
286
- const [globalActiveAdapter, setGlobalActiveAdapter] = useState(null);
287
- const [isGlobalAdapterLoading, setIsGlobalAdapterLoading] = useState(false);
288
- const [walletFacadeHooks, setWalletFacadeHooks] = useState(null);
289
- const [AdapterUiContextProviderToRender, setAdapterUiContextProviderToRender] = useState(null);
373
+ const [globalActiveRuntime, setGlobalActiveRuntime] = useState(null);
374
+ const [isGlobalRuntimeLoading, setIsGlobalRuntimeLoading] = useState(false);
375
+ const [walletSessionRegistry, setWalletSessionRegistry] = useState({});
376
+ const [activeWalletSessionEcosystem, setActiveWalletSessionEcosystem] = useState(null);
290
377
  const [uiKitConfigVersion, setUiKitConfigVersion] = useState(0);
291
378
  const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = useState(void 0);
292
- const { getAdapterForNetwork } = useAdapterContext();
379
+ const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();
380
+ const promotedNetworkIdRef = useRef(null);
293
381
  useEffect(() => {
294
382
  const abortController = new AbortController();
295
383
  async function fetchNetworkConfig() {
@@ -312,98 +400,115 @@ function WalletStateProvider({ children, initialNetworkId = null, getNetworkConf
312
400
  }, [currentGlobalNetworkId, getNetworkConfigById]);
313
401
  useEffect(() => {
314
402
  const abortController = new AbortController();
315
- async function loadAdapterAndConfigureUi() {
403
+ async function loadRuntimeAndConfigureUi() {
316
404
  if (!currentGlobalNetworkConfig) {
317
405
  if (!abortController.signal.aborted) {
318
- setGlobalActiveAdapter(null);
319
- setIsGlobalAdapterLoading(false);
320
- setAdapterUiContextProviderToRender(null);
321
- setWalletFacadeHooks(null);
406
+ const prevNetworkId = promotedNetworkIdRef.current;
407
+ setGlobalActiveRuntime(null);
408
+ setIsGlobalRuntimeLoading(false);
409
+ setActiveWalletSessionEcosystem(null);
410
+ if (prevNetworkId) {
411
+ releaseRuntime(prevNetworkId);
412
+ promotedNetworkIdRef.current = null;
413
+ }
322
414
  }
323
415
  return;
324
416
  }
325
- const { adapter: newAdapter, isLoading: newIsLoading } = getAdapterForNetwork(currentGlobalNetworkConfig);
417
+ const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(currentGlobalNetworkConfig);
326
418
  if (abortController.signal.aborted) return;
327
- setIsGlobalAdapterLoading(newIsLoading);
328
- if (newAdapter && !newIsLoading) try {
329
- const { providerComponent, hooks } = await configureAdapterUiKit(newAdapter, loadConfigModule, programmaticUiKitConfig);
419
+ setIsGlobalRuntimeLoading(newIsLoading);
420
+ if (newRuntime && !newIsLoading) try {
421
+ const { providerComponent, hooks } = await configureRuntimeUiKit(newRuntime, loadConfigModule, programmaticUiKitConfig);
330
422
  if (!abortController.signal.aborted) {
331
- setAdapterUiContextProviderToRender(() => providerComponent);
332
- setWalletFacadeHooks(hooks);
333
- setGlobalActiveAdapter(newAdapter);
423
+ const ecosystem = newRuntime.networkConfig.ecosystem;
424
+ const prevNetworkId = promotedNetworkIdRef.current;
425
+ const nextNetworkId = newRuntime.networkConfig.id;
426
+ setWalletSessionRegistry((prevRegistry) => upsertWalletSession(prevRegistry, {
427
+ ecosystem,
428
+ lastConfiguredNetworkId: nextNetworkId,
429
+ providerComponent,
430
+ hooks
431
+ }));
432
+ setGlobalActiveRuntime(newRuntime);
433
+ setActiveWalletSessionEcosystem(ecosystem);
434
+ promotedNetworkIdRef.current = nextNetworkId;
435
+ if (prevNetworkId && prevNetworkId !== nextNetworkId) releaseRuntime(prevNetworkId);
334
436
  }
335
437
  } catch (error) {
336
- if (!abortController.signal.aborted) {
337
- logger.error("[WSP loadAdapterAndConfigureUi]", "Error during adapter UI setup:", error);
338
- setAdapterUiContextProviderToRender(null);
339
- setWalletFacadeHooks(null);
340
- }
438
+ if (!abortController.signal.aborted) logger.error("[WSP loadRuntimeAndConfigureUi]", "Error during runtime UI setup:", error);
341
439
  }
342
- else if (!newAdapter && !newIsLoading) {
440
+ else if (!newRuntime && !newIsLoading) {
343
441
  if (!abortController.signal.aborted) {
344
- setAdapterUiContextProviderToRender(null);
345
- setWalletFacadeHooks(null);
346
- setGlobalActiveAdapter(null);
442
+ const prevNetworkId = promotedNetworkIdRef.current;
443
+ setGlobalActiveRuntime(null);
444
+ setActiveWalletSessionEcosystem(null);
445
+ if (prevNetworkId) {
446
+ releaseRuntime(prevNetworkId);
447
+ promotedNetworkIdRef.current = null;
448
+ }
347
449
  }
348
450
  }
349
451
  }
350
- loadAdapterAndConfigureUi();
452
+ loadRuntimeAndConfigureUi();
351
453
  return () => abortController.abort();
352
454
  }, [
353
455
  currentGlobalNetworkConfig,
354
- getAdapterForNetwork,
456
+ getRuntimeForNetwork,
457
+ releaseRuntime,
355
458
  loadConfigModule,
356
459
  uiKitConfigVersion,
357
460
  programmaticUiKitConfig
358
461
  ]);
359
462
  /**
360
463
  * Callback to set the globally active network ID.
361
- * Also clears dependent states (config, adapter, hooks) if the network ID is cleared.
464
+ * Also clears dependent states if the network ID is cleared.
362
465
  */
363
466
  const setActiveNetworkIdCallback = useCallback((networkId) => {
364
467
  logger.info("WalletStateProvider", `Setting global network ID to: ${networkId}`);
365
468
  setCurrentGlobalNetworkIdState(networkId);
366
469
  if (!networkId) {
367
470
  setCurrentGlobalNetworkConfig(null);
368
- setGlobalActiveAdapter(null);
369
- setIsGlobalAdapterLoading(false);
370
- setWalletFacadeHooks(null);
471
+ setGlobalActiveRuntime(null);
472
+ setIsGlobalRuntimeLoading(false);
473
+ setActiveWalletSessionEcosystem(null);
371
474
  }
372
475
  }, []);
373
476
  /**
374
- * Callback to explicitly trigger a re-configuration of the active adapter's UI kit.
477
+ * Callback to explicitly trigger a re-configuration of the active runtime's UI kit.
375
478
  * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.
376
479
  */
377
- const reconfigureActiveAdapterUiKit = useCallback((uiKitConfig) => {
480
+ const reconfigureActiveUiKit = useCallback((uiKitConfig) => {
378
481
  logger.info("WalletStateProvider", "Explicitly triggering UI kit re-configuration by bumping version.", uiKitConfig);
379
482
  setProgrammaticUiKitConfig(uiKitConfig);
380
483
  setUiKitConfigVersion((v) => v + 1);
381
484
  }, [setProgrammaticUiKitConfig, setUiKitConfigVersion]);
485
+ const activeWalletSession = useMemo(() => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem), [walletSessionRegistry, activeWalletSessionEcosystem]);
486
+ const walletFacadeHooks = activeWalletSession?.hooks ?? null;
382
487
  const contextValue = useMemo(() => ({
383
488
  activeNetworkId: currentGlobalNetworkId,
384
489
  setActiveNetworkId: setActiveNetworkIdCallback,
385
490
  activeNetworkConfig: currentGlobalNetworkConfig,
386
- activeAdapter: globalActiveAdapter,
387
- isAdapterLoading: isGlobalAdapterLoading,
491
+ activeRuntime: globalActiveRuntime,
492
+ isRuntimeLoading: isGlobalRuntimeLoading,
388
493
  walletFacadeHooks,
389
- reconfigureActiveAdapterUiKit
494
+ reconfigureActiveUiKit
390
495
  }), [
391
496
  currentGlobalNetworkId,
392
497
  setActiveNetworkIdCallback,
393
498
  currentGlobalNetworkConfig,
394
- globalActiveAdapter,
395
- isGlobalAdapterLoading,
499
+ globalActiveRuntime,
500
+ isGlobalRuntimeLoading,
396
501
  walletFacadeHooks,
397
- reconfigureActiveAdapterUiKit
502
+ reconfigureActiveUiKit
398
503
  ]);
399
- const ActualProviderToRender = AdapterUiContextProviderToRender;
504
+ const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;
400
505
  let childrenToRender;
401
506
  if (ActualProviderToRender) {
402
- const key = `${globalActiveAdapter?.networkConfig?.ecosystem || "unknown"}-${globalActiveAdapter?.networkConfig?.id || "unknown"}`;
403
- logger.info("[WSP RENDER]", "Rendering adapter-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
507
+ const key = activeWalletSessionEcosystem || "unknown";
508
+ logger.debug("[WSP RENDER]", "Rendering runtime-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
404
509
  childrenToRender = /* @__PURE__ */ jsx(ActualProviderToRender, { children }, key);
405
510
  } else {
406
- logger.info("[WSP RENDER]", "No adapter UI context provider to render. Rendering direct children.");
511
+ logger.debug("[WSP RENDER]", "No runtime UI context provider to render. Rendering direct children.");
407
512
  childrenToRender = children;
408
513
  }
409
514
  return /* @__PURE__ */ jsx(WalletStateContext.Provider, {
@@ -547,14 +652,14 @@ const useAnalytics = () => {
547
652
  //#endregion
548
653
  //#region src/hooks/useWalletComponents.ts
549
654
  /**
550
- * Hook that provides direct access to wallet UI components from the active adapter.
655
+ * Hook that provides direct access to wallet UI components from the active runtime.
551
656
  *
552
657
  * Use this hook when you need full control over the layout and composition of
553
658
  * wallet components. For standard layouts, prefer using `WalletConnectionUI`
554
659
  * with its props forwarding capabilities.
555
660
  *
556
- * @returns The wallet components object, or null if no adapter is active or
557
- * the adapter doesn't provide wallet components.
661
+ * @returns The wallet components object, or null if no runtime is active or
662
+ * the runtime doesn't provide wallet components.
558
663
  *
559
664
  * @example
560
665
  * ```tsx
@@ -589,10 +694,11 @@ const useAnalytics = () => {
589
694
  * ```
590
695
  */
591
696
  function useWalletComponents() {
592
- const { activeAdapter } = useWalletState();
593
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") return null;
697
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
698
+ const activeUiKit = activeRuntime?.uiKit;
699
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem) || !activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
594
700
  try {
595
- return activeAdapter.getEcosystemWalletComponents() ?? null;
701
+ return activeUiKit.getEcosystemWalletComponents() ?? null;
596
702
  } catch {
597
703
  return null;
598
704
  }
@@ -602,6 +708,10 @@ function useWalletComponents() {
602
708
  //#region src/hooks/useDerivedAccountStatus.ts
603
709
  const defaultAccountStatus = {
604
710
  isConnected: false,
711
+ isConnecting: false,
712
+ isDisconnected: true,
713
+ isReconnecting: false,
714
+ status: "disconnected",
605
715
  address: void 0,
606
716
  chainId: void 0
607
717
  };
@@ -616,6 +726,10 @@ function useDerivedAccountStatus() {
616
726
  const accountHookOutput = walletFacadeHooks?.useAccount ? walletFacadeHooks.useAccount() : void 0;
617
727
  if (isRecordWithProperties(accountHookOutput)) return {
618
728
  isConnected: "isConnected" in accountHookOutput && typeof accountHookOutput.isConnected === "boolean" ? accountHookOutput.isConnected : defaultAccountStatus.isConnected,
729
+ isConnecting: "isConnecting" in accountHookOutput && typeof accountHookOutput.isConnecting === "boolean" ? accountHookOutput.isConnecting : defaultAccountStatus.isConnecting,
730
+ isDisconnected: "isDisconnected" in accountHookOutput && typeof accountHookOutput.isDisconnected === "boolean" ? accountHookOutput.isDisconnected : defaultAccountStatus.isDisconnected,
731
+ isReconnecting: "isReconnecting" in accountHookOutput && typeof accountHookOutput.isReconnecting === "boolean" ? accountHookOutput.isReconnecting : defaultAccountStatus.isReconnecting,
732
+ status: "status" in accountHookOutput && typeof accountHookOutput.status === "string" ? accountHookOutput.status : defaultAccountStatus.status,
619
733
  address: "address" in accountHookOutput && typeof accountHookOutput.address === "string" ? accountHookOutput.address : defaultAccountStatus.address,
620
734
  chainId: "chainId" in accountHookOutput && typeof accountHookOutput.chainId === "number" ? accountHookOutput.chainId : defaultAccountStatus.chainId
621
735
  };
@@ -734,21 +848,21 @@ function useDerivedDisconnect() {
734
848
  * This hook detects that scenario and invokes a callback to re-queue the network switch.
735
849
  *
736
850
  * @param selectedNetworkConfigId - Currently selected network in the app
737
- * @param selectedAdapter - Currently active adapter instance
851
+ * @param selectedCapability - Currently active wallet capability instance
738
852
  * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)
739
853
  * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued
740
854
  */
741
- function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter, networkToSwitchTo, onRequeueSwitch) {
855
+ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedCapability, networkToSwitchTo, onRequeueSwitch) {
742
856
  const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();
743
857
  const prevConnectedRef = useRef(isConnected);
744
858
  useEffect(() => {
745
859
  const isReconnection = !prevConnectedRef.current && isConnected;
746
860
  prevConnectedRef.current = isConnected;
747
- if (!isReconnection || !selectedNetworkConfigId || !selectedAdapter) return;
861
+ if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) return;
748
862
  if (networkToSwitchTo === selectedNetworkConfigId) return;
749
- const adapterConfig = selectedAdapter.networkConfig;
750
- if (!("chainId" in adapterConfig) || !walletChainId) return;
751
- const targetChainId = Number(adapterConfig.chainId);
863
+ const selectedNetworkConfig = selectedCapability.networkConfig;
864
+ if (!("chainId" in selectedNetworkConfig) || !walletChainId) return;
865
+ const targetChainId = Number(selectedNetworkConfig.chainId);
752
866
  if (walletChainId !== targetChainId) {
753
867
  logger.info("useWalletReconnectionHandler", `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`);
754
868
  onRequeueSwitch(selectedNetworkConfigId);
@@ -757,7 +871,7 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
757
871
  isConnected,
758
872
  walletChainId,
759
873
  selectedNetworkConfigId,
760
- selectedAdapter,
874
+ selectedCapability,
761
875
  networkToSwitchTo,
762
876
  onRequeueSwitch
763
877
  ]);
@@ -789,37 +903,20 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
789
903
  */
790
904
  const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps, networkSwitcherProps }) => {
791
905
  const [isError, setIsError] = useState(false);
792
- const { activeAdapter, walletFacadeHooks } = useWalletState();
793
- useEffect(() => {
794
- logger.debug("WalletConnectionUI", "[Debug] State from useWalletState:", {
795
- adapterId: activeAdapter?.networkConfig.id,
796
- hasFacadeHooks: !!walletFacadeHooks
797
- });
798
- }, [activeAdapter, walletFacadeHooks]);
906
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
907
+ const activeUiKit = activeRuntime?.uiKit;
908
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem)) return null;
799
909
  const walletComponents = (() => {
800
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") {
801
- logger.debug("WalletConnectionUI", "[Debug] No activeAdapter or getEcosystemWalletComponents method, returning null.");
802
- return null;
803
- }
910
+ if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
804
911
  try {
805
- const components = activeAdapter.getEcosystemWalletComponents();
806
- logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
807
- return components;
912
+ return activeUiKit.getEcosystemWalletComponents();
808
913
  } catch (error) {
809
- logger.error("WalletConnectionUI", "[Debug] Error getting wallet components:", error);
914
+ logger.error("WalletConnectionUI", "Error getting wallet components:", error);
810
915
  setIsError(true);
811
916
  return null;
812
917
  }
813
918
  })();
814
- if (!walletComponents) {
815
- logger.debug("WalletConnectionUI", "[Debug] getEcosystemWalletComponents returned null/undefined, rendering null.");
816
- return null;
817
- }
818
- logger.debug("WalletConnectionUI", "Rendering wallet components:", {
819
- hasConnectButton: !!walletComponents.ConnectButton,
820
- hasAccountDisplay: !!walletComponents.AccountDisplay,
821
- hasNetworkSwitcher: !!walletComponents.NetworkSwitcher
822
- });
919
+ if (!walletComponents) return null;
823
920
  const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;
824
921
  if (isError) return /* @__PURE__ */ jsx("div", {
825
922
  className: cn("flex items-center gap-4", className),
@@ -847,18 +944,8 @@ const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps
847
944
  * Uses useWalletState to get its data.
848
945
  */
849
946
  const WalletConnectionHeader = () => {
850
- const { isAdapterLoading, activeAdapter } = useWalletState();
851
- useEffect(() => {
852
- logger.debug("WalletConnectionHeader", "[Debug] State from useWalletState:", {
853
- adapterPresent: !!activeAdapter,
854
- adapterNetwork: activeAdapter?.networkConfig.id,
855
- isLoading: isAdapterLoading
856
- });
857
- }, [activeAdapter, isAdapterLoading]);
858
- if (isAdapterLoading) {
859
- logger.debug("WalletConnectionHeader", "[Debug] Adapter loading, showing skeleton.");
860
- return /* @__PURE__ */ jsx("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
861
- }
947
+ const { isRuntimeLoading } = useWalletState();
948
+ if (isRuntimeLoading) return /* @__PURE__ */ jsx("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
862
949
  return /* @__PURE__ */ jsx(WalletConnectionUI, {});
863
950
  };
864
951
 
@@ -878,7 +965,7 @@ const WalletConnectionHeader = () => {
878
965
  * - Tracks switch attempts to prevent duplicate operations
879
966
  * - Provides completion callback for parent components to handle state cleanup
880
967
  */
881
- const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplete }) => {
968
+ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetworkSwitchComplete }) => {
882
969
  const isMountedRef = useRef(true);
883
970
  const [hasAttemptedSwitch, setHasAttemptedSwitch] = useState(false);
884
971
  const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();
@@ -895,7 +982,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
895
982
  useEffect(() => {
896
983
  logger.info("NetworkSwitchManager", "State Update:", {
897
984
  target: targetNetworkId,
898
- adapterNetwork: adapter.networkConfig.id,
985
+ walletNetwork: wallet.networkConfig.id,
899
986
  isSwitching: isSwitchingNetworkViaHook,
900
987
  hookError: !!switchNetworkError,
901
988
  canExec: !!execSwitchNetwork,
@@ -904,7 +991,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
904
991
  attempted: hasAttemptedSwitch
905
992
  });
906
993
  }, [
907
- adapter,
994
+ wallet,
908
995
  targetNetworkId,
909
996
  isSwitchingNetworkViaHook,
910
997
  switchNetworkError,
@@ -931,19 +1018,23 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
931
1018
  logger.info("NetworkSwitchManager", "Previous switch attempt concluded. Deferring to completion effect.");
932
1019
  return;
933
1020
  }
934
- if (adapter.networkConfig.id !== targetNetworkId) {
935
- completeOperation(`CRITICAL: Adapter (${adapter.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
1021
+ if (!networkCatalog.getNetworks().find((network) => network.id === targetNetworkId)) {
1022
+ completeOperation(`Target network ${targetNetworkId} is not present in the network catalog.`, { notifyComplete: false });
1023
+ return;
1024
+ }
1025
+ if (wallet.networkConfig.id !== targetNetworkId) {
1026
+ completeOperation(`CRITICAL: Wallet capability (${wallet.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
936
1027
  return;
937
1028
  }
938
1029
  if (!isConnected) {
939
1030
  completeOperation("Wallet not connected (derived status). Awaiting connection.", { notifyComplete: false });
940
1031
  return;
941
1032
  }
942
- if (!("chainId" in adapter.networkConfig)) {
1033
+ if (!("chainId" in wallet.networkConfig)) {
943
1034
  completeOperation("Network does not support chain switching (non-EVM). Operation complete (no-op).");
944
1035
  return;
945
1036
  }
946
- const targetChainToBeSwitchedTo = Number(adapter.networkConfig.chainId);
1037
+ const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);
947
1038
  if (currentChainIdFromHook === targetChainToBeSwitchedTo) {
948
1039
  completeOperation("Already on correct chain (derived status). Operation complete.");
949
1040
  return;
@@ -960,7 +1051,8 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
960
1051
  const timeoutId = setTimeout(performSwitchActual, 100);
961
1052
  return () => clearTimeout(timeoutId);
962
1053
  }, [
963
- adapter,
1054
+ wallet,
1055
+ networkCatalog,
964
1056
  targetNetworkId,
965
1057
  execSwitchNetwork,
966
1058
  isSwitchingNetworkViaHook,
@@ -992,5 +1084,5 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
992
1084
  };
993
1085
 
994
1086
  //#endregion
995
- export { AdapterContext, AdapterProvider, AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, VERSION, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAdapterContext, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useWalletComponents, useWalletReconnectionHandler, useWalletState };
1087
+ export { AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, RuntimeContext, RuntimeProvider, VERSION, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useRuntimeContext, useWalletComponents, useWalletReconnectionHandler, useWalletState };
996
1088
  //# sourceMappingURL=index.mjs.map