@abstraxn/signer-react 3.3.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/src/ConnectButton.css.d.ts +2 -0
  3. package/dist/src/ConnectButton.css.js +1 -0
  4. package/dist/src/ConnectButton.d.ts +0 -1
  5. package/dist/src/ConnectButton.js +7 -6
  6. package/dist/src/ExternalWalletButtons.css.d.ts +2 -0
  7. package/dist/src/ExternalWalletButtons.css.js +1 -0
  8. package/dist/src/ExternalWalletButtons.d.ts +4 -1
  9. package/dist/src/ExternalWalletButtons.js +6 -1
  10. package/dist/src/WalletModal.css +2 -0
  11. package/dist/src/WalletModal.css.d.ts +2 -0
  12. package/dist/src/WalletModal.css.js +1 -0
  13. package/dist/src/WalletModal.d.ts +2 -2
  14. package/dist/src/WalletModal.js +113 -59
  15. package/dist/src/components/AbstraxnLogo.d.ts +17 -0
  16. package/dist/src/components/AbstraxnLogo.js +29 -0
  17. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +406 -338
  18. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +7 -0
  19. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js +9 -0
  20. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +41 -6
  21. package/dist/src/components/OnboardingUI/OnboardingUI.css +99 -4
  22. package/dist/src/components/OnboardingUI/OnboardingUI.css.d.ts +2 -0
  23. package/dist/src/components/OnboardingUI/OnboardingUI.css.js +1 -0
  24. package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +0 -1
  25. package/dist/src/components/OnboardingUI/OnboardingUIReact.js +46 -19
  26. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +24 -1
  27. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +348 -160
  28. package/dist/src/components/OnboardingUI/components/Divider.d.ts +7 -0
  29. package/dist/src/components/OnboardingUI/components/Divider.js +9 -0
  30. package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +0 -1
  31. package/dist/src/components/OnboardingUI/components/EmailForm.js +1 -2
  32. package/dist/src/components/OnboardingUI/components/MfaForm.d.ts +0 -1
  33. package/dist/src/components/OnboardingUI/components/MfaForm.js +2 -3
  34. package/dist/src/components/OnboardingUI/components/Modal.d.ts +0 -1
  35. package/dist/src/components/OnboardingUI/components/Modal.js +12 -4
  36. package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +0 -1
  37. package/dist/src/components/OnboardingUI/components/OtpForm.js +2 -3
  38. package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +0 -1
  39. package/dist/src/components/OnboardingUI/components/PasskeyButton.js +2 -2
  40. package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +0 -1
  41. package/dist/src/components/OnboardingUI/components/SocialButtons.js +3 -3
  42. package/dist/src/components/OnboardingUI/onboardingShadowContainer.d.ts +9 -0
  43. package/dist/src/components/OnboardingUI/onboardingShadowContainer.js +36 -0
  44. package/dist/src/components/ShadowHost/ShadowHost.d.ts +18 -0
  45. package/dist/src/components/ShadowHost/ShadowHost.js +123 -0
  46. package/dist/src/components/ShadowHost/ShadowHostContext.d.ts +10 -0
  47. package/dist/src/components/ShadowHost/ShadowHostContext.js +14 -0
  48. package/dist/src/components/ShadowHost/index.d.ts +2 -0
  49. package/dist/src/components/ShadowHost/index.js +3 -0
  50. package/dist/src/components/WalletModal/components/ChainSelector.css +9 -0
  51. package/dist/src/components/WalletModal/components/ChainSelector.css.d.ts +2 -0
  52. package/dist/src/components/WalletModal/components/ChainSelector.css.js +1 -0
  53. package/dist/src/components/WalletModal/components/ChainSelector.d.ts +0 -1
  54. package/dist/src/components/WalletModal/components/ChainSelector.js +78 -23
  55. package/dist/src/components/WalletModal/components/CodeInput.d.ts +12 -0
  56. package/dist/src/components/WalletModal/components/CodeInput.js +39 -0
  57. package/dist/src/components/WalletModal/components/ExportKeyModal.css.d.ts +2 -0
  58. package/dist/src/components/WalletModal/components/ExportKeyModal.css.js +1 -0
  59. package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +0 -2
  60. package/dist/src/components/WalletModal/components/ExportKeyModal.js +2 -4
  61. package/dist/src/components/WalletModal/components/ExportWarningModal.css.d.ts +2 -0
  62. package/dist/src/components/WalletModal/components/ExportWarningModal.css.js +1 -0
  63. package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +0 -2
  64. package/dist/src/components/WalletModal/components/ExportWarningModal.js +2 -4
  65. package/dist/src/components/WalletModal/components/ManageWalletModal.css +27 -1
  66. package/dist/src/components/WalletModal/components/ManageWalletModal.css.d.ts +2 -0
  67. package/dist/src/components/WalletModal/components/ManageWalletModal.css.js +1 -0
  68. package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +0 -1
  69. package/dist/src/components/WalletModal/components/ManageWalletModal.js +66 -148
  70. package/dist/src/components/WalletModal/components/MfaVerifyModal.d.ts +15 -0
  71. package/dist/src/components/WalletModal/components/MfaVerifyModal.js +98 -0
  72. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +2 -12
  73. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css.d.ts +2 -0
  74. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css.js +1 -0
  75. package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +0 -1
  76. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +0 -1
  77. package/dist/src/components/WalletModal/components/ReceiveModal.css.d.ts +2 -0
  78. package/dist/src/components/WalletModal/components/ReceiveModal.css.js +1 -0
  79. package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +0 -1
  80. package/dist/src/components/WalletModal/components/ReceiveModal.js +2 -2
  81. package/dist/src/components/WalletModal/components/SendModal.css.d.ts +2 -0
  82. package/dist/src/components/WalletModal/components/SendModal.css.js +1 -0
  83. package/dist/src/components/WalletModal/components/SendModal.d.ts +5 -2
  84. package/dist/src/components/WalletModal/components/SendModal.js +25 -82
  85. package/dist/src/components/WalletModal/components/SignMfaModalHost.d.ts +1 -0
  86. package/dist/src/components/WalletModal/components/SignMfaModalHost.js +50 -0
  87. package/dist/src/components/WalletModal/components/SuccessModal.css.d.ts +2 -0
  88. package/dist/src/components/WalletModal/components/SuccessModal.css.js +1 -0
  89. package/dist/src/components/WalletModal/components/SuccessModal.d.ts +0 -1
  90. package/dist/src/components/WalletModal/components/SuccessModal.js +0 -1
  91. package/dist/src/components/WalletModal/components/TokenSelectorModal.css.d.ts +2 -0
  92. package/dist/src/components/WalletModal/components/TokenSelectorModal.css.js +1 -0
  93. package/dist/src/components/WalletModal/components/TokenSelectorModal.d.ts +0 -1
  94. package/dist/src/components/WalletModal/components/TokenSelectorModal.js +0 -1
  95. package/dist/src/components/WalletModal/components/UserAvatar.js +2 -1
  96. package/dist/src/components/WalletModal/components/index.d.ts +3 -0
  97. package/dist/src/components/WalletModal/components/index.js +2 -0
  98. package/dist/src/components/WalletModal/hooks/useSendTransaction.js +18 -6
  99. package/dist/src/components/icons.d.ts +2 -0
  100. package/dist/src/components/icons.js +4 -0
  101. package/dist/src/hooks.d.ts +81 -20
  102. package/dist/src/hooks.js +194 -36
  103. package/dist/src/index.d.ts +2 -2
  104. package/dist/src/index.js +2 -2
  105. package/dist/src/signMfaBridge.d.ts +7 -0
  106. package/dist/src/signMfaBridge.js +20 -0
  107. package/dist/src/types.d.ts +4 -0
  108. package/dist/src/utils/shadowFocus.d.ts +8 -0
  109. package/dist/src/utils/shadowFocus.js +15 -0
  110. package/dist/src/utils/shadowRegistry.d.ts +23 -0
  111. package/dist/src/utils/shadowRegistry.js +36 -0
  112. package/dist/src/walletConnectMobile.d.ts +9 -5
  113. package/dist/src/walletConnectMobile.js +152 -51
  114. package/package.json +9 -4
@@ -4,22 +4,154 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  * This component contains all the wallet state management and operations
5
5
  */
6
6
  import { useEffect, useState, useRef, useCallback, useMemo, } from "react";
7
- import { AbstraxnWallet, AuthenticationError, } from "@abstraxn/signer-core";
7
+ import { AbstraxnWallet, AuthenticationError, SignMfaRequiredError, TURNKEY_SUPPORTED_CAIP2, NATIVE_ASSET_SENTINEL, chainIdToCaip2, isNativeAssetEntry, extractAssetAddress, } from "@abstraxn/signer-core";
8
8
  import { OnboardingUIWeb } from "../OnboardingUI";
9
+ import { requestSignMfaCode } from "../../signMfaBridge";
9
10
  import { useConnectionEffect, } from "wagmi";
10
11
  import { parseEther, createPublicClient, http } from "viem";
11
- import { ExternalWalletButtons, resetExternalWalletViewState, } from "../../ExternalWalletButtons";
12
+ import { ExternalWalletButtons, resetExternalWalletViewState, isExternalWalletListOpen, } from "../../ExternalWalletButtons";
12
13
  import { EVM_CHAINS, SOLANA_CHAINS, getChainById, toCoreChain, } from "../../chains";
13
14
  import { AbstraxnContext } from "./context";
14
15
  import { isLinkAuthCallback } from "./utils";
15
16
  import { hasActiveWalletConnectSession, hasPendingWalletConnectApproval, isWalletConnectConnector, patchWalletConnectProviderForMobile, rememberCurrentPageForWalletReturn, resumeWalletConnectRelayer, subscribeWalletConnectForegroundResume, syncWalletConnectDappRedirect, } from "../../walletConnectMobile";
17
+ import { SignMfaModalHost } from "../WalletModal/components/SignMfaModalHost";
16
18
  /** Syncs wagmi disconnect to external wallet state; only rendered when wagmi is available (under WagmiProvider). */
17
19
  function WagmiConnectionEffectSync({ onDisconnect, }) {
18
20
  useConnectionEffect({ onDisconnect });
19
21
  return null;
20
22
  }
21
23
  export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }) {
22
- const { isInitialized, setIsInitialized, isConnected, setIsConnected, address, setAddress, user, setUser, whoami, setWhoami, chainId, setChainId, error, setError, loading, setLoading, resending, setResending, walletBalance, setWalletBalance, onboardingRef, otpIdRef, walletRef, googleCallbackHandledRef, twitterCallbackHandledRef, discordCallbackHandledRef, } = base;
24
+ const { isInitialized, setIsInitialized, isConnected, setIsConnected, address, setAddress, user, setUser, whoami, setWhoami, chainId, setChainId, error, setError, loading, setLoading, resending, setResending, walletBalance, setWalletBalance, tokenBalances, setTokenBalances, tokenBalancesLoading, setTokenBalancesLoading, tokenBalancesError, setTokenBalancesError, onboardingRef, otpIdRef, walletRef, googleCallbackHandledRef, twitterCallbackHandledRef, discordCallbackHandledRef, } = base;
25
+ const handleShowMoreWallets = useCallback(() => {
26
+ // Hide email and Google sections when "More wallets" is clicked
27
+ const onboardingAny = onboardingRef.current;
28
+ if (onboardingAny?.emailForm) {
29
+ onboardingAny.emailForm.style.display = "none";
30
+ }
31
+ if (onboardingAny?.googleButton) {
32
+ onboardingAny.googleButton.style.display = "none";
33
+ }
34
+ // Hide Discord and X (Twitter) buttons when wallet selection screen is shown
35
+ if (onboardingAny?.discordButton) {
36
+ onboardingAny.discordButton.style.display = "none";
37
+ }
38
+ if (onboardingAny?.twitterButton) {
39
+ onboardingAny.twitterButton.style.display = "none";
40
+ }
41
+ // Hide social grid if it exists
42
+ if (onboardingAny?.socialGrid) {
43
+ onboardingAny.socialGrid.style.display = "none";
44
+ }
45
+ // Extra guard: hide any social icon buttons by class
46
+ if (onboardingAny?.rootElement) {
47
+ onboardingAny.rootElement
48
+ .querySelectorAll(".onboarding-button-social-icon, .onboarding-button-social")
49
+ .forEach((el) => {
50
+ el.style.display = "none";
51
+ });
52
+ }
53
+ // Hide all passkey elements when wallet selection screen is shown
54
+ if (onboardingAny?.passkeyLoginButton) {
55
+ onboardingAny.passkeyLoginButton.style.display = "none";
56
+ }
57
+ if (onboardingAny?.passkeySignupLink) {
58
+ onboardingAny.passkeySignupLink.style.display = "none";
59
+ }
60
+ if (onboardingAny?.passkeyErrorElement) {
61
+ onboardingAny.passkeyErrorElement.style.display = "none";
62
+ }
63
+ if (onboardingAny?.passkeyDivider) {
64
+ onboardingAny.passkeyDivider.style.display = "none";
65
+ }
66
+ if (onboardingAny?.externalWalletDivider) {
67
+ onboardingAny.externalWalletDivider.style.display = "none";
68
+ }
69
+ // Also hide the divider between email and Google if it exists
70
+ if (onboardingAny?.divider) {
71
+ onboardingAny.divider.style.display = "none";
72
+ }
73
+ }, [onboardingRef]);
74
+ const handleHideMoreWallets = useCallback(() => {
75
+ // Show email and Google sections when back is clicked
76
+ const onboardingAny = onboardingRef.current;
77
+ const authMethods = onboardingAny?.config?.authMethods || [
78
+ "otp",
79
+ "google",
80
+ ];
81
+ const showEmail = authMethods.includes("otp");
82
+ const showGoogle = authMethods.includes("google");
83
+ const showTwitter = authMethods.includes("twitter");
84
+ const showDiscord = authMethods.includes("discord");
85
+ const showPasskey = authMethods.includes("passkey");
86
+ if (onboardingAny?.emailForm) {
87
+ onboardingAny.emailForm.style.display = showEmail ? "" : "none";
88
+ }
89
+ if (onboardingAny?.googleButton) {
90
+ onboardingAny.googleButton.style.display = showGoogle
91
+ ? ""
92
+ : "none";
93
+ }
94
+ // Show Discord and X (Twitter) buttons if enabled
95
+ if (onboardingAny?.discordButton) {
96
+ onboardingAny.discordButton.style.display = showDiscord
97
+ ? ""
98
+ : "none";
99
+ }
100
+ if (onboardingAny?.twitterButton) {
101
+ onboardingAny.twitterButton.style.display = showTwitter
102
+ ? ""
103
+ : "none";
104
+ }
105
+ // Show social grid if it exists and social methods are enabled
106
+ if (onboardingAny?.socialGrid) {
107
+ const hasSocialMethods = showGoogle || showTwitter || showDiscord;
108
+ onboardingAny.socialGrid.style.display = hasSocialMethods
109
+ ? ""
110
+ : "none";
111
+ }
112
+ // Extra guard: restore social icon buttons by class based on flags
113
+ if (onboardingAny?.rootElement) {
114
+ const hasSocialMethods = showGoogle || showTwitter || showDiscord;
115
+ onboardingAny.rootElement
116
+ .querySelectorAll(".onboarding-button-social-icon, .onboarding-button-social")
117
+ .forEach((el) => {
118
+ el.style.display = hasSocialMethods
119
+ ? ""
120
+ : "none";
121
+ });
122
+ }
123
+ // Show passkey controls if enabled
124
+ if (onboardingAny?.passkeyLoginButton) {
125
+ onboardingAny.passkeyLoginButton.style.display = showPasskey
126
+ ? ""
127
+ : "none";
128
+ }
129
+ if (onboardingAny?.passkeySignupLink) {
130
+ onboardingAny.passkeySignupLink.style.display = showPasskey
131
+ ? ""
132
+ : "none";
133
+ }
134
+ if (onboardingAny?.passkeyErrorElement) {
135
+ onboardingAny.passkeyErrorElement.style.display = "none";
136
+ }
137
+ // Show passkey divider if passkey is enabled and there are other auth methods
138
+ if (onboardingAny?.passkeyDivider) {
139
+ onboardingAny.passkeyDivider.style.display =
140
+ showPasskey &&
141
+ (showEmail || showGoogle || showTwitter || showDiscord)
142
+ ? ""
143
+ : "none";
144
+ }
145
+ // Show divider between email and social methods if both are enabled
146
+ if (onboardingAny?.divider) {
147
+ const hasSocialMethods = showGoogle || showTwitter || showDiscord;
148
+ onboardingAny.divider.style.display =
149
+ showEmail && hasSocialMethods ? "" : "none";
150
+ }
151
+ if (onboardingAny?.externalWalletDivider) {
152
+ onboardingAny.externalWalletDivider.style.display = "";
153
+ }
154
+ }, [onboardingRef]);
23
155
  const externalWalletsEnabled = config.externalWallets?.enabled ?? false;
24
156
  // Keep a ref to avoid re-creating callbacks when toggling config (prevents flicker)
25
157
  const externalWalletsEnabledRef = useRef(externalWalletsEnabled);
@@ -249,7 +381,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
249
381
  const showEmail = authMethods.includes("otp");
250
382
  const showGoogle = authMethods.includes("google");
251
383
  const hasEmailOrGoogle = showEmail || showGoogle;
252
- if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
384
+ // Wallet list open: email is hidden — never re-show the "or" divider.
385
+ if (isExternalWalletListOpen()) {
386
+ if (onboardingAny.externalWalletDivider) {
387
+ onboardingAny.externalWalletDivider.style.display = "none";
388
+ }
389
+ }
390
+ else if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
253
391
  onboardingAny.externalWalletDivider.style.display = "";
254
392
  }
255
393
  else if (onboardingAny.externalWalletDivider) {
@@ -375,9 +513,30 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
375
513
  ]); // Re-run if wagmi state changes
376
514
  // Initialize wallet
377
515
  useEffect(() => {
378
- // If wallet already exists (from previous mount), reuse it to prevent flicker
516
+ // If wallet already exists (from previous mount), reuse it to prevent flicker.
517
+ // StrictMode still destroys OnboardingUIWeb in cleanup — rebuild its shadow
518
+ // tree here so the OAuth success loader can paint again while Mount1's
519
+ // in-flight callback finishes.
379
520
  if (walletRef.current) {
380
- return;
521
+ const onboarding = onboardingRef.current;
522
+ if (onboarding && !onboarding.hasLiveShadow()) {
523
+ onboarding.init();
524
+ if (typeof window !== "undefined") {
525
+ const urlParams = new URLSearchParams(window.location.search);
526
+ const hasSuccess = urlParams.get("success") === "true";
527
+ const mfaRequired = urlParams.get("mfaRequired") === "true";
528
+ if (hasSuccess &&
529
+ !mfaRequired &&
530
+ !isLinkAuthCallback(urlParams)) {
531
+ onboarding.showLoadingModal();
532
+ }
533
+ }
534
+ }
535
+ return () => {
536
+ if (onboardingRef.current) {
537
+ onboardingRef.current.destroy();
538
+ }
539
+ };
381
540
  }
382
541
  // Compute supported chains from chain config (outside useMemo since we're in useEffect)
383
542
  // This should include ALL chains that might be shown in the UI (availableChains)
@@ -545,10 +704,21 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
545
704
  walletInstance.on("chainChanged", (newChainId) => {
546
705
  setChainId(newChainId);
547
706
  });
548
- // Initialize OnboardingUI only if not already created (prevents flicker on config change)
549
- if (onboardingRef.current) {
707
+ // Initialize OnboardingUI only if not already created (prevents flicker on config change).
708
+ // A destroyed StrictMode instance leaves a non-null ref with no live shadow — treat that
709
+ // as "not created" so we rebuild instead of skipping OAuth loader setup.
710
+ if (onboardingRef.current?.hasLiveShadow()) {
550
711
  return;
551
712
  }
713
+ if (onboardingRef.current) {
714
+ try {
715
+ onboardingRef.current.destroy();
716
+ }
717
+ catch {
718
+ // Ignore — instance may already be partially torn down.
719
+ }
720
+ onboardingRef.current = null;
721
+ }
552
722
  const uiConfig = config.ui || {};
553
723
  const onboarding = new OnboardingUIWeb({
554
724
  logo: uiConfig.logo,
@@ -889,16 +1059,10 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
889
1059
  resetExternalWalletViewState();
890
1060
  },
891
1061
  }, externalWalletsEnabled); // Pass externalWalletsEnabled as second parameter
892
- // Inject custom CSS if provided
893
- if (uiConfig.customCSS) {
894
- const styleId = "abstraxn-custom-css";
895
- if (!document.getElementById(styleId)) {
896
- const style = document.createElement("style");
897
- style.id = styleId;
898
- style.textContent = uiConfig.customCSS;
899
- document.head.appendChild(style);
900
- }
901
- }
1062
+ // Note: customCSS is injected by OnboardingUIWeb itself (already threaded
1063
+ // into its constructor config above as `customCSS: uiConfig.customCSS`),
1064
+ // inside its own shadow container — this used to be duplicated here via
1065
+ // a separate document.head injection, which is now redundant and removed.
902
1066
  const hasAuthParams = (params) => params.get("success") === "true" ||
903
1067
  params.get("error") ||
904
1068
  params.get("code") ||
@@ -932,6 +1096,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
932
1096
  // Hide immediately - use synchronous approach to prevent flicker
933
1097
  // Only hide if NOT handling a callback
934
1098
  const onboardingAny = onboarding;
1099
+ let oauthLoaderTimeout = null;
935
1100
  if (!shouldKeepOpen && onboardingAny.modalOverlay) {
936
1101
  // Hide immediately without delay to prevent flicker
937
1102
  onboardingAny.modalOverlay.classList.remove("onboarding-modal-open");
@@ -943,9 +1108,9 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
943
1108
  // Skip when mfaRequired=true so MFA verification screen can show instead.
944
1109
  const mfaRequired = urlParams.get("mfaRequired") === "true";
945
1110
  if (!mfaRequired) {
946
- setTimeout(() => {
1111
+ oauthLoaderTimeout = setTimeout(() => {
947
1112
  const onboardingInstance = onboardingRef.current;
948
- if (onboardingInstance && onboardingInstance.showLoadingModal) {
1113
+ if (onboardingInstance) {
949
1114
  onboardingInstance.showLoadingModal();
950
1115
  }
951
1116
  }, 150);
@@ -1283,8 +1448,14 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1283
1448
  });
1284
1449
  }
1285
1450
  return () => {
1451
+ if (oauthLoaderTimeout !== null) {
1452
+ clearTimeout(oauthLoaderTimeout);
1453
+ }
1286
1454
  if (onboardingRef.current) {
1287
1455
  onboardingRef.current.destroy();
1456
+ // Keep the instance in the ref so StrictMode remount can call init() on the
1457
+ // same object (preserving auth callbacks / config). destroy() already clears
1458
+ // the shadow tree; the walletRef early-return path re-inits it.
1288
1459
  }
1289
1460
  };
1290
1461
  }, []); // Only run once on mount
@@ -1312,9 +1483,16 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1312
1483
  }
1313
1484
  if (onboardingRef.current) {
1314
1485
  const onboarding = onboardingRef.current;
1315
- // Re-initialize if destroyed
1486
+ // Re-initialize if destroyed.
1487
+ // Uses `.isConnected`, not `document.body.contains(...)`: the modal
1488
+ // overlay now lives inside a shadow root, and `contains()` walks the
1489
+ // tree via `parentNode` — a ShadowRoot's `parentNode` is always null,
1490
+ // so `contains()` would report "not attached" even when the modal is
1491
+ // genuinely still live, forcing a needless re-init on every call.
1492
+ // `isConnected` is defined in terms of the shadow-including root, so it
1493
+ // correctly reports true across shadow boundaries.
1316
1494
  if (!onboarding.modalOverlay ||
1317
- !document.body.contains(onboarding.modalOverlay)) {
1495
+ !onboarding.modalOverlay.isConnected) {
1318
1496
  onboarding.init();
1319
1497
  // Wait for init to complete
1320
1498
  setTimeout(() => {
@@ -1349,7 +1527,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1349
1527
  const showEmail = authMethods.includes("otp");
1350
1528
  const showGoogle = authMethods.includes("google");
1351
1529
  const hasEmailOrGoogle = showEmail || showGoogle;
1352
- if (onboarding.externalWalletDivider && hasEmailOrGoogle) {
1530
+ if (isExternalWalletListOpen()) {
1531
+ if (onboarding.externalWalletDivider) {
1532
+ onboarding.externalWalletDivider.style.display = "none";
1533
+ }
1534
+ }
1535
+ else if (onboarding.externalWalletDivider && hasEmailOrGoogle) {
1353
1536
  onboarding.externalWalletDivider.style.display = "";
1354
1537
  }
1355
1538
  else if (onboarding.externalWalletDivider) {
@@ -1444,7 +1627,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1444
1627
  const showEmail = authMethods.includes("otp");
1445
1628
  const showGoogle = authMethods.includes("google");
1446
1629
  const hasEmailOrGoogle = showEmail || showGoogle;
1447
- if (onboarding.externalWalletDivider && hasEmailOrGoogle) {
1630
+ if (isExternalWalletListOpen()) {
1631
+ if (onboarding.externalWalletDivider) {
1632
+ onboarding.externalWalletDivider.style.display = "none";
1633
+ }
1634
+ }
1635
+ else if (onboarding.externalWalletDivider && hasEmailOrGoogle) {
1448
1636
  onboarding.externalWalletDivider.style.display = "";
1449
1637
  }
1450
1638
  else if (onboarding.externalWalletDivider) {
@@ -2491,7 +2679,27 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
2491
2679
  if (!walletRef.current?.isConnected) {
2492
2680
  throw new Error("Wallet not connected");
2493
2681
  }
2494
- return await walletRef.current.disableMfaWithSignedPayload();
2682
+ const performDisable = () => walletRef.current.disableMfaWithSignedPayload();
2683
+ try {
2684
+ return await performDisable();
2685
+ }
2686
+ catch (error) {
2687
+ // Safety net: if sign MFA was not cleared (or expired between verify and confirm),
2688
+ // open the shared sign-MFA modal, verify-sign, then retry once.
2689
+ const isSignMfaRequired = error instanceof SignMfaRequiredError ||
2690
+ error?.code === "SIGN_MFA_REQUIRED" ||
2691
+ (typeof error?.message === "string" &&
2692
+ error.message.includes("SIGN_MFA_REQUIRED"));
2693
+ if (!isSignMfaRequired) {
2694
+ throw error;
2695
+ }
2696
+ const code = await requestSignMfaCode();
2697
+ const verifyResponse = await walletRef.current.verifySignMfa(code);
2698
+ if (!verifyResponse?.verified) {
2699
+ throw new Error("Incorrect MFA code. Please try again.");
2700
+ }
2701
+ return await performDisable();
2702
+ }
2495
2703
  }, []);
2496
2704
  const [mfaStatus, setMfaStatus] = useState(null);
2497
2705
  const [mfaStatusLoading, setMfaStatusLoading] = useState(false);
@@ -3392,44 +3600,153 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3392
3600
  }
3393
3601
  return map;
3394
3602
  }, [config.chains]);
3395
- // Fetch balance for Abstraxn wallet (not external wallet) - reusable for on-demand refetch
3603
+ // Guards against overlapping concurrent fetches for the same address+chain (e.g. if
3604
+ // multiple callers trigger a refetch in quick succession) - a defense-in-depth measure
3605
+ // on top of the effect triggers being keyed off primitives rather than callback identity.
3606
+ const balanceFetchInFlightKeyRef = useRef(null);
3607
+ // Fetch native balance + full token list for the Abstraxn wallet (not external wallet)
3608
+ // in one combined refresh - reusable for on-demand refetch. Turnkey is called at most
3609
+ // once per refresh; native-balance extraction and token-list mapping are independently
3610
+ // try/caught so a failure in one cannot corrupt the other (see plan review point D).
3396
3611
  const refetchWalletBalance = useCallback(async () => {
3397
3612
  if (isExternalWalletConnected || !address || !currentChainId) {
3398
3613
  setWalletBalance(null);
3614
+ setTokenBalances([]);
3615
+ setTokenBalancesError(null);
3616
+ return;
3617
+ }
3618
+ const fetchKey = `${address}:${currentChainId}`;
3619
+ if (balanceFetchInFlightKeyRef.current === fetchKey) {
3399
3620
  return;
3400
3621
  }
3622
+ balanceFetchInFlightKeyRef.current = fetchKey;
3401
3623
  try {
3402
3624
  const currentChain = getChainById(currentChainId) || customChainsById.get(currentChainId);
3403
3625
  if (!currentChain || currentChain.type !== "evm") {
3404
3626
  setWalletBalance(null);
3627
+ setTokenBalances([]);
3628
+ setTokenBalancesError(null);
3405
3629
  return;
3406
3630
  }
3407
- const publicClient = createPublicClient({
3408
- chain: {
3409
- id: currentChain.id,
3410
- name: currentChain.name,
3411
- nativeCurrency: currentChain.nativeCurrency,
3412
- rpcUrls: {
3413
- default: {
3414
- http: [currentChain.rpcUrl],
3631
+ const fetchRpcNativeBalance = async () => {
3632
+ const publicClient = createPublicClient({
3633
+ chain: {
3634
+ id: currentChain.id,
3635
+ name: currentChain.name,
3636
+ nativeCurrency: currentChain.nativeCurrency,
3637
+ rpcUrls: {
3638
+ default: {
3639
+ http: [currentChain.rpcUrl],
3640
+ },
3415
3641
  },
3416
3642
  },
3417
- },
3418
- transport: http(currentChain.rpcUrl),
3419
- });
3420
- const balance = await publicClient.getBalance({
3421
- address: address,
3422
- });
3423
- setWalletBalance(balance);
3424
- }
3425
- catch (error) {
3426
- console.error("Failed to fetch balance:", error);
3427
- setWalletBalance(null);
3643
+ transport: http(currentChain.rpcUrl),
3644
+ });
3645
+ return await publicClient.getBalance({ address: address });
3646
+ };
3647
+ const caip2 = chainIdToCaip2(currentChainId, TURNKEY_SUPPORTED_CAIP2);
3648
+ const wallet = walletRef.current;
3649
+ if (caip2 && wallet) {
3650
+ setTokenBalancesLoading(true);
3651
+ let entries = [];
3652
+ let turnkeyCallFailed = false;
3653
+ try {
3654
+ entries = await wallet.getTurnkeyBalances(caip2, address);
3655
+ }
3656
+ catch (error) {
3657
+ turnkeyCallFailed = true;
3658
+ console.warn("Turnkey balance fetch failed, falling back to RPC:", error);
3659
+ }
3660
+ if (!turnkeyCallFailed) {
3661
+ // Native balance extraction - its own try/catch, independent of the token list.
3662
+ try {
3663
+ const nativeEntry = entries.find((entry) => isNativeAssetEntry({ caip19: entry.caip19, symbol: entry.symbol, decimals: entry.decimals }, currentChain.nativeCurrency));
3664
+ setWalletBalance(nativeEntry?.balance !== undefined ? BigInt(nativeEntry.balance) : null);
3665
+ }
3666
+ catch (error) {
3667
+ console.error("Failed to extract native balance from Turnkey response:", error);
3668
+ setWalletBalance(null);
3669
+ }
3670
+ // Token list mapping - its own try/catch, per-entry skip on parse failure.
3671
+ try {
3672
+ const tokens = [];
3673
+ for (const entry of entries) {
3674
+ try {
3675
+ const isNative = isNativeAssetEntry({ caip19: entry.caip19, symbol: entry.symbol, decimals: entry.decimals }, currentChain.nativeCurrency);
3676
+ const tokenAddress = isNative
3677
+ ? NATIVE_ASSET_SENTINEL
3678
+ : extractAssetAddress({ caip19: entry.caip19 });
3679
+ if (!isNative && tokenAddress === undefined) {
3680
+ console.warn("Skipping unrecognized asset in balance list:", entry.caip19);
3681
+ continue;
3682
+ }
3683
+ tokens.push({
3684
+ address: tokenAddress,
3685
+ symbol: entry.symbol ?? "",
3686
+ name: entry.name ?? entry.symbol ?? "",
3687
+ decimals: entry.decimals ?? 18,
3688
+ balance: entry.balance !== undefined ? BigInt(entry.balance) : 0n,
3689
+ isNative,
3690
+ });
3691
+ }
3692
+ catch (entryError) {
3693
+ console.warn("Skipping malformed balance entry:", entryError);
3694
+ }
3695
+ }
3696
+ setTokenBalances(tokens);
3697
+ setTokenBalancesError(null);
3698
+ }
3699
+ catch (error) {
3700
+ setTokenBalancesError(error instanceof Error ? error : new Error("Failed to load token balances"));
3701
+ }
3702
+ finally {
3703
+ setTokenBalancesLoading(false);
3704
+ }
3705
+ return;
3706
+ }
3707
+ setTokenBalancesLoading(false);
3708
+ }
3709
+ // Not Turnkey-covered, or the Turnkey call itself failed - fall back to RPC for
3710
+ // native balance, and mirror it as a single native-only token list entry so both
3711
+ // stay consistent with each other.
3712
+ try {
3713
+ const balance = await fetchRpcNativeBalance();
3714
+ setWalletBalance(balance);
3715
+ setTokenBalances([
3716
+ {
3717
+ address: NATIVE_ASSET_SENTINEL,
3718
+ symbol: currentChain.nativeCurrency.symbol,
3719
+ name: currentChain.nativeCurrency.name,
3720
+ decimals: currentChain.nativeCurrency.decimals,
3721
+ balance,
3722
+ isNative: true,
3723
+ },
3724
+ ]);
3725
+ setTokenBalancesError(null);
3726
+ }
3727
+ catch (error) {
3728
+ console.error("Failed to fetch balance:", error);
3729
+ setWalletBalance(null);
3730
+ setTokenBalancesError(error instanceof Error ? error : new Error("Failed to load balance"));
3731
+ }
3428
3732
  }
3429
- }, [address, currentChainId, isExternalWalletConnected, customChainsById]);
3733
+ finally {
3734
+ if (balanceFetchInFlightKeyRef.current === fetchKey) {
3735
+ balanceFetchInFlightKeyRef.current = null;
3736
+ }
3737
+ }
3738
+ }, [address, currentChainId, isExternalWalletConnected, customChainsById, walletRef]);
3739
+ const refetchTokenBalances = refetchWalletBalance;
3740
+ // Trigger on the actual primitives that should cause a refetch - not on
3741
+ // refetchWalletBalance's own identity, which can churn on unrelated re-renders
3742
+ // (e.g. if the consuming app passes `config` as a fresh object/array literal every
3743
+ // render, customChainsById's memo recomputes and gives this callback a new identity
3744
+ // even though nothing relevant changed). Depending on the callback there caused a
3745
+ // continuous refetch loop instead of "once per real change".
3746
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3430
3747
  useEffect(() => {
3431
3748
  refetchWalletBalance();
3432
- }, [refetchWalletBalance]);
3749
+ }, [address, currentChainId, isExternalWalletConnected]);
3433
3750
  // Single refetch for UI: Abstraxn wallet = RPC call, external wallet = wagmi refetch
3434
3751
  const refetchBalance = useCallback(async () => {
3435
3752
  if (isExternalWalletConnected && wagmiBalance?.refetch) {
@@ -3521,15 +3838,19 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3521
3838
  }, [currentChainId, customChainsById]);
3522
3839
  // Enhanced switchChain that works for both EVM and Solana
3523
3840
  const switchChainEnhanced = useCallback(async (targetChainId) => {
3841
+ console.log("[switchChainEnhanced] called with", targetChainId);
3524
3842
  if (!walletRef.current) {
3843
+ console.log("[switchChainEnhanced] aborting: wallet not initialized");
3525
3844
  throw new Error("Wallet not initialized");
3526
3845
  }
3527
3846
  // Check if chain is in availableChains (user-configured chains)
3528
3847
  const targetChainInAvailable = availableChains?.find((c) => c.id === targetChainId);
3848
+ console.log("[switchChainEnhanced] targetChainInAvailable:", targetChainInAvailable, "availableChains ids:", availableChains?.map((c) => c.id));
3529
3849
  if (!targetChainInAvailable) {
3530
3850
  throw new Error(`Chain with ID ${targetChainId} is not available. Please configure it in your SDK setup.`);
3531
3851
  }
3532
3852
  const targetChain = getChainById(targetChainId) || customChainsById.get(targetChainId);
3853
+ console.log("[switchChainEnhanced] resolved targetChain:", targetChain);
3533
3854
  if (!targetChain) {
3534
3855
  throw new Error(`Chain with ID ${targetChainId} is not supported`);
3535
3856
  }
@@ -3539,21 +3860,27 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3539
3860
  if (targetChain.type === "evm") {
3540
3861
  // For EVM chains, use the existing switchChain
3541
3862
  if (isExternalWalletConnected && wagmiSwitchChain) {
3863
+ console.log("[switchChainEnhanced] external wallet path - calling wagmi switchChainAsync");
3542
3864
  // External wallet - use wagmi switchChain
3543
3865
  await wagmiSwitchChain.switchChainAsync({ chainId: targetChainId });
3544
3866
  setExternalWalletChainId(targetChainId);
3867
+ console.log("[switchChainEnhanced] wagmi switchChainAsync resolved");
3545
3868
  }
3546
3869
  else {
3870
+ console.log("[switchChainEnhanced] embedded wallet path - calling switchChain()");
3547
3871
  // Abstraxn wallet - try to switch, but if chain is not in wallet's supportedChains,
3548
3872
  // we'll update the state directly (for chains that are in availableChains but not in wallet config)
3549
3873
  try {
3550
3874
  await switchChain(targetChainId);
3875
+ console.log("[switchChainEnhanced] switchChain() resolved without throwing");
3551
3876
  }
3552
3877
  catch (switchErr) {
3878
+ console.log("[switchChainEnhanced] switchChain() threw:", switchErr?.message, switchErr);
3553
3879
  // If the wallet doesn't support this chain, but it's in availableChains,
3554
3880
  // we can still update the state to allow UI to work
3555
3881
  if (switchErr?.message?.includes("not supported") &&
3556
3882
  targetChainInAvailable) {
3883
+ console.log("[switchChainEnhanced] recovering via direct setChainId fallback");
3557
3884
  // Update chainId state directly for chains in availableChains
3558
3885
  setChainId(targetChainId);
3559
3886
  // Emit chainChanged event if wallet supports it
@@ -3563,6 +3890,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3563
3890
  }
3564
3891
  }
3565
3892
  else {
3893
+ console.log("[switchChainEnhanced] NOT recovering - rethrowing");
3566
3894
  throw switchErr;
3567
3895
  }
3568
3896
  }
@@ -3576,10 +3904,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3576
3904
  }
3577
3905
  // Update chainId state
3578
3906
  if (!isExternalWalletConnected) {
3907
+ console.log("[switchChainEnhanced] final setChainId call with", targetChainId);
3579
3908
  setChainId(targetChainId);
3580
3909
  }
3581
3910
  }
3582
3911
  catch (err) {
3912
+ console.log("[switchChainEnhanced] caught at top level:", err);
3583
3913
  const error = err instanceof Error
3584
3914
  ? err
3585
3915
  : new Error(`Failed to switch to chain ${targetChainId}: ${err instanceof Error ? err.message : String(err)}`);
@@ -3800,6 +4130,10 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3800
4130
  // Balance (for Abstraxn wallet)
3801
4131
  walletBalance: !isExternalWalletConnected ? walletBalance : undefined,
3802
4132
  refetchBalance,
4133
+ tokenBalances: !isExternalWalletConnected ? tokenBalances : [],
4134
+ tokenBalancesLoading,
4135
+ tokenBalancesError,
4136
+ refetchTokenBalances,
3803
4137
  // Connection type
3804
4138
  connectionType,
3805
4139
  // Pre-fill email for email-OTP flow
@@ -3846,7 +4180,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3846
4180
  const showEmail = authMethods.includes("otp");
3847
4181
  const showGoogle = authMethods.includes("google");
3848
4182
  const hasEmailOrGoogle = showEmail || showGoogle;
3849
- if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
4183
+ if (isExternalWalletListOpen()) {
4184
+ if (onboardingAny.externalWalletDivider) {
4185
+ onboardingAny.externalWalletDivider.style.display = "none";
4186
+ }
4187
+ }
4188
+ else if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
3850
4189
  onboardingAny.externalWalletDivider.style.display = "";
3851
4190
  }
3852
4191
  else if (onboardingAny.externalWalletDivider) {
@@ -3866,84 +4205,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3866
4205
  // Check if the container is the same as the one we created the root for
3867
4206
  const isSameContainer = externalWalletContainerRef.current === container;
3868
4207
  const rootStillValid = externalWalletRootRef.current && containerInDOM && isSameContainer;
3869
- const renderExternalWalletButtons = () => (_jsx(AbstraxnContext.Provider, { value: valueRef.current, children: _jsx(ExternalWalletButtons, { onConnect: async () => { }, onShowMoreWallets: () => {
3870
- const onboardingAny = onboardingRef.current;
3871
- if (onboardingAny?.emailForm) {
3872
- onboardingAny.emailForm.style.display = "none";
3873
- }
3874
- if (onboardingAny?.googleButton) {
3875
- onboardingAny.googleButton.style.display = "none";
3876
- }
3877
- // Hide all passkey elements when wallet selection screen is shown
3878
- if (onboardingAny?.passkeyLoginButton) {
3879
- onboardingAny.passkeyLoginButton.style.display = "none";
3880
- }
3881
- if (onboardingAny?.passkeySignupLink) {
3882
- onboardingAny.passkeySignupLink.style.display = "none";
3883
- }
3884
- if (onboardingAny?.passkeyErrorElement) {
3885
- onboardingAny.passkeyErrorElement.style.display = "none";
3886
- }
3887
- if (onboardingAny?.passkeyDivider) {
3888
- onboardingAny.passkeyDivider.style.display = "none";
3889
- }
3890
- if (onboardingAny?.externalWalletDivider) {
3891
- onboardingAny.externalWalletDivider.style.display = "none";
3892
- }
3893
- if (onboardingAny?.divider) {
3894
- onboardingAny.divider.style.display = "none";
3895
- }
3896
- }, onHideMoreWallets: () => {
3897
- const onboardingAny = onboardingRef.current;
3898
- const authMethods = onboardingAny?.config?.authMethods || [
3899
- "otp",
3900
- "google",
3901
- ];
3902
- const showEmail = authMethods.includes("otp");
3903
- const showGoogle = authMethods.includes("google");
3904
- const showPasskey = authMethods.includes("passkey");
3905
- if (onboardingAny?.emailForm) {
3906
- onboardingAny.emailForm.style.display = showEmail ? "" : "none";
3907
- }
3908
- if (onboardingAny?.googleButton) {
3909
- onboardingAny.googleButton.style.display = showGoogle
3910
- ? ""
3911
- : "none";
3912
- }
3913
- // Show passkey controls if enabled
3914
- if (onboardingAny?.passkeyLoginButton) {
3915
- onboardingAny.passkeyLoginButton.style.display = showPasskey
3916
- ? ""
3917
- : "none";
3918
- }
3919
- if (onboardingAny?.passkeySignupLink) {
3920
- onboardingAny.passkeySignupLink.style.display = showPasskey
3921
- ? ""
3922
- : "none";
3923
- }
3924
- if (onboardingAny?.passkeyErrorElement) {
3925
- onboardingAny.passkeyErrorElement.style.display = "none";
3926
- }
3927
- // Show passkey divider if passkey is enabled and there are other auth methods
3928
- if (onboardingAny?.passkeyDivider) {
3929
- onboardingAny.passkeyDivider.style.display =
3930
- showPasskey && (showEmail || showGoogle) ? "" : "none";
3931
- }
3932
- if (onboardingAny?.divider) {
3933
- onboardingAny.divider.style.display =
3934
- showEmail && showGoogle ? "" : "none";
3935
- }
3936
- const hasEmailOrGoogle = showEmail || showGoogle;
3937
- if (onboardingAny?.externalWalletDivider && hasEmailOrGoogle) {
3938
- onboardingAny.externalWalletDivider.style.display = "";
3939
- }
3940
- else if (onboardingAny?.externalWalletDivider) {
3941
- onboardingAny.externalWalletDivider.style.display = "none";
3942
- }
3943
- } }) }));
4208
+ const renderExternalWalletButtons = () => (_jsx(AbstraxnContext.Provider, { value: valueRef.current, children: _jsx(ExternalWalletButtons, { onConnect: async () => { }, onShowMoreWallets: handleShowMoreWallets, onHideMoreWallets: handleHideMoreWallets }) }));
3944
4209
  if (rootStillValid) {
3945
4210
  externalWalletRootRef.current.render(renderExternalWalletButtons());
3946
4211
  externalWalletMountedRef.current = true;
4212
+ if (isExternalWalletListOpen()) {
4213
+ handleShowMoreWallets();
4214
+ }
3947
4215
  }
3948
4216
  else {
3949
4217
  try {
@@ -3965,6 +4233,9 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3965
4233
  externalWalletContainerRef.current = container; // Track the container
3966
4234
  rootInstance.render(renderExternalWalletButtons());
3967
4235
  externalWalletMountedRef.current = true;
4236
+ if (isExternalWalletListOpen()) {
4237
+ handleShowMoreWallets();
4238
+ }
3968
4239
  }
3969
4240
  catch (error) {
3970
4241
  externalWalletMountedRef.current = false;
@@ -4037,7 +4308,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
4037
4308
  const showEmail = authMethods.includes("otp");
4038
4309
  const showGoogle = authMethods.includes("google");
4039
4310
  const hasEmailOrGoogle = showEmail || showGoogle;
4040
- if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
4311
+ if (isExternalWalletListOpen()) {
4312
+ if (onboardingAny.externalWalletDivider) {
4313
+ onboardingAny.externalWalletDivider.style.display = "none";
4314
+ }
4315
+ }
4316
+ else if (onboardingAny.externalWalletDivider && hasEmailOrGoogle) {
4041
4317
  onboardingAny.externalWalletDivider.style.display = "";
4042
4318
  }
4043
4319
  else if (onboardingAny.externalWalletDivider) {
@@ -4088,91 +4364,11 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
4088
4364
  rootInstance.render(_jsx(AbstraxnContext.Provider, { value: valueRef.current, children: _jsx(ExternalWalletButtons, { onConnect: async (_connectorId, _address) => {
4089
4365
  // console.log('External wallet connected:', connectorId, address);
4090
4366
  // Modal will be closed automatically by the useEffect that watches wagmiAccount
4091
- }, onShowMoreWallets: () => {
4092
- // Hide email and Google sections when "More wallets" is clicked
4093
- const onboardingAny = onboardingRef.current;
4094
- if (onboardingAny?.emailForm) {
4095
- onboardingAny.emailForm.style.display = "none";
4096
- }
4097
- if (onboardingAny?.googleButton) {
4098
- onboardingAny.googleButton.style.display = "none";
4099
- }
4100
- // Hide all passkey elements when wallet selection screen is shown
4101
- if (onboardingAny?.passkeyLoginButton) {
4102
- onboardingAny.passkeyLoginButton.style.display = "none";
4103
- }
4104
- if (onboardingAny?.passkeySignupLink) {
4105
- onboardingAny.passkeySignupLink.style.display = "none";
4106
- }
4107
- if (onboardingAny?.passkeyErrorElement) {
4108
- onboardingAny.passkeyErrorElement.style.display = "none";
4109
- }
4110
- if (onboardingAny?.passkeyDivider) {
4111
- onboardingAny.passkeyDivider.style.display = "none";
4112
- }
4113
- if (onboardingAny?.externalWalletDivider) {
4114
- onboardingAny.externalWalletDivider.style.display = "none";
4115
- }
4116
- // Also hide the divider between email and Google if it exists
4117
- if (onboardingAny?.divider) {
4118
- onboardingAny.divider.style.display = "none";
4119
- }
4120
- }, onHideMoreWallets: () => {
4121
- // Show email and Google sections when back is clicked (restore initial screen)
4122
- const onboardingAny = onboardingRef.current;
4123
- const authMethods = onboardingAny?.config?.authMethods || [
4124
- "otp",
4125
- "google",
4126
- ];
4127
- const showEmail = authMethods.includes("otp");
4128
- const showGoogle = authMethods.includes("google");
4129
- const showPasskey = authMethods.includes("passkey");
4130
- // Show email form if enabled
4131
- if (onboardingAny?.emailForm) {
4132
- onboardingAny.emailForm.style.display = showEmail
4133
- ? ""
4134
- : "none";
4135
- }
4136
- // Show Google button if enabled
4137
- if (onboardingAny?.googleButton) {
4138
- onboardingAny.googleButton.style.display = showGoogle
4139
- ? ""
4140
- : "none";
4141
- }
4142
- // Show passkey controls if enabled
4143
- if (onboardingAny?.passkeyLoginButton) {
4144
- onboardingAny.passkeyLoginButton.style.display = showPasskey
4145
- ? ""
4146
- : "none";
4147
- }
4148
- if (onboardingAny?.passkeySignupLink) {
4149
- onboardingAny.passkeySignupLink.style.display = showPasskey
4150
- ? ""
4151
- : "none";
4152
- }
4153
- if (onboardingAny?.passkeyErrorElement) {
4154
- onboardingAny.passkeyErrorElement.style.display = "none";
4155
- }
4156
- // Show passkey divider if passkey is enabled and there are other auth methods
4157
- if (onboardingAny?.passkeyDivider) {
4158
- onboardingAny.passkeyDivider.style.display =
4159
- showPasskey && (showEmail || showGoogle) ? "" : "none";
4160
- }
4161
- // Show divider between email and Google if both are enabled
4162
- if (onboardingAny?.divider) {
4163
- onboardingAny.divider.style.display =
4164
- showEmail && showGoogle ? "" : "none";
4165
- }
4166
- // Show the "or" divider before external wallets only if email/Google are visible
4167
- const hasEmailOrGoogle = showEmail || showGoogle;
4168
- if (onboardingAny?.externalWalletDivider && hasEmailOrGoogle) {
4169
- onboardingAny.externalWalletDivider.style.display = "";
4170
- }
4171
- else if (onboardingAny?.externalWalletDivider) {
4172
- onboardingAny.externalWalletDivider.style.display = "none";
4173
- }
4174
- } }) }));
4367
+ }, onShowMoreWallets: handleShowMoreWallets, onHideMoreWallets: handleHideMoreWallets }) }));
4175
4368
  externalWalletMountedRef.current = true;
4369
+ if (isExternalWalletListOpen()) {
4370
+ handleShowMoreWallets();
4371
+ }
4176
4372
  }
4177
4373
  catch (error) {
4178
4374
  console.error("Failed to mount ExternalWalletButtons:", error);
@@ -4216,140 +4412,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
4216
4412
  try {
4217
4413
  externalWalletRootRef.current.render(_jsx(AbstraxnContext.Provider, { value: valueRef.current, children: _jsx(ExternalWalletButtons, { onConnect: async (_connectorId, _address) => {
4218
4414
  // console.log('External wallet connected:', connectorId, address);
4219
- }, onShowMoreWallets: () => {
4220
- // Hide email and Google sections when "More wallets" is clicked
4221
- const onboardingAny = onboardingRef.current;
4222
- if (onboardingAny?.emailForm) {
4223
- onboardingAny.emailForm.style.display = "none";
4224
- }
4225
- if (onboardingAny?.googleButton) {
4226
- onboardingAny.googleButton.style.display = "none";
4227
- }
4228
- // Hide Discord and X (Twitter) buttons when wallet selection screen is shown
4229
- if (onboardingAny?.discordButton) {
4230
- onboardingAny.discordButton.style.display = "none";
4231
- }
4232
- if (onboardingAny?.twitterButton) {
4233
- onboardingAny.twitterButton.style.display = "none";
4234
- }
4235
- // Hide social grid if it exists
4236
- if (onboardingAny?.socialGrid) {
4237
- onboardingAny.socialGrid.style.display = "none";
4238
- }
4239
- // Extra guard: hide any social icon buttons by class
4240
- if (onboardingAny?.rootElement) {
4241
- onboardingAny.rootElement
4242
- .querySelectorAll(".onboarding-button-social-icon, .onboarding-button-social")
4243
- .forEach((el) => {
4244
- el.style.display = "none";
4245
- });
4246
- }
4247
- // Hide all passkey elements when wallet selection screen is shown
4248
- if (onboardingAny?.passkeyLoginButton) {
4249
- onboardingAny.passkeyLoginButton.style.display = "none";
4250
- }
4251
- if (onboardingAny?.passkeySignupLink) {
4252
- onboardingAny.passkeySignupLink.style.display = "none";
4253
- }
4254
- if (onboardingAny?.passkeyErrorElement) {
4255
- onboardingAny.passkeyErrorElement.style.display = "none";
4256
- }
4257
- if (onboardingAny?.passkeyDivider) {
4258
- onboardingAny.passkeyDivider.style.display = "none";
4259
- }
4260
- if (onboardingAny?.externalWalletDivider) {
4261
- onboardingAny.externalWalletDivider.style.display = "none";
4262
- }
4263
- // Also hide the divider between email and Google if it exists
4264
- if (onboardingAny?.divider) {
4265
- onboardingAny.divider.style.display = "none";
4266
- }
4267
- }, onHideMoreWallets: () => {
4268
- // Show email and Google sections when back is clicked
4269
- const onboardingAny = onboardingRef.current;
4270
- const authMethods = onboardingAny?.config?.authMethods || [
4271
- "otp",
4272
- "google",
4273
- ];
4274
- const showEmail = authMethods.includes("otp");
4275
- const showGoogle = authMethods.includes("google");
4276
- const showTwitter = authMethods.includes("twitter");
4277
- const showDiscord = authMethods.includes("discord");
4278
- const showPasskey = authMethods.includes("passkey");
4279
- if (onboardingAny?.emailForm) {
4280
- onboardingAny.emailForm.style.display = showEmail ? "" : "none";
4281
- }
4282
- if (onboardingAny?.googleButton) {
4283
- onboardingAny.googleButton.style.display = showGoogle
4284
- ? ""
4285
- : "none";
4286
- }
4287
- // Show Discord and X (Twitter) buttons if enabled
4288
- if (onboardingAny?.discordButton) {
4289
- onboardingAny.discordButton.style.display = showDiscord
4290
- ? ""
4291
- : "none";
4292
- }
4293
- if (onboardingAny?.twitterButton) {
4294
- onboardingAny.twitterButton.style.display = showTwitter
4295
- ? ""
4296
- : "none";
4297
- }
4298
- // Show social grid if it exists and social methods are enabled
4299
- if (onboardingAny?.socialGrid) {
4300
- const hasSocialMethods = showGoogle || showTwitter || showDiscord;
4301
- onboardingAny.socialGrid.style.display = hasSocialMethods
4302
- ? ""
4303
- : "none";
4304
- }
4305
- // Extra guard: restore social icon buttons by class based on flags
4306
- if (onboardingAny?.rootElement) {
4307
- const hasSocialMethods = showGoogle || showTwitter || showDiscord;
4308
- onboardingAny.rootElement
4309
- .querySelectorAll(".onboarding-button-social-icon, .onboarding-button-social")
4310
- .forEach((el) => {
4311
- el.style.display = hasSocialMethods
4312
- ? ""
4313
- : "none";
4314
- });
4315
- }
4316
- // Show passkey controls if enabled
4317
- if (onboardingAny?.passkeyLoginButton) {
4318
- onboardingAny.passkeyLoginButton.style.display = showPasskey
4319
- ? ""
4320
- : "none";
4321
- }
4322
- if (onboardingAny?.passkeySignupLink) {
4323
- onboardingAny.passkeySignupLink.style.display = showPasskey
4324
- ? ""
4325
- : "none";
4326
- }
4327
- if (onboardingAny?.passkeyErrorElement) {
4328
- onboardingAny.passkeyErrorElement.style.display = "none";
4329
- }
4330
- // Show passkey divider if passkey is enabled and there are other auth methods
4331
- if (onboardingAny?.passkeyDivider) {
4332
- onboardingAny.passkeyDivider.style.display =
4333
- showPasskey &&
4334
- (showEmail || showGoogle || showTwitter || showDiscord)
4335
- ? ""
4336
- : "none";
4337
- }
4338
- // Show divider between email and social methods if both are enabled
4339
- if (onboardingAny?.divider) {
4340
- const hasSocialMethods = showGoogle || showTwitter || showDiscord;
4341
- onboardingAny.divider.style.display =
4342
- showEmail && hasSocialMethods ? "" : "none";
4343
- }
4344
- if (onboardingAny?.externalWalletDivider) {
4345
- onboardingAny.externalWalletDivider.style.display = "";
4346
- }
4347
- } }) }));
4415
+ }, onShowMoreWallets: handleShowMoreWallets, onHideMoreWallets: handleHideMoreWallets }) }));
4348
4416
  }
4349
4417
  catch (error) {
4350
4418
  console.warn("Failed to update ExternalWalletButtons:", error);
4351
4419
  }
4352
4420
  }, [value, externalWalletsEnabled]);
4353
- return (_jsxs(AbstraxnContext.Provider, { value: value, children: [wagmi ? (_jsx(WagmiConnectionEffectSync, { onDisconnect: handleWagmiDisconnect })) : null, children] }));
4421
+ return (_jsxs(AbstraxnContext.Provider, { value: value, children: [wagmi ? (_jsx(WagmiConnectionEffectSync, { onDisconnect: handleWagmiDisconnect })) : null, children, _jsx(SignMfaModalHost, {})] }));
4354
4422
  }
4355
4423
  //# sourceMappingURL=AbstraxnProviderInner.js.map