signer-test-sdk-react 0.0.11 → 0.0.13
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 +1 -1
- package/dist/src/AbstraxnProvider.d.ts +2 -5
- package/dist/src/AbstraxnProvider.js +550 -50
- package/dist/src/AbstraxnProvider.js.map +1 -1
- package/dist/src/WalletModal.css +1973 -23
- package/dist/src/WalletModal.d.ts +2 -1
- package/dist/src/WalletModal.js +200 -30
- package/dist/src/WalletModal.js.map +1 -1
- package/dist/src/chains.d.ts +55 -0
- package/dist/src/chains.js +221 -0
- package/dist/src/chains.js.map +1 -0
- package/dist/src/components/OnboardingUI/OnboardingUIReact.js +24 -3
- package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +11 -2
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +266 -36
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/PasskeyButton.js +1 -1
- package/dist/src/components/OnboardingUI/components/PasskeyButton.js.map +1 -1
- package/dist/src/components/QRCode.d.ts +13 -0
- package/dist/src/components/QRCode.js +6 -0
- package/dist/src/components/QRCode.js.map +1 -0
- package/dist/src/components/WalletModal/components/ChainSelector.css +180 -0
- package/dist/src/components/WalletModal/components/ChainSelector.d.ts +10 -0
- package/dist/src/components/WalletModal/components/ChainSelector.js +34 -0
- package/dist/src/components/WalletModal/components/ChainSelector.js.map +1 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.css +133 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +9 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.js +31 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.css +2 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +11 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.js +18 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.css +160 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +12 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.js +21 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +128 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +17 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +10 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.css +101 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +8 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.js +22 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/SendModal.css +234 -0
- package/dist/src/components/WalletModal/components/SendModal.d.ts +18 -0
- package/dist/src/components/WalletModal/components/SendModal.js +127 -0
- package/dist/src/components/WalletModal/components/SendModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/SuccessModal.css +86 -0
- package/dist/src/components/WalletModal/components/SuccessModal.d.ts +13 -0
- package/dist/src/components/WalletModal/components/SuccessModal.js +8 -0
- package/dist/src/components/WalletModal/components/SuccessModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/UserAvatar.d.ts +9 -0
- package/dist/src/components/WalletModal/components/UserAvatar.js +31 -0
- package/dist/src/components/WalletModal/components/UserAvatar.js.map +1 -0
- package/dist/src/components/WalletModal/components/index.d.ts +21 -0
- package/dist/src/components/WalletModal/components/index.js +13 -0
- package/dist/src/components/WalletModal/components/index.js.map +1 -0
- package/dist/src/components/WalletModal/hooks/index.d.ts +6 -0
- package/dist/src/components/WalletModal/hooks/index.js +7 -0
- package/dist/src/components/WalletModal/hooks/index.js.map +1 -0
- package/dist/src/components/WalletModal/hooks/useAddressValidation.d.ts +4 -0
- package/dist/src/components/WalletModal/hooks/useAddressValidation.js +17 -0
- package/dist/src/components/WalletModal/hooks/useAddressValidation.js.map +1 -0
- package/dist/src/components/WalletModal/hooks/useAmountValidation.d.ts +4 -0
- package/dist/src/components/WalletModal/hooks/useAmountValidation.js +29 -0
- package/dist/src/components/WalletModal/hooks/useAmountValidation.js.map +1 -0
- package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +20 -0
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js +55 -0
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js.map +1 -0
- package/dist/src/components/WalletModal/index.d.ts +5 -0
- package/dist/src/components/WalletModal/index.js +7 -0
- package/dist/src/components/WalletModal/index.js.map +1 -0
- package/dist/src/components/WalletModal/utils/addressUtils.d.ts +19 -0
- package/dist/src/components/WalletModal/utils/addressUtils.js +62 -0
- package/dist/src/components/WalletModal/utils/addressUtils.js.map +1 -0
- package/dist/src/components/WalletModal/utils/formatUtils.d.ts +20 -0
- package/dist/src/components/WalletModal/utils/formatUtils.js +47 -0
- package/dist/src/components/WalletModal/utils/formatUtils.js.map +1 -0
- package/dist/src/components/WalletModal/utils/index.d.ts +5 -0
- package/dist/src/components/WalletModal/utils/index.js +6 -0
- package/dist/src/components/WalletModal/utils/index.js.map +1 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +29 -0
- package/dist/src/wagmiConfig.js +6 -2
- package/dist/src/wagmiConfig.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Abstraxn Wallet Provider - React Context Provider
|
|
4
4
|
* Wrap your app with this provider to use Abstraxn Wallet SDK
|
|
5
5
|
*/
|
|
6
|
-
import { createContext, useContext, useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
|
6
|
+
import React, { createContext, useContext, useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
|
7
7
|
import { AbstraxnWallet, AuthenticationError } from 'signer-test-sdk-core';
|
|
8
8
|
import { OnboardingUIWeb } from './components/OnboardingUI';
|
|
9
9
|
import { WagmiProvider, useAccount, useConnect, useDisconnect, useSignMessage, useSendTransaction, useSwitchChain, useBalance, useChainId } from 'wagmi';
|
|
10
10
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
11
11
|
import { createWagmiConfig } from './wagmiConfig';
|
|
12
|
-
import { parseEther } from 'viem';
|
|
12
|
+
import { parseEther, createPublicClient, http } from 'viem';
|
|
13
13
|
import { ExternalWalletButtons } from './ExternalWalletButtons';
|
|
14
|
+
import { EVM_CHAINS, SOLANA_CHAINS, getChainById, toCoreChain } from './chains';
|
|
14
15
|
export const AbstraxnContext = createContext(null);
|
|
15
|
-
//
|
|
16
|
-
const defaultQueryClient = new QueryClient({
|
|
17
|
-
defaultOptions: {
|
|
18
|
-
queries: {
|
|
19
|
-
refetchOnWindowFocus: false,
|
|
20
|
-
retry: false,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|
|
16
|
+
// QueryClient will be created inside the component to ensure React context is available
|
|
24
17
|
// Base provider logic (shared between with and without wagmi)
|
|
25
18
|
function useAbstraxnProviderBase(_config) {
|
|
26
19
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
@@ -31,6 +24,7 @@ function useAbstraxnProviderBase(_config) {
|
|
|
31
24
|
const [chainId, setChainId] = useState(null);
|
|
32
25
|
const [error, setError] = useState(null);
|
|
33
26
|
const [loading, setLoading] = useState(false);
|
|
27
|
+
const [walletBalance, setWalletBalance] = useState(null);
|
|
34
28
|
const onboardingRef = useRef(null);
|
|
35
29
|
const otpIdRef = useRef(null);
|
|
36
30
|
const walletRef = useRef(null);
|
|
@@ -46,6 +40,7 @@ function useAbstraxnProviderBase(_config) {
|
|
|
46
40
|
chainId, setChainId,
|
|
47
41
|
error, setError,
|
|
48
42
|
loading, setLoading,
|
|
43
|
+
walletBalance, setWalletBalance,
|
|
49
44
|
onboardingRef, otpIdRef, walletRef, googleCallbackHandledRef, twitterCallbackHandledRef, discordCallbackHandledRef,
|
|
50
45
|
};
|
|
51
46
|
}
|
|
@@ -83,7 +78,7 @@ function AbstraxnProviderWithoutWagmi({ config, children }) {
|
|
|
83
78
|
return _jsx(AbstraxnProviderInner, { config: config, children: children, base: base, wagmi: null });
|
|
84
79
|
}
|
|
85
80
|
function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
86
|
-
const { isInitialized, setIsInitialized, isConnected, setIsConnected, address, setAddress, user, setUser, whoami, setWhoami, chainId, setChainId, error, setError, loading, setLoading, onboardingRef, otpIdRef, walletRef, googleCallbackHandledRef, twitterCallbackHandledRef, discordCallbackHandledRef, } = base;
|
|
81
|
+
const { isInitialized, setIsInitialized, isConnected, setIsConnected, address, setAddress, user, setUser, whoami, setWhoami, chainId, setChainId, error, setError, loading, setLoading, walletBalance, setWalletBalance, onboardingRef, otpIdRef, walletRef, googleCallbackHandledRef, twitterCallbackHandledRef, discordCallbackHandledRef, } = base;
|
|
87
82
|
const externalWalletsEnabled = config.externalWallets?.enabled ?? false;
|
|
88
83
|
// Keep a ref to avoid re-creating callbacks when toggling config (prevents flicker)
|
|
89
84
|
const externalWalletsEnabledRef = useRef(externalWalletsEnabled);
|
|
@@ -144,18 +139,105 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
144
139
|
}
|
|
145
140
|
});
|
|
146
141
|
}, [externalWalletsEnabled]);
|
|
142
|
+
// Restore external wallet connection on mount if wagmi has already restored it
|
|
143
|
+
// This runs after wagmi has had time to restore from localStorage
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!externalWalletsEnabled || !wagmiAccount) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Check if wagmi has already restored a connection from localStorage
|
|
149
|
+
// This happens automatically with wagmi's persistence when storage is configured
|
|
150
|
+
const checkAndRestore = () => {
|
|
151
|
+
if (wagmiAccount.isConnected && wagmiAccount.address && !isExternalWalletConnected) {
|
|
152
|
+
const walletAddress = wagmiAccount.address.toLowerCase();
|
|
153
|
+
const currentChainId = wagmiChainIdHook || wagmiAccount.chainId || null;
|
|
154
|
+
// Set explicitConnectionRef FIRST to prevent auto-disconnect from interfering
|
|
155
|
+
explicitConnectionRef.current = true;
|
|
156
|
+
autoDisconnectHandledRef.current = false;
|
|
157
|
+
// Restore external wallet state
|
|
158
|
+
setIsExternalWalletConnected(true);
|
|
159
|
+
setExternalWalletAddress(walletAddress);
|
|
160
|
+
setAddress(walletAddress);
|
|
161
|
+
setIsConnected(true);
|
|
162
|
+
setExternalWalletChainId(currentChainId);
|
|
163
|
+
setChainId(currentChainId);
|
|
164
|
+
// Update refs to track the restored connection
|
|
165
|
+
lastAddressRef.current = walletAddress;
|
|
166
|
+
lastChainIdRef.current = currentChainId;
|
|
167
|
+
lastConnectionTimeRef.current = Date.now();
|
|
168
|
+
return true; // Indicate restoration happened
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
// Check immediately
|
|
173
|
+
if (checkAndRestore()) {
|
|
174
|
+
return; // Already restored
|
|
175
|
+
}
|
|
176
|
+
// Also check after delays to catch wagmi's async restoration
|
|
177
|
+
// Wagmi might restore the connection asynchronously after the component mounts
|
|
178
|
+
let timeout2 = null;
|
|
179
|
+
const timeout1 = setTimeout(() => {
|
|
180
|
+
if (!checkAndRestore()) {
|
|
181
|
+
// Check one more time after a longer delay
|
|
182
|
+
timeout2 = setTimeout(() => {
|
|
183
|
+
checkAndRestore();
|
|
184
|
+
}, 300);
|
|
185
|
+
}
|
|
186
|
+
}, 150);
|
|
187
|
+
return () => {
|
|
188
|
+
clearTimeout(timeout1);
|
|
189
|
+
if (timeout2) {
|
|
190
|
+
clearTimeout(timeout2);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}, [externalWalletsEnabled, wagmiAccount?.isConnected, wagmiAccount?.address, wagmiChainIdHook, isExternalWalletConnected]); // Re-run if wagmi state changes
|
|
147
194
|
// Initialize wallet
|
|
148
195
|
useEffect(() => {
|
|
149
196
|
// If wallet already exists (from previous mount), reuse it to prevent flicker
|
|
150
197
|
if (walletRef.current) {
|
|
151
198
|
return;
|
|
152
199
|
}
|
|
200
|
+
// Compute supported chains from chain config (outside useMemo since we're in useEffect)
|
|
201
|
+
// This should include ALL chains that might be shown in the UI (availableChains)
|
|
202
|
+
// We use the same logic as availableChains to ensure consistency
|
|
203
|
+
let computedSupportedChains = undefined;
|
|
204
|
+
if (config.supportedChains) {
|
|
205
|
+
// Use explicitly provided chains
|
|
206
|
+
computedSupportedChains = config.supportedChains;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Compute from chains config - use same logic as availableChains
|
|
210
|
+
const chainConfig = config.chains;
|
|
211
|
+
const chains = [];
|
|
212
|
+
// Priority 1: Check new chains config format
|
|
213
|
+
if (chainConfig?.supportedEvmChains && chainConfig.supportedEvmChains.length > 0) {
|
|
214
|
+
chainConfig.supportedEvmChains.forEach(chainName => {
|
|
215
|
+
const chainData = EVM_CHAINS[chainName];
|
|
216
|
+
if (chainData && chainData.type === 'evm') {
|
|
217
|
+
chains.push(toCoreChain(chainData));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// Priority 2: If no specific config, include default chains that might be shown
|
|
222
|
+
// This ensures Base and other common chains are available even if not explicitly configured
|
|
223
|
+
if (chains.length === 0) {
|
|
224
|
+
// Include common chains that are likely to be in availableChains
|
|
225
|
+
chains.push(toCoreChain(EVM_CHAINS.ethereum));
|
|
226
|
+
chains.push(toCoreChain(EVM_CHAINS.polygon));
|
|
227
|
+
chains.push(toCoreChain(EVM_CHAINS.base));
|
|
228
|
+
}
|
|
229
|
+
computedSupportedChains = chains.length > 0 ? chains : [
|
|
230
|
+
toCoreChain(EVM_CHAINS.ethereum),
|
|
231
|
+
toCoreChain(EVM_CHAINS.polygon),
|
|
232
|
+
toCoreChain(EVM_CHAINS.base),
|
|
233
|
+
];
|
|
234
|
+
}
|
|
153
235
|
const walletInstance = new AbstraxnWallet({
|
|
154
236
|
apiKey: config.apiKey,
|
|
155
237
|
authMethods: config.authMethods,
|
|
156
238
|
googleClientId: config.googleClientId,
|
|
157
|
-
defaultChainId: config.defaultChainId,
|
|
158
|
-
supportedChains:
|
|
239
|
+
defaultChainId: config.chains?.defaultChainId || config.defaultChainId,
|
|
240
|
+
supportedChains: computedSupportedChains,
|
|
159
241
|
autoConnect: config.autoConnect ?? false,
|
|
160
242
|
enableLogging: config.enableLogging ?? false,
|
|
161
243
|
});
|
|
@@ -187,6 +269,24 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
187
269
|
catch (err) {
|
|
188
270
|
console.error('Error getting wallet info on connect:', err);
|
|
189
271
|
}
|
|
272
|
+
// Ensure loading modal is closed when connected (whoami received)
|
|
273
|
+
if (onboardingRef.current) {
|
|
274
|
+
const onboardingAny = onboardingRef.current;
|
|
275
|
+
if (onboardingAny.hideLoadingModal) {
|
|
276
|
+
onboardingAny.hideLoadingModal();
|
|
277
|
+
}
|
|
278
|
+
// Also ensure the overlay is hidden if it was opened
|
|
279
|
+
if (onboardingAny.modalOverlay && onboardingAny.modalOverlay.classList.contains('onboarding-modal-open')) {
|
|
280
|
+
onboardingAny.modalOverlay.classList.remove('onboarding-modal-open');
|
|
281
|
+
onboardingAny.modalOverlay.classList.add('onboarding-modal-closing');
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
if (onboardingAny.modalOverlay) {
|
|
284
|
+
onboardingAny.modalOverlay.style.display = 'none';
|
|
285
|
+
}
|
|
286
|
+
}, 200);
|
|
287
|
+
document.body.style.overflow = '';
|
|
288
|
+
}
|
|
289
|
+
}
|
|
190
290
|
});
|
|
191
291
|
walletInstance.on('disconnect', () => {
|
|
192
292
|
setIsConnected(false);
|
|
@@ -250,7 +350,9 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
250
350
|
}
|
|
251
351
|
catch (err) {
|
|
252
352
|
console.error('OTP Verify Error:', err);
|
|
253
|
-
|
|
353
|
+
// Return the actual error message from the API
|
|
354
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to verify OTP';
|
|
355
|
+
return { success: false, error: errorMessage };
|
|
254
356
|
}
|
|
255
357
|
},
|
|
256
358
|
onGoogleLogin: async () => {
|
|
@@ -294,22 +396,6 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
294
396
|
onLoginSuccess: async (data) => {
|
|
295
397
|
// Clear any previous errors on successful login
|
|
296
398
|
setError(null);
|
|
297
|
-
// Hide onboarding UI
|
|
298
|
-
if (onboardingRef.current) {
|
|
299
|
-
const onboardingAny = onboardingRef.current;
|
|
300
|
-
if (onboardingAny.modalOverlay) {
|
|
301
|
-
onboardingAny.modalOverlay.classList.remove('onboarding-modal-open');
|
|
302
|
-
onboardingAny.modalOverlay.classList.add('onboarding-modal-closing');
|
|
303
|
-
setTimeout(() => {
|
|
304
|
-
if (onboardingAny.modalOverlay) {
|
|
305
|
-
onboardingAny.modalOverlay.style.display = 'none';
|
|
306
|
-
}
|
|
307
|
-
}, 200);
|
|
308
|
-
}
|
|
309
|
-
document.body.style.overflow = '';
|
|
310
|
-
}
|
|
311
|
-
// Set connected state
|
|
312
|
-
setIsConnected(true);
|
|
313
399
|
// Set user if provided in data
|
|
314
400
|
if (data.user) {
|
|
315
401
|
setUser(data.user);
|
|
@@ -323,9 +409,45 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
323
409
|
setAddress(addr);
|
|
324
410
|
const cid = await walletInstance.getChainId();
|
|
325
411
|
setChainId(cid);
|
|
412
|
+
// Only hide onboarding UI and set connected if whoami succeeds
|
|
413
|
+
if (onboardingRef.current) {
|
|
414
|
+
const onboardingAny = onboardingRef.current;
|
|
415
|
+
if (onboardingAny.hideLoadingModal) {
|
|
416
|
+
onboardingAny.hideLoadingModal();
|
|
417
|
+
}
|
|
418
|
+
if (onboardingAny.modalOverlay) {
|
|
419
|
+
onboardingAny.modalOverlay.classList.remove('onboarding-modal-open');
|
|
420
|
+
onboardingAny.modalOverlay.classList.add('onboarding-modal-closing');
|
|
421
|
+
setTimeout(() => {
|
|
422
|
+
if (onboardingAny.modalOverlay) {
|
|
423
|
+
onboardingAny.modalOverlay.style.display = 'none';
|
|
424
|
+
}
|
|
425
|
+
}, 200);
|
|
426
|
+
}
|
|
427
|
+
document.body.style.overflow = '';
|
|
428
|
+
}
|
|
429
|
+
// Set connected state only after whoami succeeds
|
|
430
|
+
setIsConnected(true);
|
|
326
431
|
}
|
|
327
432
|
catch (err) {
|
|
328
433
|
console.error('Error loading whoami after login:', err);
|
|
434
|
+
// Hide loading modal on whoami error
|
|
435
|
+
if (onboardingRef.current) {
|
|
436
|
+
const onboardingAny = onboardingRef.current;
|
|
437
|
+
if (onboardingAny.hideLoadingModal) {
|
|
438
|
+
onboardingAny.hideLoadingModal();
|
|
439
|
+
}
|
|
440
|
+
// Show error modal/screen
|
|
441
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to load user information';
|
|
442
|
+
if (onboardingAny.showError) {
|
|
443
|
+
onboardingAny.showError(errorMessage);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Set error state
|
|
447
|
+
const error = err instanceof Error ? err : new Error('Failed to load user information after login');
|
|
448
|
+
setError(error);
|
|
449
|
+
// Don't set connected if whoami fails
|
|
450
|
+
setIsConnected(false);
|
|
329
451
|
}
|
|
330
452
|
}
|
|
331
453
|
},
|
|
@@ -347,6 +469,20 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
347
469
|
params.get('error') ||
|
|
348
470
|
params.get('code') ||
|
|
349
471
|
params.get('accessToken');
|
|
472
|
+
const getAuthProviderFromToken = (token) => {
|
|
473
|
+
try {
|
|
474
|
+
const base64Url = token.split('.')[1];
|
|
475
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
476
|
+
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
|
|
477
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
478
|
+
}).join(''));
|
|
479
|
+
const payload = JSON.parse(jsonPayload);
|
|
480
|
+
return (payload.authProvider || payload.provider || '').toLowerCase();
|
|
481
|
+
}
|
|
482
|
+
catch (e) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
350
486
|
const matchesProvider = (provider, params) => {
|
|
351
487
|
const providerParam = (params.get('provider') || params.get('authProvider') || '').toLowerCase();
|
|
352
488
|
const path = window.location.pathname.toLowerCase();
|
|
@@ -356,6 +492,17 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
356
492
|
}
|
|
357
493
|
return providerParam === provider;
|
|
358
494
|
}
|
|
495
|
+
// Check accessToken for provider
|
|
496
|
+
const accessToken = params.get('accessToken');
|
|
497
|
+
if (accessToken) {
|
|
498
|
+
const tokenProvider = getAuthProviderFromToken(accessToken);
|
|
499
|
+
if (tokenProvider) {
|
|
500
|
+
if (provider === 'twitter') {
|
|
501
|
+
return tokenProvider === 'twitter' || tokenProvider === 'x';
|
|
502
|
+
}
|
|
503
|
+
return tokenProvider === provider;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
359
506
|
if (provider === 'twitter') {
|
|
360
507
|
return path.includes('/twitter') || path.includes('/x');
|
|
361
508
|
}
|
|
@@ -375,12 +522,16 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
375
522
|
onboardingAny.modalOverlay.style.display = 'none';
|
|
376
523
|
document.body.style.overflow = '';
|
|
377
524
|
}
|
|
378
|
-
else if (hasSuccess
|
|
379
|
-
// If success=true is in URL,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
525
|
+
else if (hasSuccess) {
|
|
526
|
+
// If success=true is in URL, show loading modal
|
|
527
|
+
// Use setTimeout to ensure onboarding is fully initialized
|
|
528
|
+
setTimeout(() => {
|
|
529
|
+
const onboardingInstance = onboardingRef.current;
|
|
530
|
+
if (onboardingInstance && onboardingInstance.showLoadingModal) {
|
|
531
|
+
// Directly show loading modal - it creates its own overlay with header and footer
|
|
532
|
+
onboardingInstance.showLoadingModal();
|
|
533
|
+
}
|
|
534
|
+
}, 150);
|
|
384
535
|
}
|
|
385
536
|
onboardingRef.current = onboarding;
|
|
386
537
|
// Handle Google OAuth callback (only in useEffect, not exposed)
|
|
@@ -420,12 +571,17 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
420
571
|
}
|
|
421
572
|
catch (err) {
|
|
422
573
|
console.error('Google callback error:', err);
|
|
423
|
-
// Hide loading modal on error
|
|
574
|
+
// Hide loading modal on error (including whoami API failures)
|
|
424
575
|
if (onboardingRef.current) {
|
|
425
576
|
const onboardingAny = onboardingRef.current;
|
|
577
|
+
// Always try to hide loading modal first
|
|
426
578
|
if (onboardingAny.hideLoadingModal) {
|
|
427
579
|
onboardingAny.hideLoadingModal();
|
|
428
580
|
}
|
|
581
|
+
// Also hide any main modal overlay
|
|
582
|
+
if (onboardingAny.modalOverlay) {
|
|
583
|
+
onboardingAny.modalOverlay.style.display = 'none';
|
|
584
|
+
}
|
|
429
585
|
// Show error modal/screen
|
|
430
586
|
const errorMessage = err instanceof Error ? err.message : 'Google authentication failed';
|
|
431
587
|
if (onboardingAny.showError) {
|
|
@@ -434,6 +590,8 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
434
590
|
}
|
|
435
591
|
setError(err instanceof Error ? err : new Error('Google callback failed'));
|
|
436
592
|
googleCallbackHandledRef.current = false;
|
|
593
|
+
// Ensure body scroll is restored
|
|
594
|
+
document.body.style.overflow = '';
|
|
437
595
|
}
|
|
438
596
|
};
|
|
439
597
|
const handleDiscordCallback = async () => {
|
|
@@ -472,12 +630,17 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
472
630
|
}
|
|
473
631
|
catch (err) {
|
|
474
632
|
console.error('Discord callback error:', err);
|
|
475
|
-
// Hide loading modal on error
|
|
633
|
+
// Hide loading modal on error (including whoami API failures)
|
|
476
634
|
if (onboardingRef.current) {
|
|
477
635
|
const onboardingAny = onboardingRef.current;
|
|
636
|
+
// Always try to hide loading modal first
|
|
478
637
|
if (onboardingAny.hideLoadingModal) {
|
|
479
638
|
onboardingAny.hideLoadingModal();
|
|
480
639
|
}
|
|
640
|
+
// Also hide any main modal overlay
|
|
641
|
+
if (onboardingAny.modalOverlay) {
|
|
642
|
+
onboardingAny.modalOverlay.style.display = 'none';
|
|
643
|
+
}
|
|
481
644
|
// Show error modal/screen
|
|
482
645
|
const errorMessage = err instanceof Error ? err.message : 'Discord authentication failed';
|
|
483
646
|
if (onboardingAny.showError) {
|
|
@@ -486,6 +649,8 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
486
649
|
}
|
|
487
650
|
setError(err instanceof Error ? err : new Error('Discord callback failed'));
|
|
488
651
|
discordCallbackHandledRef.current = false;
|
|
652
|
+
// Ensure body scroll is restored
|
|
653
|
+
document.body.style.overflow = '';
|
|
489
654
|
}
|
|
490
655
|
};
|
|
491
656
|
const handleTwitterCallback = async () => {
|
|
@@ -524,12 +689,17 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
524
689
|
}
|
|
525
690
|
catch (err) {
|
|
526
691
|
console.error('Twitter callback error:', err);
|
|
527
|
-
// Hide loading modal on error
|
|
692
|
+
// Hide loading modal on error (including whoami API failures)
|
|
528
693
|
if (onboardingRef.current) {
|
|
529
694
|
const onboardingAny = onboardingRef.current;
|
|
695
|
+
// Always try to hide loading modal first
|
|
530
696
|
if (onboardingAny.hideLoadingModal) {
|
|
531
697
|
onboardingAny.hideLoadingModal();
|
|
532
698
|
}
|
|
699
|
+
// Also hide any main modal overlay
|
|
700
|
+
if (onboardingAny.modalOverlay) {
|
|
701
|
+
onboardingAny.modalOverlay.style.display = 'none';
|
|
702
|
+
}
|
|
533
703
|
// Show error modal/screen
|
|
534
704
|
const errorMessage = err instanceof Error ? err.message : 'Twitter authentication failed';
|
|
535
705
|
if (onboardingAny.showError) {
|
|
@@ -538,6 +708,8 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
538
708
|
}
|
|
539
709
|
setError(err instanceof Error ? err : new Error('Twitter callback failed'));
|
|
540
710
|
twitterCallbackHandledRef.current = false;
|
|
711
|
+
// Ensure body scroll is restored
|
|
712
|
+
document.body.style.overflow = '';
|
|
541
713
|
}
|
|
542
714
|
};
|
|
543
715
|
handleGoogleCallback();
|
|
@@ -659,6 +831,10 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
659
831
|
// Modal already exists, just show it
|
|
660
832
|
// Use requestAnimationFrame to prevent blinking
|
|
661
833
|
requestAnimationFrame(() => {
|
|
834
|
+
// Check if we're on OTP screen - if so, ensure social buttons stay hidden
|
|
835
|
+
const isOtpScreen = onboarding.otpVerificationScreen &&
|
|
836
|
+
onboarding.otpVerificationScreen.parentElement &&
|
|
837
|
+
onboarding.otpVerificationScreen.offsetParent !== null;
|
|
662
838
|
onboarding.modalOverlay.classList.remove('onboarding-modal-closing');
|
|
663
839
|
onboarding.modalOverlay.classList.add('onboarding-modal-open');
|
|
664
840
|
onboarding.modalOverlay.style.display = 'flex';
|
|
@@ -666,11 +842,39 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
666
842
|
onboarding.rootElement.style.display = '';
|
|
667
843
|
}
|
|
668
844
|
document.body.style.overflow = 'hidden';
|
|
845
|
+
// If on OTP screen, ensure all login elements (social buttons, etc.) are hidden
|
|
846
|
+
if (isOtpScreen) {
|
|
847
|
+
// On OTP screen - ensure all login elements are hidden
|
|
848
|
+
if (onboarding.hideLoginElements && typeof onboarding.hideLoginElements === 'function') {
|
|
849
|
+
onboarding.hideLoginElements();
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
// Fallback: manually hide elements
|
|
853
|
+
if (onboarding.googleButton)
|
|
854
|
+
onboarding.googleButton.style.display = 'none';
|
|
855
|
+
if (onboarding.twitterButton)
|
|
856
|
+
onboarding.twitterButton.style.display = 'none';
|
|
857
|
+
if (onboarding.discordButton)
|
|
858
|
+
onboarding.discordButton.style.display = 'none';
|
|
859
|
+
if (onboarding.socialGrid)
|
|
860
|
+
onboarding.socialGrid.style.display = 'none';
|
|
861
|
+
if (onboarding.divider)
|
|
862
|
+
onboarding.divider.style.display = 'none';
|
|
863
|
+
if (onboarding.passkeyLoginButton)
|
|
864
|
+
onboarding.passkeyLoginButton.style.display = 'none';
|
|
865
|
+
if (onboarding.passkeySignupLink)
|
|
866
|
+
onboarding.passkeySignupLink.style.display = 'none';
|
|
867
|
+
if (onboarding.passkeyDivider)
|
|
868
|
+
onboarding.passkeyDivider.style.display = 'none';
|
|
869
|
+
if (onboarding.externalWalletContainer)
|
|
870
|
+
onboarding.externalWalletContainer.style.display = 'none';
|
|
871
|
+
if (onboarding.externalWalletDivider)
|
|
872
|
+
onboarding.externalWalletDivider.style.display = 'none';
|
|
873
|
+
}
|
|
874
|
+
}
|
|
669
875
|
// CRITICAL: Always ensure external wallets are mounted when modal reopens
|
|
670
876
|
if (externalWalletsEnabledRef.current && onboarding.externalWalletContainer) {
|
|
671
877
|
// Check if we're on OTP screen - if so, don't show external wallets
|
|
672
|
-
const isOtpScreen = onboarding.otpVerificationScreen &&
|
|
673
|
-
onboarding.otpVerificationScreen.parentElement;
|
|
674
878
|
if (!isOtpScreen) {
|
|
675
879
|
// Only show external wallets if not on OTP screen
|
|
676
880
|
onboarding.externalWalletContainer.style.display = '';
|
|
@@ -1248,8 +1452,13 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
1248
1452
|
if (!externalWalletsEnabled || !wagmiAccount) {
|
|
1249
1453
|
return;
|
|
1250
1454
|
}
|
|
1251
|
-
// Prevent auto-connect
|
|
1252
|
-
|
|
1455
|
+
// Prevent auto-connect (but allow restoration from persistence)
|
|
1456
|
+
// Only auto-disconnect if:
|
|
1457
|
+
// 1. wagmiAccount is connected
|
|
1458
|
+
// 2. We don't have explicitConnectionRef set (meaning user didn't explicitly connect)
|
|
1459
|
+
// 3. We haven't already handled auto-disconnect
|
|
1460
|
+
// 4. We don't have a stored address (meaning this isn't a restoration from persistence)
|
|
1461
|
+
if (wagmiAccount.isConnected && !explicitConnectionRef.current && !autoDisconnectHandledRef.current && lastAddressRef.current === null) {
|
|
1253
1462
|
autoDisconnectHandledRef.current = true;
|
|
1254
1463
|
if (wagmiDisconnect) {
|
|
1255
1464
|
setTimeout(() => {
|
|
@@ -1305,11 +1514,16 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
1305
1514
|
const addressChanged = lastAddressRef.current !== formattedAddress;
|
|
1306
1515
|
const chainIdChanged = lastChainIdRef.current !== currentChainId;
|
|
1307
1516
|
// Also check if we need to reconnect (lastAddressRef is null but wagmiAccount is connected)
|
|
1308
|
-
// This handles the case where user disconnects and then reconnects
|
|
1517
|
+
// This handles the case where user disconnects and then reconnects, OR page reload
|
|
1309
1518
|
const needsReconnect = lastAddressRef.current === null && wagmiAccount.isConnected && formattedAddress;
|
|
1310
1519
|
// Only update if something actually changed OR if we need to reconnect
|
|
1311
1520
|
if (addressChanged || chainIdChanged || needsReconnect) {
|
|
1312
1521
|
isUpdatingRef.current = true;
|
|
1522
|
+
// If this is a restoration (needsReconnect), set explicitConnectionRef to prevent auto-disconnect
|
|
1523
|
+
if (needsReconnect) {
|
|
1524
|
+
explicitConnectionRef.current = true;
|
|
1525
|
+
autoDisconnectHandledRef.current = false;
|
|
1526
|
+
}
|
|
1313
1527
|
// Update address if it changed or if we need to reconnect
|
|
1314
1528
|
if (addressChanged || needsReconnect) {
|
|
1315
1529
|
setIsExternalWalletConnected(true);
|
|
@@ -1653,6 +1867,177 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
1653
1867
|
const currentChainId = isExternalWalletConnected && externalWalletChainId
|
|
1654
1868
|
? externalWalletChainId
|
|
1655
1869
|
: chainId;
|
|
1870
|
+
// Fetch balance for Abstraxn wallet (not external wallet)
|
|
1871
|
+
useEffect(() => {
|
|
1872
|
+
if (isExternalWalletConnected || !address || !currentChainId) {
|
|
1873
|
+
setWalletBalance(null);
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
const fetchBalance = async () => {
|
|
1877
|
+
try {
|
|
1878
|
+
const currentChain = getChainById(currentChainId);
|
|
1879
|
+
if (!currentChain || currentChain.type !== 'evm') {
|
|
1880
|
+
setWalletBalance(null);
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
const publicClient = createPublicClient({
|
|
1884
|
+
chain: {
|
|
1885
|
+
id: currentChain.id,
|
|
1886
|
+
name: currentChain.name,
|
|
1887
|
+
nativeCurrency: currentChain.nativeCurrency,
|
|
1888
|
+
rpcUrls: {
|
|
1889
|
+
default: {
|
|
1890
|
+
http: [currentChain.rpcUrl],
|
|
1891
|
+
},
|
|
1892
|
+
},
|
|
1893
|
+
},
|
|
1894
|
+
transport: http(currentChain.rpcUrl),
|
|
1895
|
+
});
|
|
1896
|
+
const balance = await publicClient.getBalance({
|
|
1897
|
+
address: address,
|
|
1898
|
+
});
|
|
1899
|
+
setWalletBalance(balance);
|
|
1900
|
+
}
|
|
1901
|
+
catch (error) {
|
|
1902
|
+
console.error('Failed to fetch balance:', error);
|
|
1903
|
+
setWalletBalance(null);
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
fetchBalance();
|
|
1907
|
+
// Refetch balance every 10 seconds
|
|
1908
|
+
const interval = setInterval(fetchBalance, 10000);
|
|
1909
|
+
return () => clearInterval(interval);
|
|
1910
|
+
}, [address, currentChainId, isExternalWalletConnected]);
|
|
1911
|
+
// Compute available chains from config - supports both legacy and new format
|
|
1912
|
+
const availableChains = useMemo(() => {
|
|
1913
|
+
const chains = [];
|
|
1914
|
+
const chainConfig = config.chains;
|
|
1915
|
+
// Priority 1: Check new chains config format
|
|
1916
|
+
if (chainConfig?.supportedEvmChains && chainConfig.supportedEvmChains.length > 0) {
|
|
1917
|
+
// Use configured chains from new format
|
|
1918
|
+
chainConfig.supportedEvmChains.forEach(chainName => {
|
|
1919
|
+
const chain = EVM_CHAINS[chainName];
|
|
1920
|
+
if (chain) {
|
|
1921
|
+
chains.push(chain);
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
// Priority 2: Check legacy supportedChains format
|
|
1926
|
+
else if (config.supportedChains && config.supportedChains.length > 0) {
|
|
1927
|
+
// Convert legacy Chain format to ChainData
|
|
1928
|
+
config.supportedChains.forEach(legacyChain => {
|
|
1929
|
+
// Try to find matching chain by ID
|
|
1930
|
+
const chainData = getChainById(legacyChain.id);
|
|
1931
|
+
if (chainData) {
|
|
1932
|
+
chains.push(chainData);
|
|
1933
|
+
}
|
|
1934
|
+
else {
|
|
1935
|
+
// If not found, create ChainData from legacy chain
|
|
1936
|
+
const newChain = {
|
|
1937
|
+
id: legacyChain.id,
|
|
1938
|
+
name: legacyChain.name.toLowerCase().replace(/\s+/g, '-'),
|
|
1939
|
+
displayName: legacyChain.name,
|
|
1940
|
+
rpcUrl: legacyChain.rpcUrl,
|
|
1941
|
+
explorerUrl: `https://etherscan.io`, // Default explorer
|
|
1942
|
+
nativeCurrency: legacyChain.nativeCurrency,
|
|
1943
|
+
type: 'evm', // Assume EVM for legacy chains
|
|
1944
|
+
isTestnet: legacyChain.id !== 1 && legacyChain.id !== 137 && legacyChain.id !== 8453, // Common mainnets
|
|
1945
|
+
};
|
|
1946
|
+
chains.push(newChain);
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
// Priority 3: Default chains only if nothing configured at all
|
|
1951
|
+
// Only show defaults if both config.chains and config.supportedChains are missing
|
|
1952
|
+
else if (!chainConfig && !config.supportedChains) {
|
|
1953
|
+
chains.push(EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base);
|
|
1954
|
+
}
|
|
1955
|
+
// Add Solana chains if enabled (from new config format)
|
|
1956
|
+
if (chainConfig?.enableSolana) {
|
|
1957
|
+
chains.push(SOLANA_CHAINS.solana);
|
|
1958
|
+
}
|
|
1959
|
+
// Remove duplicates by chain ID
|
|
1960
|
+
const uniqueChains = chains.filter((chain, index, self) => index === self.findIndex(c => c.id === chain.id));
|
|
1961
|
+
// Only return defaults if absolutely nothing was configured
|
|
1962
|
+
return uniqueChains.length > 0
|
|
1963
|
+
? uniqueChains
|
|
1964
|
+
: (!chainConfig && !config.supportedChains
|
|
1965
|
+
? [EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base]
|
|
1966
|
+
: []);
|
|
1967
|
+
}, [config.chains, config.supportedChains]);
|
|
1968
|
+
// Get current chain data
|
|
1969
|
+
const currentChain = useMemo(() => {
|
|
1970
|
+
if (!currentChainId)
|
|
1971
|
+
return null;
|
|
1972
|
+
return getChainById(currentChainId) || null;
|
|
1973
|
+
}, [currentChainId]);
|
|
1974
|
+
// Enhanced switchChain that works for both EVM and Solana
|
|
1975
|
+
const switchChainEnhanced = useCallback(async (targetChainId) => {
|
|
1976
|
+
if (!walletRef.current) {
|
|
1977
|
+
throw new Error('Wallet not initialized');
|
|
1978
|
+
}
|
|
1979
|
+
// Check if chain is in availableChains (user-configured chains)
|
|
1980
|
+
const targetChainInAvailable = availableChains?.find(c => c.id === targetChainId);
|
|
1981
|
+
if (!targetChainInAvailable) {
|
|
1982
|
+
throw new Error(`Chain with ID ${targetChainId} is not available. Please configure it in your SDK setup.`);
|
|
1983
|
+
}
|
|
1984
|
+
const targetChain = getChainById(targetChainId);
|
|
1985
|
+
if (!targetChain) {
|
|
1986
|
+
throw new Error(`Chain with ID ${targetChainId} is not supported`);
|
|
1987
|
+
}
|
|
1988
|
+
setLoading(true);
|
|
1989
|
+
setError(null);
|
|
1990
|
+
try {
|
|
1991
|
+
if (targetChain.type === 'evm') {
|
|
1992
|
+
// For EVM chains, use the existing switchChain
|
|
1993
|
+
if (isExternalWalletConnected && wagmiSwitchChain) {
|
|
1994
|
+
// External wallet - use wagmi switchChain
|
|
1995
|
+
await wagmiSwitchChain.switchChainAsync({ chainId: targetChainId });
|
|
1996
|
+
setExternalWalletChainId(targetChainId);
|
|
1997
|
+
}
|
|
1998
|
+
else {
|
|
1999
|
+
// Abstraxn wallet - try to switch, but if chain is not in wallet's supportedChains,
|
|
2000
|
+
// we'll update the state directly (for chains that are in availableChains but not in wallet config)
|
|
2001
|
+
try {
|
|
2002
|
+
await switchChain(targetChainId);
|
|
2003
|
+
}
|
|
2004
|
+
catch (switchErr) {
|
|
2005
|
+
// If the wallet doesn't support this chain, but it's in availableChains,
|
|
2006
|
+
// we can still update the state to allow UI to work
|
|
2007
|
+
if (switchErr?.message?.includes('not supported') && targetChainInAvailable) {
|
|
2008
|
+
// Update chainId state directly for chains in availableChains
|
|
2009
|
+
setChainId(targetChainId);
|
|
2010
|
+
// Emit chainChanged event if wallet supports it
|
|
2011
|
+
if (walletRef.current && typeof walletRef.current.emit === 'function') {
|
|
2012
|
+
walletRef.current.emit('chainChanged', targetChainId);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
else {
|
|
2016
|
+
throw switchErr;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
else if (targetChain.type === 'solana') {
|
|
2022
|
+
// For Solana, we might need different handling
|
|
2023
|
+
// For now, just update the chainId state
|
|
2024
|
+
// TODO: Implement actual Solana chain switching when wallet supports it
|
|
2025
|
+
setChainId(targetChainId);
|
|
2026
|
+
}
|
|
2027
|
+
// Update chainId state
|
|
2028
|
+
if (!isExternalWalletConnected) {
|
|
2029
|
+
setChainId(targetChainId);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
catch (err) {
|
|
2033
|
+
const error = err instanceof Error ? err : new Error(`Failed to switch to chain ${targetChainId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2034
|
+
setError(error);
|
|
2035
|
+
throw error;
|
|
2036
|
+
}
|
|
2037
|
+
finally {
|
|
2038
|
+
setLoading(false);
|
|
2039
|
+
}
|
|
2040
|
+
}, [isExternalWalletConnected, wagmiSwitchChain, switchChain, availableChains]);
|
|
1656
2041
|
const value = {
|
|
1657
2042
|
wallet: walletRef.current,
|
|
1658
2043
|
isInitialized,
|
|
@@ -1692,6 +2077,12 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
1692
2077
|
externalWalletNetwork: externalWalletsEnabled && wagmiChainIdHook ? getNetworkName(wagmiChainIdHook) : undefined,
|
|
1693
2078
|
availableConnectors: externalWalletsEnabled ? availableConnectors : undefined,
|
|
1694
2079
|
switchExternalWalletChain: externalWalletsEnabled ? switchExternalWalletChain : undefined,
|
|
2080
|
+
// Chain management
|
|
2081
|
+
switchChainEnhanced,
|
|
2082
|
+
currentChain,
|
|
2083
|
+
availableChains,
|
|
2084
|
+
// Balance (for Abstraxn wallet)
|
|
2085
|
+
walletBalance: !isExternalWalletConnected ? walletBalance : undefined,
|
|
1695
2086
|
};
|
|
1696
2087
|
// Ref to store latest value to avoid dependency issues (defined after value)
|
|
1697
2088
|
const valueRef = useRef(value);
|
|
@@ -2160,19 +2551,118 @@ function AbstraxnProviderInner({ config, children, base, wagmi }) {
|
|
|
2160
2551
|
}, [value, externalWalletsEnabled]);
|
|
2161
2552
|
return (_jsx(AbstraxnContext.Provider, { value: value, children: children }));
|
|
2162
2553
|
}
|
|
2554
|
+
/**
|
|
2555
|
+
* Check if React Query is available and can be used safely
|
|
2556
|
+
* This is a simple check - we can't fully verify React is properly set up
|
|
2557
|
+
* without trying to use it, but we can check if the dependencies exist
|
|
2558
|
+
*/
|
|
2559
|
+
function canUseReactQuery() {
|
|
2560
|
+
try {
|
|
2561
|
+
// Check if React is available
|
|
2562
|
+
if (typeof React === 'undefined' || React === null) {
|
|
2563
|
+
return { canUse: false, reason: 'React is not available' };
|
|
2564
|
+
}
|
|
2565
|
+
// Check if React has the version property (indicates it's a valid React object)
|
|
2566
|
+
if (!React.version) {
|
|
2567
|
+
return { canUse: false, reason: 'React version is not available' };
|
|
2568
|
+
}
|
|
2569
|
+
// Check if QueryClientProvider is available
|
|
2570
|
+
if (typeof QueryClientProvider === 'undefined' || QueryClientProvider === null) {
|
|
2571
|
+
return {
|
|
2572
|
+
canUse: false,
|
|
2573
|
+
reason: '@tanstack/react-query is not installed. Please install it: npm install @tanstack/react-query@^5.90.16'
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
return { canUse: true };
|
|
2577
|
+
}
|
|
2578
|
+
catch (error) {
|
|
2579
|
+
return { canUse: false, reason: `Error checking React Query: ${error}` };
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
/**
|
|
2583
|
+
* Wrapper component that conditionally uses QueryClientProvider
|
|
2584
|
+
* Falls back to rendering children directly if React Query is not available
|
|
2585
|
+
*/
|
|
2586
|
+
function QueryClientWrapper({ children, queryClient }) {
|
|
2587
|
+
// Check if we can safely use QueryClientProvider
|
|
2588
|
+
const queryCheck = canUseReactQuery();
|
|
2589
|
+
if (!queryCheck.canUse) {
|
|
2590
|
+
console.error('❌ React Query is not available. External wallets are disabled.\n' +
|
|
2591
|
+
`Reason: ${queryCheck.reason || 'Unknown'}\n` +
|
|
2592
|
+
'Please install @tanstack/react-query@^5.90.16 and ensure there is only one React instance.');
|
|
2593
|
+
// Return children without QueryClientProvider - external wallets won't work but app won't crash
|
|
2594
|
+
return _jsx(_Fragment, { children: children });
|
|
2595
|
+
}
|
|
2596
|
+
try {
|
|
2597
|
+
return _jsx(QueryClientProvider, { client: queryClient, children: children });
|
|
2598
|
+
}
|
|
2599
|
+
catch (error) {
|
|
2600
|
+
console.error('Failed to render QueryClientProvider:', error);
|
|
2601
|
+
// Fallback: render children without QueryClientProvider
|
|
2602
|
+
return _jsx(_Fragment, { children: children });
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2163
2605
|
/**
|
|
2164
2606
|
* Main AbstraxnProvider component with optional WagmiProvider wrapper
|
|
2165
2607
|
*/
|
|
2608
|
+
/**
|
|
2609
|
+
* Hook to create QueryClient
|
|
2610
|
+
* Creates a QueryClient instance that's only created once per component instance
|
|
2611
|
+
* Since @tanstack/react-query is now only in peerDependencies, the app's instance will be used
|
|
2612
|
+
* This prevents multiple instances of the package from being bundled
|
|
2613
|
+
*/
|
|
2614
|
+
function useQueryClientSafe() {
|
|
2615
|
+
// Create QueryClient using useState to ensure it's only created once
|
|
2616
|
+
// Since @tanstack/react-query is in peerDependencies, this will use the app's instance
|
|
2617
|
+
const [queryClient] = useState(() => {
|
|
2618
|
+
return new QueryClient({
|
|
2619
|
+
defaultOptions: {
|
|
2620
|
+
queries: {
|
|
2621
|
+
refetchOnWindowFocus: false,
|
|
2622
|
+
retry: false,
|
|
2623
|
+
},
|
|
2624
|
+
},
|
|
2625
|
+
});
|
|
2626
|
+
});
|
|
2627
|
+
return queryClient;
|
|
2628
|
+
}
|
|
2166
2629
|
export function AbstraxnProvider({ config, children }) {
|
|
2167
2630
|
const externalWalletsEnabled = config.externalWallets?.enabled ?? false;
|
|
2631
|
+
// Get or create QueryClient - tries to use existing one from context first
|
|
2632
|
+
// This ensures we use the app's QueryClient instance if it exists, preventing multiple instances
|
|
2633
|
+
const queryClient = useQueryClientSafe();
|
|
2168
2634
|
// Create wagmi config if external wallets are enabled
|
|
2169
2635
|
// Use useMemo to ensure config is created synchronously during render
|
|
2170
2636
|
// This prevents the "Loading wallet connectors..." state from flashing or blocking initial render
|
|
2637
|
+
// Compute chains for wagmi config (needs to be done before useMemo)
|
|
2638
|
+
const wagmiChains = useMemo(() => {
|
|
2639
|
+
// Use the same logic as availableChains but convert to Core Chain format
|
|
2640
|
+
const chains = [];
|
|
2641
|
+
const chainConfig = config.chains;
|
|
2642
|
+
// Priority 1: Check new chains config format
|
|
2643
|
+
if (chainConfig?.supportedEvmChains && chainConfig.supportedEvmChains.length > 0) {
|
|
2644
|
+
chainConfig.supportedEvmChains.forEach(chainName => {
|
|
2645
|
+
const chainData = EVM_CHAINS[chainName];
|
|
2646
|
+
if (chainData && chainData.type === 'evm') {
|
|
2647
|
+
chains.push(toCoreChain(chainData));
|
|
2648
|
+
}
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
// Priority 2: Check legacy supportedChains format
|
|
2652
|
+
else if (config.supportedChains && config.supportedChains.length > 0) {
|
|
2653
|
+
chains.push(...config.supportedChains);
|
|
2654
|
+
}
|
|
2655
|
+
// Priority 3: Default chains
|
|
2656
|
+
else {
|
|
2657
|
+
chains.push(toCoreChain(EVM_CHAINS.ethereum));
|
|
2658
|
+
}
|
|
2659
|
+
return chains.length > 0 ? chains : [toCoreChain(EVM_CHAINS.ethereum)];
|
|
2660
|
+
}, [config.chains, config.supportedChains]);
|
|
2171
2661
|
const { wagmiConfig, configError } = useMemo(() => {
|
|
2172
2662
|
if (!externalWalletsEnabled)
|
|
2173
2663
|
return { wagmiConfig: null, configError: null };
|
|
2174
2664
|
try {
|
|
2175
|
-
const configResult = createWagmiConfig(
|
|
2665
|
+
const configResult = createWagmiConfig(wagmiChains, config.externalWallets?.walletConnectProjectId, config.externalWallets?.connectors, config.ui?.theme || 'dark');
|
|
2176
2666
|
return { wagmiConfig: configResult, configError: null };
|
|
2177
2667
|
}
|
|
2178
2668
|
catch (error) {
|
|
@@ -2182,9 +2672,19 @@ export function AbstraxnProvider({ config, children }) {
|
|
|
2182
2672
|
configError: error instanceof Error ? error : new Error('Failed to create wagmi config')
|
|
2183
2673
|
};
|
|
2184
2674
|
}
|
|
2185
|
-
}, [externalWalletsEnabled,
|
|
2675
|
+
}, [externalWalletsEnabled, wagmiChains, config.externalWallets?.walletConnectProjectId, config.externalWallets?.connectors]);
|
|
2186
2676
|
// If external wallets are enabled, wrap with QueryClientProvider and WagmiProvider
|
|
2187
2677
|
if (externalWalletsEnabled) {
|
|
2678
|
+
// Check if React Query is available BEFORE doing anything else
|
|
2679
|
+
// This prevents errors from happening in the first place
|
|
2680
|
+
const queryCheck = canUseReactQuery();
|
|
2681
|
+
if (!queryCheck.canUse) {
|
|
2682
|
+
console.error('❌ External wallets are disabled because React Query is not available.\n' +
|
|
2683
|
+
`Reason: ${queryCheck.reason || 'Unknown'}\n` +
|
|
2684
|
+
'Please install @tanstack/react-query@^5.90.16 and ensure there is only one React instance.\n' +
|
|
2685
|
+
'Falling back to Abstraxn wallet only.');
|
|
2686
|
+
return _jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children });
|
|
2687
|
+
}
|
|
2188
2688
|
if (configError) {
|
|
2189
2689
|
console.error('Failed to create wagmi config:', configError);
|
|
2190
2690
|
// Fallback to provider without wagmi if config creation fails
|
|
@@ -2193,9 +2693,9 @@ export function AbstraxnProvider({ config, children }) {
|
|
|
2193
2693
|
if (!wagmiConfig) {
|
|
2194
2694
|
// Show loading state while config is being created
|
|
2195
2695
|
// Don't render AbstraxnProviderInner until wagmiConfig is ready
|
|
2196
|
-
return (_jsx(
|
|
2696
|
+
return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: 'none' }, children: "Loading wallet connectors..." }) }));
|
|
2197
2697
|
}
|
|
2198
|
-
return (_jsx(
|
|
2698
|
+
return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx(WagmiProvider, { config: wagmiConfig, children: _jsx(AbstraxnProviderWithWagmi, { config: config, children: children }) }) }));
|
|
2199
2699
|
}
|
|
2200
2700
|
// If external wallets are disabled, use the provider without wagmi
|
|
2201
2701
|
return _jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children });
|