@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/README.md +53 -58
- package/dist/index.cjs +269 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -50
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +61 -50
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +267 -187
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
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 = "
|
|
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
|
|
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
|
|
16
|
+
* is in the `RuntimeProvider` component.
|
|
17
17
|
*
|
|
18
|
-
* The
|
|
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
|
|
24
|
-
* Components can access this through the
|
|
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
|
|
26
|
+
const RuntimeContext = createContext(null);
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
//#region src/hooks/AdapterProvider.tsx
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* RuntimeProvider.tsx
|
|
32
32
|
*
|
|
33
|
-
* This file implements the
|
|
34
|
-
*
|
|
35
|
-
* that only one
|
|
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
|
|
38
|
-
* components to access the same
|
|
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
|
|
43
|
+
* is why runtime loading is controlled carefully.
|
|
44
44
|
*/
|
|
45
45
|
/**
|
|
46
|
-
* Provider component that manages
|
|
47
|
-
* to avoid creating multiple instances of the same
|
|
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
|
|
51
|
-
* 2. Tracks loading states for
|
|
52
|
-
* 3. Provides a function to get or load
|
|
53
|
-
* 4. Ensures
|
|
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
|
|
56
|
-
const [
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
}, [
|
|
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
|
|
104
|
+
* Function to get or create a runtime for a network
|
|
68
105
|
*
|
|
69
|
-
* IMPORTANT:
|
|
70
|
-
*
|
|
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
|
|
74
|
-
* 2. Reports loading state for
|
|
75
|
-
* 3. Initiates
|
|
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
|
|
114
|
+
const getRuntimeForNetwork = useCallback((networkConfig) => {
|
|
78
115
|
if (!networkConfig) return {
|
|
79
|
-
|
|
116
|
+
runtime: null,
|
|
80
117
|
isLoading: false
|
|
81
118
|
};
|
|
82
119
|
const networkId = networkConfig.id;
|
|
83
|
-
logger.debug("
|
|
84
|
-
if (
|
|
85
|
-
logger.debug("
|
|
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
|
-
|
|
124
|
+
runtime: runtimeRegistry[networkId],
|
|
88
125
|
isLoading: false
|
|
89
126
|
};
|
|
90
127
|
}
|
|
91
128
|
if (loadingNetworks.has(networkId)) {
|
|
92
|
-
logger.debug("
|
|
129
|
+
logger.debug("RuntimeProvider", `Runtime for network ${networkId} is currently loading`);
|
|
93
130
|
return {
|
|
94
|
-
|
|
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("
|
|
104
|
-
|
|
105
|
-
logger.info("
|
|
106
|
-
type:
|
|
107
|
-
objectId: Object.prototype.toString.call(
|
|
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
|
-
|
|
146
|
+
setRuntimeRegistry((prev) => ({
|
|
110
147
|
...prev,
|
|
111
|
-
[networkId]:
|
|
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("
|
|
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
|
-
|
|
164
|
+
runtime: null,
|
|
128
165
|
isLoading: true
|
|
129
166
|
};
|
|
130
167
|
}, [
|
|
131
|
-
|
|
168
|
+
runtimeRegistry,
|
|
132
169
|
loadingNetworks,
|
|
133
|
-
|
|
170
|
+
resolveRuntime
|
|
134
171
|
]);
|
|
135
|
-
const contextValue = useMemo(() => ({
|
|
136
|
-
|
|
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/@
|
|
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
|
|
217
|
-
* It's a critical part of the
|
|
218
|
-
* access the centralized
|
|
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
|
|
221
|
-
* - Only one
|
|
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
|
|
263
|
+
* - Better performance by eliminating redundant runtime initialization
|
|
224
264
|
*/
|
|
225
265
|
/**
|
|
226
|
-
* Hook to access the
|
|
266
|
+
* Hook to access the runtime context
|
|
227
267
|
*
|
|
228
|
-
* This hook provides access to the
|
|
229
|
-
* retrieves or creates
|
|
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
|
|
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
|
|
235
|
-
* @returns The
|
|
274
|
+
* @throws Error if used outside of a RuntimeProvider context
|
|
275
|
+
* @returns The runtime context value
|
|
236
276
|
*/
|
|
237
|
-
function
|
|
238
|
-
const context = useContext(
|
|
239
|
-
if (!context) throw new Error("
|
|
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
|
|
313
|
+
* Configures the runtime's UI kit capability and returns the ecosystem session artifacts.
|
|
247
314
|
*/
|
|
248
|
-
async function
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
logger.info("[WSP
|
|
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 =
|
|
256
|
-
const hooks =
|
|
257
|
-
logger.info("[WSP
|
|
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
|
|
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 `
|
|
274
|
-
* leveraging the `
|
|
275
|
-
* 4.
|
|
276
|
-
*
|
|
277
|
-
*
|
|
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
|
|
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 [
|
|
287
|
-
const [
|
|
288
|
-
const [
|
|
289
|
-
const [
|
|
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 {
|
|
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
|
|
391
|
+
async function loadRuntimeAndConfigureUi() {
|
|
316
392
|
if (!currentGlobalNetworkConfig) {
|
|
317
393
|
if (!abortController.signal.aborted) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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 {
|
|
405
|
+
const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(currentGlobalNetworkConfig);
|
|
326
406
|
if (abortController.signal.aborted) return;
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
const { providerComponent, hooks } = await
|
|
407
|
+
setIsGlobalRuntimeLoading(newIsLoading);
|
|
408
|
+
if (newRuntime && !newIsLoading) try {
|
|
409
|
+
const { providerComponent, hooks } = await configureRuntimeUiKit(newRuntime, loadConfigModule, programmaticUiKitConfig);
|
|
330
410
|
if (!abortController.signal.aborted) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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 (!
|
|
428
|
+
else if (!newRuntime && !newIsLoading) {
|
|
343
429
|
if (!abortController.signal.aborted) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
440
|
+
loadRuntimeAndConfigureUi();
|
|
351
441
|
return () => abortController.abort();
|
|
352
442
|
}, [
|
|
353
443
|
currentGlobalNetworkConfig,
|
|
354
|
-
|
|
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
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
387
|
-
|
|
479
|
+
activeRuntime: globalActiveRuntime,
|
|
480
|
+
isRuntimeLoading: isGlobalRuntimeLoading,
|
|
388
481
|
walletFacadeHooks,
|
|
389
|
-
|
|
482
|
+
reconfigureActiveUiKit
|
|
390
483
|
}), [
|
|
391
484
|
currentGlobalNetworkId,
|
|
392
485
|
setActiveNetworkIdCallback,
|
|
393
486
|
currentGlobalNetworkConfig,
|
|
394
|
-
|
|
395
|
-
|
|
487
|
+
globalActiveRuntime,
|
|
488
|
+
isGlobalRuntimeLoading,
|
|
396
489
|
walletFacadeHooks,
|
|
397
|
-
|
|
490
|
+
reconfigureActiveUiKit
|
|
398
491
|
]);
|
|
399
|
-
const ActualProviderToRender =
|
|
492
|
+
const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;
|
|
400
493
|
let childrenToRender;
|
|
401
494
|
if (ActualProviderToRender) {
|
|
402
|
-
const key =
|
|
403
|
-
logger.
|
|
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.
|
|
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
|
|
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
|
|
557
|
-
* the
|
|
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 {
|
|
593
|
-
|
|
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
|
|
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
|
|
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,
|
|
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 || !
|
|
849
|
+
if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) return;
|
|
748
850
|
if (networkToSwitchTo === selectedNetworkConfigId) return;
|
|
749
|
-
const
|
|
750
|
-
if (!("chainId" in
|
|
751
|
-
const targetChainId = Number(
|
|
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
|
-
|
|
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 {
|
|
793
|
-
|
|
794
|
-
|
|
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 (!
|
|
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
|
-
|
|
806
|
-
logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
|
|
807
|
-
return components;
|
|
900
|
+
return activeUiKit.getEcosystemWalletComponents();
|
|
808
901
|
} catch (error) {
|
|
809
|
-
logger.error("WalletConnectionUI", "
|
|
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 {
|
|
851
|
-
|
|
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 = ({
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
935
|
-
completeOperation(`
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 {
|
|
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
|