@nexus-cross/connect-kit-react 1.3.0-beta.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-ZL2BHNN6.js +143 -0
- package/dist/client.d.ts +46 -4
- package/dist/client.js +1 -3
- package/dist/index.d.ts +378 -28
- package/dist/index.js +1 -19
- package/package.json +11 -8
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/components/ConnectButton.d.ts +0 -102
- package/dist/components/ConnectButton.d.ts.map +0 -1
- package/dist/components/ConnectButton.js +0 -584
- package/dist/components/ConnectButton.js.map +0 -1
- package/dist/components/CrossConnectKitProvider.d.ts +0 -38
- package/dist/components/CrossConnectKitProvider.d.ts.map +0 -1
- package/dist/components/CrossConnectKitProvider.js +0 -790
- package/dist/components/CrossConnectKitProvider.js.map +0 -1
- package/dist/components/CrossConnectModal.d.ts +0 -20
- package/dist/components/CrossConnectModal.d.ts.map +0 -1
- package/dist/components/CrossConnectModal.js +0 -80
- package/dist/components/CrossConnectModal.js.map +0 -1
- package/dist/components/OtherWalletsModal.d.ts +0 -20
- package/dist/components/OtherWalletsModal.d.ts.map +0 -1
- package/dist/components/OtherWalletsModal.js +0 -300
- package/dist/components/OtherWalletsModal.js.map +0 -1
- package/dist/config/createConnectKitConfig.d.ts +0 -36
- package/dist/config/createConnectKitConfig.d.ts.map +0 -1
- package/dist/config/createConnectKitConfig.js +0 -80
- package/dist/config/createConnectKitConfig.js.map +0 -1
- package/dist/context/CrossConnectKitContext.d.ts +0 -100
- package/dist/context/CrossConnectKitContext.d.ts.map +0 -1
- package/dist/context/CrossConnectKitContext.js +0 -10
- package/dist/context/CrossConnectKitContext.js.map +0 -1
- package/dist/context/CrossConnectKitThemeContext.d.ts +0 -41
- package/dist/context/CrossConnectKitThemeContext.d.ts.map +0 -1
- package/dist/context/CrossConnectKitThemeContext.js +0 -22
- package/dist/context/CrossConnectKitThemeContext.js.map +0 -1
- package/dist/hooks/useCrossxEmbeddedInfo.d.ts +0 -41
- package/dist/hooks/useCrossxEmbeddedInfo.d.ts.map +0 -1
- package/dist/hooks/useCrossxEmbeddedInfo.js +0 -241
- package/dist/hooks/useCrossxEmbeddedInfo.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/runtime-log.d.ts +0 -2
- package/dist/runtime-log.d.ts.map +0 -1
- package/dist/runtime-log.js +0 -44
- package/dist/runtime-log.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -2
- package/dist/version.js.map +0 -1
|
@@ -1,790 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { WagmiProvider, useAccount, useConnect, useDisconnect } from 'wagmi';
|
|
4
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
-
import { clearWalletSessionStorage, onOpenOtherWallets, pickInitialState, resolveCurrentWallet, } from '@nexus-cross/connect-kit-wagmi';
|
|
6
|
-
import { resolveThemeMode } from '@nexus-cross/connect-kit-core';
|
|
7
|
-
import { buildDesignSystemCss } from '@nexus-cross/crossx-design-system';
|
|
8
|
-
import { OnRampProvider } from '@nexus-cross/onramp/react';
|
|
9
|
-
import { CrossConnectKitContext } from '../context/CrossConnectKitContext.js';
|
|
10
|
-
import { CrossConnectKitThemeContext, } from '../context/CrossConnectKitThemeContext.js';
|
|
11
|
-
import { CrossConnectModal } from './CrossConnectModal.js';
|
|
12
|
-
const queryClient = new QueryClient();
|
|
13
|
-
const INTENT_STORAGE_KEY = 'crossx-kit:last-wallet-intent';
|
|
14
|
-
/**
|
|
15
|
-
* True when the thrown error is the crossy-sdk embedded connector's
|
|
16
|
-
* deliberate handoff to another wallet. Those surfaces look like
|
|
17
|
-
* UserRejectedRequestError with a cause message "User requested external
|
|
18
|
-
* wallet connection" — they are flow control, not real failures.
|
|
19
|
-
*/
|
|
20
|
-
function isExternalWalletHandoff(err) {
|
|
21
|
-
const messages = [];
|
|
22
|
-
let cur = err;
|
|
23
|
-
let depth = 0;
|
|
24
|
-
while (cur && depth < 5) {
|
|
25
|
-
const obj = cur;
|
|
26
|
-
if (typeof obj.message === 'string')
|
|
27
|
-
messages.push(obj.message);
|
|
28
|
-
cur = obj.cause;
|
|
29
|
-
depth += 1;
|
|
30
|
-
}
|
|
31
|
-
return messages.some((m) => /external wallet connection/i.test(m));
|
|
32
|
-
}
|
|
33
|
-
function readIntent() {
|
|
34
|
-
if (typeof window === 'undefined')
|
|
35
|
-
return null;
|
|
36
|
-
try {
|
|
37
|
-
return window.localStorage.getItem(INTENT_STORAGE_KEY) ?? null;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function writeIntent(walletId) {
|
|
44
|
-
if (typeof window === 'undefined')
|
|
45
|
-
return;
|
|
46
|
-
try {
|
|
47
|
-
if (walletId)
|
|
48
|
-
window.localStorage.setItem(INTENT_STORAGE_KEY, walletId);
|
|
49
|
-
else
|
|
50
|
-
window.localStorage.removeItem(INTENT_STORAGE_KEY);
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
/* storage unavailable (private mode, quota) — intent falls back to in-memory only */
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function readActiveProvider(storageKey, fallback) {
|
|
57
|
-
if (typeof window === 'undefined')
|
|
58
|
-
return fallback;
|
|
59
|
-
try {
|
|
60
|
-
const v = window.localStorage.getItem(storageKey);
|
|
61
|
-
return v === 'cross' || v === 'reown' ? v : fallback;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return fallback;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function writeActiveProvider(storageKey, value) {
|
|
68
|
-
if (typeof window === 'undefined')
|
|
69
|
-
return;
|
|
70
|
-
try {
|
|
71
|
-
window.localStorage.setItem(storageKey, value);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
/* storage unavailable — falls back to in-memory only */
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function CrossConnectKitInner({ children, config, activeProvider, onRequestProviderSwap, pendingConnectWalletId, clearPending, themeMode, themeTokens, extraWallets, walletAllowlist, }) {
|
|
78
|
-
const [otherWalletsOpen, setOtherWalletsOpen] = useState(false);
|
|
79
|
-
const [lastIntent, setLastIntent] = useState(() => readIntent());
|
|
80
|
-
// Unified, in-memory "connect attempt in flight" marker. Deliberately
|
|
81
|
-
// NOT seeded from storage (unlike `lastIntent`) so a stale persisted
|
|
82
|
-
// intent from a previous page load can never surface a phantom
|
|
83
|
-
// "Connecting…" spinner. Set at the top of `runConnect` / wrapped
|
|
84
|
-
// `oauth.signIn`; cleared on success, cancel, or error.
|
|
85
|
-
const [connectingWalletId, setConnectingWalletId] = useState(null);
|
|
86
|
-
const { address, isConnected, isConnecting, isReconnecting, connector: activeConnector, } = useAccount();
|
|
87
|
-
const { connectors, connectAsync } = useConnect();
|
|
88
|
-
const { disconnectAsync: wagmiDisconnectAsync } = useDisconnect();
|
|
89
|
-
const { kitConfig, connectorRegistry, crossProvider, reownProvider } = config;
|
|
90
|
-
const activeAdapter = activeProvider === 'cross' ? crossProvider : reownProvider;
|
|
91
|
-
const currentWallet = useMemo(() => {
|
|
92
|
-
if (!isConnected || !activeConnector)
|
|
93
|
-
return null;
|
|
94
|
-
return resolveCurrentWallet(connectorRegistry, activeConnector.id, lastIntent);
|
|
95
|
-
}, [isConnected, activeConnector, connectorRegistry, lastIntent]);
|
|
96
|
-
const availableWallets = useMemo(() => connectorRegistry.entries.filter((e) => e.type !== 'embedded'), [connectorRegistry]);
|
|
97
|
-
// On-ramp 자동 통합 — kitConfig.onRampEnabled === true일 때 OnRampProvider를
|
|
98
|
-
// 자동 wrap한다. 백엔드 식별자는 kitConfig.crossProjectId를 X-Project-Id로
|
|
99
|
-
// 그대로 사용. 어댑터/훅은 `@nexus-cross/onramp/react`에서 제공되며, 여기서는
|
|
100
|
-
// wagmi useAccount의 address를 walletStateReader로 주입만 한다. ref 기반이라
|
|
101
|
-
// 매 렌더에 객체가 재생성되지 않고 어댑터는 1회만 만들어진다.
|
|
102
|
-
const addressRef = useRef(address);
|
|
103
|
-
addressRef.current = address;
|
|
104
|
-
const onRampEnabled = !!kitConfig.onRampEnabled && !!kitConfig.crossProjectId?.trim();
|
|
105
|
-
const onRampConfig = useMemo(() => {
|
|
106
|
-
if (!onRampEnabled)
|
|
107
|
-
return null;
|
|
108
|
-
const projectId = kitConfig.crossProjectId.trim();
|
|
109
|
-
return {
|
|
110
|
-
projectId,
|
|
111
|
-
walletState: {
|
|
112
|
-
getAddress: () => addressRef.current,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}, [onRampEnabled, kitConfig.crossProjectId]);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
return onOpenOtherWallets(() => setOtherWalletsOpen(true));
|
|
118
|
-
}, []);
|
|
119
|
-
// When OtherWalletsModal opens on top of the crossy-sdk-js login
|
|
120
|
-
// modal, the SDK is supposed to dismiss its own overlay in response to
|
|
121
|
-
// the `CROSSxError: User requested external wallet connection` thrown
|
|
122
|
-
// from `signIn`. A race between our modal mount and the SDK's cleanup
|
|
123
|
-
// occasionally leaves the login overlay lingering in the DOM — remove
|
|
124
|
-
// it defensively so the user doesn't see two stacked modals.
|
|
125
|
-
//
|
|
126
|
-
// The SDK mounts every overlay under the shared id `__crossx-confirm-
|
|
127
|
-
// overlay` (see BrowserConfirmationAdapter.ts). Removing it here also
|
|
128
|
-
// aborts the SDK Promise, but the flow is already cancelling anyway.
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (!otherWalletsOpen)
|
|
131
|
-
return;
|
|
132
|
-
if (typeof document === 'undefined')
|
|
133
|
-
return;
|
|
134
|
-
// Defer a tick so the SDK's own close path runs first when it's
|
|
135
|
-
// going to. Only remove if the overlay is still there afterwards.
|
|
136
|
-
const timer = setTimeout(() => {
|
|
137
|
-
document
|
|
138
|
-
.querySelectorAll('#__crossx-confirm-overlay')
|
|
139
|
-
.forEach((el) => el.remove());
|
|
140
|
-
}, 50);
|
|
141
|
-
return () => clearTimeout(timer);
|
|
142
|
-
}, [otherWalletsOpen]);
|
|
143
|
-
const isConnectedRef = useRef(isConnected);
|
|
144
|
-
const prevConnectedRef = useRef(isConnected);
|
|
145
|
-
isConnectedRef.current = isConnected;
|
|
146
|
-
// Tracks whether the active adapter's own in-page modal (AppKit QR view)
|
|
147
|
-
// is currently open. Lets the window-focus cancel watcher below stand
|
|
148
|
-
// down while a QR modal is up — that path self-manages cancellation via
|
|
149
|
-
// `subscribeModalState`, and a desktop user blurring/refocusing while
|
|
150
|
-
// scanning on their phone must NOT be read as a dismissal.
|
|
151
|
-
const adapterModalOpenRef = useRef(false);
|
|
152
|
-
// Diagnostic: surface the provider / account state inside CrossConnectKitInner
|
|
153
|
-
// so the user can see in browser devtools which config is mounted and
|
|
154
|
-
// what wagmi reports. Cheap to keep on — fires only on state change.
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
console.debug('[crossx-kit] state', {
|
|
157
|
-
activeProvider,
|
|
158
|
-
isConnected,
|
|
159
|
-
isConnecting,
|
|
160
|
-
isReconnecting,
|
|
161
|
-
activeConnectorId: activeConnector?.id,
|
|
162
|
-
currentWallet,
|
|
163
|
-
lastIntent,
|
|
164
|
-
});
|
|
165
|
-
}, [
|
|
166
|
-
activeProvider,
|
|
167
|
-
isConnected,
|
|
168
|
-
isConnecting,
|
|
169
|
-
isReconnecting,
|
|
170
|
-
activeConnector?.id,
|
|
171
|
-
currentWallet,
|
|
172
|
-
lastIntent,
|
|
173
|
-
]);
|
|
174
|
-
// Auto-close the adapter's native modal once wagmi reports a
|
|
175
|
-
// connection. AppKit's QR-only views (e.g. ConnectingWalletConnectBasic)
|
|
176
|
-
// don't always dismiss themselves when the session is established —
|
|
177
|
-
// explicitly close on the rising edge of `isConnected` so the modal
|
|
178
|
-
// doesn't linger over the connected UI.
|
|
179
|
-
useEffect(() => {
|
|
180
|
-
if (isConnected && !prevConnectedRef.current) {
|
|
181
|
-
activeAdapter?.closeModal?.({ kitConfig });
|
|
182
|
-
}
|
|
183
|
-
prevConnectedRef.current = isConnected;
|
|
184
|
-
}, [isConnected, activeAdapter, kitConfig]);
|
|
185
|
-
// Release the unified connecting marker the moment wagmi reports a
|
|
186
|
-
// connection — the terminal "success" for every path. Cancel/error
|
|
187
|
-
// terminals are handled by `clearIntent()` (wallet paths) and the
|
|
188
|
-
// wrapped `oauth.signIn` catch (social paths).
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
if (isConnected)
|
|
191
|
-
setConnectingWalletId(null);
|
|
192
|
-
}, [isConnected]);
|
|
193
|
-
const findConnector = useCallback((connectorId) => connectors.find((c) => c.id === connectorId), [connectors]);
|
|
194
|
-
// Push `themeMode` / `themeTokens` changes into the embedded crossy-sdk
|
|
195
|
-
// at runtime. The SDK owns a live confirmation modal adapter whose
|
|
196
|
-
// palette is frozen at `crossxConnector(...)` construction, so without
|
|
197
|
-
// this effect the DApp's `setThemeMode(...)` only affects our CSS
|
|
198
|
-
// variables — the SDK's next signature / login modal would still open
|
|
199
|
-
// in the stale palette. `provider.sdk.applyTheme` is crossy-sdk 2.0's
|
|
200
|
-
// public runtime API (see `CROSSxSDK.applyTheme`).
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
const embeddedEntry = connectorRegistry.getEntry('cross_embedded');
|
|
203
|
-
if (!embeddedEntry)
|
|
204
|
-
return;
|
|
205
|
-
const crossx = findConnector(embeddedEntry.connectorId);
|
|
206
|
-
if (!crossx)
|
|
207
|
-
return;
|
|
208
|
-
let cancelled = false;
|
|
209
|
-
void (async () => {
|
|
210
|
-
try {
|
|
211
|
-
const provider = (await crossx.getProvider());
|
|
212
|
-
if (cancelled)
|
|
213
|
-
return;
|
|
214
|
-
provider?.sdk?.applyTheme?.(themeMode, themeTokens ?? undefined);
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
console.debug('[crossx-kit] applyTheme skipped:', err);
|
|
218
|
-
}
|
|
219
|
-
})();
|
|
220
|
-
return () => {
|
|
221
|
-
cancelled = true;
|
|
222
|
-
};
|
|
223
|
-
}, [connectorRegistry, findConnector, themeMode, themeTokens]);
|
|
224
|
-
const recordIntent = useCallback((walletId) => {
|
|
225
|
-
setLastIntent(walletId);
|
|
226
|
-
writeIntent(walletId);
|
|
227
|
-
}, []);
|
|
228
|
-
const clearIntent = useCallback(() => {
|
|
229
|
-
setLastIntent(null);
|
|
230
|
-
writeIntent(null);
|
|
231
|
-
// Cancel/error/dismissal terminal — also drop the connecting marker so
|
|
232
|
-
// the unified `isConnecting` releases in lockstep with the intent.
|
|
233
|
-
setConnectingWalletId(null);
|
|
234
|
-
}, []);
|
|
235
|
-
// Watch the adapter's native modal. When the user dismisses it without
|
|
236
|
-
// completing pairing, wagmi is still "not connected" and any in-flight
|
|
237
|
-
// connectAsync (e.g. cross_wallet's UniversalConnector waiting for a
|
|
238
|
-
// QR scan) is left dangling. Force a clean disconnect so the
|
|
239
|
-
// "Connecting…" state doesn't stick and lastIntent doesn't poison the
|
|
240
|
-
// next attempt.
|
|
241
|
-
useEffect(() => {
|
|
242
|
-
if (!activeAdapter?.subscribeModalState)
|
|
243
|
-
return;
|
|
244
|
-
let sawOpen = false;
|
|
245
|
-
return activeAdapter.subscribeModalState({ kitConfig }, (open) => {
|
|
246
|
-
adapterModalOpenRef.current = open;
|
|
247
|
-
if (open) {
|
|
248
|
-
sawOpen = true;
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
if (!sawOpen)
|
|
252
|
-
return;
|
|
253
|
-
sawOpen = false;
|
|
254
|
-
// Defer so a pair completing at the moment of dismissal can flip
|
|
255
|
-
// wagmi `isConnected` before we decide the user cancelled.
|
|
256
|
-
setTimeout(() => {
|
|
257
|
-
if (isConnectedRef.current)
|
|
258
|
-
return;
|
|
259
|
-
void (async () => {
|
|
260
|
-
try {
|
|
261
|
-
await wagmiDisconnectAsync();
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
/* already-disconnected is fine */
|
|
265
|
-
}
|
|
266
|
-
clearIntent();
|
|
267
|
-
})();
|
|
268
|
-
}, 250);
|
|
269
|
-
});
|
|
270
|
-
}, [activeAdapter, kitConfig, wagmiDisconnectAsync, clearIntent]);
|
|
271
|
-
// Belt-and-suspenders cancel detection for connect flows that open a
|
|
272
|
-
// SEPARATE BROWSER WINDOW whose underlying promise does NOT reject when
|
|
273
|
-
// the user simply closes that window — most notably the injected
|
|
274
|
-
// MetaMask / Binance extension approval popup (`eth_requestAccounts`
|
|
275
|
-
// stays pending on close), and external wallet/OAuth popups in general.
|
|
276
|
-
// Those windows steal focus from the opener; closing them returns it.
|
|
277
|
-
//
|
|
278
|
-
// `subscribeModalState` only covers the adapters' own in-page AppKit
|
|
279
|
-
// modal (QR view), which never blurs the opener — so it can't see an
|
|
280
|
-
// extension popup dismissal. This focus watcher fills that gap WITHOUT
|
|
281
|
-
// touching the QR path: an in-page modal triggers no window blur, so
|
|
282
|
-
// `sawBlur` stays false and this is a no-op for it.
|
|
283
|
-
//
|
|
284
|
-
// On focus-return we wait one grace tick for a genuine approval to land
|
|
285
|
-
// (`isConnected`) before releasing the unified connecting marker — so a
|
|
286
|
-
// successful approve (popup closes → focus returns → connectAsync
|
|
287
|
-
// resolves) is not mistaken for a cancel.
|
|
288
|
-
useEffect(() => {
|
|
289
|
-
if (!connectingWalletId)
|
|
290
|
-
return;
|
|
291
|
-
if (isConnected)
|
|
292
|
-
return;
|
|
293
|
-
if (typeof window === 'undefined')
|
|
294
|
-
return;
|
|
295
|
-
// Embedded social (Google/Apple) sign-in is exempt. crossy-sdk's OAuth
|
|
296
|
-
// popup closes the moment the social provider auth completes, but for a
|
|
297
|
-
// NEW user the SDK then keeps working IN-PAGE — wallet create + PIN
|
|
298
|
-
// entry — which routinely outlasts the grace tick. Treating that
|
|
299
|
-
// focus-return as a cancel would `wagmiDisconnectAsync()` mid-flight and
|
|
300
|
-
// tear the connection down before it lands (symptom: connect only shows
|
|
301
|
-
// up after a reload). The OAuth path already surfaces genuine
|
|
302
|
-
// cancellation via the `oauth.signIn` promise (rejection / `{ success:
|
|
303
|
-
// false }`), which the wrapped `oauth.signIn` catch turns into a marker
|
|
304
|
-
// release — so this watcher is both redundant and harmful here.
|
|
305
|
-
if (connectingWalletId === 'cross_embedded')
|
|
306
|
-
return;
|
|
307
|
-
// Seed from current focus: the popup may already have stolen focus by
|
|
308
|
-
// the time this effect attaches, in which case the `blur` event has
|
|
309
|
-
// already fired and we'd otherwise miss it.
|
|
310
|
-
let sawBlur = typeof document !== 'undefined' && !document.hasFocus();
|
|
311
|
-
let graceTimer = null;
|
|
312
|
-
const onBlur = () => {
|
|
313
|
-
sawBlur = true;
|
|
314
|
-
};
|
|
315
|
-
const onFocus = () => {
|
|
316
|
-
if (!sawBlur)
|
|
317
|
-
return;
|
|
318
|
-
if (graceTimer)
|
|
319
|
-
clearTimeout(graceTimer);
|
|
320
|
-
graceTimer = setTimeout(() => {
|
|
321
|
-
if (isConnectedRef.current)
|
|
322
|
-
return;
|
|
323
|
-
// An adapter QR modal is up — leave cancellation to
|
|
324
|
-
// subscribeModalState; the window blur was a scan-on-phone detour,
|
|
325
|
-
// not a dismissal of an extension/OAuth popup.
|
|
326
|
-
if (adapterModalOpenRef.current)
|
|
327
|
-
return;
|
|
328
|
-
void (async () => {
|
|
329
|
-
try {
|
|
330
|
-
await wagmiDisconnectAsync();
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
/* already-disconnected is fine */
|
|
334
|
-
}
|
|
335
|
-
clearIntent();
|
|
336
|
-
})();
|
|
337
|
-
}, 1000);
|
|
338
|
-
};
|
|
339
|
-
window.addEventListener('blur', onBlur);
|
|
340
|
-
window.addEventListener('focus', onFocus);
|
|
341
|
-
return () => {
|
|
342
|
-
window.removeEventListener('blur', onBlur);
|
|
343
|
-
window.removeEventListener('focus', onFocus);
|
|
344
|
-
if (graceTimer)
|
|
345
|
-
clearTimeout(graceTimer);
|
|
346
|
-
};
|
|
347
|
-
}, [connectingWalletId, isConnected, wagmiDisconnectAsync, clearIntent]);
|
|
348
|
-
const runConnect = useCallback(async (walletId) => {
|
|
349
|
-
console.debug('[crossx-kit] runConnect enter', {
|
|
350
|
-
walletId,
|
|
351
|
-
activeProvider,
|
|
352
|
-
isConnected,
|
|
353
|
-
activeConnectorId: activeConnector?.id,
|
|
354
|
-
});
|
|
355
|
-
const entry = connectorRegistry.getEntry(walletId);
|
|
356
|
-
if (!entry) {
|
|
357
|
-
// Reown 어댑터가 빠진 상태에서 reown-side 지갑을 호출한 경우 — 일반
|
|
358
|
-
// "Unknown walletId" 보다 원인을 명시해 DApp 개발자가 reownProjectId
|
|
359
|
-
// 누락을 즉시 알아챌 수 있도록 한다. createConnectKitConfig 는
|
|
360
|
-
// reownProjectId 가 없으면 reownAdapter 를 조용히 omit 하므로 호출
|
|
361
|
-
// 시점까지 silent failure 가 되기 쉽다.
|
|
362
|
-
if ((walletId === 'metamask' || walletId === 'binance') &&
|
|
363
|
-
!reownProvider) {
|
|
364
|
-
console.error(`[crossx-kit] "${walletId}" requires the Reown adapter, but it is ` +
|
|
365
|
-
`not configured. Pass \`reownProjectId\` (and ensure \`reown\` is ` +
|
|
366
|
-
`not set to \`false\`) when calling createConnectKitConfig() or ` +
|
|
367
|
-
`createCrossxConfig() to enable WalletConnect-based wallets.`);
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
console.error(`[crossx-kit] Unknown walletId: "${walletId}"`);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
// Light the unified connecting marker now — BEFORE any provider swap
|
|
374
|
-
// or `beforeConnect` branch — so the spinner appears the instant the
|
|
375
|
-
// user commits to a wallet, uniformly across the wagmi connectAsync
|
|
376
|
-
// path and the reown direct-connect path (which never touches wagmi's
|
|
377
|
-
// own `isConnecting`). A provider swap remounts this subtree, but the
|
|
378
|
-
// post-swap `pendingConnectWalletId` effect re-enters `runConnect` and
|
|
379
|
-
// re-sets it.
|
|
380
|
-
setConnectingWalletId(walletId);
|
|
381
|
-
if (entry.providerKind !== activeProvider) {
|
|
382
|
-
console.debug('[crossx-kit] provider swap needed', {
|
|
383
|
-
from: activeProvider,
|
|
384
|
-
to: entry.providerKind,
|
|
385
|
-
walletId,
|
|
386
|
-
});
|
|
387
|
-
recordIntent(walletId);
|
|
388
|
-
try {
|
|
389
|
-
await wagmiDisconnectAsync();
|
|
390
|
-
}
|
|
391
|
-
catch {
|
|
392
|
-
/* noop */
|
|
393
|
-
}
|
|
394
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
395
|
-
onRequestProviderSwap(entry.providerKind, walletId);
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
recordIntent(walletId);
|
|
399
|
-
if (activeAdapter?.beforeConnect) {
|
|
400
|
-
try {
|
|
401
|
-
// Forward the active theme so the adapter's library-native
|
|
402
|
-
// modal (e.g. AppKit's CROSSx QR view) can sync with the
|
|
403
|
-
// host's current palette — including post-bootstrap toggles
|
|
404
|
-
// via `useCrossConnectKitTheme().setThemeMode(...)`.
|
|
405
|
-
const result = await activeAdapter.beforeConnect({ kitConfig, themeMode }, walletId);
|
|
406
|
-
console.debug('[crossx-kit] beforeConnect result', { walletId, result });
|
|
407
|
-
if (result === 'handled')
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
catch (err) {
|
|
411
|
-
console.warn('[crossx-kit] adapter.beforeConnect threw for "%s":', walletId, err);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const primary = findConnector(entry.connectorId);
|
|
415
|
-
console.debug('[crossx-kit] findConnector', {
|
|
416
|
-
lookupId: entry.connectorId,
|
|
417
|
-
found: primary?.id,
|
|
418
|
-
availableIds: connectors.map((c) => c.id),
|
|
419
|
-
});
|
|
420
|
-
if (!primary) {
|
|
421
|
-
console.error(`[crossx-kit] Connector "${entry.connectorId}" not found on provider ` +
|
|
422
|
-
`"${entry.providerKind}". Available: ${connectors.map((c) => c.id).join(', ')}`);
|
|
423
|
-
clearIntent();
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
try {
|
|
427
|
-
console.debug('[crossx-kit] connectAsync start', { connectorId: primary.id });
|
|
428
|
-
const result = await connectAsync({ connector: primary });
|
|
429
|
-
console.debug('[crossx-kit] connectAsync resolved', {
|
|
430
|
-
accountsLen: result.accounts.length,
|
|
431
|
-
chainId: result.chainId,
|
|
432
|
-
});
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
catch (err) {
|
|
436
|
-
const handoff = isExternalWalletHandoff(err);
|
|
437
|
-
if (!handoff) {
|
|
438
|
-
console.error('[crossx-kit] connectAsync error for "%s":', walletId, err);
|
|
439
|
-
try {
|
|
440
|
-
await wagmiDisconnectAsync();
|
|
441
|
-
}
|
|
442
|
-
catch {
|
|
443
|
-
/* ignore */
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (!entry.fallbackConnectorId) {
|
|
447
|
-
clearIntent();
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (entry.fallbackConnectorId) {
|
|
452
|
-
const fallback = findConnector(entry.fallbackConnectorId);
|
|
453
|
-
if (fallback) {
|
|
454
|
-
try {
|
|
455
|
-
await connectAsync({ connector: fallback });
|
|
456
|
-
}
|
|
457
|
-
catch (err) {
|
|
458
|
-
console.error('[crossx-kit] Fallback connect failed for "%s":', walletId, err);
|
|
459
|
-
try {
|
|
460
|
-
await wagmiDisconnectAsync();
|
|
461
|
-
}
|
|
462
|
-
catch {
|
|
463
|
-
/* ignore */
|
|
464
|
-
}
|
|
465
|
-
clearIntent();
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
clearIntent();
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}, [
|
|
473
|
-
connectorRegistry,
|
|
474
|
-
connectors,
|
|
475
|
-
findConnector,
|
|
476
|
-
connectAsync,
|
|
477
|
-
wagmiDisconnectAsync,
|
|
478
|
-
recordIntent,
|
|
479
|
-
clearIntent,
|
|
480
|
-
activeAdapter,
|
|
481
|
-
activeProvider,
|
|
482
|
-
kitConfig,
|
|
483
|
-
onRequestProviderSwap,
|
|
484
|
-
themeMode,
|
|
485
|
-
]);
|
|
486
|
-
// After a provider swap, pendingConnectWalletId is set and the
|
|
487
|
-
// WagmiProvider has just remounted with the new config. Fire the
|
|
488
|
-
// connect for that wallet now that the hooks point at the right
|
|
489
|
-
// config.
|
|
490
|
-
useEffect(() => {
|
|
491
|
-
if (!pendingConnectWalletId)
|
|
492
|
-
return;
|
|
493
|
-
const wallet = pendingConnectWalletId;
|
|
494
|
-
clearPending();
|
|
495
|
-
void runConnect(wallet);
|
|
496
|
-
// We intentionally only depend on the pending id and active provider —
|
|
497
|
-
// runConnect has its own deps but refetching here would re-trigger
|
|
498
|
-
// connects on unrelated state changes.
|
|
499
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
500
|
-
}, [pendingConnectWalletId, activeProvider]);
|
|
501
|
-
const connectWallet = useCallback(async () => {
|
|
502
|
-
// Open the kit's unified connect modal. The user picks Google /
|
|
503
|
-
// Apple (delegated to `oauth.signIn`) or a wallet (delegated to
|
|
504
|
-
// `runConnect`). The SDK's own modal is no longer invoked directly —
|
|
505
|
-
// social entries route through the headless `signInWithProvider`.
|
|
506
|
-
setOtherWalletsOpen(true);
|
|
507
|
-
}, []);
|
|
508
|
-
const disconnect = useCallback(async () => {
|
|
509
|
-
try {
|
|
510
|
-
await wagmiDisconnectAsync();
|
|
511
|
-
}
|
|
512
|
-
catch (err) {
|
|
513
|
-
console.error('[crossx-kit] wagmi disconnect error:', err);
|
|
514
|
-
}
|
|
515
|
-
if (activeAdapter?.teardown) {
|
|
516
|
-
try {
|
|
517
|
-
await activeAdapter.teardown({ kitConfig });
|
|
518
|
-
}
|
|
519
|
-
catch (err) {
|
|
520
|
-
console.error('[crossx-kit] adapter teardown error:', err);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
clearWalletSessionStorage();
|
|
524
|
-
clearIntent();
|
|
525
|
-
}, [wagmiDisconnectAsync, activeAdapter, kitConfig, clearIntent]);
|
|
526
|
-
const selectWallet = useCallback(async () => {
|
|
527
|
-
const embeddedEntry = connectorRegistry.getEntry('cross_embedded');
|
|
528
|
-
if (!embeddedEntry)
|
|
529
|
-
return null;
|
|
530
|
-
const crossx = findConnector(embeddedEntry.connectorId);
|
|
531
|
-
if (!crossx)
|
|
532
|
-
return null;
|
|
533
|
-
try {
|
|
534
|
-
const provider = (await crossx.getProvider());
|
|
535
|
-
const sdk = provider?.sdk;
|
|
536
|
-
if (!sdk?.selectWallet)
|
|
537
|
-
return null;
|
|
538
|
-
const result = await sdk.selectWallet(sdk.currentAddress ?? undefined);
|
|
539
|
-
if (result?.address && provider.notifyAccountsChanged) {
|
|
540
|
-
provider.notifyAccountsChanged([result.address]);
|
|
541
|
-
}
|
|
542
|
-
return result;
|
|
543
|
-
}
|
|
544
|
-
catch {
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
}, [connectorRegistry, findConnector]);
|
|
548
|
-
// Wrap the OAuth port so the social (Google/Apple) path feeds the same
|
|
549
|
-
// unified connecting marker as the wallet path. `oauth.signIn` opens the
|
|
550
|
-
// SDK popup and "forgets" (see OAuthPort docs): success surfaces later as
|
|
551
|
-
// wagmi `isConnected` (cleared by the effect above); a rejection here is
|
|
552
|
-
// the user closing the popup, so we drop the marker on catch.
|
|
553
|
-
const oauth = useMemo(() => {
|
|
554
|
-
const base = config.oauth;
|
|
555
|
-
if (!base)
|
|
556
|
-
return null;
|
|
557
|
-
return {
|
|
558
|
-
signIn: async (provider) => {
|
|
559
|
-
setConnectingWalletId('cross_embedded');
|
|
560
|
-
try {
|
|
561
|
-
await base.signIn(provider);
|
|
562
|
-
}
|
|
563
|
-
catch (err) {
|
|
564
|
-
setConnectingWalletId(null);
|
|
565
|
-
throw err;
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
};
|
|
569
|
-
}, [config.oauth]);
|
|
570
|
-
// Unified connecting flag: true from the moment a connect attempt starts
|
|
571
|
-
// until it succeeds. `connectingWalletId` is only ever set by an explicit
|
|
572
|
-
// user action (modal pick → runConnect, social → wrapped signIn) and is
|
|
573
|
-
// cleared on success/cancel/error, so this is free of the background
|
|
574
|
-
// `isConnecting` flapping that wagmi exhibits during session restore.
|
|
575
|
-
const kitIsConnecting = connectingWalletId != null && !isConnected;
|
|
576
|
-
const contextValue = useMemo(() => ({
|
|
577
|
-
kitConfig,
|
|
578
|
-
connectorRegistry,
|
|
579
|
-
availableWallets,
|
|
580
|
-
connect: runConnect,
|
|
581
|
-
connectWallet,
|
|
582
|
-
disconnect,
|
|
583
|
-
selectWallet,
|
|
584
|
-
currentWallet,
|
|
585
|
-
lastIntent,
|
|
586
|
-
isConnecting: kitIsConnecting,
|
|
587
|
-
connectingWalletId,
|
|
588
|
-
openOtherWallets: () => setOtherWalletsOpen(true),
|
|
589
|
-
closeOtherWallets: () => setOtherWalletsOpen(false),
|
|
590
|
-
otherWalletsOpen,
|
|
591
|
-
oauth,
|
|
592
|
-
extraWallets,
|
|
593
|
-
walletAllowlist,
|
|
594
|
-
onRampEnabled,
|
|
595
|
-
}), [
|
|
596
|
-
kitConfig,
|
|
597
|
-
connectorRegistry,
|
|
598
|
-
availableWallets,
|
|
599
|
-
runConnect,
|
|
600
|
-
connectWallet,
|
|
601
|
-
disconnect,
|
|
602
|
-
selectWallet,
|
|
603
|
-
currentWallet,
|
|
604
|
-
lastIntent,
|
|
605
|
-
kitIsConnecting,
|
|
606
|
-
connectingWalletId,
|
|
607
|
-
otherWalletsOpen,
|
|
608
|
-
oauth,
|
|
609
|
-
extraWallets,
|
|
610
|
-
walletAllowlist,
|
|
611
|
-
onRampEnabled,
|
|
612
|
-
]);
|
|
613
|
-
const inner = (_jsxs(CrossConnectKitContext.Provider, { value: contextValue, children: [children, _jsx(CrossConnectModal, { open: otherWalletsOpen, onOpenChange: setOtherWalletsOpen })] }));
|
|
614
|
-
// kitConfig.onRampId 설정 시 OnRampProvider로 wrap. 미설정이면 그대로 통과 —
|
|
615
|
-
// useOnRamp / useOnRampEligibility 훅을 호출하면 OnRampProvider 미마운트 에러로
|
|
616
|
-
// fail-fast (DApp이 enable 의도를 놓치는 걸 빨리 알아차리도록).
|
|
617
|
-
return onRampConfig ? (_jsx(OnRampProvider, { config: onRampConfig, children: inner })) : (inner);
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* Patterns that are benign tear-down noise from the WalletConnect
|
|
621
|
-
* universal provider: a WebSocket closes while a subscribe request is
|
|
622
|
-
* mid-flight (most commonly when WagmiProvider remounts during a
|
|
623
|
-
* provider swap). They surface as uncaught promise rejections because
|
|
624
|
-
* WC's internal EventEmitter throws into the void — silence them so
|
|
625
|
-
* consumers' error tooling stays clean. Actual connection failures
|
|
626
|
-
* still surface through wagmi's connectAsync error path.
|
|
627
|
-
*/
|
|
628
|
-
const WC_BENIGN_ERROR_PATTERNS = [
|
|
629
|
-
/Connection interrupted while trying to subscribe/i,
|
|
630
|
-
/No matching key\. subscription:/i,
|
|
631
|
-
// Session proposal TTL expired without a wallet accepting (default
|
|
632
|
-
// 5 min). The modal already surfaces its own "expired" UI — the
|
|
633
|
-
// secondary rejection on the shared emitter is just noise.
|
|
634
|
-
/Proposal expired/i,
|
|
635
|
-
];
|
|
636
|
-
function isBenignWcError(reason) {
|
|
637
|
-
const msg = reason?.message;
|
|
638
|
-
if (typeof msg !== 'string')
|
|
639
|
-
return false;
|
|
640
|
-
return WC_BENIGN_ERROR_PATTERNS.some((p) => p.test(msg));
|
|
641
|
-
}
|
|
642
|
-
const EMPTY_EXTRA_WALLETS = {};
|
|
643
|
-
/**
|
|
644
|
-
* Strip the persisted `connections` from the SSR hydration state before
|
|
645
|
-
* handing it to wagmi.
|
|
646
|
-
*
|
|
647
|
-
* wagmi serializes each persisted connection's connector down to
|
|
648
|
-
* `{ id, name, type, uid }` (no methods — see `@wagmi/core` createConfig
|
|
649
|
-
* `partialize`). On the client, `WagmiProvider` hydrates with
|
|
650
|
-
* `status: 'reconnecting'` and only re-attaches the *real* connector
|
|
651
|
-
* after the async `reconnect()` resolves — for the WalletConnect-based
|
|
652
|
-
* cross side that's after the relay session is restored, which can take
|
|
653
|
-
* seconds.
|
|
654
|
-
*
|
|
655
|
-
* The trap: wagmi v3's `getConnection` reports `isConnected: !!address`
|
|
656
|
-
* while `status === 'reconnecting'`, so a consumer reading
|
|
657
|
-
* `useConnection().isConnected` sees `true` and can fire a write against
|
|
658
|
-
* the still-method-less connector → `connector.getChainId is not a
|
|
659
|
-
* function` (and similar). Clearing `connections` keeps the hydrated
|
|
660
|
-
* `chainId` for first paint but makes `isConnected` stay false until
|
|
661
|
-
* `reconnect()` swaps in a real, usable connector — matching wagmi v2's
|
|
662
|
-
* `useAccount()` semantics (not connected while reconnecting).
|
|
663
|
-
*/
|
|
664
|
-
function sanitizeInitialState(state) {
|
|
665
|
-
if (!state)
|
|
666
|
-
return state;
|
|
667
|
-
return { ...state, connections: new Map(), current: null };
|
|
668
|
-
}
|
|
669
|
-
export function CrossConnectKitProvider({ children, config, initialState, extraWallets = EMPTY_EXTRA_WALLETS, walletAllowlist, }) {
|
|
670
|
-
const [activeProvider, setActiveProviderState] = useState(() => readActiveProvider(config.activeProviderStorageKey, config.defaultProvider));
|
|
671
|
-
const [pendingConnectWalletId, setPendingConnectWalletId] = useState(null);
|
|
672
|
-
const swapInFlight = useRef(false);
|
|
673
|
-
// ── Theme state ───────────────────────────────────────────────────
|
|
674
|
-
// Resolve the initial mode from `kitConfig.theme` (+ `autoDetectTheme`)
|
|
675
|
-
// and expose runtime overrides via the ThemeContext. `modeOverride` /
|
|
676
|
-
// `tokensOverride === null` means "use the value from the config".
|
|
677
|
-
const { kitConfig } = config;
|
|
678
|
-
const [modeOverride, setModeOverride] = useState(null);
|
|
679
|
-
const [tokensOverride, setTokensOverride] = useState(null);
|
|
680
|
-
// Bumped on `prefers-color-scheme` change so `configMode` re-resolves.
|
|
681
|
-
const [osThemeTick, setOsThemeTick] = useState(0);
|
|
682
|
-
const configMode = useMemo(() => resolveThemeMode(kitConfig.theme, kitConfig.autoDetectTheme),
|
|
683
|
-
// `osThemeTick` is intentionally part of the dep list — it is the
|
|
684
|
-
// signal we're allowed to re-consult `prefers-color-scheme`.
|
|
685
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
686
|
-
[kitConfig.theme, kitConfig.autoDetectTheme, osThemeTick]);
|
|
687
|
-
const activeMode = modeOverride ?? configMode;
|
|
688
|
-
const activeTokens = tokensOverride ?? kitConfig.themeTokens;
|
|
689
|
-
// Follow OS `prefers-color-scheme` when `autoDetectTheme` is on and
|
|
690
|
-
// the DApp hasn't called `setThemeMode` manually.
|
|
691
|
-
useEffect(() => {
|
|
692
|
-
if (!kitConfig.autoDetectTheme)
|
|
693
|
-
return;
|
|
694
|
-
if (modeOverride)
|
|
695
|
-
return;
|
|
696
|
-
if (typeof window === 'undefined' || !window.matchMedia)
|
|
697
|
-
return;
|
|
698
|
-
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
699
|
-
const handler = () => setOsThemeTick((n) => n + 1);
|
|
700
|
-
try {
|
|
701
|
-
mql.addEventListener('change', handler);
|
|
702
|
-
return () => mql.removeEventListener('change', handler);
|
|
703
|
-
}
|
|
704
|
-
catch {
|
|
705
|
-
// Legacy Safari (< 14) fallback.
|
|
706
|
-
mql.addListener(handler);
|
|
707
|
-
return () => mql.removeListener(handler);
|
|
708
|
-
}
|
|
709
|
-
}, [kitConfig.autoDetectTheme, modeOverride]);
|
|
710
|
-
// Tag the documentElement so the design-system rules
|
|
711
|
-
// (`[data-ds-theme="dark"] { --ds-... }`) are active, and mirror onto
|
|
712
|
-
// `data-theme` which dapp-ui component stylesheets already key on.
|
|
713
|
-
useEffect(() => {
|
|
714
|
-
if (typeof document === 'undefined')
|
|
715
|
-
return;
|
|
716
|
-
const root = document.documentElement;
|
|
717
|
-
const prevDs = root.getAttribute('data-ds-theme');
|
|
718
|
-
const prevTheme = root.getAttribute('data-theme');
|
|
719
|
-
// Drive the design-system palette switch (`[data-ds-theme="…"]`).
|
|
720
|
-
root.setAttribute('data-ds-theme', activeMode);
|
|
721
|
-
// Only set `data-theme` if the host page hasn't already claimed it —
|
|
722
|
-
// many apps manage their own global theme toggle via that attribute.
|
|
723
|
-
if (!prevTheme) {
|
|
724
|
-
root.setAttribute('data-theme', activeMode);
|
|
725
|
-
}
|
|
726
|
-
return () => {
|
|
727
|
-
if (prevDs == null)
|
|
728
|
-
root.removeAttribute('data-ds-theme');
|
|
729
|
-
else
|
|
730
|
-
root.setAttribute('data-ds-theme', prevDs);
|
|
731
|
-
if (prevTheme == null)
|
|
732
|
-
root.removeAttribute('data-theme');
|
|
733
|
-
};
|
|
734
|
-
}, [activeMode]);
|
|
735
|
-
// Design-system stylesheet: `--ds-*` variables (both modes) + `.ds-*`
|
|
736
|
-
// typography classes. Mode-independent — palette flips via the
|
|
737
|
-
// `data-ds-theme` attribute set above — so it's built once per config.
|
|
738
|
-
const designSystemCss = useMemo(() => buildDesignSystemCss(config.designSystem), [config.designSystem]);
|
|
739
|
-
const themeContextValue = useMemo(() => ({
|
|
740
|
-
mode: activeMode,
|
|
741
|
-
tokens: activeTokens,
|
|
742
|
-
setThemeMode: (mode) => setModeOverride(mode),
|
|
743
|
-
setThemeTokens: (tokens) => setTokensOverride(tokens),
|
|
744
|
-
}), [activeMode, activeTokens]);
|
|
745
|
-
useEffect(() => {
|
|
746
|
-
if (typeof window === 'undefined')
|
|
747
|
-
return;
|
|
748
|
-
const handler = (event) => {
|
|
749
|
-
if (isBenignWcError(event.reason)) {
|
|
750
|
-
event.preventDefault();
|
|
751
|
-
}
|
|
752
|
-
};
|
|
753
|
-
window.addEventListener('unhandledrejection', handler);
|
|
754
|
-
return () => window.removeEventListener('unhandledrejection', handler);
|
|
755
|
-
}, []);
|
|
756
|
-
const requestProviderSwap = useCallback((kind, walletId) => {
|
|
757
|
-
if (swapInFlight.current)
|
|
758
|
-
return;
|
|
759
|
-
swapInFlight.current = true;
|
|
760
|
-
setPendingConnectWalletId(walletId);
|
|
761
|
-
setActiveProviderState(kind);
|
|
762
|
-
writeActiveProvider(config.activeProviderStorageKey, kind);
|
|
763
|
-
// Clear the lock after this microtask so subsequent user actions
|
|
764
|
-
// (post-remount) can swap again if needed.
|
|
765
|
-
queueMicrotask(() => {
|
|
766
|
-
swapInFlight.current = false;
|
|
767
|
-
});
|
|
768
|
-
}, [config.activeProviderStorageKey]);
|
|
769
|
-
const clearPending = useCallback(() => setPendingConnectWalletId(null), []);
|
|
770
|
-
// Pick the wagmi config + matching hydration slice for the active side.
|
|
771
|
-
const wagmiConfig = activeProvider === 'cross' ? config.crossWagmiConfig : config.reownWagmiConfig;
|
|
772
|
-
const initial = sanitizeInitialState(pickInitialState(initialState, activeProvider));
|
|
773
|
-
if (!wagmiConfig) {
|
|
774
|
-
// The DApp requested an active provider that isn't configured —
|
|
775
|
-
// fall through to the other side rather than crash.
|
|
776
|
-
const fallback = activeProvider === 'cross' ? config.reownWagmiConfig : config.crossWagmiConfig;
|
|
777
|
-
if (!fallback) {
|
|
778
|
-
throw new Error('[crossx-kit] createCrossxConfig returned no wagmi Config for either side');
|
|
779
|
-
}
|
|
780
|
-
const otherKind = activeProvider === 'cross' ? 'reown' : 'cross';
|
|
781
|
-
// Schedule a state fix for next render.
|
|
782
|
-
queueMicrotask(() => setActiveProviderState(otherKind));
|
|
783
|
-
}
|
|
784
|
-
const effectiveConfig = wagmiConfig ??
|
|
785
|
-
(activeProvider === 'cross'
|
|
786
|
-
? config.reownWagmiConfig
|
|
787
|
-
: config.crossWagmiConfig);
|
|
788
|
-
return (_jsxs(CrossConnectKitThemeContext.Provider, { value: themeContextValue, children: [_jsx("style", { "data-crossx-design-system": "", children: designSystemCss }), _jsx(WagmiProvider, { config: effectiveConfig, initialState: initial, children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx(CrossConnectKitInner, { config: config, activeProvider: activeProvider, onRequestProviderSwap: requestProviderSwap, pendingConnectWalletId: pendingConnectWalletId, clearPending: clearPending, themeMode: activeMode, themeTokens: activeTokens, extraWallets: extraWallets, walletAllowlist: walletAllowlist ?? null, children: children }) }) }, activeProvider)] }));
|
|
789
|
-
}
|
|
790
|
-
//# sourceMappingURL=CrossConnectKitProvider.js.map
|