@openzeppelin/ui-react 1.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/LICENSE +661 -0
- package/README.md +195 -0
- package/dist/index-B_dOGVNW.d.ts +421 -0
- package/dist/index-B_dOGVNW.d.ts.map +1 -0
- package/dist/index-Cfc5cQFd.d.cts +421 -0
- package/dist/index-Cfc5cQFd.d.cts.map +1 -0
- package/dist/index.cjs +909 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +421 -0
- package/dist/index.d.ts +421 -0
- package/dist/index.js +864 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,909 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
let react = require("react");
|
|
29
|
+
react = __toESM(react);
|
|
30
|
+
let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
|
|
31
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
let _openzeppelin_ui_components = require("@openzeppelin/ui-components");
|
|
33
|
+
|
|
34
|
+
//#region src/hooks/AdapterContext.tsx
|
|
35
|
+
/**
|
|
36
|
+
* AdapterContext.tsx
|
|
37
|
+
*
|
|
38
|
+
* This file defines the React Context used for the adapter singleton pattern.
|
|
39
|
+
* It provides types and the context definition, but the actual implementation
|
|
40
|
+
* is in the AdapterProvider component.
|
|
41
|
+
*
|
|
42
|
+
* The adapter singleton pattern ensures that only one adapter instance exists
|
|
43
|
+
* per network configuration, which is critical for consistent wallet connection
|
|
44
|
+
* state across the application.
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* The React Context that provides adapter registry access throughout the app
|
|
48
|
+
* Components can access this through the useAdapterContext hook
|
|
49
|
+
*/
|
|
50
|
+
const AdapterContext = (0, react.createContext)(null);
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/hooks/AdapterProvider.tsx
|
|
54
|
+
/**
|
|
55
|
+
* AdapterProvider.tsx
|
|
56
|
+
*
|
|
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.
|
|
60
|
+
*
|
|
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
|
|
63
|
+
* connection state.
|
|
64
|
+
*
|
|
65
|
+
* IMPORTANT: This implementation needs special care to avoid React state update errors
|
|
66
|
+
* during component rendering. Direct state updates during render are not allowed, which
|
|
67
|
+
* is why adapter loading is controlled carefully.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Provider component that manages adapter instances centrally
|
|
71
|
+
* to avoid creating multiple instances of the same adapter.
|
|
72
|
+
*
|
|
73
|
+
* 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
|
+
*/
|
|
79
|
+
function AdapterProvider({ children, resolveAdapter }) {
|
|
80
|
+
const [adapterRegistry, setAdapterRegistry] = (0, react.useState)({});
|
|
81
|
+
const [loadingNetworks, setLoadingNetworks] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
82
|
+
(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),
|
|
86
|
+
loadingCount: loadingNetworks.size,
|
|
87
|
+
loadingNetworkIds: Array.from(loadingNetworks)
|
|
88
|
+
});
|
|
89
|
+
}, [adapterRegistry, loadingNetworks]);
|
|
90
|
+
/**
|
|
91
|
+
* Function to get or create an adapter for a network
|
|
92
|
+
*
|
|
93
|
+
* IMPORTANT: The actual adapter loading is handled in the useConfiguredAdapterSingleton hook
|
|
94
|
+
* to avoid React state updates during render, which would cause errors.
|
|
95
|
+
*
|
|
96
|
+
* 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
|
|
100
|
+
*/
|
|
101
|
+
const getAdapterForNetwork = (0, react.useCallback)((networkConfig) => {
|
|
102
|
+
if (!networkConfig) return {
|
|
103
|
+
adapter: null,
|
|
104
|
+
isLoading: false
|
|
105
|
+
};
|
|
106
|
+
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}`);
|
|
110
|
+
return {
|
|
111
|
+
adapter: adapterRegistry[networkId],
|
|
112
|
+
isLoading: false
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (loadingNetworks.has(networkId)) {
|
|
116
|
+
_openzeppelin_ui_utils.logger.debug("AdapterProvider", `Adapter for network ${networkId} is currently loading`);
|
|
117
|
+
return {
|
|
118
|
+
adapter: null,
|
|
119
|
+
isLoading: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
setLoadingNetworks((prev) => {
|
|
123
|
+
const newSet = new Set(prev);
|
|
124
|
+
newSet.add(networkId);
|
|
125
|
+
return newSet;
|
|
126
|
+
});
|
|
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)
|
|
132
|
+
});
|
|
133
|
+
setAdapterRegistry((prev) => ({
|
|
134
|
+
...prev,
|
|
135
|
+
[networkId]: adapter
|
|
136
|
+
}));
|
|
137
|
+
setLoadingNetworks((prev) => {
|
|
138
|
+
const newSet = new Set(prev);
|
|
139
|
+
newSet.delete(networkId);
|
|
140
|
+
return newSet;
|
|
141
|
+
});
|
|
142
|
+
}).catch((error) => {
|
|
143
|
+
_openzeppelin_ui_utils.logger.error("AdapterProvider", `Error loading adapter for network ${networkId}:`, error);
|
|
144
|
+
setLoadingNetworks((prev) => {
|
|
145
|
+
const newSet = new Set(prev);
|
|
146
|
+
newSet.delete(networkId);
|
|
147
|
+
return newSet;
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
adapter: null,
|
|
152
|
+
isLoading: true
|
|
153
|
+
};
|
|
154
|
+
}, [
|
|
155
|
+
adapterRegistry,
|
|
156
|
+
loadingNetworks,
|
|
157
|
+
resolveAdapter
|
|
158
|
+
]);
|
|
159
|
+
const contextValue = (0, react.useMemo)(() => ({ getAdapterForNetwork }), [getAdapterForNetwork]);
|
|
160
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdapterContext.Provider, {
|
|
161
|
+
value: contextValue,
|
|
162
|
+
children
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/hooks/WalletStateContext.tsx
|
|
168
|
+
const WalletStateContext = (0, react.createContext)(void 0);
|
|
169
|
+
/**
|
|
170
|
+
* Hook to access wallet state from WalletStateProvider.
|
|
171
|
+
* @throws Error if used outside of WalletStateProvider
|
|
172
|
+
*/
|
|
173
|
+
function useWalletState() {
|
|
174
|
+
const context = react.default.useContext(WalletStateContext);
|
|
175
|
+
if (context === void 0) throw new Error("useWalletState must be used within a WalletStateProvider");
|
|
176
|
+
return context;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/hooks/useAdapterContext.ts
|
|
181
|
+
/**
|
|
182
|
+
* useAdapterContext.ts
|
|
183
|
+
*
|
|
184
|
+
* This file provides a hook to access the AdapterContext throughout the application.
|
|
185
|
+
* It's a critical part of the adapter singleton pattern, allowing components to
|
|
186
|
+
* access the centralized adapter registry.
|
|
187
|
+
*
|
|
188
|
+
* The adapter singleton pattern ensures:
|
|
189
|
+
* - Only one adapter instance exists per network
|
|
190
|
+
* - Wallet connection state is consistent across the app
|
|
191
|
+
* - Better performance by eliminating redundant adapter initialization
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* Hook to access the adapter context
|
|
195
|
+
*
|
|
196
|
+
* This hook provides access to the getAdapterForNetwork function which
|
|
197
|
+
* retrieves or creates adapter instances from the singleton registry.
|
|
198
|
+
*
|
|
199
|
+
* Components should typically use useConfiguredAdapterSingleton instead
|
|
200
|
+
* of this hook directly, as it handles React state update timing properly.
|
|
201
|
+
*
|
|
202
|
+
* @throws Error if used outside of an AdapterProvider context
|
|
203
|
+
* @returns The adapter context value
|
|
204
|
+
*/
|
|
205
|
+
function useAdapterContext() {
|
|
206
|
+
const context = (0, react.useContext)(AdapterContext);
|
|
207
|
+
if (!context) throw new Error("useAdapterContext must be used within an AdapterProvider");
|
|
208
|
+
return context;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/hooks/WalletStateProvider.tsx
|
|
213
|
+
/**
|
|
214
|
+
* Configures the adapter's UI kit and returns the UI provider component and hooks.
|
|
215
|
+
*/
|
|
216
|
+
async function configureAdapterUiKit(adapter, loadConfigModule, programmaticOverrides = {}) {
|
|
217
|
+
try {
|
|
218
|
+
if (typeof adapter.configureUiKit === "function") {
|
|
219
|
+
_openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit] Calling configureUiKit for adapter:", adapter?.networkConfig?.id);
|
|
220
|
+
await adapter.configureUiKit(programmaticOverrides, { loadUiKitNativeConfig: loadConfigModule });
|
|
221
|
+
_openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit] configureUiKit completed for adapter:", adapter?.networkConfig?.id);
|
|
222
|
+
}
|
|
223
|
+
const providerComponent = adapter.getEcosystemReactUiContextProvider?.() || null;
|
|
224
|
+
const hooks = adapter.getEcosystemReactHooks?.() || null;
|
|
225
|
+
_openzeppelin_ui_utils.logger.info("[WSP configureAdapterUiKit]", "UI provider and hooks retrieved successfully.");
|
|
226
|
+
return {
|
|
227
|
+
providerComponent,
|
|
228
|
+
hooks
|
|
229
|
+
};
|
|
230
|
+
} catch (error) {
|
|
231
|
+
_openzeppelin_ui_utils.logger.error("[WSP configureAdapterUiKit]", "Error during adapter UI setup:", error);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* @name WalletStateProvider
|
|
237
|
+
* @description This provider is a central piece of the application's state management for wallet and network interactions.
|
|
238
|
+
* It is responsible for:
|
|
239
|
+
* 1. Managing the globally selected active network ID (`activeNetworkId`).
|
|
240
|
+
* 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.
|
|
241
|
+
* 3. Fetching and providing the corresponding `ContractAdapter` instance (`activeAdapter`) for the active network,
|
|
242
|
+
* leveraging the `AdapterProvider` to ensure adapter singletons.
|
|
243
|
+
* 4. Storing and providing the `EcosystemSpecificReactHooks` (`walletFacadeHooks`) from the active adapter.
|
|
244
|
+
* 5. Rendering the adapter-specific UI context provider (e.g., WagmiProvider for EVM) around its children,
|
|
245
|
+
* which is essential for the facade hooks to function correctly.
|
|
246
|
+
* 6. Providing a function (`setActiveNetworkId`) to change the globally active network.
|
|
247
|
+
*
|
|
248
|
+
* Consumers use the `useWalletState()` hook to access this global state.
|
|
249
|
+
* It should be placed high in the component tree, inside an `<AdapterProvider>`.
|
|
250
|
+
*/
|
|
251
|
+
function WalletStateProvider({ children, initialNetworkId = null, getNetworkConfigById, loadConfigModule }) {
|
|
252
|
+
const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = (0, react.useState)(initialNetworkId);
|
|
253
|
+
const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] = (0, react.useState)(null);
|
|
254
|
+
const [globalActiveAdapter, setGlobalActiveAdapter] = (0, react.useState)(null);
|
|
255
|
+
const [isGlobalAdapterLoading, setIsGlobalAdapterLoading] = (0, react.useState)(false);
|
|
256
|
+
const [walletFacadeHooks, setWalletFacadeHooks] = (0, react.useState)(null);
|
|
257
|
+
const [AdapterUiContextProviderToRender, setAdapterUiContextProviderToRender] = (0, react.useState)(null);
|
|
258
|
+
const [uiKitConfigVersion, setUiKitConfigVersion] = (0, react.useState)(0);
|
|
259
|
+
const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = (0, react.useState)(void 0);
|
|
260
|
+
const { getAdapterForNetwork } = useAdapterContext();
|
|
261
|
+
(0, react.useEffect)(() => {
|
|
262
|
+
const abortController = new AbortController();
|
|
263
|
+
async function fetchNetworkConfig() {
|
|
264
|
+
if (!currentGlobalNetworkId) {
|
|
265
|
+
if (!abortController.signal.aborted) setCurrentGlobalNetworkConfig(null);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
const config = await Promise.resolve(getNetworkConfigById(currentGlobalNetworkId));
|
|
270
|
+
if (!abortController.signal.aborted) setCurrentGlobalNetworkConfig(config || null);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (!abortController.signal.aborted) {
|
|
273
|
+
_openzeppelin_ui_utils.logger.error("[WSP fetchNetworkConfig]", "Failed to fetch network config:", error);
|
|
274
|
+
setCurrentGlobalNetworkConfig(null);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
fetchNetworkConfig();
|
|
279
|
+
return () => abortController.abort();
|
|
280
|
+
}, [currentGlobalNetworkId, getNetworkConfigById]);
|
|
281
|
+
(0, react.useEffect)(() => {
|
|
282
|
+
const abortController = new AbortController();
|
|
283
|
+
async function loadAdapterAndConfigureUi() {
|
|
284
|
+
if (!currentGlobalNetworkConfig) {
|
|
285
|
+
if (!abortController.signal.aborted) {
|
|
286
|
+
setGlobalActiveAdapter(null);
|
|
287
|
+
setIsGlobalAdapterLoading(false);
|
|
288
|
+
setAdapterUiContextProviderToRender(null);
|
|
289
|
+
setWalletFacadeHooks(null);
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const { adapter: newAdapter, isLoading: newIsLoading } = getAdapterForNetwork(currentGlobalNetworkConfig);
|
|
294
|
+
if (abortController.signal.aborted) return;
|
|
295
|
+
setIsGlobalAdapterLoading(newIsLoading);
|
|
296
|
+
if (newAdapter && !newIsLoading) try {
|
|
297
|
+
const { providerComponent, hooks } = await configureAdapterUiKit(newAdapter, loadConfigModule, programmaticUiKitConfig);
|
|
298
|
+
if (!abortController.signal.aborted) {
|
|
299
|
+
setAdapterUiContextProviderToRender(() => providerComponent);
|
|
300
|
+
setWalletFacadeHooks(hooks);
|
|
301
|
+
setGlobalActiveAdapter(newAdapter);
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (!abortController.signal.aborted) {
|
|
305
|
+
_openzeppelin_ui_utils.logger.error("[WSP loadAdapterAndConfigureUi]", "Error during adapter UI setup:", error);
|
|
306
|
+
setAdapterUiContextProviderToRender(null);
|
|
307
|
+
setWalletFacadeHooks(null);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else if (!newAdapter && !newIsLoading) {
|
|
311
|
+
if (!abortController.signal.aborted) {
|
|
312
|
+
setAdapterUiContextProviderToRender(null);
|
|
313
|
+
setWalletFacadeHooks(null);
|
|
314
|
+
setGlobalActiveAdapter(null);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
loadAdapterAndConfigureUi();
|
|
319
|
+
return () => abortController.abort();
|
|
320
|
+
}, [
|
|
321
|
+
currentGlobalNetworkConfig,
|
|
322
|
+
getAdapterForNetwork,
|
|
323
|
+
loadConfigModule,
|
|
324
|
+
uiKitConfigVersion,
|
|
325
|
+
programmaticUiKitConfig
|
|
326
|
+
]);
|
|
327
|
+
/**
|
|
328
|
+
* Callback to set the globally active network ID.
|
|
329
|
+
* Also clears dependent states (config, adapter, hooks) if the network ID is cleared.
|
|
330
|
+
*/
|
|
331
|
+
const setActiveNetworkIdCallback = (0, react.useCallback)((networkId) => {
|
|
332
|
+
_openzeppelin_ui_utils.logger.info("WalletStateProvider", `Setting global network ID to: ${networkId}`);
|
|
333
|
+
setCurrentGlobalNetworkIdState(networkId);
|
|
334
|
+
if (!networkId) {
|
|
335
|
+
setCurrentGlobalNetworkConfig(null);
|
|
336
|
+
setGlobalActiveAdapter(null);
|
|
337
|
+
setIsGlobalAdapterLoading(false);
|
|
338
|
+
setWalletFacadeHooks(null);
|
|
339
|
+
}
|
|
340
|
+
}, []);
|
|
341
|
+
/**
|
|
342
|
+
* Callback to explicitly trigger a re-configuration of the active adapter's UI kit.
|
|
343
|
+
* This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.
|
|
344
|
+
*/
|
|
345
|
+
const reconfigureActiveAdapterUiKit = (0, react.useCallback)((uiKitConfig) => {
|
|
346
|
+
_openzeppelin_ui_utils.logger.info("WalletStateProvider", "Explicitly triggering UI kit re-configuration by bumping version.", uiKitConfig);
|
|
347
|
+
setProgrammaticUiKitConfig(uiKitConfig);
|
|
348
|
+
setUiKitConfigVersion((v) => v + 1);
|
|
349
|
+
}, [setProgrammaticUiKitConfig, setUiKitConfigVersion]);
|
|
350
|
+
const contextValue = (0, react.useMemo)(() => ({
|
|
351
|
+
activeNetworkId: currentGlobalNetworkId,
|
|
352
|
+
setActiveNetworkId: setActiveNetworkIdCallback,
|
|
353
|
+
activeNetworkConfig: currentGlobalNetworkConfig,
|
|
354
|
+
activeAdapter: globalActiveAdapter,
|
|
355
|
+
isAdapterLoading: isGlobalAdapterLoading,
|
|
356
|
+
walletFacadeHooks,
|
|
357
|
+
reconfigureActiveAdapterUiKit
|
|
358
|
+
}), [
|
|
359
|
+
currentGlobalNetworkId,
|
|
360
|
+
setActiveNetworkIdCallback,
|
|
361
|
+
currentGlobalNetworkConfig,
|
|
362
|
+
globalActiveAdapter,
|
|
363
|
+
isGlobalAdapterLoading,
|
|
364
|
+
walletFacadeHooks,
|
|
365
|
+
reconfigureActiveAdapterUiKit
|
|
366
|
+
]);
|
|
367
|
+
const ActualProviderToRender = AdapterUiContextProviderToRender;
|
|
368
|
+
let childrenToRender;
|
|
369
|
+
if (ActualProviderToRender) {
|
|
370
|
+
const key = `${globalActiveAdapter?.networkConfig?.ecosystem || "unknown"}-${globalActiveAdapter?.networkConfig?.id || "unknown"}`;
|
|
371
|
+
_openzeppelin_ui_utils.logger.info("[WSP RENDER]", "Rendering adapter-provided UI context provider:", ActualProviderToRender.displayName || ActualProviderToRender.name || "UnknownComponent", "with key:", key);
|
|
372
|
+
childrenToRender = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActualProviderToRender, { children }, key);
|
|
373
|
+
} else {
|
|
374
|
+
_openzeppelin_ui_utils.logger.info("[WSP RENDER]", "No adapter UI context provider to render. Rendering direct children.");
|
|
375
|
+
childrenToRender = children;
|
|
376
|
+
}
|
|
377
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WalletStateContext.Provider, {
|
|
378
|
+
value: contextValue,
|
|
379
|
+
children: childrenToRender
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/hooks/AnalyticsContext.tsx
|
|
385
|
+
/**
|
|
386
|
+
* Analytics context for providing analytics functionality to React components.
|
|
387
|
+
* Must be used within an AnalyticsProvider.
|
|
388
|
+
*/
|
|
389
|
+
const AnalyticsContext = (0, react.createContext)(null);
|
|
390
|
+
/**
|
|
391
|
+
* Internal hook to access analytics context.
|
|
392
|
+
* Throws an error if used outside of an AnalyticsProvider.
|
|
393
|
+
*
|
|
394
|
+
* @internal
|
|
395
|
+
* @throws Error if used outside of AnalyticsProvider
|
|
396
|
+
*/
|
|
397
|
+
const useAnalyticsContext = () => {
|
|
398
|
+
const context = (0, react.useContext)(AnalyticsContext);
|
|
399
|
+
if (!context) throw new Error("useAnalyticsContext must be used within an AnalyticsProvider");
|
|
400
|
+
return context;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/hooks/AnalyticsProvider.tsx
|
|
405
|
+
/**
|
|
406
|
+
* Analytics Provider component.
|
|
407
|
+
* Provides analytics functionality throughout the React component tree.
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```tsx
|
|
411
|
+
* function App() {
|
|
412
|
+
* return (
|
|
413
|
+
* <AnalyticsProvider tagId={import.meta.env.VITE_GA_TAG_ID} autoInit={true}>
|
|
414
|
+
* <YourApp />
|
|
415
|
+
* </AnalyticsProvider>
|
|
416
|
+
* );
|
|
417
|
+
* }
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
const AnalyticsProvider = ({ tagId, autoInit = true, children }) => {
|
|
421
|
+
(0, react.useEffect)(() => {
|
|
422
|
+
if (autoInit && tagId) _openzeppelin_ui_utils.AnalyticsService.initialize(tagId);
|
|
423
|
+
}, [tagId, autoInit]);
|
|
424
|
+
const contextValue = (0, react.useMemo)(() => ({
|
|
425
|
+
tagId,
|
|
426
|
+
isEnabled: () => _openzeppelin_ui_utils.AnalyticsService.isEnabled(),
|
|
427
|
+
initialize: (tagIdOverride) => {
|
|
428
|
+
const effectiveTagId = tagIdOverride || tagId;
|
|
429
|
+
if (effectiveTagId) _openzeppelin_ui_utils.AnalyticsService.initialize(effectiveTagId);
|
|
430
|
+
},
|
|
431
|
+
trackEvent: (eventName, parameters) => {
|
|
432
|
+
try {
|
|
433
|
+
_openzeppelin_ui_utils.AnalyticsService.trackEvent(eventName, parameters);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
_openzeppelin_ui_utils.logger.error("AnalyticsProvider", "Error tracking event:", error);
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
trackPageView: (pageName, pagePath) => {
|
|
439
|
+
try {
|
|
440
|
+
_openzeppelin_ui_utils.AnalyticsService.trackPageView(pageName, pagePath);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
_openzeppelin_ui_utils.logger.error("AnalyticsProvider", "Error tracking page view:", error);
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
trackNetworkSelection: (networkId, ecosystem) => {
|
|
446
|
+
try {
|
|
447
|
+
_openzeppelin_ui_utils.AnalyticsService.trackNetworkSelection(networkId, ecosystem);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
_openzeppelin_ui_utils.logger.error("AnalyticsProvider", "Error tracking network selection:", error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}), [tagId]);
|
|
453
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalyticsContext.Provider, {
|
|
454
|
+
value: contextValue,
|
|
455
|
+
children
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/hooks/useAnalytics.ts
|
|
461
|
+
/**
|
|
462
|
+
* Custom hook for accessing analytics functionality.
|
|
463
|
+
*
|
|
464
|
+
* This hook provides a convenient interface for tracking user interactions
|
|
465
|
+
* throughout the application. It must be used within an AnalyticsProvider.
|
|
466
|
+
*
|
|
467
|
+
* For app-specific tracking methods, create a wrapper hook that uses this
|
|
468
|
+
* hook and adds your custom tracking functions.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```tsx
|
|
472
|
+
* // Basic usage
|
|
473
|
+
* function MyComponent() {
|
|
474
|
+
* const { trackEvent, trackPageView, isEnabled } = useAnalytics();
|
|
475
|
+
*
|
|
476
|
+
* const handleClick = () => {
|
|
477
|
+
* trackEvent('button_clicked', { button_name: 'submit' });
|
|
478
|
+
* };
|
|
479
|
+
*
|
|
480
|
+
* return (
|
|
481
|
+
* <div>
|
|
482
|
+
* Analytics enabled: {isEnabled().toString()}
|
|
483
|
+
* <button onClick={handleClick}>Submit</button>
|
|
484
|
+
* </div>
|
|
485
|
+
* );
|
|
486
|
+
* }
|
|
487
|
+
* ```
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```tsx
|
|
491
|
+
* // Creating app-specific wrapper hook
|
|
492
|
+
* function useMyAppAnalytics() {
|
|
493
|
+
* const analytics = useAnalytics();
|
|
494
|
+
*
|
|
495
|
+
* return {
|
|
496
|
+
* ...analytics,
|
|
497
|
+
* trackFormSubmit: (formName: string) => {
|
|
498
|
+
* analytics.trackEvent('form_submitted', { form_name: formName });
|
|
499
|
+
* },
|
|
500
|
+
* };
|
|
501
|
+
* }
|
|
502
|
+
* ```
|
|
503
|
+
*
|
|
504
|
+
* @returns Analytics context with tracking methods and state
|
|
505
|
+
* @throws Error if used outside of AnalyticsProvider
|
|
506
|
+
*/
|
|
507
|
+
const useAnalytics = () => {
|
|
508
|
+
try {
|
|
509
|
+
return useAnalyticsContext();
|
|
510
|
+
} catch {
|
|
511
|
+
throw new Error("useAnalytics must be used within an AnalyticsProvider");
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/hooks/useDerivedAccountStatus.ts
|
|
517
|
+
const defaultAccountStatus = {
|
|
518
|
+
isConnected: false,
|
|
519
|
+
address: void 0,
|
|
520
|
+
chainId: void 0
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* A custom hook that consumes useWalletState to get the walletFacadeHooks,
|
|
524
|
+
* then calls the useAccount facade hook (if available) and returns a structured,
|
|
525
|
+
* safely-accessed account status (isConnected, address, chainId).
|
|
526
|
+
* Provides default values if the hook or its properties are unavailable.
|
|
527
|
+
*/
|
|
528
|
+
function useDerivedAccountStatus() {
|
|
529
|
+
const { walletFacadeHooks } = useWalletState();
|
|
530
|
+
const accountHookOutput = walletFacadeHooks?.useAccount ? walletFacadeHooks.useAccount() : void 0;
|
|
531
|
+
if ((0, _openzeppelin_ui_utils.isRecordWithProperties)(accountHookOutput)) return {
|
|
532
|
+
isConnected: "isConnected" in accountHookOutput && typeof accountHookOutput.isConnected === "boolean" ? accountHookOutput.isConnected : defaultAccountStatus.isConnected,
|
|
533
|
+
address: "address" in accountHookOutput && typeof accountHookOutput.address === "string" ? accountHookOutput.address : defaultAccountStatus.address,
|
|
534
|
+
chainId: "chainId" in accountHookOutput && typeof accountHookOutput.chainId === "number" ? accountHookOutput.chainId : defaultAccountStatus.chainId
|
|
535
|
+
};
|
|
536
|
+
return defaultAccountStatus;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/hooks/useDerivedSwitchChainStatus.ts
|
|
541
|
+
const defaultSwitchChainStatus = {
|
|
542
|
+
switchChain: void 0,
|
|
543
|
+
isSwitching: false,
|
|
544
|
+
error: null
|
|
545
|
+
};
|
|
546
|
+
/**
|
|
547
|
+
* A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,
|
|
548
|
+
* then calls the `useSwitchChain` facade hook (if available) and returns a structured,
|
|
549
|
+
* safely-accessed status and control function for network switching.
|
|
550
|
+
* Provides default values if the hook or its properties are unavailable.
|
|
551
|
+
*/
|
|
552
|
+
function useDerivedSwitchChainStatus() {
|
|
553
|
+
const { walletFacadeHooks } = useWalletState();
|
|
554
|
+
const switchChainHookOutput = walletFacadeHooks?.useSwitchChain ? walletFacadeHooks.useSwitchChain() : void 0;
|
|
555
|
+
if ((0, _openzeppelin_ui_utils.isRecordWithProperties)(switchChainHookOutput)) return {
|
|
556
|
+
switchChain: "switchChain" in switchChainHookOutput && typeof switchChainHookOutput.switchChain === "function" ? switchChainHookOutput.switchChain : defaultSwitchChainStatus.switchChain,
|
|
557
|
+
isSwitching: "isPending" in switchChainHookOutput && typeof switchChainHookOutput.isPending === "boolean" ? switchChainHookOutput.isPending : defaultSwitchChainStatus.isSwitching,
|
|
558
|
+
error: "error" in switchChainHookOutput && switchChainHookOutput.error instanceof Error ? switchChainHookOutput.error : defaultSwitchChainStatus.error
|
|
559
|
+
};
|
|
560
|
+
return defaultSwitchChainStatus;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/hooks/useDerivedChainInfo.ts
|
|
565
|
+
const defaultChainInfo = {
|
|
566
|
+
currentChainId: void 0,
|
|
567
|
+
availableChains: []
|
|
568
|
+
};
|
|
569
|
+
/**
|
|
570
|
+
* A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,
|
|
571
|
+
* then calls the `useChainId` and `useChains` facade hooks (if available)
|
|
572
|
+
* and returns a structured object with this information.
|
|
573
|
+
* Provides default values if the hooks or their properties are unavailable.
|
|
574
|
+
*/
|
|
575
|
+
function useDerivedChainInfo() {
|
|
576
|
+
const { walletFacadeHooks } = useWalletState();
|
|
577
|
+
let chainIdToReturn = defaultChainInfo.currentChainId;
|
|
578
|
+
const chainIdHookOutput = walletFacadeHooks?.useChainId ? walletFacadeHooks.useChainId() : void 0;
|
|
579
|
+
if (typeof chainIdHookOutput === "number") chainIdToReturn = chainIdHookOutput;
|
|
580
|
+
else if (chainIdHookOutput !== void 0) _openzeppelin_ui_utils.logger.warn("useDerivedChainInfo", "useChainId facade hook returned non-numeric value:", chainIdHookOutput);
|
|
581
|
+
let chainsToReturn = defaultChainInfo.availableChains;
|
|
582
|
+
const chainsHookOutput = walletFacadeHooks?.useChains ? walletFacadeHooks.useChains() : void 0;
|
|
583
|
+
if (Array.isArray(chainsHookOutput)) chainsToReturn = chainsHookOutput;
|
|
584
|
+
else if (chainsHookOutput !== void 0) _openzeppelin_ui_utils.logger.warn("useDerivedChainInfo", "useChains facade hook returned non-array value:", chainsHookOutput);
|
|
585
|
+
return {
|
|
586
|
+
currentChainId: chainIdToReturn,
|
|
587
|
+
availableChains: chainsToReturn
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/hooks/useDerivedConnectStatus.ts
|
|
593
|
+
const defaultConnectStatus = {
|
|
594
|
+
connect: void 0,
|
|
595
|
+
connectors: [],
|
|
596
|
+
isConnecting: false,
|
|
597
|
+
error: null,
|
|
598
|
+
pendingConnector: void 0
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,
|
|
602
|
+
* then calls the `useConnect` facade hook (if available) and returns a structured,
|
|
603
|
+
* safely-accessed status and control functions for wallet connection.
|
|
604
|
+
*/
|
|
605
|
+
function useDerivedConnectStatus() {
|
|
606
|
+
const { walletFacadeHooks } = useWalletState();
|
|
607
|
+
const connectHookOutput = walletFacadeHooks?.useConnect ? walletFacadeHooks.useConnect() : void 0;
|
|
608
|
+
if ((0, _openzeppelin_ui_utils.isRecordWithProperties)(connectHookOutput)) return {
|
|
609
|
+
connect: "connect" in connectHookOutput && typeof connectHookOutput.connect === "function" ? connectHookOutput.connect : defaultConnectStatus.connect,
|
|
610
|
+
connectors: "connectors" in connectHookOutput && Array.isArray(connectHookOutput.connectors) ? connectHookOutput.connectors : defaultConnectStatus.connectors,
|
|
611
|
+
isConnecting: "isPending" in connectHookOutput && typeof connectHookOutput.isPending === "boolean" ? connectHookOutput.isPending : "isLoading" in connectHookOutput && typeof connectHookOutput.isLoading === "boolean" ? connectHookOutput.isLoading : defaultConnectStatus.isConnecting,
|
|
612
|
+
error: "error" in connectHookOutput && connectHookOutput.error instanceof Error ? connectHookOutput.error : defaultConnectStatus.error,
|
|
613
|
+
pendingConnector: "pendingConnector" in connectHookOutput && typeof connectHookOutput.pendingConnector === "object" ? connectHookOutput.pendingConnector : defaultConnectStatus.pendingConnector
|
|
614
|
+
};
|
|
615
|
+
return defaultConnectStatus;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
//#endregion
|
|
619
|
+
//#region src/hooks/useDerivedDisconnect.ts
|
|
620
|
+
const defaultDisconnectStatus = {
|
|
621
|
+
disconnect: void 0,
|
|
622
|
+
isDisconnecting: false,
|
|
623
|
+
error: null
|
|
624
|
+
};
|
|
625
|
+
/**
|
|
626
|
+
* A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,
|
|
627
|
+
* then calls the `useDisconnect` facade hook (if available) and returns a structured,
|
|
628
|
+
* safely-accessed status and control function for wallet disconnection.
|
|
629
|
+
*/
|
|
630
|
+
function useDerivedDisconnect() {
|
|
631
|
+
const { walletFacadeHooks } = useWalletState();
|
|
632
|
+
const disconnectHookOutput = walletFacadeHooks?.useDisconnect ? walletFacadeHooks.useDisconnect() : void 0;
|
|
633
|
+
if ((0, _openzeppelin_ui_utils.isRecordWithProperties)(disconnectHookOutput)) return {
|
|
634
|
+
disconnect: "disconnect" in disconnectHookOutput && typeof disconnectHookOutput.disconnect === "function" ? disconnectHookOutput.disconnect : defaultDisconnectStatus.disconnect,
|
|
635
|
+
isDisconnecting: "isPending" in disconnectHookOutput && typeof disconnectHookOutput.isPending === "boolean" ? disconnectHookOutput.isPending : "isLoading" in disconnectHookOutput && typeof disconnectHookOutput.isLoading === "boolean" ? disconnectHookOutput.isLoading : defaultDisconnectStatus.isDisconnecting,
|
|
636
|
+
error: "error" in disconnectHookOutput && disconnectHookOutput.error instanceof Error ? disconnectHookOutput.error : defaultDisconnectStatus.error
|
|
637
|
+
};
|
|
638
|
+
return defaultDisconnectStatus;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
//#endregion
|
|
642
|
+
//#region src/hooks/useWalletReconnectionHandler.ts
|
|
643
|
+
/**
|
|
644
|
+
* Hook that detects wallet reconnection and re-queues network switch if needed.
|
|
645
|
+
*
|
|
646
|
+
* When a user disconnects their wallet and then reconnects in the same session,
|
|
647
|
+
* the wallet may connect to a different chain than what's selected in the app.
|
|
648
|
+
* This hook detects that scenario and invokes a callback to re-queue the network switch.
|
|
649
|
+
*
|
|
650
|
+
* @param selectedNetworkConfigId - Currently selected network in the app
|
|
651
|
+
* @param selectedAdapter - Currently active adapter instance
|
|
652
|
+
* @param networkToSwitchTo - Current network switch queue state (null if no switch pending)
|
|
653
|
+
* @param onRequeueSwitch - Callback invoked when a network switch should be re-queued
|
|
654
|
+
*/
|
|
655
|
+
function useWalletReconnectionHandler(selectedNetworkConfigId, selectedAdapter, networkToSwitchTo, onRequeueSwitch) {
|
|
656
|
+
const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();
|
|
657
|
+
const prevConnectedRef = (0, react.useRef)(isConnected);
|
|
658
|
+
(0, react.useEffect)(() => {
|
|
659
|
+
const isReconnection = !prevConnectedRef.current && isConnected;
|
|
660
|
+
prevConnectedRef.current = isConnected;
|
|
661
|
+
if (!isReconnection || !selectedNetworkConfigId || !selectedAdapter) return;
|
|
662
|
+
if (networkToSwitchTo === selectedNetworkConfigId) return;
|
|
663
|
+
const adapterConfig = selectedAdapter.networkConfig;
|
|
664
|
+
if (!("chainId" in adapterConfig) || !walletChainId) return;
|
|
665
|
+
const targetChainId = Number(adapterConfig.chainId);
|
|
666
|
+
if (walletChainId !== targetChainId) {
|
|
667
|
+
_openzeppelin_ui_utils.logger.info("useWalletReconnectionHandler", `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`);
|
|
668
|
+
onRequeueSwitch(selectedNetworkConfigId);
|
|
669
|
+
}
|
|
670
|
+
}, [
|
|
671
|
+
isConnected,
|
|
672
|
+
walletChainId,
|
|
673
|
+
selectedNetworkConfigId,
|
|
674
|
+
selectedAdapter,
|
|
675
|
+
networkToSwitchTo,
|
|
676
|
+
onRequeueSwitch
|
|
677
|
+
]);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
//#endregion
|
|
681
|
+
//#region src/components/WalletConnectionUI.tsx
|
|
682
|
+
/**
|
|
683
|
+
* Component that displays wallet connection UI components
|
|
684
|
+
* provided by the active adapter.
|
|
685
|
+
*/
|
|
686
|
+
const WalletConnectionUI = ({ className }) => {
|
|
687
|
+
const [isError, setIsError] = (0, react.useState)(false);
|
|
688
|
+
const { activeAdapter, walletFacadeHooks } = useWalletState();
|
|
689
|
+
(0, react.useEffect)(() => {
|
|
690
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] State from useWalletState:", {
|
|
691
|
+
adapterId: activeAdapter?.networkConfig.id,
|
|
692
|
+
hasFacadeHooks: !!walletFacadeHooks
|
|
693
|
+
});
|
|
694
|
+
}, [activeAdapter, walletFacadeHooks]);
|
|
695
|
+
const walletComponents = (() => {
|
|
696
|
+
if (!activeAdapter || typeof activeAdapter.getEcosystemWalletComponents !== "function") {
|
|
697
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] No activeAdapter or getEcosystemWalletComponents method, returning null.");
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const components = activeAdapter.getEcosystemWalletComponents();
|
|
702
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] walletComponents from adapter:", components);
|
|
703
|
+
return components;
|
|
704
|
+
} catch (error) {
|
|
705
|
+
_openzeppelin_ui_utils.logger.error("WalletConnectionUI", "[Debug] Error getting wallet components:", error);
|
|
706
|
+
setIsError(true);
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
})();
|
|
710
|
+
if (!walletComponents) {
|
|
711
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "[Debug] getEcosystemWalletComponents returned null/undefined, rendering null.");
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionUI", "Rendering wallet components:", {
|
|
715
|
+
hasConnectButton: !!walletComponents.ConnectButton,
|
|
716
|
+
hasAccountDisplay: !!walletComponents.AccountDisplay,
|
|
717
|
+
hasNetworkSwitcher: !!walletComponents.NetworkSwitcher
|
|
718
|
+
});
|
|
719
|
+
const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;
|
|
720
|
+
if (isError) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
721
|
+
className: (0, _openzeppelin_ui_utils.cn)("flex items-center gap-4", className),
|
|
722
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Button, {
|
|
723
|
+
variant: "destructive",
|
|
724
|
+
size: "sm",
|
|
725
|
+
onClick: () => window.location.reload(),
|
|
726
|
+
children: "Wallet Error - Retry"
|
|
727
|
+
})
|
|
728
|
+
});
|
|
729
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
730
|
+
className: (0, _openzeppelin_ui_utils.cn)("flex items-center gap-4", className),
|
|
731
|
+
children: [
|
|
732
|
+
NetworkSwitcher && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSwitcher, {}),
|
|
733
|
+
AccountDisplay && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountDisplay, {}),
|
|
734
|
+
ConnectButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectButton, {})
|
|
735
|
+
]
|
|
736
|
+
});
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
//#endregion
|
|
740
|
+
//#region src/components/WalletConnectionHeader.tsx
|
|
741
|
+
/**
|
|
742
|
+
* Component that renders the wallet connection UI.
|
|
743
|
+
* Uses useWalletState to get its data.
|
|
744
|
+
*/
|
|
745
|
+
const WalletConnectionHeader = () => {
|
|
746
|
+
const { isAdapterLoading, activeAdapter } = useWalletState();
|
|
747
|
+
(0, react.useEffect)(() => {
|
|
748
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionHeader", "[Debug] State from useWalletState:", {
|
|
749
|
+
adapterPresent: !!activeAdapter,
|
|
750
|
+
adapterNetwork: activeAdapter?.networkConfig.id,
|
|
751
|
+
isLoading: isAdapterLoading
|
|
752
|
+
});
|
|
753
|
+
}, [activeAdapter, isAdapterLoading]);
|
|
754
|
+
if (isAdapterLoading) {
|
|
755
|
+
_openzeppelin_ui_utils.logger.debug("WalletConnectionHeader", "[Debug] Adapter loading, showing skeleton.");
|
|
756
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
|
|
757
|
+
}
|
|
758
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WalletConnectionUI, {});
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
//#endregion
|
|
762
|
+
//#region src/components/NetworkSwitchManager.tsx
|
|
763
|
+
/**
|
|
764
|
+
* Component that handles wallet network switching based on the selected network.
|
|
765
|
+
*
|
|
766
|
+
* This component manages the lifecycle of network switching operations,
|
|
767
|
+
* coordinating between the wallet's current chain state and the target network.
|
|
768
|
+
* It's designed to be used in any application that needs seamless wallet network switching.
|
|
769
|
+
*
|
|
770
|
+
* Features:
|
|
771
|
+
* - Automatically initiates network switch when mounted with a target network
|
|
772
|
+
* - Handles EVM chain switching gracefully
|
|
773
|
+
* - No-ops for non-EVM networks that don't support chain switching
|
|
774
|
+
* - Tracks switch attempts to prevent duplicate operations
|
|
775
|
+
* - Provides completion callback for parent components to handle state cleanup
|
|
776
|
+
*/
|
|
777
|
+
const NetworkSwitchManager = ({ adapter, targetNetworkId, onNetworkSwitchComplete }) => {
|
|
778
|
+
const isMountedRef = (0, react.useRef)(true);
|
|
779
|
+
const [hasAttemptedSwitch, setHasAttemptedSwitch] = (0, react.useState)(false);
|
|
780
|
+
const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();
|
|
781
|
+
const { switchChain: execSwitchNetwork, isSwitching: isSwitchingNetworkViaHook, error: switchNetworkError } = useDerivedSwitchChainStatus();
|
|
782
|
+
(0, react.useEffect)(() => {
|
|
783
|
+
isMountedRef.current = true;
|
|
784
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", `Mounted with target: ${targetNetworkId}, current attempt status: ${hasAttemptedSwitch}`);
|
|
785
|
+
setHasAttemptedSwitch(false);
|
|
786
|
+
return () => {
|
|
787
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", `Unmounting, was for target: ${targetNetworkId}`);
|
|
788
|
+
isMountedRef.current = false;
|
|
789
|
+
};
|
|
790
|
+
}, [targetNetworkId]);
|
|
791
|
+
(0, react.useEffect)(() => {
|
|
792
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "State Update:", {
|
|
793
|
+
target: targetNetworkId,
|
|
794
|
+
adapterNetwork: adapter.networkConfig.id,
|
|
795
|
+
isSwitching: isSwitchingNetworkViaHook,
|
|
796
|
+
hookError: !!switchNetworkError,
|
|
797
|
+
canExec: !!execSwitchNetwork,
|
|
798
|
+
connected: isConnected,
|
|
799
|
+
walletChain: currentChainIdFromHook,
|
|
800
|
+
attempted: hasAttemptedSwitch
|
|
801
|
+
});
|
|
802
|
+
}, [
|
|
803
|
+
adapter,
|
|
804
|
+
targetNetworkId,
|
|
805
|
+
isSwitchingNetworkViaHook,
|
|
806
|
+
switchNetworkError,
|
|
807
|
+
execSwitchNetwork,
|
|
808
|
+
isConnected,
|
|
809
|
+
currentChainIdFromHook,
|
|
810
|
+
hasAttemptedSwitch
|
|
811
|
+
]);
|
|
812
|
+
(0, react.useEffect)(() => {
|
|
813
|
+
const completeOperation = (logMessage, options = { notifyComplete: true }) => {
|
|
814
|
+
if (logMessage) _openzeppelin_ui_utils.logger.info("NetworkSwitchManager", logMessage);
|
|
815
|
+
if (options.notifyComplete && isMountedRef.current && onNetworkSwitchComplete) onNetworkSwitchComplete();
|
|
816
|
+
if (isMountedRef.current) setHasAttemptedSwitch(false);
|
|
817
|
+
};
|
|
818
|
+
if (!execSwitchNetwork) {
|
|
819
|
+
completeOperation("No switchChain function available from hook. Operation halted.", { notifyComplete: false });
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (isSwitchingNetworkViaHook && hasAttemptedSwitch) {
|
|
823
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "Hook reports switch in progress for current attempt. Waiting...");
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (hasAttemptedSwitch && !isSwitchingNetworkViaHook) {
|
|
827
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "Previous switch attempt concluded. Deferring to completion effect.");
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
if (adapter.networkConfig.id !== targetNetworkId) {
|
|
831
|
+
completeOperation(`CRITICAL: Adapter (${adapter.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`, { notifyComplete: false });
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (!isConnected) {
|
|
835
|
+
completeOperation("Wallet not connected (derived status). Awaiting connection.", { notifyComplete: false });
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (!("chainId" in adapter.networkConfig)) {
|
|
839
|
+
completeOperation("Network does not support chain switching (non-EVM). Operation complete (no-op).");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const targetChainToBeSwitchedTo = Number(adapter.networkConfig.chainId);
|
|
843
|
+
if (currentChainIdFromHook === targetChainToBeSwitchedTo) {
|
|
844
|
+
completeOperation("Already on correct chain (derived status). Operation complete.");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const performSwitchActual = () => {
|
|
848
|
+
if (!isMountedRef.current || isSwitchingNetworkViaHook || hasAttemptedSwitch) {
|
|
849
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", `Switch attempt aborted in timeout or already handled. Conditions: isSwitching: ${isSwitchingNetworkViaHook}, hasAttempted: ${hasAttemptedSwitch}`);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", `Attempting switch to ${targetChainToBeSwitchedTo} via derived hook.`);
|
|
853
|
+
setHasAttemptedSwitch(true);
|
|
854
|
+
execSwitchNetwork({ chainId: targetChainToBeSwitchedTo });
|
|
855
|
+
};
|
|
856
|
+
const timeoutId = setTimeout(performSwitchActual, 100);
|
|
857
|
+
return () => clearTimeout(timeoutId);
|
|
858
|
+
}, [
|
|
859
|
+
adapter,
|
|
860
|
+
targetNetworkId,
|
|
861
|
+
execSwitchNetwork,
|
|
862
|
+
isSwitchingNetworkViaHook,
|
|
863
|
+
onNetworkSwitchComplete,
|
|
864
|
+
isConnected,
|
|
865
|
+
currentChainIdFromHook,
|
|
866
|
+
hasAttemptedSwitch
|
|
867
|
+
]);
|
|
868
|
+
(0, react.useEffect)(() => {
|
|
869
|
+
if (!isMountedRef.current || !execSwitchNetwork || !hasAttemptedSwitch) return;
|
|
870
|
+
if (!isSwitchingNetworkViaHook) {
|
|
871
|
+
let completionMessage = "Switch hook operation concluded.";
|
|
872
|
+
if (switchNetworkError) {
|
|
873
|
+
_openzeppelin_ui_utils.logger.error("NetworkSwitchManager", "Error from derived switch hook:", switchNetworkError);
|
|
874
|
+
completionMessage = "Switch hook completed with error.";
|
|
875
|
+
} else _openzeppelin_ui_utils.logger.info("NetworkSwitchManager", "Derived switch hook completed successfully.");
|
|
876
|
+
if (onNetworkSwitchComplete) onNetworkSwitchComplete();
|
|
877
|
+
if (isMountedRef.current) setHasAttemptedSwitch(false);
|
|
878
|
+
_openzeppelin_ui_utils.logger.info("NetworkSwitchManager", completionMessage);
|
|
879
|
+
}
|
|
880
|
+
}, [
|
|
881
|
+
isSwitchingNetworkViaHook,
|
|
882
|
+
switchNetworkError,
|
|
883
|
+
execSwitchNetwork,
|
|
884
|
+
hasAttemptedSwitch,
|
|
885
|
+
onNetworkSwitchComplete
|
|
886
|
+
]);
|
|
887
|
+
return null;
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
//#endregion
|
|
891
|
+
exports.AdapterContext = AdapterContext;
|
|
892
|
+
exports.AdapterProvider = AdapterProvider;
|
|
893
|
+
exports.AnalyticsContext = AnalyticsContext;
|
|
894
|
+
exports.AnalyticsProvider = AnalyticsProvider;
|
|
895
|
+
exports.NetworkSwitchManager = NetworkSwitchManager;
|
|
896
|
+
exports.WalletConnectionHeader = WalletConnectionHeader;
|
|
897
|
+
exports.WalletConnectionUI = WalletConnectionUI;
|
|
898
|
+
exports.WalletStateContext = WalletStateContext;
|
|
899
|
+
exports.WalletStateProvider = WalletStateProvider;
|
|
900
|
+
exports.useAdapterContext = useAdapterContext;
|
|
901
|
+
exports.useAnalytics = useAnalytics;
|
|
902
|
+
exports.useDerivedAccountStatus = useDerivedAccountStatus;
|
|
903
|
+
exports.useDerivedChainInfo = useDerivedChainInfo;
|
|
904
|
+
exports.useDerivedConnectStatus = useDerivedConnectStatus;
|
|
905
|
+
exports.useDerivedDisconnect = useDerivedDisconnect;
|
|
906
|
+
exports.useDerivedSwitchChainStatus = useDerivedSwitchChainStatus;
|
|
907
|
+
exports.useWalletReconnectionHandler = useWalletReconnectionHandler;
|
|
908
|
+
exports.useWalletState = useWalletState;
|
|
909
|
+
//# sourceMappingURL=index.cjs.map
|