@openzeppelin/ui-react 1.2.0 → 2.0.0

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,94 +4,131 @@ 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.0";
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);
58
59
  useEffect(() => {
59
- const adapterCount = Object.keys(adapterRegistry).length;
60
- if (adapterCount > 0) logger.info("AdapterProvider", `Registry contains ${adapterCount} adapters:`, {
61
- networkIds: Object.keys(adapterRegistry),
60
+ runtimeRegistryRef.current = runtimeRegistry;
61
+ }, [runtimeRegistry]);
62
+ useEffect(() => {
63
+ return () => {
64
+ Object.values(runtimeRegistryRef.current).forEach((runtime) => {
65
+ runtime.dispose();
66
+ });
67
+ };
68
+ }, []);
69
+ useEffect(() => {
70
+ const runtimeCount = Object.keys(runtimeRegistry).length;
71
+ if (runtimeCount > 0) logger.info("RuntimeProvider", `Registry contains ${runtimeCount} runtimes:`, {
72
+ networkIds: Object.keys(runtimeRegistry),
62
73
  loadingCount: loadingNetworks.size,
63
74
  loadingNetworkIds: Array.from(loadingNetworks)
64
75
  });
65
- }, [adapterRegistry, loadingNetworks]);
76
+ }, [runtimeRegistry, loadingNetworks]);
77
+ /**
78
+ * Evicts a runtime from the registry by network ID and disposes it.
79
+ * This is the safe counterpart to `getRuntimeForNetwork`: callers should only
80
+ * release a runtime after they have already promoted its replacement.
81
+ *
82
+ * Disposal is deferred to the next macrotask so React's commit phase
83
+ * (including development-mode prop diffing) can finish reading the old
84
+ * runtime's properties without hitting the RuntimeDisposedError proxy trap.
85
+ */
86
+ const releaseRuntime = useCallback((networkId) => {
87
+ const runtime = runtimeRegistryRef.current[networkId];
88
+ if (!runtime) return;
89
+ logger.info("RuntimeProvider", `Releasing runtime for network ${networkId}`);
90
+ setRuntimeRegistry((prev) => {
91
+ const next = { ...prev };
92
+ delete next[networkId];
93
+ return next;
94
+ });
95
+ setTimeout(() => {
96
+ try {
97
+ runtime.dispose();
98
+ } catch (error) {
99
+ logger.error("RuntimeProvider", `Error disposing runtime for network ${networkId}:`, error);
100
+ }
101
+ }, 0);
102
+ }, []);
66
103
  /**
67
- * Function to get or create an adapter for a network
104
+ * Function to get or create a runtime for a network
68
105
  *
69
- * IMPORTANT: The actual adapter loading is handled in the useConfiguredAdapterSingleton hook
70
- * to avoid React state updates during render, which would cause errors.
106
+ * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates
107
+ * during render, which would cause errors.
71
108
  *
72
109
  * 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
110
+ * 1. Returns existing runtimes immediately if available
111
+ * 2. Reports loading state for runtimes being initialized
112
+ * 3. Initiates runtime loading when needed
76
113
  */
77
- const getAdapterForNetwork = useCallback((networkConfig) => {
114
+ const getRuntimeForNetwork = useCallback((networkConfig) => {
78
115
  if (!networkConfig) return {
79
- adapter: null,
116
+ runtime: null,
80
117
  isLoading: false
81
118
  };
82
119
  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}`);
120
+ logger.debug("RuntimeProvider", `Runtime requested for network ${networkId}`);
121
+ if (runtimeRegistry[networkId]) {
122
+ logger.debug("RuntimeProvider", `Using existing runtime for network ${networkId}`);
86
123
  return {
87
- adapter: adapterRegistry[networkId],
124
+ runtime: runtimeRegistry[networkId],
88
125
  isLoading: false
89
126
  };
90
127
  }
91
128
  if (loadingNetworks.has(networkId)) {
92
- logger.debug("AdapterProvider", `Adapter for network ${networkId} is currently loading`);
129
+ logger.debug("RuntimeProvider", `Runtime for network ${networkId} is currently loading`);
93
130
  return {
94
- adapter: null,
131
+ runtime: null,
95
132
  isLoading: true
96
133
  };
97
134
  }
@@ -100,15 +137,15 @@ function AdapterProvider({ children, resolveAdapter }) {
100
137
  newSet.add(networkId);
101
138
  return newSet;
102
139
  });
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)
140
+ logger.info("RuntimeProvider", `Starting runtime initialization for network ${networkId} (${networkConfig.name})`);
141
+ resolveRuntime(networkConfig).then((runtime) => {
142
+ logger.info("RuntimeProvider", `Runtime for network ${networkId} loaded successfully`, {
143
+ type: runtime.constructor.name,
144
+ objectId: Object.prototype.toString.call(runtime)
108
145
  });
109
- setAdapterRegistry((prev) => ({
146
+ setRuntimeRegistry((prev) => ({
110
147
  ...prev,
111
- [networkId]: adapter
148
+ [networkId]: runtime
112
149
  }));
113
150
  setLoadingNetworks((prev) => {
114
151
  const newSet = new Set(prev);
@@ -116,7 +153,7 @@ function AdapterProvider({ children, resolveAdapter }) {
116
153
  return newSet;
117
154
  });
118
155
  }).catch((error) => {
119
- logger.error("AdapterProvider", `Error loading adapter for network ${networkId}:`, error);
156
+ logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
120
157
  setLoadingNetworks((prev) => {
121
158
  const newSet = new Set(prev);
122
159
  newSet.delete(networkId);
@@ -124,16 +161,19 @@ function AdapterProvider({ children, resolveAdapter }) {
124
161
  });
125
162
  });
126
163
  return {
127
- adapter: null,
164
+ runtime: null,
128
165
  isLoading: true
129
166
  };
130
167
  }, [
131
- adapterRegistry,
168
+ runtimeRegistry,
132
169
  loadingNetworks,
133
- resolveAdapter
170
+ resolveRuntime
134
171
  ]);
135
- const contextValue = useMemo(() => ({ getAdapterForNetwork }), [getAdapterForNetwork]);
136
- return /* @__PURE__ */ jsx(AdapterContext.Provider, {
172
+ const contextValue = useMemo(() => ({
173
+ getRuntimeForNetwork,
174
+ releaseRuntime
175
+ }), [getRuntimeForNetwork, releaseRuntime]);
176
+ return /* @__PURE__ */ jsx(RuntimeContext.Provider, {
137
177
  value: contextValue,
138
178
  children
139
179
  });
@@ -151,7 +191,7 @@ function AdapterProvider({ children, resolveAdapter }) {
151
191
  * consuming package's bundle. This creates MULTIPLE instances of this module:
152
192
  *
153
193
  * 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
194
+ * 2. The adapter package's inlined copy → .vite/deps/@openzeppelin_adapter_evm.js
155
195
  *
156
196
  * Since React contexts use referential identity, these two module instances have
157
197
  * DIFFERENT context objects. When the adapter's components call useWalletState(),
@@ -213,54 +253,88 @@ function useWalletState() {
213
253
  /**
214
254
  * useAdapterContext.ts
215
255
  *
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.
256
+ * This file provides a hook to access the runtime context throughout the application.
257
+ * It's a critical part of the runtime singleton pattern, allowing components to
258
+ * access the centralized runtime registry.
219
259
  *
220
- * The adapter singleton pattern ensures:
221
- * - Only one adapter instance exists per network
260
+ * The runtime singleton pattern ensures:
261
+ * - Only one runtime instance exists per network
222
262
  * - Wallet connection state is consistent across the app
223
- * - Better performance by eliminating redundant adapter initialization
263
+ * - Better performance by eliminating redundant runtime initialization
224
264
  */
225
265
  /**
226
- * Hook to access the adapter context
266
+ * Hook to access the runtime context
227
267
  *
228
- * This hook provides access to the getAdapterForNetwork function which
229
- * retrieves or creates adapter instances from the singleton registry.
268
+ * This hook provides access to the `getRuntimeForNetwork` function which
269
+ * retrieves or creates runtime instances from the singleton registry.
230
270
  *
231
- * Components should typically use useConfiguredAdapterSingleton instead
271
+ * Components should typically use the higher-level wallet/runtime hooks instead
232
272
  * of this hook directly, as it handles React state update timing properly.
233
273
  *
234
- * @throws Error if used outside of an AdapterProvider context
235
- * @returns The adapter context value
274
+ * @throws Error if used outside of a RuntimeProvider context
275
+ * @returns The runtime context value
236
276
  */
237
- function useAdapterContext() {
238
- const context = useContext(AdapterContext);
239
- if (!context) throw new Error("useAdapterContext must be used within an AdapterProvider");
277
+ function useRuntimeContext() {
278
+ const context = useContext(RuntimeContext);
279
+ if (!context) throw new Error("useRuntimeContext must be used within a RuntimeProvider");
240
280
  return context;
241
281
  }
242
282
 
283
+ //#endregion
284
+ //#region src/hooks/walletSessionRegistry.ts
285
+ /**
286
+ * Returns the cached wallet session for an ecosystem, if present.
287
+ *
288
+ * @param registry - Current internal wallet session registry.
289
+ * @param ecosystem - Ecosystem key to resolve.
290
+ * @returns The cached session entry or `null` when the ecosystem has not been configured yet.
291
+ */
292
+ function getWalletSession(registry, ecosystem) {
293
+ if (!ecosystem) return null;
294
+ return registry[ecosystem] ?? null;
295
+ }
296
+ /**
297
+ * Inserts or replaces the cached wallet session for a single ecosystem.
298
+ *
299
+ * @param registry - Current internal wallet session registry.
300
+ * @param session - Session entry to cache.
301
+ * @returns A new registry object containing the upserted ecosystem session.
302
+ */
303
+ function upsertWalletSession(registry, session) {
304
+ return {
305
+ ...registry,
306
+ [session.ecosystem]: session
307
+ };
308
+ }
309
+
243
310
  //#endregion
244
311
  //#region src/hooks/WalletStateProvider.tsx
245
312
  /**
246
- * Configures the adapter's UI kit and returns the UI provider component and hooks.
313
+ * Configures the runtime's UI kit capability and returns the ecosystem session artifacts.
247
314
  */
248
- async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOverrides = {}) {
315
+ async function configureRuntimeUiKit(runtime, loadConfigModule, programmaticOverrides = {}) {
316
+ const uiKit = runtime.uiKit;
317
+ if (!uiKit) return {
318
+ providerComponent: null,
319
+ hooks: null
320
+ };
249
321
  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);
322
+ const hasUiKitOverride = Object.keys(programmaticOverrides).length > 0;
323
+ if (typeof uiKit.configureUiKit === "function") {
324
+ const nextUiKitConfig = { ...programmaticOverrides };
325
+ 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}`);
326
+ await uiKit.configureUiKit(nextUiKitConfig, { loadUiKitNativeConfig: loadConfigModule });
327
+ logger.info("[WSP configureRuntimeUiKit] configureUiKit completed for runtime:", runtime.networkConfig.id);
254
328
  }
255
- const providerComponent = adapter.getEcosystemReactUiContextProvider?.() || null;
256
- const hooks = adapter.getEcosystemReactHooks?.() || null;
257
- logger.info("[WSP configureAdapterUiKit]", "UI provider and hooks retrieved successfully.");
329
+ const providerComponent = uiKit.getEcosystemReactUiContextProvider?.() || null;
330
+ const hooks = uiKit.getEcosystemReactHooks?.() || null;
331
+ logger.info("[WSP configureRuntimeUiKit]", "UI provider and hooks retrieved successfully.");
258
332
  return {
259
333
  providerComponent,
260
334
  hooks
261
335
  };
262
336
  } catch (error) {
263
- logger.error("[WSP configureAdapterUiKit]", "Error during adapter UI setup:", error);
337
+ logger.error("[WSP configureRuntimeUiKit]", "Error during runtime UI setup:", error);
264
338
  throw error;
265
339
  }
266
340
  }
@@ -270,26 +344,28 @@ async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOver
270
344
  * It is responsible for:
271
345
  * 1. Managing the globally selected active network ID (`activeNetworkId`).
272
346
  * 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.
347
+ * 3. Fetching and providing the corresponding `EcosystemRuntime` (`activeRuntime`) for the active network,
348
+ * leveraging the `RuntimeProvider` to ensure runtime singletons.
349
+ * 4. Caching ecosystem-scoped wallet session artifacts (provider roots and facade hooks)
350
+ * independently from the network-scoped runtime.
351
+ * 5. Rendering the active ecosystem wallet provider (e.g., WagmiProvider for EVM) around its
352
+ * children, which is essential for the facade hooks to function correctly.
278
353
  * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.
279
354
  *
280
355
  * Consumers use the `useWalletState()` hook to access this global state.
281
- * It should be placed high in the component tree, inside an `<AdapterProvider>`.
356
+ * It should be placed high in the component tree, inside a `<RuntimeProvider>`.
282
357
  */
283
358
  function WalletStateProvider({ children, initialNetworkId = null, getNetworkConfigById, loadConfigModule }) {
284
359
  const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = useState(initialNetworkId);
285
360
  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);
361
+ const [globalActiveRuntime, setGlobalActiveRuntime] = useState(null);
362
+ const [isGlobalRuntimeLoading, setIsGlobalRuntimeLoading] = useState(false);
363
+ const [walletSessionRegistry, setWalletSessionRegistry] = useState({});
364
+ const [activeWalletSessionEcosystem, setActiveWalletSessionEcosystem] = useState(null);
290
365
  const [uiKitConfigVersion, setUiKitConfigVersion] = useState(0);
291
366
  const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = useState(void 0);
292
- const { getAdapterForNetwork } = useAdapterContext();
367
+ const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();
368
+ const promotedNetworkIdRef = useRef(null);
293
369
  useEffect(() => {
294
370
  const abortController = new AbortController();
295
371
  async function fetchNetworkConfig() {
@@ -312,98 +388,115 @@ function WalletStateProvider({ children, initialNetworkId = null, getNetworkConf
312
388
  }, [currentGlobalNetworkId, getNetworkConfigById]);
313
389
  useEffect(() => {
314
390
  const abortController = new AbortController();
315
- async function loadAdapterAndConfigureUi() {
391
+ async function loadRuntimeAndConfigureUi() {
316
392
  if (!currentGlobalNetworkConfig) {
317
393
  if (!abortController.signal.aborted) {
318
- setGlobalActiveAdapter(null);
319
- setIsGlobalAdapterLoading(false);
320
- setAdapterUiContextProviderToRender(null);
321
- setWalletFacadeHooks(null);
394
+ const prevNetworkId = promotedNetworkIdRef.current;
395
+ setGlobalActiveRuntime(null);
396
+ setIsGlobalRuntimeLoading(false);
397
+ setActiveWalletSessionEcosystem(null);
398
+ if (prevNetworkId) {
399
+ releaseRuntime(prevNetworkId);
400
+ promotedNetworkIdRef.current = null;
401
+ }
322
402
  }
323
403
  return;
324
404
  }
325
- const { adapter: newAdapter, isLoading: newIsLoading } = getAdapterForNetwork(currentGlobalNetworkConfig);
405
+ const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(currentGlobalNetworkConfig);
326
406
  if (abortController.signal.aborted) return;
327
- setIsGlobalAdapterLoading(newIsLoading);
328
- if (newAdapter && !newIsLoading) try {
329
- const { providerComponent, hooks } = await configureAdapterUiKit(newAdapter, loadConfigModule, programmaticUiKitConfig);
407
+ setIsGlobalRuntimeLoading(newIsLoading);
408
+ if (newRuntime && !newIsLoading) try {
409
+ const { providerComponent, hooks } = await configureRuntimeUiKit(newRuntime, loadConfigModule, programmaticUiKitConfig);
330
410
  if (!abortController.signal.aborted) {
331
- setAdapterUiContextProviderToRender(() => providerComponent);
332
- setWalletFacadeHooks(hooks);
333
- setGlobalActiveAdapter(newAdapter);
411
+ const ecosystem = newRuntime.networkConfig.ecosystem;
412
+ const prevNetworkId = promotedNetworkIdRef.current;
413
+ const nextNetworkId = newRuntime.networkConfig.id;
414
+ setWalletSessionRegistry((prevRegistry) => upsertWalletSession(prevRegistry, {
415
+ ecosystem,
416
+ lastConfiguredNetworkId: nextNetworkId,
417
+ providerComponent,
418
+ hooks
419
+ }));
420
+ setGlobalActiveRuntime(newRuntime);
421
+ setActiveWalletSessionEcosystem(ecosystem);
422
+ promotedNetworkIdRef.current = nextNetworkId;
423
+ if (prevNetworkId && prevNetworkId !== nextNetworkId) releaseRuntime(prevNetworkId);
334
424
  }
335
425
  } 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
- }
426
+ if (!abortController.signal.aborted) logger.error("[WSP loadRuntimeAndConfigureUi]", "Error during runtime UI setup:", error);
341
427
  }
342
- else if (!newAdapter && !newIsLoading) {
428
+ else if (!newRuntime && !newIsLoading) {
343
429
  if (!abortController.signal.aborted) {
344
- setAdapterUiContextProviderToRender(null);
345
- setWalletFacadeHooks(null);
346
- setGlobalActiveAdapter(null);
430
+ const prevNetworkId = promotedNetworkIdRef.current;
431
+ setGlobalActiveRuntime(null);
432
+ setActiveWalletSessionEcosystem(null);
433
+ if (prevNetworkId) {
434
+ releaseRuntime(prevNetworkId);
435
+ promotedNetworkIdRef.current = null;
436
+ }
347
437
  }
348
438
  }
349
439
  }
350
- loadAdapterAndConfigureUi();
440
+ loadRuntimeAndConfigureUi();
351
441
  return () => abortController.abort();
352
442
  }, [
353
443
  currentGlobalNetworkConfig,
354
- getAdapterForNetwork,
444
+ getRuntimeForNetwork,
445
+ releaseRuntime,
355
446
  loadConfigModule,
356
447
  uiKitConfigVersion,
357
448
  programmaticUiKitConfig
358
449
  ]);
359
450
  /**
360
451
  * Callback to set the globally active network ID.
361
- * Also clears dependent states (config, adapter, hooks) if the network ID is cleared.
452
+ * Also clears dependent states if the network ID is cleared.
362
453
  */
363
454
  const setActiveNetworkIdCallback = useCallback((networkId) => {
364
455
  logger.info("WalletStateProvider", `Setting global network ID to: ${networkId}`);
365
456
  setCurrentGlobalNetworkIdState(networkId);
366
457
  if (!networkId) {
367
458
  setCurrentGlobalNetworkConfig(null);
368
- setGlobalActiveAdapter(null);
369
- setIsGlobalAdapterLoading(false);
370
- setWalletFacadeHooks(null);
459
+ setGlobalActiveRuntime(null);
460
+ setIsGlobalRuntimeLoading(false);
461
+ setActiveWalletSessionEcosystem(null);
371
462
  }
372
463
  }, []);
373
464
  /**
374
- * Callback to explicitly trigger a re-configuration of the active adapter's UI kit.
465
+ * Callback to explicitly trigger a re-configuration of the active runtime's UI kit.
375
466
  * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.
376
467
  */
377
- const reconfigureActiveAdapterUiKit = useCallback((uiKitConfig) => {
468
+ const reconfigureActiveUiKit = useCallback((uiKitConfig) => {
378
469
  logger.info("WalletStateProvider", "Explicitly triggering UI kit re-configuration by bumping version.", uiKitConfig);
379
470
  setProgrammaticUiKitConfig(uiKitConfig);
380
471
  setUiKitConfigVersion((v) => v + 1);
381
472
  }, [setProgrammaticUiKitConfig, setUiKitConfigVersion]);
473
+ const activeWalletSession = useMemo(() => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem), [walletSessionRegistry, activeWalletSessionEcosystem]);
474
+ const walletFacadeHooks = activeWalletSession?.hooks ?? null;
382
475
  const contextValue = useMemo(() => ({
383
476
  activeNetworkId: currentGlobalNetworkId,
384
477
  setActiveNetworkId: setActiveNetworkIdCallback,
385
478
  activeNetworkConfig: currentGlobalNetworkConfig,
386
- activeAdapter: globalActiveAdapter,
387
- isAdapterLoading: isGlobalAdapterLoading,
479
+ activeRuntime: globalActiveRuntime,
480
+ isRuntimeLoading: isGlobalRuntimeLoading,
388
481
  walletFacadeHooks,
389
- reconfigureActiveAdapterUiKit
482
+ reconfigureActiveUiKit
390
483
  }), [
391
484
  currentGlobalNetworkId,
392
485
  setActiveNetworkIdCallback,
393
486
  currentGlobalNetworkConfig,
394
- globalActiveAdapter,
395
- isGlobalAdapterLoading,
487
+ globalActiveRuntime,
488
+ isGlobalRuntimeLoading,
396
489
  walletFacadeHooks,
397
- reconfigureActiveAdapterUiKit
490
+ reconfigureActiveUiKit
398
491
  ]);
399
- const ActualProviderToRender = AdapterUiContextProviderToRender;
492
+ const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;
400
493
  let childrenToRender;
401
494
  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);
495
+ const key = activeWalletSessionEcosystem || "unknown";
496
+ logger.debug("[WSP RENDER]", "Rendering runtime-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
404
497
  childrenToRender = /* @__PURE__ */ jsx(ActualProviderToRender, { children }, key);
405
498
  } else {
406
- logger.info("[WSP RENDER]", "No adapter UI context provider to render. Rendering direct children.");
499
+ logger.debug("[WSP RENDER]", "No runtime UI context provider to render. Rendering direct children.");
407
500
  childrenToRender = children;
408
501
  }
409
502
  return /* @__PURE__ */ jsx(WalletStateContext.Provider, {
@@ -547,14 +640,14 @@ const useAnalytics = () => {
547
640
  //#endregion
548
641
  //#region src/hooks/useWalletComponents.ts
549
642
  /**
550
- * Hook that provides direct access to wallet UI components from the active adapter.
643
+ * Hook that provides direct access to wallet UI components from the active runtime.
551
644
  *
552
645
  * Use this hook when you need full control over the layout and composition of
553
646
  * wallet components. For standard layouts, prefer using `WalletConnectionUI`
554
647
  * with its props forwarding capabilities.
555
648
  *
556
- * @returns The wallet components object, or null if no adapter is active or
557
- * the adapter doesn't provide wallet components.
649
+ * @returns The wallet components object, or null if no runtime is active or
650
+ * the runtime doesn't provide wallet components.
558
651
  *
559
652
  * @example
560
653
  * ```tsx
@@ -589,10 +682,11 @@ const useAnalytics = () => {
589
682
  * ```
590
683
  */
591
684
  function useWalletComponents() {
592
- const { activeAdapter } = useWalletState();
593
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") return null;
685
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
686
+ const activeUiKit = activeRuntime?.uiKit;
687
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem) || !activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
594
688
  try {
595
- return activeAdapter.getEcosystemWalletComponents() ?? null;
689
+ return activeUiKit.getEcosystemWalletComponents() ?? null;
596
690
  } catch {
597
691
  return null;
598
692
  }
@@ -602,6 +696,10 @@ function useWalletComponents() {
602
696
  //#region src/hooks/useDerivedAccountStatus.ts
603
697
  const defaultAccountStatus = {
604
698
  isConnected: false,
699
+ isConnecting: false,
700
+ isDisconnected: true,
701
+ isReconnecting: false,
702
+ status: "disconnected",
605
703
  address: void 0,
606
704
  chainId: void 0
607
705
  };
@@ -616,6 +714,10 @@ function useDerivedAccountStatus() {
616
714
  const accountHookOutput = walletFacadeHooks?.useAccount ? walletFacadeHooks.useAccount() : void 0;
617
715
  if (isRecordWithProperties(accountHookOutput)) return {
618
716
  isConnected: "isConnected" in accountHookOutput && typeof accountHookOutput.isConnected === "boolean" ? accountHookOutput.isConnected : defaultAccountStatus.isConnected,
717
+ isConnecting: "isConnecting" in accountHookOutput && typeof accountHookOutput.isConnecting === "boolean" ? accountHookOutput.isConnecting : defaultAccountStatus.isConnecting,
718
+ isDisconnected: "isDisconnected" in accountHookOutput && typeof accountHookOutput.isDisconnected === "boolean" ? accountHookOutput.isDisconnected : defaultAccountStatus.isDisconnected,
719
+ isReconnecting: "isReconnecting" in accountHookOutput && typeof accountHookOutput.isReconnecting === "boolean" ? accountHookOutput.isReconnecting : defaultAccountStatus.isReconnecting,
720
+ status: "status" in accountHookOutput && typeof accountHookOutput.status === "string" ? accountHookOutput.status : defaultAccountStatus.status,
619
721
  address: "address" in accountHookOutput && typeof accountHookOutput.address === "string" ? accountHookOutput.address : defaultAccountStatus.address,
620
722
  chainId: "chainId" in accountHookOutput && typeof accountHookOutput.chainId === "number" ? accountHookOutput.chainId : defaultAccountStatus.chainId
621
723
  };
@@ -734,21 +836,21 @@ function useDerivedDisconnect() {
734
836
  * This hook detects that scenario and invokes a callback to re-queue the network switch.
735
837
  *
736
838
  * @param selectedNetworkConfigId - Currently selected network in the app
737
- * @param selectedAdapter - Currently active adapter instance
839
+ * @param selectedCapability - Currently active wallet capability instance
738
840
  * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)
739
841
  * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued
740
842
  */
741
- function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter, networkToSwitchTo, onRequeueSwitch) {
843
+ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedCapability, networkToSwitchTo, onRequeueSwitch) {
742
844
  const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();
743
845
  const prevConnectedRef = useRef(isConnected);
744
846
  useEffect(() => {
745
847
  const isReconnection = !prevConnectedRef.current && isConnected;
746
848
  prevConnectedRef.current = isConnected;
747
- if (!isReconnection || !selectedNetworkConfigId || !selectedAdapter) return;
849
+ if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) return;
748
850
  if (networkToSwitchTo === selectedNetworkConfigId) return;
749
- const adapterConfig = selectedAdapter.networkConfig;
750
- if (!("chainId" in adapterConfig) || !walletChainId) return;
751
- const targetChainId = Number(adapterConfig.chainId);
851
+ const selectedNetworkConfig = selectedCapability.networkConfig;
852
+ if (!("chainId" in selectedNetworkConfig) || !walletChainId) return;
853
+ const targetChainId = Number(selectedNetworkConfig.chainId);
752
854
  if (walletChainId !== targetChainId) {
753
855
  logger.info("useWalletReconnectionHandler", `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`);
754
856
  onRequeueSwitch(selectedNetworkConfigId);
@@ -757,7 +859,7 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
757
859
  isConnected,
758
860
  walletChainId,
759
861
  selectedNetworkConfigId,
760
- selectedAdapter,
862
+ selectedCapability,
761
863
  networkToSwitchTo,
762
864
  onRequeueSwitch
763
865
  ]);
@@ -789,37 +891,20 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
789
891
  */
790
892
  const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps, networkSwitcherProps }) => {
791
893
  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]);
894
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
895
+ const activeUiKit = activeRuntime?.uiKit;
896
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem)) return null;
799
897
  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
- }
898
+ if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
804
899
  try {
805
- const components = activeAdapter.getEcosystemWalletComponents();
806
- logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
807
- return components;
900
+ return activeUiKit.getEcosystemWalletComponents();
808
901
  } catch (error) {
809
- logger.error("WalletConnectionUI", "[Debug] Error getting wallet components:", error);
902
+ logger.error("WalletConnectionUI", "Error getting wallet components:", error);
810
903
  setIsError(true);
811
904
  return null;
812
905
  }
813
906
  })();
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
- });
907
+ if (!walletComponents) return null;
823
908
  const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;
824
909
  if (isError) return /* @__PURE__ */ jsx("div", {
825
910
  className: cn("flex items-center gap-4", className),
@@ -847,18 +932,8 @@ const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps
847
932
  * Uses useWalletState to get its data.
848
933
  */
849
934
  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
- }
935
+ const { isRuntimeLoading } = useWalletState();
936
+ if (isRuntimeLoading) return /* @__PURE__ */ jsx("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
862
937
  return /* @__PURE__ */ jsx(WalletConnectionUI, {});
863
938
  };
864
939
 
@@ -878,7 +953,7 @@ const WalletConnectionHeader = () => {
878
953
  * - Tracks switch attempts to prevent duplicate operations
879
954
  * - Provides completion callback for parent components to handle state cleanup
880
955
  */
881
- const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplete }) => {
956
+ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetworkSwitchComplete }) => {
882
957
  const isMountedRef = useRef(true);
883
958
  const [hasAttemptedSwitch, setHasAttemptedSwitch] = useState(false);
884
959
  const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();
@@ -895,7 +970,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
895
970
  useEffect(() => {
896
971
  logger.info("NetworkSwitchManager", "State Update:", {
897
972
  target: targetNetworkId,
898
- adapterNetwork: adapter.networkConfig.id,
973
+ walletNetwork: wallet.networkConfig.id,
899
974
  isSwitching: isSwitchingNetworkViaHook,
900
975
  hookError: !!switchNetworkError,
901
976
  canExec: !!execSwitchNetwork,
@@ -904,7 +979,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
904
979
  attempted: hasAttemptedSwitch
905
980
  });
906
981
  }, [
907
- adapter,
982
+ wallet,
908
983
  targetNetworkId,
909
984
  isSwitchingNetworkViaHook,
910
985
  switchNetworkError,
@@ -931,19 +1006,23 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
931
1006
  logger.info("NetworkSwitchManager", "Previous switch attempt concluded. Deferring to completion effect.");
932
1007
  return;
933
1008
  }
934
- if (adapter.networkConfig.id !== targetNetworkId) {
935
- completeOperation(`CRITICAL: Adapter (${adapter.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
1009
+ if (!networkCatalog.getNetworks().find((network) => network.id === targetNetworkId)) {
1010
+ completeOperation(`Target network ${targetNetworkId} is not present in the network catalog.`, { notifyComplete: false });
1011
+ return;
1012
+ }
1013
+ if (wallet.networkConfig.id !== targetNetworkId) {
1014
+ completeOperation(`CRITICAL: Wallet capability (${wallet.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
936
1015
  return;
937
1016
  }
938
1017
  if (!isConnected) {
939
1018
  completeOperation("Wallet not connected (derived status). Awaiting connection.", { notifyComplete: false });
940
1019
  return;
941
1020
  }
942
- if (!("chainId" in adapter.networkConfig)) {
1021
+ if (!("chainId" in wallet.networkConfig)) {
943
1022
  completeOperation("Network does not support chain switching (non-EVM). Operation complete (no-op).");
944
1023
  return;
945
1024
  }
946
- const targetChainToBeSwitchedTo = Number(adapter.networkConfig.chainId);
1025
+ const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);
947
1026
  if (currentChainIdFromHook === targetChainToBeSwitchedTo) {
948
1027
  completeOperation("Already on correct chain (derived status). Operation complete.");
949
1028
  return;
@@ -960,7 +1039,8 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
960
1039
  const timeoutId = setTimeout(performSwitchActual, 100);
961
1040
  return () => clearTimeout(timeoutId);
962
1041
  }, [
963
- adapter,
1042
+ wallet,
1043
+ networkCatalog,
964
1044
  targetNetworkId,
965
1045
  execSwitchNetwork,
966
1046
  isSwitchingNetworkViaHook,
@@ -992,5 +1072,5 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
992
1072
  };
993
1073
 
994
1074
  //#endregion
995
- export { AdapterContext, AdapterProvider, AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, VERSION, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAdapterContext, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useWalletComponents, useWalletReconnectionHandler, useWalletState };
1075
+ export { AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, RuntimeContext, RuntimeProvider, VERSION, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useRuntimeContext, useWalletComponents, useWalletReconnectionHandler, useWalletState };
996
1076
  //# sourceMappingURL=index.mjs.map