@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.
@@ -3,91 +3,132 @@ import { AnalyticsService, cn, isRecordWithProperties, logger } from "@openzeppe
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { Button } from "@openzeppelin/ui-components";
5
5
 
6
+ //#region src/version.ts
7
+ const VERSION = "2.0.0";
8
+
9
+ //#endregion
6
10
  //#region src/hooks/AdapterContext.tsx
7
11
  /**
8
12
  * AdapterContext.tsx
9
13
  *
10
- * 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.
11
15
  * It provides types and the context definition, but the actual implementation
12
- * is in the AdapterProvider component.
16
+ * is in the `RuntimeProvider` component.
13
17
  *
14
- * The adapter singleton pattern ensures that only one adapter instance exists
18
+ * The runtime singleton pattern ensures that only one runtime instance exists
15
19
  * per network configuration, which is critical for consistent wallet connection
16
20
  * state across the application.
17
21
  */
18
22
  /**
19
- * The React Context that provides adapter registry access throughout the app
20
- * 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.
21
25
  */
22
- const AdapterContext = createContext(null);
26
+ const RuntimeContext = createContext(null);
23
27
 
24
28
  //#endregion
25
29
  //#region src/hooks/AdapterProvider.tsx
26
30
  /**
27
- * AdapterProvider.tsx
31
+ * RuntimeProvider.tsx
28
32
  *
29
- * This file implements the Adapter Provider component which manages a registry of
30
- * adapter instances. It's a key part of the adapter singleton pattern which ensures
31
- * 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.
32
36
  *
33
- * The adapter registry is shared across the application via React Context, allowing
34
- * 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
35
39
  * connection state.
36
40
  *
37
41
  * IMPORTANT: This implementation needs special care to avoid React state update errors
38
42
  * during component rendering. Direct state updates during render are not allowed, which
39
- * is why adapter loading is controlled carefully.
43
+ * is why runtime loading is controlled carefully.
40
44
  */
41
45
  /**
42
- * Provider component that manages adapter instances centrally
43
- * 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.
44
48
  *
45
49
  * This component:
46
- * 1. Maintains a registry of adapter instances by network ID
47
- * 2. Tracks loading states for adapters being initialized
48
- * 3. Provides a function to get or load adapters for specific networks
49
- * 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
50
54
  */
51
- function AdapterProvider({ children, resolveAdapter }) {
52
- const [adapterRegistry, setAdapterRegistry] = useState({});
55
+ function RuntimeProvider({ children, resolveRuntime }) {
56
+ const [runtimeRegistry, setRuntimeRegistry] = useState({});
53
57
  const [loadingNetworks, setLoadingNetworks] = useState(/* @__PURE__ */ new Set());
58
+ const runtimeRegistryRef = useRef(runtimeRegistry);
59
+ useEffect(() => {
60
+ runtimeRegistryRef.current = runtimeRegistry;
61
+ }, [runtimeRegistry]);
62
+ useEffect(() => {
63
+ return () => {
64
+ Object.values(runtimeRegistryRef.current).forEach((runtime) => {
65
+ runtime.dispose();
66
+ });
67
+ };
68
+ }, []);
54
69
  useEffect(() => {
55
- const adapterCount = Object.keys(adapterRegistry).length;
56
- if (adapterCount > 0) logger.info("AdapterProvider", `Registry contains ${adapterCount} adapters:`, {
57
- networkIds: Object.keys(adapterRegistry),
70
+ const runtimeCount = Object.keys(runtimeRegistry).length;
71
+ if (runtimeCount > 0) logger.info("RuntimeProvider", `Registry contains ${runtimeCount} runtimes:`, {
72
+ networkIds: Object.keys(runtimeRegistry),
58
73
  loadingCount: loadingNetworks.size,
59
74
  loadingNetworkIds: Array.from(loadingNetworks)
60
75
  });
61
- }, [adapterRegistry, loadingNetworks]);
76
+ }, [runtimeRegistry, loadingNetworks]);
62
77
  /**
63
- * Function to get or create an adapter for a network
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.
64
81
  *
65
- * IMPORTANT: The actual adapter loading is handled in the useConfiguredAdapterSingleton hook
66
- * to avoid React state updates during render, which would cause errors.
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
+ }, []);
103
+ /**
104
+ * Function to get or create a runtime for a network
105
+ *
106
+ * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates
107
+ * during render, which would cause errors.
67
108
  *
68
109
  * This function:
69
- * 1. Returns existing adapters immediately if available
70
- * 2. Reports loading state for adapters being initialized
71
- * 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
72
113
  */
73
- const getAdapterForNetwork = useCallback((networkConfig) => {
114
+ const getRuntimeForNetwork = useCallback((networkConfig) => {
74
115
  if (!networkConfig) return {
75
- adapter: null,
116
+ runtime: null,
76
117
  isLoading: false
77
118
  };
78
119
  const networkId = networkConfig.id;
79
- logger.debug("AdapterProvider", `Adapter requested for network ${networkId}`);
80
- if (adapterRegistry[networkId]) {
81
- 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}`);
82
123
  return {
83
- adapter: adapterRegistry[networkId],
124
+ runtime: runtimeRegistry[networkId],
84
125
  isLoading: false
85
126
  };
86
127
  }
87
128
  if (loadingNetworks.has(networkId)) {
88
- logger.debug("AdapterProvider", `Adapter for network ${networkId} is currently loading`);
129
+ logger.debug("RuntimeProvider", `Runtime for network ${networkId} is currently loading`);
89
130
  return {
90
- adapter: null,
131
+ runtime: null,
91
132
  isLoading: true
92
133
  };
93
134
  }
@@ -96,15 +137,15 @@ function AdapterProvider({ children, resolveAdapter }) {
96
137
  newSet.add(networkId);
97
138
  return newSet;
98
139
  });
99
- logger.info("AdapterProvider", `Starting adapter initialization for network ${networkId} (${networkConfig.name})`);
100
- resolveAdapter(networkConfig).then((adapter) => {
101
- logger.info("AdapterProvider", `Adapter for network ${networkId} loaded successfully`, {
102
- type: adapter.constructor.name,
103
- 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)
104
145
  });
105
- setAdapterRegistry((prev) => ({
146
+ setRuntimeRegistry((prev) => ({
106
147
  ...prev,
107
- [networkId]: adapter
148
+ [networkId]: runtime
108
149
  }));
109
150
  setLoadingNetworks((prev) => {
110
151
  const newSet = new Set(prev);
@@ -112,7 +153,7 @@ function AdapterProvider({ children, resolveAdapter }) {
112
153
  return newSet;
113
154
  });
114
155
  }).catch((error) => {
115
- logger.error("AdapterProvider", `Error loading adapter for network ${networkId}:`, error);
156
+ logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
116
157
  setLoadingNetworks((prev) => {
117
158
  const newSet = new Set(prev);
118
159
  newSet.delete(networkId);
@@ -120,16 +161,19 @@ function AdapterProvider({ children, resolveAdapter }) {
120
161
  });
121
162
  });
122
163
  return {
123
- adapter: null,
164
+ runtime: null,
124
165
  isLoading: true
125
166
  };
126
167
  }, [
127
- adapterRegistry,
168
+ runtimeRegistry,
128
169
  loadingNetworks,
129
- resolveAdapter
170
+ resolveRuntime
130
171
  ]);
131
- const contextValue = useMemo(() => ({ getAdapterForNetwork }), [getAdapterForNetwork]);
132
- return /* @__PURE__ */ jsx(AdapterContext.Provider, {
172
+ const contextValue = useMemo(() => ({
173
+ getRuntimeForNetwork,
174
+ releaseRuntime
175
+ }), [getRuntimeForNetwork, releaseRuntime]);
176
+ return /* @__PURE__ */ jsx(RuntimeContext.Provider, {
133
177
  value: contextValue,
134
178
  children
135
179
  });
@@ -147,7 +191,7 @@ function AdapterProvider({ children, resolveAdapter }) {
147
191
  * consuming package's bundle. This creates MULTIPLE instances of this module:
148
192
  *
149
193
  * 1. The app's direct import → packages/react/dist/index.js
150
- * 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
151
195
  *
152
196
  * Since React contexts use referential identity, these two module instances have
153
197
  * DIFFERENT context objects. When the adapter's components call useWalletState(),
@@ -209,54 +253,88 @@ function useWalletState() {
209
253
  /**
210
254
  * useAdapterContext.ts
211
255
  *
212
- * This file provides a hook to access the AdapterContext throughout the application.
213
- * It's a critical part of the adapter singleton pattern, allowing components to
214
- * 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.
215
259
  *
216
- * The adapter singleton pattern ensures:
217
- * - Only one adapter instance exists per network
260
+ * The runtime singleton pattern ensures:
261
+ * - Only one runtime instance exists per network
218
262
  * - Wallet connection state is consistent across the app
219
- * - Better performance by eliminating redundant adapter initialization
263
+ * - Better performance by eliminating redundant runtime initialization
220
264
  */
221
265
  /**
222
- * Hook to access the adapter context
266
+ * Hook to access the runtime context
223
267
  *
224
- * This hook provides access to the getAdapterForNetwork function which
225
- * 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.
226
270
  *
227
- * Components should typically use useConfiguredAdapterSingleton instead
271
+ * Components should typically use the higher-level wallet/runtime hooks instead
228
272
  * of this hook directly, as it handles React state update timing properly.
229
273
  *
230
- * @throws Error if used outside of an AdapterProvider context
231
- * @returns The adapter context value
274
+ * @throws Error if used outside of a RuntimeProvider context
275
+ * @returns The runtime context value
232
276
  */
233
- function useAdapterContext() {
234
- const context = useContext(AdapterContext);
235
- 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");
236
280
  return context;
237
281
  }
238
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
+
239
310
  //#endregion
240
311
  //#region src/hooks/WalletStateProvider.tsx
241
312
  /**
242
- * 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.
243
314
  */
244
- 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
+ };
245
321
  try {
246
- if (typeof adapter.configureUiKit === "function") {
247
- logger.info("[WSP configureAdapterUiKit] Calling configureUiKit for adapter:", adapter?.networkConfig?.id);
248
- await adapter.configureUiKit(programmaticOverrides, { loadUiKitNativeConfig: loadConfigModule });
249
- 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);
250
328
  }
251
- const providerComponent = adapter.getEcosystemReactUiContextProvider?.() || null;
252
- const hooks = adapter.getEcosystemReactHooks?.() || null;
253
- 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.");
254
332
  return {
255
333
  providerComponent,
256
334
  hooks
257
335
  };
258
336
  } catch (error) {
259
- logger.error("[WSP configureAdapterUiKit]", "Error during adapter UI setup:", error);
337
+ logger.error("[WSP configureRuntimeUiKit]", "Error during runtime UI setup:", error);
260
338
  throw error;
261
339
  }
262
340
  }
@@ -266,26 +344,28 @@ async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOver
266
344
  * It is responsible for:
267
345
  * 1. Managing the globally selected active network ID (`activeNetworkId`).
268
346
  * 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.
269
- * 3. Fetching and providing the corresponding `ContractAdapter` instance (`activeAdapter`) for the active network,
270
- * leveraging the `AdapterProvider` to ensure adapter singletons.
271
- * 4. Storing and providing the `EcosystemSpecificReactHooks` (`walletFacadeHooks`) from the active adapter.
272
- * 5. Rendering the adapter-specific UI context provider (e.g., WagmiProvider for EVM) around its children,
273
- * 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.
274
353
  * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.
275
354
  *
276
355
  * Consumers use the `useWalletState()` hook to access this global state.
277
- * 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>`.
278
357
  */
279
358
  function WalletStateProvider({ children, initialNetworkId = null, getNetworkConfigById, loadConfigModule }) {
280
359
  const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = useState(initialNetworkId);
281
360
  const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] = useState(null);
282
- const [globalActiveAdapter, setGlobalActiveAdapter] = useState(null);
283
- const [isGlobalAdapterLoading, setIsGlobalAdapterLoading] = useState(false);
284
- const [walletFacadeHooks, setWalletFacadeHooks] = useState(null);
285
- 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);
286
365
  const [uiKitConfigVersion, setUiKitConfigVersion] = useState(0);
287
366
  const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = useState(void 0);
288
- const { getAdapterForNetwork } = useAdapterContext();
367
+ const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();
368
+ const promotedNetworkIdRef = useRef(null);
289
369
  useEffect(() => {
290
370
  const abortController = new AbortController();
291
371
  async function fetchNetworkConfig() {
@@ -308,98 +388,115 @@ function WalletStateProvider({ children, initialNetworkId = null, getNetworkConf
308
388
  }, [currentGlobalNetworkId, getNetworkConfigById]);
309
389
  useEffect(() => {
310
390
  const abortController = new AbortController();
311
- async function loadAdapterAndConfigureUi() {
391
+ async function loadRuntimeAndConfigureUi() {
312
392
  if (!currentGlobalNetworkConfig) {
313
393
  if (!abortController.signal.aborted) {
314
- setGlobalActiveAdapter(null);
315
- setIsGlobalAdapterLoading(false);
316
- setAdapterUiContextProviderToRender(null);
317
- 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
+ }
318
402
  }
319
403
  return;
320
404
  }
321
- const { adapter: newAdapter, isLoading: newIsLoading } = getAdapterForNetwork(currentGlobalNetworkConfig);
405
+ const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(currentGlobalNetworkConfig);
322
406
  if (abortController.signal.aborted) return;
323
- setIsGlobalAdapterLoading(newIsLoading);
324
- if (newAdapter && !newIsLoading) try {
325
- 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);
326
410
  if (!abortController.signal.aborted) {
327
- setAdapterUiContextProviderToRender(() => providerComponent);
328
- setWalletFacadeHooks(hooks);
329
- 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);
330
424
  }
331
425
  } catch (error) {
332
- if (!abortController.signal.aborted) {
333
- logger.error("[WSP loadAdapterAndConfigureUi]", "Error during adapter UI setup:", error);
334
- setAdapterUiContextProviderToRender(null);
335
- setWalletFacadeHooks(null);
336
- }
426
+ if (!abortController.signal.aborted) logger.error("[WSP loadRuntimeAndConfigureUi]", "Error during runtime UI setup:", error);
337
427
  }
338
- else if (!newAdapter && !newIsLoading) {
428
+ else if (!newRuntime && !newIsLoading) {
339
429
  if (!abortController.signal.aborted) {
340
- setAdapterUiContextProviderToRender(null);
341
- setWalletFacadeHooks(null);
342
- setGlobalActiveAdapter(null);
430
+ const prevNetworkId = promotedNetworkIdRef.current;
431
+ setGlobalActiveRuntime(null);
432
+ setActiveWalletSessionEcosystem(null);
433
+ if (prevNetworkId) {
434
+ releaseRuntime(prevNetworkId);
435
+ promotedNetworkIdRef.current = null;
436
+ }
343
437
  }
344
438
  }
345
439
  }
346
- loadAdapterAndConfigureUi();
440
+ loadRuntimeAndConfigureUi();
347
441
  return () => abortController.abort();
348
442
  }, [
349
443
  currentGlobalNetworkConfig,
350
- getAdapterForNetwork,
444
+ getRuntimeForNetwork,
445
+ releaseRuntime,
351
446
  loadConfigModule,
352
447
  uiKitConfigVersion,
353
448
  programmaticUiKitConfig
354
449
  ]);
355
450
  /**
356
451
  * Callback to set the globally active network ID.
357
- * Also clears dependent states (config, adapter, hooks) if the network ID is cleared.
452
+ * Also clears dependent states if the network ID is cleared.
358
453
  */
359
454
  const setActiveNetworkIdCallback = useCallback((networkId) => {
360
455
  logger.info("WalletStateProvider", `Setting global network ID to: ${networkId}`);
361
456
  setCurrentGlobalNetworkIdState(networkId);
362
457
  if (!networkId) {
363
458
  setCurrentGlobalNetworkConfig(null);
364
- setGlobalActiveAdapter(null);
365
- setIsGlobalAdapterLoading(false);
366
- setWalletFacadeHooks(null);
459
+ setGlobalActiveRuntime(null);
460
+ setIsGlobalRuntimeLoading(false);
461
+ setActiveWalletSessionEcosystem(null);
367
462
  }
368
463
  }, []);
369
464
  /**
370
- * 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.
371
466
  * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.
372
467
  */
373
- const reconfigureActiveAdapterUiKit = useCallback((uiKitConfig) => {
468
+ const reconfigureActiveUiKit = useCallback((uiKitConfig) => {
374
469
  logger.info("WalletStateProvider", "Explicitly triggering UI kit re-configuration by bumping version.", uiKitConfig);
375
470
  setProgrammaticUiKitConfig(uiKitConfig);
376
471
  setUiKitConfigVersion((v) => v + 1);
377
472
  }, [setProgrammaticUiKitConfig, setUiKitConfigVersion]);
473
+ const activeWalletSession = useMemo(() => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem), [walletSessionRegistry, activeWalletSessionEcosystem]);
474
+ const walletFacadeHooks = activeWalletSession?.hooks ?? null;
378
475
  const contextValue = useMemo(() => ({
379
476
  activeNetworkId: currentGlobalNetworkId,
380
477
  setActiveNetworkId: setActiveNetworkIdCallback,
381
478
  activeNetworkConfig: currentGlobalNetworkConfig,
382
- activeAdapter: globalActiveAdapter,
383
- isAdapterLoading: isGlobalAdapterLoading,
479
+ activeRuntime: globalActiveRuntime,
480
+ isRuntimeLoading: isGlobalRuntimeLoading,
384
481
  walletFacadeHooks,
385
- reconfigureActiveAdapterUiKit
482
+ reconfigureActiveUiKit
386
483
  }), [
387
484
  currentGlobalNetworkId,
388
485
  setActiveNetworkIdCallback,
389
486
  currentGlobalNetworkConfig,
390
- globalActiveAdapter,
391
- isGlobalAdapterLoading,
487
+ globalActiveRuntime,
488
+ isGlobalRuntimeLoading,
392
489
  walletFacadeHooks,
393
- reconfigureActiveAdapterUiKit
490
+ reconfigureActiveUiKit
394
491
  ]);
395
- const ActualProviderToRender = AdapterUiContextProviderToRender;
492
+ const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;
396
493
  let childrenToRender;
397
494
  if (ActualProviderToRender) {
398
- const key = `${globalActiveAdapter?.networkConfig?.ecosystem || "unknown"}-${globalActiveAdapter?.networkConfig?.id || "unknown"}`;
399
- 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);
400
497
  childrenToRender = /* @__PURE__ */ jsx(ActualProviderToRender, { children }, key);
401
498
  } else {
402
- 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.");
403
500
  childrenToRender = children;
404
501
  }
405
502
  return /* @__PURE__ */ jsx(WalletStateContext.Provider, {
@@ -543,14 +640,14 @@ const useAnalytics = () => {
543
640
  //#endregion
544
641
  //#region src/hooks/useWalletComponents.ts
545
642
  /**
546
- * 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.
547
644
  *
548
645
  * Use this hook when you need full control over the layout and composition of
549
646
  * wallet components. For standard layouts, prefer using `WalletConnectionUI`
550
647
  * with its props forwarding capabilities.
551
648
  *
552
- * @returns The wallet components object, or null if no adapter is active or
553
- * 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.
554
651
  *
555
652
  * @example
556
653
  * ```tsx
@@ -585,10 +682,11 @@ const useAnalytics = () => {
585
682
  * ```
586
683
  */
587
684
  function useWalletComponents() {
588
- const { activeAdapter } = useWalletState();
589
- 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;
590
688
  try {
591
- return activeAdapter.getEcosystemWalletComponents() ?? null;
689
+ return activeUiKit.getEcosystemWalletComponents() ?? null;
592
690
  } catch {
593
691
  return null;
594
692
  }
@@ -598,6 +696,10 @@ function useWalletComponents() {
598
696
  //#region src/hooks/useDerivedAccountStatus.ts
599
697
  const defaultAccountStatus = {
600
698
  isConnected: false,
699
+ isConnecting: false,
700
+ isDisconnected: true,
701
+ isReconnecting: false,
702
+ status: "disconnected",
601
703
  address: void 0,
602
704
  chainId: void 0
603
705
  };
@@ -612,6 +714,10 @@ function useDerivedAccountStatus() {
612
714
  const accountHookOutput = walletFacadeHooks?.useAccount ? walletFacadeHooks.useAccount() : void 0;
613
715
  if (isRecordWithProperties(accountHookOutput)) return {
614
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,
615
721
  address: "address" in accountHookOutput && typeof accountHookOutput.address === "string" ? accountHookOutput.address : defaultAccountStatus.address,
616
722
  chainId: "chainId" in accountHookOutput && typeof accountHookOutput.chainId === "number" ? accountHookOutput.chainId : defaultAccountStatus.chainId
617
723
  };
@@ -730,21 +836,21 @@ function useDerivedDisconnect() {
730
836
  * This hook detects that scenario and invokes a callback to re-queue the network switch.
731
837
  *
732
838
  * @param selectedNetworkConfigId - Currently selected network in the app
733
- * @param selectedAdapter - Currently active adapter instance
839
+ * @param selectedCapability - Currently active wallet capability instance
734
840
  * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)
735
841
  * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued
736
842
  */
737
- function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter, networkToSwitchTo, onRequeueSwitch) {
843
+ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedCapability, networkToSwitchTo, onRequeueSwitch) {
738
844
  const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();
739
845
  const prevConnectedRef = useRef(isConnected);
740
846
  useEffect(() => {
741
847
  const isReconnection = !prevConnectedRef.current && isConnected;
742
848
  prevConnectedRef.current = isConnected;
743
- if (!isReconnection || !selectedNetworkConfigId || !selectedAdapter) return;
849
+ if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) return;
744
850
  if (networkToSwitchTo === selectedNetworkConfigId) return;
745
- const adapterConfig = selectedAdapter.networkConfig;
746
- if (!("chainId" in adapterConfig) || !walletChainId) return;
747
- const targetChainId = Number(adapterConfig.chainId);
851
+ const selectedNetworkConfig = selectedCapability.networkConfig;
852
+ if (!("chainId" in selectedNetworkConfig) || !walletChainId) return;
853
+ const targetChainId = Number(selectedNetworkConfig.chainId);
748
854
  if (walletChainId !== targetChainId) {
749
855
  logger.info("useWalletReconnectionHandler", `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`);
750
856
  onRequeueSwitch(selectedNetworkConfigId);
@@ -753,7 +859,7 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
753
859
  isConnected,
754
860
  walletChainId,
755
861
  selectedNetworkConfigId,
756
- selectedAdapter,
862
+ selectedCapability,
757
863
  networkToSwitchTo,
758
864
  onRequeueSwitch
759
865
  ]);
@@ -785,37 +891,20 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter,
785
891
  */
786
892
  const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps, networkSwitcherProps }) => {
787
893
  const [isError, setIsError] = useState(false);
788
- const { activeAdapter, walletFacadeHooks } = useWalletState();
789
- useEffect(() => {
790
- logger.debug("WalletConnectionUI", "[Debug] State from useWalletState:", {
791
- adapterId: activeAdapter?.networkConfig.id,
792
- hasFacadeHooks: !!walletFacadeHooks
793
- });
794
- }, [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;
795
897
  const walletComponents = (() => {
796
- if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") {
797
- logger.debug("WalletConnectionUI", "[Debug] No activeAdapter or getEcosystemWalletComponents method, returning null.");
798
- return null;
799
- }
898
+ if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== "function") return null;
800
899
  try {
801
- const components = activeAdapter.getEcosystemWalletComponents();
802
- logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
803
- return components;
900
+ return activeUiKit.getEcosystemWalletComponents();
804
901
  } catch (error) {
805
- logger.error("WalletConnectionUI", "[Debug] Error getting wallet components:", error);
902
+ logger.error("WalletConnectionUI", "Error getting wallet components:", error);
806
903
  setIsError(true);
807
904
  return null;
808
905
  }
809
906
  })();
810
- if (!walletComponents) {
811
- logger.debug("WalletConnectionUI", "[Debug] getEcosystemWalletComponents returned null/undefined, rendering null.");
812
- return null;
813
- }
814
- logger.debug("WalletConnectionUI", "Rendering wallet components:", {
815
- hasConnectButton: !!walletComponents.ConnectButton,
816
- hasAccountDisplay: !!walletComponents.AccountDisplay,
817
- hasNetworkSwitcher: !!walletComponents.NetworkSwitcher
818
- });
907
+ if (!walletComponents) return null;
819
908
  const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;
820
909
  if (isError) return /* @__PURE__ */ jsx("div", {
821
910
  className: cn("flex items-center gap-4", className),
@@ -843,18 +932,8 @@ const WalletConnectionUI = ({ className, connectButtonProps, accountDisplayProps
843
932
  * Uses useWalletState to get its data.
844
933
  */
845
934
  const WalletConnectionHeader = () => {
846
- const { isAdapterLoading, activeAdapter } = useWalletState();
847
- useEffect(() => {
848
- logger.debug("WalletConnectionHeader", "[Debug] State from useWalletState:", {
849
- adapterPresent: !!activeAdapter,
850
- adapterNetwork: activeAdapter?.networkConfig.id,
851
- isLoading: isAdapterLoading
852
- });
853
- }, [activeAdapter, isAdapterLoading]);
854
- if (isAdapterLoading) {
855
- logger.debug("WalletConnectionHeader", "[Debug] Adapter loading, showing skeleton.");
856
- return /* @__PURE__ */ jsx("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
857
- }
935
+ const { isRuntimeLoading } = useWalletState();
936
+ if (isRuntimeLoading) return /* @__PURE__ */ jsx("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
858
937
  return /* @__PURE__ */ jsx(WalletConnectionUI, {});
859
938
  };
860
939
 
@@ -874,7 +953,7 @@ const WalletConnectionHeader = () => {
874
953
  * - Tracks switch attempts to prevent duplicate operations
875
954
  * - Provides completion callback for parent components to handle state cleanup
876
955
  */
877
- const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplete }) => {
956
+ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetworkSwitchComplete }) => {
878
957
  const isMountedRef = useRef(true);
879
958
  const [hasAttemptedSwitch, setHasAttemptedSwitch] = useState(false);
880
959
  const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();
@@ -891,7 +970,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
891
970
  useEffect(() => {
892
971
  logger.info("NetworkSwitchManager", "State Update:", {
893
972
  target: targetNetworkId,
894
- adapterNetwork: adapter.networkConfig.id,
973
+ walletNetwork: wallet.networkConfig.id,
895
974
  isSwitching: isSwitchingNetworkViaHook,
896
975
  hookError: !!switchNetworkError,
897
976
  canExec: !!execSwitchNetwork,
@@ -900,7 +979,7 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
900
979
  attempted: hasAttemptedSwitch
901
980
  });
902
981
  }, [
903
- adapter,
982
+ wallet,
904
983
  targetNetworkId,
905
984
  isSwitchingNetworkViaHook,
906
985
  switchNetworkError,
@@ -927,19 +1006,23 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
927
1006
  logger.info("NetworkSwitchManager", "Previous switch attempt concluded. Deferring to completion effect.");
928
1007
  return;
929
1008
  }
930
- if (adapter.networkConfig.id !== targetNetworkId) {
931
- 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 });
932
1015
  return;
933
1016
  }
934
1017
  if (!isConnected) {
935
1018
  completeOperation("Wallet not connected (derived status). Awaiting connection.", { notifyComplete: false });
936
1019
  return;
937
1020
  }
938
- if (!("chainId" in adapter.networkConfig)) {
1021
+ if (!("chainId" in wallet.networkConfig)) {
939
1022
  completeOperation("Network does not support chain switching (non-EVM). Operation complete (no-op).");
940
1023
  return;
941
1024
  }
942
- const targetChainToBeSwitchedTo = Number(adapter.networkConfig.chainId);
1025
+ const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);
943
1026
  if (currentChainIdFromHook === targetChainToBeSwitchedTo) {
944
1027
  completeOperation("Already on correct chain (derived status). Operation complete.");
945
1028
  return;
@@ -956,7 +1039,8 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
956
1039
  const timeoutId = setTimeout(performSwitchActual, 100);
957
1040
  return () => clearTimeout(timeoutId);
958
1041
  }, [
959
- adapter,
1042
+ wallet,
1043
+ networkCatalog,
960
1044
  targetNetworkId,
961
1045
  execSwitchNetwork,
962
1046
  isSwitchingNetworkViaHook,
@@ -988,5 +1072,5 @@ const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplet
988
1072
  };
989
1073
 
990
1074
  //#endregion
991
- export { AdapterContext, AdapterProvider, AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAdapterContext, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useWalletComponents, useWalletReconnectionHandler, useWalletState };
992
- //# sourceMappingURL=index.js.map
1075
+ export { AnalyticsContext, AnalyticsProvider, NetworkSwitchManager, RuntimeContext, RuntimeProvider, VERSION, WalletConnectionHeader, WalletConnectionUI, WalletStateContext, WalletStateProvider, useAnalytics, useDerivedAccountStatus, useDerivedChainInfo, useDerivedConnectStatus, useDerivedDisconnect, useDerivedSwitchChainStatus, useRuntimeContext, useWalletComponents, useWalletReconnectionHandler, useWalletState };
1076
+ //# sourceMappingURL=index.mjs.map