@openzeppelin/ui-react 1.1.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.cjs CHANGED
@@ -31,91 +31,132 @@ let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
31
31
  let react_jsx_runtime = require("react/jsx-runtime");
32
32
  let _openzeppelin_ui_components = require("@openzeppelin/ui-components");
33
33
 
34
+ //#region src/version.ts
35
+ const VERSION = "2.0.0";
36
+
37
+ //#endregion
34
38
  //#region src/hooks/AdapterContext.tsx
35
39
  /**
36
40
  * AdapterContext.tsx
37
41
  *
38
- * This file defines the React Context used for the adapter singleton pattern.
42
+ * This file defines the React Context used for the runtime singleton pattern.
39
43
  * It provides types and the context definition, but the actual implementation
40
- * is in the AdapterProvider component.
44
+ * is in the `RuntimeProvider` component.
41
45
  *
42
- * The adapter singleton pattern ensures that only one adapter instance exists
46
+ * The runtime singleton pattern ensures that only one runtime instance exists
43
47
  * per network configuration, which is critical for consistent wallet connection
44
48
  * state across the application.
45
49
  */
46
50
  /**
47
- * The React Context that provides adapter registry access throughout the app
48
- * Components can access this through the useAdapterContext hook
51
+ * The React Context that provides runtime registry access throughout the app.
52
+ * Components can access this through the `useRuntimeContext` hook.
49
53
  */
50
- const AdapterContext = (0, react.createContext)(null);
54
+ const RuntimeContext = (0, react.createContext)(null);
51
55
 
52
56
  //#endregion
53
57
  //#region src/hooks/AdapterProvider.tsx
54
58
  /**
55
- * AdapterProvider.tsx
59
+ * RuntimeProvider.tsx
56
60
  *
57
- * This file implements the Adapter Provider component which manages a registry of
58
- * adapter instances. It's a key part of the adapter singleton pattern which ensures
59
- * that only one adapter instance exists per network configuration.
61
+ * This file implements the Runtime Provider component which manages a registry of
62
+ * runtime instances. It's a key part of the runtime singleton pattern which ensures
63
+ * that only one runtime instance exists per network configuration.
60
64
  *
61
- * The adapter registry is shared across the application via React Context, allowing
62
- * components to access the same adapter instances and maintain consistent wallet
65
+ * The runtime registry is shared across the application via React Context, allowing
66
+ * components to access the same runtime instances and maintain consistent wallet
63
67
  * connection state.
64
68
  *
65
69
  * IMPORTANT: This implementation needs special care to avoid React state update errors
66
70
  * during component rendering. Direct state updates during render are not allowed, which
67
- * is why adapter loading is controlled carefully.
71
+ * is why runtime loading is controlled carefully.
68
72
  */
69
73
  /**
70
- * Provider component that manages adapter instances centrally
71
- * to avoid creating multiple instances of the same adapter.
74
+ * Provider component that manages runtime instances centrally
75
+ * to avoid creating multiple instances of the same runtime.
72
76
  *
73
77
  * This component:
74
- * 1. Maintains a registry of adapter instances by network ID
75
- * 2. Tracks loading states for adapters being initialized
76
- * 3. Provides a function to get or load adapters for specific networks
77
- * 4. Ensures adapter instances are reused when possible
78
+ * 1. Maintains a registry of runtime instances by network ID
79
+ * 2. Tracks loading states for runtimes being initialized
80
+ * 3. Provides a function to get or load runtimes for specific networks
81
+ * 4. Ensures runtime instances are reused when possible
78
82
  */
79
- function AdapterProvider({ children, resolveAdapter }) {
80
- const [adapterRegistry, setAdapterRegistry] = (0, react.useState)({});
83
+ function RuntimeProvider({ children, resolveRuntime }) {
84
+ const [runtimeRegistry, setRuntimeRegistry] = (0, react.useState)({});
81
85
  const [loadingNetworks, setLoadingNetworks] = (0, react.useState)(/* @__PURE__ */ new Set());
86
+ const runtimeRegistryRef = (0, react.useRef)(runtimeRegistry);
87
+ (0, react.useEffect)(() => {
88
+ runtimeRegistryRef.current = runtimeRegistry;
89
+ }, [runtimeRegistry]);
90
+ (0, react.useEffect)(() => {
91
+ return () => {
92
+ Object.values(runtimeRegistryRef.current).forEach((runtime) => {
93
+ runtime.dispose();
94
+ });
95
+ };
96
+ }, []);
82
97
  (0, react.useEffect)(() => {
83
- const adapterCount = Object.keys(adapterRegistry).length;
84
- if (adapterCount > 0) _openzeppelin_ui_utils.logger.info("AdapterProvider", `Registry contains ${adapterCount} adapters:`, {
85
- networkIds: Object.keys(adapterRegistry),
98
+ const runtimeCount = Object.keys(runtimeRegistry).length;
99
+ if (runtimeCount > 0) _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Registry contains ${runtimeCount} runtimes:`, {
100
+ networkIds: Object.keys(runtimeRegistry),
86
101
  loadingCount: loadingNetworks.size,
87
102
  loadingNetworkIds: Array.from(loadingNetworks)
88
103
  });
89
- }, [adapterRegistry, loadingNetworks]);
104
+ }, [runtimeRegistry, loadingNetworks]);
90
105
  /**
91
- * Function to get or create an adapter for a network
106
+ * Evicts a runtime from the registry by network ID and disposes it.
107
+ * This is the safe counterpart to `getRuntimeForNetwork`: callers should only
108
+ * release a runtime after they have already promoted its replacement.
92
109
  *
93
- * IMPORTANT: The actual adapter loading is handled in the useConfiguredAdapterSingleton hook
94
- * to avoid React state updates during render, which would cause errors.
110
+ * Disposal is deferred to the next macrotask so React's commit phase
111
+ * (including development-mode prop diffing) can finish reading the old
112
+ * runtime's properties without hitting the RuntimeDisposedError proxy trap.
113
+ */
114
+ const releaseRuntime = (0, react.useCallback)((networkId) => {
115
+ const runtime = runtimeRegistryRef.current[networkId];
116
+ if (!runtime) return;
117
+ _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Releasing runtime for network ${networkId}`);
118
+ setRuntimeRegistry((prev) => {
119
+ const next = { ...prev };
120
+ delete next[networkId];
121
+ return next;
122
+ });
123
+ setTimeout(() => {
124
+ try {
125
+ runtime.dispose();
126
+ } catch (error) {
127
+ _openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error disposing runtime for network ${networkId}:`, error);
128
+ }
129
+ }, 0);
130
+ }, []);
131
+ /**
132
+ * Function to get or create a runtime for a network
133
+ *
134
+ * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates
135
+ * during render, which would cause errors.
95
136
  *
96
137
  * This function:
97
- * 1. Returns existing adapters immediately if available
98
- * 2. Reports loading state for adapters being initialized
99
- * 3. Initiates adapter loading when needed
138
+ * 1. Returns existing runtimes immediately if available
139
+ * 2. Reports loading state for runtimes being initialized
140
+ * 3. Initiates runtime loading when needed
100
141
  */
101
- const getAdapterForNetwork = (0, react.useCallback)((networkConfig) => {
142
+ const getRuntimeForNetwork = (0, react.useCallback)((networkConfig) => {
102
143
  if (!networkConfig) return {
103
- adapter: null,
144
+ runtime: null,
104
145
  isLoading: false
105
146
  };
106
147
  const networkId = networkConfig.id;
107
- _openzeppelin_ui_utils.logger.debug("AdapterProvider", `Adapter requested for network ${networkId}`);
108
- if (adapterRegistry[networkId]) {
109
- _openzeppelin_ui_utils.logger.debug("AdapterProvider", `Using existing adapter for network ${networkId}`);
148
+ _openzeppelin_ui_utils.logger.debug("RuntimeProvider", `Runtime requested for network ${networkId}`);
149
+ if (runtimeRegistry[networkId]) {
150
+ _openzeppelin_ui_utils.logger.debug("RuntimeProvider", `Using existing runtime for network ${networkId}`);
110
151
  return {
111
- adapter: adapterRegistry[networkId],
152
+ runtime: runtimeRegistry[networkId],
112
153
  isLoading: false
113
154
  };
114
155
  }
115
156
  if (loadingNetworks.has(networkId)) {
116
- _openzeppelin_ui_utils.logger.debug("AdapterProvider", `Adapter for network ${networkId} is currently loading`);
157
+ _openzeppelin_ui_utils.logger.debug("RuntimeProvider", `Runtime for network ${networkId} is currently loading`);
117
158
  return {
118
- adapter: null,
159
+ runtime: null,
119
160
  isLoading: true
120
161
  };
121
162
  }
@@ -124,15 +165,15 @@ function AdapterProvider({ children, resolveAdapter }) {
124
165
  newSet.add(networkId);
125
166
  return newSet;
126
167
  });
127
- _openzeppelin_ui_utils.logger.info("AdapterProvider", `Starting adapter initialization for network ${networkId} (${networkConfig.name})`);
128
- resolveAdapter(networkConfig).then((adapter) => {
129
- _openzeppelin_ui_utils.logger.info("AdapterProvider", `Adapter for network ${networkId} loaded successfully`, {
130
- type: adapter.constructor.name,
131
- objectId: Object.prototype.toString.call(adapter)
168
+ _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Starting runtime initialization for network ${networkId} (${networkConfig.name})`);
169
+ resolveRuntime(networkConfig).then((runtime) => {
170
+ _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Runtime for network ${networkId} loaded successfully`, {
171
+ type: runtime.constructor.name,
172
+ objectId: Object.prototype.toString.call(runtime)
132
173
  });
133
- setAdapterRegistry((prev) => ({
174
+ setRuntimeRegistry((prev) => ({
134
175
  ...prev,
135
- [networkId]: adapter
176
+ [networkId]: runtime
136
177
  }));
137
178
  setLoadingNetworks((prev) => {
138
179
  const newSet = new Set(prev);
@@ -140,7 +181,7 @@ function AdapterProvider({ children, resolveAdapter }) {
140
181
  return newSet;
141
182
  });
142
183
  }).catch((error) => {
143
- _openzeppelin_ui_utils.logger.error("AdapterProvider", `Error loading adapter for network ${networkId}:`, error);
184
+ _openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
144
185
  setLoadingNetworks((prev) => {
145
186
  const newSet = new Set(prev);
146
187
  newSet.delete(networkId);
@@ -148,16 +189,19 @@ function AdapterProvider({ children, resolveAdapter }) {
148
189
  });
149
190
  });
150
191
  return {
151
- adapter: null,
192
+ runtime: null,
152
193
  isLoading: true
153
194
  };
154
195
  }, [
155
- adapterRegistry,
196
+ runtimeRegistry,
156
197
  loadingNetworks,
157
- resolveAdapter
198
+ resolveRuntime
158
199
  ]);
159
- const contextValue = (0, react.useMemo)(() => ({ getAdapterForNetwork }), [getAdapterForNetwork]);
160
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdapterContext.Provider, {
200
+ const contextValue = (0, react.useMemo)(() => ({
201
+ getRuntimeForNetwork,
202
+ releaseRuntime
203
+ }), [getRuntimeForNetwork, releaseRuntime]);
204
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuntimeContext.Provider, {
161
205
  value: contextValue,
162
206
  children
163
207
  });
@@ -175,7 +219,7 @@ function AdapterProvider({ children, resolveAdapter }) {
175
219
  * consuming package's bundle. This creates MULTIPLE instances of this module:
176
220
  *
177
221
  * 1. The app's direct import → packages/react/dist/index.js
178
- * 2. The adapter's inlined copy → .vite/deps/@openzeppelin_ui-builder-adapter-evm.js
222
+ * 2. The adapter package's inlined copy → .vite/deps/@openzeppelin_adapter_evm.js
179
223
  *
180
224
  * Since React contexts use referential identity, these two module instances have
181
225
  * DIFFERENT context objects. When the adapter's components call useWalletState(),
@@ -237,54 +281,88 @@ function useWalletState() {
237
281
  /**
238
282
  * useAdapterContext.ts
239
283
  *
240
- * This file provides a hook to access the AdapterContext throughout the application.
241
- * It's a critical part of the adapter singleton pattern, allowing components to
242
- * access the centralized adapter registry.
284
+ * This file provides a hook to access the runtime context throughout the application.
285
+ * It's a critical part of the runtime singleton pattern, allowing components to
286
+ * access the centralized runtime registry.
243
287
  *
244
- * The adapter singleton pattern ensures:
245
- * - Only one adapter instance exists per network
288
+ * The runtime singleton pattern ensures:
289
+ * - Only one runtime instance exists per network
246
290
  * - Wallet connection state is consistent across the app
247
- * - Better performance by eliminating redundant adapter initialization
291
+ * - Better performance by eliminating redundant runtime initialization
248
292
  */
249
293
  /**
250
- * Hook to access the adapter context
294
+ * Hook to access the runtime context
251
295
  *
252
- * This hook provides access to the getAdapterForNetwork function which
253
- * retrieves or creates adapter instances from the singleton registry.
296
+ * This hook provides access to the `getRuntimeForNetwork` function which
297
+ * retrieves or creates runtime instances from the singleton registry.
254
298
  *
255
- * Components should typically use useConfiguredAdapterSingleton instead
299
+ * Components should typically use the higher-level wallet/runtime hooks instead
256
300
  * of this hook directly, as it handles React state update timing properly.
257
301
  *
258
- * @throws Error if used outside of an AdapterProvider context
259
- * @returns The adapter context value
302
+ * @throws Error if used outside of a RuntimeProvider context
303
+ * @returns The runtime context value
260
304
  */
261
- function useAdapterContext() {
262
- const context = (0, react.useContext)(AdapterContext);
263
- if (!context) throw new Error("useAdapterContext must be used within an AdapterProvider");
305
+ function useRuntimeContext() {
306
+ const context = (0, react.useContext)(RuntimeContext);
307
+ if (!context) throw new Error("useRuntimeContext must be used within a RuntimeProvider");
264
308
  return context;
265
309
  }
266
310
 
311
+ //#endregion
312
+ //#region src/hooks/walletSessionRegistry.ts
313
+ /**
314
+ * Returns the cached wallet session for an ecosystem, if present.
315
+ *
316
+ * @param registry - Current internal wallet session registry.
317
+ * @param ecosystem - Ecosystem key to resolve.
318
+ * @returns The cached session entry or `null` when the ecosystem has not been configured yet.
319
+ */
320
+ function getWalletSession(registry, ecosystem) {
321
+ if (!ecosystem) return null;
322
+ return registry[ecosystem] ?? null;
323
+ }
324
+ /**
325
+ * Inserts or replaces the cached wallet session for a single ecosystem.
326
+ *
327
+ * @param registry - Current internal wallet session registry.
328
+ * @param session - Session entry to cache.
329
+ * @returns A new registry object containing the upserted ecosystem session.
330
+ */
331
+ function upsertWalletSession(registry, session) {
332
+ return {
333
+ ...registry,
334
+ [session.ecosystem]: session
335
+ };
336
+ }
337
+
267
338
  //#endregion
268
339
  //#region src/hooks/WalletStateProvider.tsx
269
340
  /**
270
- * Configures the adapter's UI kit and returns the UI provider component and hooks.
341
+ * Configures the runtime's UI kit capability and returns the ecosystem session artifacts.
271
342
  */
272
- async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOverrides = {}) {
343
+ async function configureRuntimeUiKit(runtime, loadConfigModule, programmaticOverrides = {}) {
344
+ const uiKit = runtime.uiKit;
345
+ if (!uiKit) return {
346
+ providerComponent: null,
347
+ hooks: null
348
+ };
273
349
  try {
274
- if (typeof adapter.configureUiKit === "function") {
275
- _openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit] Calling configureUiKit for adapter:", adapter?.networkConfig?.id);
276
- await adapter.configureUiKit(programmaticOverrides, { loadUiKitNativeConfig: loadConfigModule });
277
- _openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit] configureUiKit completed for adapter:", adapter?.networkConfig?.id);
350
+ const hasUiKitOverride = Object.keys(programmaticOverrides).length > 0;
351
+ if (typeof uiKit.configureUiKit === "function") {
352
+ const nextUiKitConfig = { ...programmaticOverrides };
353
+ _openzeppelin_ui_utils.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}`);
354
+ await uiKit.configureUiKit(nextUiKitConfig, { loadUiKitNativeConfig: loadConfigModule });
355
+ _openzeppelin_ui_utils.logger.info("[WSP configureRuntimeUiKit] configureUiKit completed for runtime:", runtime.networkConfig.id);
278
356
  }
279
- const providerComponent = adapter.getEcosystemReactUiContextProvider?.() || null;
280
- const hooks = adapter.getEcosystemReactHooks?.() || null;
281
- _openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit]", "UI provider and hooks retrieved successfully.");
357
+ const providerComponent = uiKit.getEcosystemReactUiContextProvider?.() || null;
358
+ const hooks = uiKit.getEcosystemReactHooks?.() || null;
359
+ _openzeppelin_ui_utils.logger.info("[WSP configureRuntimeUiKit]", "UI provider and hooks retrieved successfully.");
282
360
  return {
283
361
  providerComponent,
284
362
  hooks
285
363
  };
286
364
  } catch (error) {
287
- _openzeppelin_ui_utils.logger.error("[WSP configureAdapterUiKit]", "Error during adapter UI setup:", error);
365
+ _openzeppelin_ui_utils.logger.error("[WSP configureRuntimeUiKit]", "Error during runtime UI setup:", error);
288
366
  throw error;
289
367
  }
290
368
  }
@@ -294,26 +372,28 @@ async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOver
294
372
  * It is responsible for:
295
373
  * 1. Managing the globally selected active network ID (`activeNetworkId`).
296
374
  * 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.
297
- * 3. Fetching and providing the corresponding `ContractAdapter` instance (`activeAdapter`) for the active network,
298
- * leveraging the `AdapterProvider` to ensure adapter singletons.
299
- * 4. Storing and providing the `EcosystemSpecificReactHooks` (`walletFacadeHooks`) from the active adapter.
300
- * 5. Rendering the adapter-specific UI context provider (e.g., WagmiProvider for EVM) around its children,
301
- * which is essential for the facade hooks to function correctly.
375
+ * 3. Fetching and providing the corresponding `EcosystemRuntime` (`activeRuntime`) for the active network,
376
+ * leveraging the `RuntimeProvider` to ensure runtime singletons.
377
+ * 4. Caching ecosystem-scoped wallet session artifacts (provider roots and facade hooks)
378
+ * independently from the network-scoped runtime.
379
+ * 5. Rendering the active ecosystem wallet provider (e.g., WagmiProvider for EVM) around its
380
+ * children, which is essential for the facade hooks to function correctly.
302
381
  * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.
303
382
  *
304
383
  * Consumers use the `useWalletState()` hook to access this global state.
305
- * It should be placed high in the component tree, inside an `<AdapterProvider>`.
384
+ * It should be placed high in the component tree, inside a `<RuntimeProvider>`.
306
385
  */
307
386
  function WalletStateProvider({ children, initialNetworkId = null, getNetworkConfigById, loadConfigModule }) {
308
387
  const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = (0, react.useState)(initialNetworkId);
309
388
  const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] = (0, react.useState)(null);
310
- const [globalActiveAdapter, setGlobalActiveAdapter] = (0, react.useState)(null);
311
- const [isGlobalAdapterLoading, setIsGlobalAdapterLoading] = (0, react.useState)(false);
312
- const [walletFacadeHooks, setWalletFacadeHooks] = (0, react.useState)(null);
313
- const [AdapterUiContextProviderToRender, setAdapterUiContextProviderToRender] = (0, react.useState)(null);
389
+ const [globalActiveRuntime, setGlobalActiveRuntime] = (0, react.useState)(null);
390
+ const [isGlobalRuntimeLoading, setIsGlobalRuntimeLoading] = (0, react.useState)(false);
391
+ const [walletSessionRegistry, setWalletSessionRegistry] = (0, react.useState)({});
392
+ const [activeWalletSessionEcosystem, setActiveWalletSessionEcosystem] = (0, react.useState)(null);
314
393
  const [uiKitConfigVersion, setUiKitConfigVersion] = (0, react.useState)(0);
315
394
  const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = (0, react.useState)(void 0);
316
- const { getAdapterForNetwork } = useAdapterContext();
395
+ const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();
396
+ const promotedNetworkIdRef = (0, react.useRef)(null);
317
397
  (0, react.useEffect)(() => {
318
398
  const abortController = new AbortController();
319
399
  async function fetchNetworkConfig() {
@@ -336,98 +416,115 @@ function WalletStateProvider({ children, initialNetworkId = null, getNetworkConf
336
416
  }, [currentGlobalNetworkId, getNetworkConfigById]);
337
417
  (0, react.useEffect)(() => {
338
418
  const abortController = new AbortController();
339
- async function loadAdapterAndConfigureUi() {
419
+ async function loadRuntimeAndConfigureUi() {
340
420
  if (!currentGlobalNetworkConfig) {
341
421
  if (!abortController.signal.aborted) {
342
- setGlobalActiveAdapter(null);
343
- setIsGlobalAdapterLoading(false);
344
- setAdapterUiContextProviderToRender(null);
345
- setWalletFacadeHooks(null);
422
+ const prevNetworkId = promotedNetworkIdRef.current;
423
+ setGlobalActiveRuntime(null);
424
+ setIsGlobalRuntimeLoading(false);
425
+ setActiveWalletSessionEcosystem(null);
426
+ if (prevNetworkId) {
427
+ releaseRuntime(prevNetworkId);
428
+ promotedNetworkIdRef.current = null;
429
+ }
346
430
  }
347
431
  return;
348
432
  }
349
- const { adapter: newAdapter, isLoading: newIsLoading } = getAdapterForNetwork(currentGlobalNetworkConfig);
433
+ const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(currentGlobalNetworkConfig);
350
434
  if (abortController.signal.aborted) return;
351
- setIsGlobalAdapterLoading(newIsLoading);
352
- if (newAdapter && !newIsLoading) try {
353
- const { providerComponent, hooks } = await configureAdapterUiKit(newAdapter, loadConfigModule, programmaticUiKitConfig);
435
+ setIsGlobalRuntimeLoading(newIsLoading);
436
+ if (newRuntime && !newIsLoading) try {
437
+ const { providerComponent, hooks } = await configureRuntimeUiKit(newRuntime, loadConfigModule, programmaticUiKitConfig);
354
438
  if (!abortController.signal.aborted) {
355
- setAdapterUiContextProviderToRender(() => providerComponent);
356
- setWalletFacadeHooks(hooks);
357
- setGlobalActiveAdapter(newAdapter);
439
+ const ecosystem = newRuntime.networkConfig.ecosystem;
440
+ const prevNetworkId = promotedNetworkIdRef.current;
441
+ const nextNetworkId = newRuntime.networkConfig.id;
442
+ setWalletSessionRegistry((prevRegistry) => upsertWalletSession(prevRegistry, {
443
+ ecosystem,
444
+ lastConfiguredNetworkId: nextNetworkId,
445
+ providerComponent,
446
+ hooks
447
+ }));
448
+ setGlobalActiveRuntime(newRuntime);
449
+ setActiveWalletSessionEcosystem(ecosystem);
450
+ promotedNetworkIdRef.current = nextNetworkId;
451
+ if (prevNetworkId && prevNetworkId !== nextNetworkId) releaseRuntime(prevNetworkId);
358
452
  }
359
453
  } catch (error) {
360
- if (!abortController.signal.aborted) {
361
- _openzeppelin_ui_utils.logger.error("[WSP loadAdapterAndConfigureUi]", "Error during adapter UI setup:", error);
362
- setAdapterUiContextProviderToRender(null);
363
- setWalletFacadeHooks(null);
364
- }
454
+ if (!abortController.signal.aborted) _openzeppelin_ui_utils.logger.error("[WSP loadRuntimeAndConfigureUi]", "Error during runtime UI setup:", error);
365
455
  }
366
- else if (!newAdapter && !newIsLoading) {
456
+ else if (!newRuntime && !newIsLoading) {
367
457
  if (!abortController.signal.aborted) {
368
- setAdapterUiContextProviderToRender(null);
369
- setWalletFacadeHooks(null);
370
- setGlobalActiveAdapter(null);
458
+ const prevNetworkId = promotedNetworkIdRef.current;
459
+ setGlobalActiveRuntime(null);
460
+ setActiveWalletSessionEcosystem(null);
461
+ if (prevNetworkId) {
462
+ releaseRuntime(prevNetworkId);
463
+ promotedNetworkIdRef.current = null;
464
+ }
371
465
  }
372
466
  }
373
467
  }
374
- loadAdapterAndConfigureUi();
468
+ loadRuntimeAndConfigureUi();
375
469
  return () => abortController.abort();
376
470
  }, [
377
471
  currentGlobalNetworkConfig,
378
- getAdapterForNetwork,
472
+ getRuntimeForNetwork,
473
+ releaseRuntime,
379
474
  loadConfigModule,
380
475
  uiKitConfigVersion,
381
476
  programmaticUiKitConfig
382
477
  ]);
383
478
  /**
384
479
  * Callback to set the globally active network ID.
385
- * Also clears dependent states (config, adapter, hooks) if the network ID is cleared.
480
+ * Also clears dependent states if the network ID is cleared.
386
481
  */
387
482
  const setActiveNetworkIdCallback = (0, react.useCallback)((networkId) => {
388
483
  _openzeppelin_ui_utils.logger.info("WalletStateProvider", `Setting global network ID to: ${networkId}`);
389
484
  setCurrentGlobalNetworkIdState(networkId);
390
485
  if (!networkId) {
391
486
  setCurrentGlobalNetworkConfig(null);
392
- setGlobalActiveAdapter(null);
393
- setIsGlobalAdapterLoading(false);
394
- setWalletFacadeHooks(null);
487
+ setGlobalActiveRuntime(null);
488
+ setIsGlobalRuntimeLoading(false);
489
+ setActiveWalletSessionEcosystem(null);
395
490
  }
396
491
  }, []);
397
492
  /**
398
- * Callback to explicitly trigger a re-configuration of the active adapter's UI kit.
493
+ * Callback to explicitly trigger a re-configuration of the active runtime's UI kit.
399
494
  * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.
400
495
  */
401
- const reconfigureActiveAdapterUiKit = (0, react.useCallback)((uiKitConfig) => {
496
+ const reconfigureActiveUiKit = (0, react.useCallback)((uiKitConfig) => {
402
497
  _openzeppelin_ui_utils.logger.info("WalletStateProvider", "Explicitly triggering UI kit re-configuration by bumping version.", uiKitConfig);
403
498
  setProgrammaticUiKitConfig(uiKitConfig);
404
499
  setUiKitConfigVersion((v) => v + 1);
405
500
  }, [setProgrammaticUiKitConfig, setUiKitConfigVersion]);
501
+ const activeWalletSession = (0, react.useMemo)(() => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem), [walletSessionRegistry, activeWalletSessionEcosystem]);
502
+ const walletFacadeHooks = activeWalletSession?.hooks ?? null;
406
503
  const contextValue = (0, react.useMemo)(() => ({
407
504
  activeNetworkId: currentGlobalNetworkId,
408
505
  setActiveNetworkId: setActiveNetworkIdCallback,
409
506
  activeNetworkConfig: currentGlobalNetworkConfig,
410
- activeAdapter: globalActiveAdapter,
411
- isAdapterLoading: isGlobalAdapterLoading,
507
+ activeRuntime: globalActiveRuntime,
508
+ isRuntimeLoading: isGlobalRuntimeLoading,
412
509
  walletFacadeHooks,
413
- reconfigureActiveAdapterUiKit
510
+ reconfigureActiveUiKit
414
511
  }), [
415
512
  currentGlobalNetworkId,
416
513
  setActiveNetworkIdCallback,
417
514
  currentGlobalNetworkConfig,
418
- globalActiveAdapter,
419
- isGlobalAdapterLoading,
515
+ globalActiveRuntime,
516
+ isGlobalRuntimeLoading,
420
517
  walletFacadeHooks,
421
- reconfigureActiveAdapterUiKit
518
+ reconfigureActiveUiKit
422
519
  ]);
423
- const ActualProviderToRender = AdapterUiContextProviderToRender;
520
+ const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;
424
521
  let childrenToRender;
425
522
  if (ActualProviderToRender) {
426
- const key = `${globalActiveAdapter?.networkConfig?.ecosystem || "unknown"}-${globalActiveAdapter?.networkConfig?.id || "unknown"}`;
427
- _openzeppelin_ui_utils.logger.info("[WSP RENDER]", "Rendering adapter-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
523
+ const key = activeWalletSessionEcosystem || "unknown";
524
+ _openzeppelin_ui_utils.logger.debug("[WSP RENDER]", "Rendering runtime-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
428
525
  childrenToRender = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActualProviderToRender, { children }, key);
429
526
  } else {
430
- _openzeppelin_ui_utils.logger.info("[WSP RENDER]", "No adapter UI context provider to render. Rendering direct children.");
527
+ _openzeppelin_ui_utils.logger.debug("[WSP RENDER]", "No runtime UI context provider to render. Rendering direct children.");
431
528
  childrenToRender = children;
432
529
  }
433
530
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WalletStateContext.Provider, {
@@ -571,14 +668,14 @@ const useAnalytics = () => {
571
668
  //#endregion
572
669
  //#region src/hooks/useWalletComponents.ts
573
670
  /**
574
- * Hook that provides direct access to wallet UI components from the active adapter.
671
+ * Hook that provides direct access to wallet UI components from the active runtime.
575
672
  *
576
673
  * Use this hook when you need full control over the layout and composition of
577
674
  * wallet components. For standard layouts, prefer using `WalletConnectionUI`
578
675
  * with its props forwarding capabilities.
579
676
  *
580
- * @returns The wallet components object, or null if no adapter is active or
581
- * the adapter doesn't provide wallet components.
677
+ * @returns The wallet components object, or null if no runtime is active or
678
+ * the runtime doesn't provide wallet components.
582
679
  *
583
680
  * @example
584
681
  * ```tsx
@@ -613,10 +710,11 @@ const useAnalytics = () => {
613
710
  * ```
614
711
  */
615
712
  function useWalletComponents() {
616
- const { activeAdapter } = useWalletState();
617
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") return null;
713
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
714
+ const activeUiKit = activeRuntime?.uiKit;
715
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem) || !activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
618
716
  try {
619
- return activeAdapter.getEcosystemWalletComponents() ?? null;
717
+ return activeUiKit.getEcosystemWalletComponents() ?? null;
620
718
  } catch {
621
719
  return null;
622
720
  }
@@ -626,6 +724,10 @@ function useWalletComponents() {
626
724
  //#region src/hooks/useDerivedAccountStatus.ts
627
725
  const defaultAccountStatus = {
628
726
  isConnected: false,
727
+ isConnecting: false,
728
+ isDisconnected: true,
729
+ isReconnecting: false,
730
+ status: "disconnected",
629
731
  address: void 0,
630
732
  chainId: void 0
631
733
  };
@@ -640,6 +742,10 @@ function useDerivedAccountStatus() {
640
742
  const accountHookOutput = walletFacadeHooks?.useAccount ? walletFacadeHooks.useAccount() : void 0;
641
743
  if ((0, _openzeppelin_ui_utils.isRecordWithProperties)(accountHookOutput)) return {
642
744
  isConnected: "isConnected" in accountHookOutput && typeof accountHookOutput.isConnected === "boolean" ? accountHookOutput.isConnected : defaultAccountStatus.isConnected,
745
+ isConnecting: "isConnecting" in accountHookOutput && typeof accountHookOutput.isConnecting === "boolean" ? accountHookOutput.isConnecting : defaultAccountStatus.isConnecting,
746
+ isDisconnected: "isDisconnected" in accountHookOutput && typeof accountHookOutput.isDisconnected === "boolean" ? accountHookOutput.isDisconnected : defaultAccountStatus.isDisconnected,
747
+ isReconnecting: "isReconnecting" in accountHookOutput && typeof accountHookOutput.isReconnecting === "boolean" ? accountHookOutput.isReconnecting : defaultAccountStatus.isReconnecting,
748
+ status: "status" in accountHookOutput && typeof accountHookOutput.status === "string" ? accountHookOutput.status : defaultAccountStatus.status,
643
749
  address: "address" in accountHookOutput && typeof accountHookOutput.address === "string" ? accountHookOutput.address : defaultAccountStatus.address,
644
750
  chainId: "chainId" in accountHookOutput && typeof accountHookOutput.chainId === "number" ? accountHookOutput.chainId : defaultAccountStatus.chainId
645
751
  };
@@ -758,21 +864,21 @@ function useDerivedDisconnect() {
758
864
  * This hook detects that scenario and invokes a callback to re-queue the network switch.
759
865
  *
760
866
  * @param selectedNetworkConfigId - Currently selected network in the app
761
- * @param selectedAdapter - Currently active adapter instance
867
+ * @param selectedCapability - Currently active wallet capability instance
762
868
  * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)
763
869
  * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued
764
870
  */
765
- function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter, networkToSwitchTo, onRequeueSwitch) {
871
+ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedCapability, networkToSwitchTo, onRequeueSwitch) {
766
872
  const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();
767
873
  const prevConnectedRef = (0, react.useRef)(isConnected);
768
874
  (0, react.useEffect)(() => {
769
875
  const isReconnection = !prevConnectedRef.current && isConnected;
770
876
  prevConnectedRef.current = isConnected;
771
- if (!isReconnection || !selectedNetworkConfigId || !selectedAdapter) return;
877
+ if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) return;
772
878
  if (networkToSwitchTo === selectedNetworkConfigId) return;
773
- const adapterConfig = selectedAdapter.networkConfig;
774
- if (!("chainId" in adapterConfig) || !walletChainId) return;
775
- const targetChainId = Number(adapterConfig.chainId);
879
+ const selectedNetworkConfig = selectedCapability.networkConfig;
880
+ if (!("chainId" in selectedNetworkConfig) || !walletChainId) return;
881
+ const targetChainId = Number(selectedNetworkConfig.chainId);
776
882
  if (walletChainId !== targetChainId) {
777
883
  _openzeppelin_ui_utils.logger.info("useWalletReconnectionHandler", `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`);
778
884
  onRequeueSwitch(selectedNetworkConfigId);
@@ -781,7 +887,7 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
781
887
  isConnected,
782
888
  walletChainId,
783
889
  selectedNetworkConfigId,
784
- selectedAdapter,
890
+ selectedCapability,
785
891
  networkToSwitchTo,
786
892
  onRequeueSwitch
787
893
  ]);
@@ -813,37 +919,20 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
813
919
  */
814
920
  const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps, networkSwitcherProps }) => {
815
921
  const [isError, setIsError] = (0, react.useState)(false);
816
- const { activeAdapter, walletFacadeHooks } = useWalletState();
817
- (0, react.useEffect)(() => {
818
- _openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] State from useWalletState:", {
819
- adapterId: activeAdapter?.networkConfig.id,
820
- hasFacadeHooks: !!walletFacadeHooks
821
- });
822
- }, [activeAdapter, walletFacadeHooks]);
922
+ const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();
923
+ const activeUiKit = activeRuntime?.uiKit;
924
+ if (!!(isRuntimeLoading && activeRuntime?.networkConfig?.ecosystem && activeNetworkConfig?.ecosystem && activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem)) return null;
823
925
  const walletComponents = (() => {
824
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") {
825
- _openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] No activeAdapter or getEcosystemWalletComponents method, returning null.");
826
- return null;
827
- }
926
+ if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
828
927
  try {
829
- const components = activeAdapter.getEcosystemWalletComponents();
830
- _openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
831
- return components;
928
+ return activeUiKit.getEcosystemWalletComponents();
832
929
  } catch (error) {
833
- _openzeppelin_ui_utils.logger.error("WalletConnectionUI", "[Debug] Error getting wallet components:", error);
930
+ _openzeppelin_ui_utils.logger.error("WalletConnectionUI", "Error getting wallet components:", error);
834
931
  setIsError(true);
835
932
  return null;
836
933
  }
837
934
  })();
838
- if (!walletComponents) {
839
- _openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] getEcosystemWalletComponents returned null/undefined, rendering null.");
840
- return null;
841
- }
842
- _openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "Rendering wallet components:", {
843
- hasConnectButton: !!walletComponents.ConnectButton,
844
- hasAccountDisplay: !!walletComponents.AccountDisplay,
845
- hasNetworkSwitcher: !!walletComponents.NetworkSwitcher
846
- });
935
+ if (!walletComponents) return null;
847
936
  const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;
848
937
  if (isError) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
849
938
  className: (0, _openzeppelin_ui_utils.cn)("flex items-center gap-4", className),
@@ -871,18 +960,8 @@ const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps
871
960
  * Uses useWalletState to get its data.
872
961
  */
873
962
  const WalletConnectionHeader = () => {
874
- const { isAdapterLoading, activeAdapter } = useWalletState();
875
- (0, react.useEffect)(() => {
876
- _openzeppelin_ui_utils.logger.debug("WalletConnectionHeader", "[Debug] State from useWalletState:", {
877
- adapterPresent: !!activeAdapter,
878
- adapterNetwork: activeAdapter?.networkConfig.id,
879
- isLoading: isAdapterLoading
880
- });
881
- }, [activeAdapter, isAdapterLoading]);
882
- if (isAdapterLoading) {
883
- _openzeppelin_ui_utils.logger.debug("WalletConnectionHeader", "[Debug] Adapter loading, showing skeleton.");
884
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
885
- }
963
+ const { isRuntimeLoading } = useWalletState();
964
+ if (isRuntimeLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
886
965
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WalletConnectionUI, {});
887
966
  };
888
967
 
@@ -902,7 +981,7 @@ const WalletConnectionHeader = () => {
902
981
  * - Tracks switch attempts to prevent duplicate operations
903
982
  * - Provides completion callback for parent components to handle state cleanup
904
983
  */
905
- const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplete }) => {
984
+ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetworkSwitchComplete }) => {
906
985
  const isMountedRef = (0, react.useRef)(true);
907
986
  const [hasAttemptedSwitch, setHasAttemptedSwitch] = (0, react.useState)(false);
908
987
  const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();
@@ -919,7 +998,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
919
998
  (0, react.useEffect)(() => {
920
999
  _openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "State Update:", {
921
1000
  target: targetNetworkId,
922
- adapterNetwork: adapter.networkConfig.id,
1001
+ walletNetwork: wallet.networkConfig.id,
923
1002
  isSwitching: isSwitchingNetworkViaHook,
924
1003
  hookError: !!switchNetworkError,
925
1004
  canExec: !!execSwitchNetwork,
@@ -928,7 +1007,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
928
1007
  attempted: hasAttemptedSwitch
929
1008
  });
930
1009
  }, [
931
- adapter,
1010
+ wallet,
932
1011
  targetNetworkId,
933
1012
  isSwitchingNetworkViaHook,
934
1013
  switchNetworkError,
@@ -955,19 +1034,23 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
955
1034
  _openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "Previous switch attempt concluded. Deferring to completion effect.");
956
1035
  return;
957
1036
  }
958
- if (adapter.networkConfig.id !== targetNetworkId) {
959
- completeOperation(`CRITICAL: Adapter (${adapter.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
1037
+ if (!networkCatalog.getNetworks().find((network) => network.id === targetNetworkId)) {
1038
+ completeOperation(`Target network ${targetNetworkId} is not present in the network catalog.`, { notifyComplete: false });
1039
+ return;
1040
+ }
1041
+ if (wallet.networkConfig.id !== targetNetworkId) {
1042
+ completeOperation(`CRITICAL: Wallet capability (${wallet.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
960
1043
  return;
961
1044
  }
962
1045
  if (!isConnected) {
963
1046
  completeOperation("Wallet not connected (derived status). Awaiting connection.", { notifyComplete: false });
964
1047
  return;
965
1048
  }
966
- if (!("chainId" in adapter.networkConfig)) {
1049
+ if (!("chainId" in wallet.networkConfig)) {
967
1050
  completeOperation("Network does not support chain switching (non-EVM). Operation complete (no-op).");
968
1051
  return;
969
1052
  }
970
- const targetChainToBeSwitchedTo = Number(adapter.networkConfig.chainId);
1053
+ const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);
971
1054
  if (currentChainIdFromHook === targetChainToBeSwitchedTo) {
972
1055
  completeOperation("Already on correct chain (derived status). Operation complete.");
973
1056
  return;
@@ -984,7 +1067,8 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
984
1067
  const timeoutId = setTimeout(performSwitchActual, 100);
985
1068
  return () => clearTimeout(timeoutId);
986
1069
  }, [
987
- adapter,
1070
+ wallet,
1071
+ networkCatalog,
988
1072
  targetNetworkId,
989
1073
  execSwitchNetwork,
990
1074
  isSwitchingNetworkViaHook,
@@ -1016,22 +1100,23 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
1016
1100
  };
1017
1101
 
1018
1102
  //#endregion
1019
- exports.AdapterContext = AdapterContext;
1020
- exports.AdapterProvider = AdapterProvider;
1021
1103
  exports.AnalyticsContext = AnalyticsContext;
1022
1104
  exports.AnalyticsProvider = AnalyticsProvider;
1023
1105
  exports.NetworkSwitchManager = NetworkSwitchManager;
1106
+ exports.RuntimeContext = RuntimeContext;
1107
+ exports.RuntimeProvider = RuntimeProvider;
1108
+ exports.VERSION = VERSION;
1024
1109
  exports.WalletConnectionHeader = WalletConnectionHeader;
1025
1110
  exports.WalletConnectionUI = WalletConnectionUI;
1026
1111
  exports.WalletStateContext = WalletStateContext;
1027
1112
  exports.WalletStateProvider = WalletStateProvider;
1028
- exports.useAdapterContext = useAdapterContext;
1029
1113
  exports.useAnalytics = useAnalytics;
1030
1114
  exports.useDerivedAccountStatus = useDerivedAccountStatus;
1031
1115
  exports.useDerivedChainInfo = useDerivedChainInfo;
1032
1116
  exports.useDerivedConnectStatus = useDerivedConnectStatus;
1033
1117
  exports.useDerivedDisconnect = useDerivedDisconnect;
1034
1118
  exports.useDerivedSwitchChainStatus = useDerivedSwitchChainStatus;
1119
+ exports.useRuntimeContext = useRuntimeContext;
1035
1120
  exports.useWalletComponents = useWalletComponents;
1036
1121
  exports.useWalletReconnectionHandler = useWalletReconnectionHandler;
1037
1122
  exports.useWalletState = useWalletState;