@abstraxn/signer-react 3.1.3 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,16 +5,40 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [3.1.3] - 2026-03-31
8
+ ## [3.2.0] - 2026-04-22
9
+
10
+ ### Added
11
+
12
+ - **Account Abstraction owner hook** – Added `useSmartAccountOwner()` to expose a viem-compatible custom local account for AA integrations (e.g. permissionless). The hook returns `{ smartAccountOwner, isReady, address }`, supports optional address override, and is exported from `@abstraxn/signer-react`.
13
+ - **EIP-7702 authorization signing support** – `smartAccountOwner` now implements `signAuthorization(...)` so 7702 flows can sign delegation authorizations through the SDK backend path.
9
14
 
10
15
  ### Changed
11
16
 
12
- - **Embedded-only wagmi context** – When `externalWallets.enabled` is `false`, the provider still mounts `QueryClientProvider` and `WagmiProvider` using a **connector-free** wagmi config (`createWagmiConfig(..., embedOnly: true)`), so hooks like `useConfig`, `useAccount`, and unified chain switching keep working for email/social embedded flows without registering injected/MetaMask/WalletConnect connectors. If React Query is missing, the error message now describes wagmi hook requirements instead of blaming disabled external wallets.
13
- - **`createWagmiConfig` embed mode** – New optional `embedOnly` flag builds a minimal wagmi `Config` (empty `connectors`, shared HTTP transports and storage) for embedded-only usage; chain list must still be non-empty.
17
+ - **AA signing now uses backend signing APIs** – AA owner signing routes through existing authenticated backend signing methods (`signTransactionViaAPI` / `signTypedTxViaAPI`) instead of requiring app-level Turnkey account creation.
18
+ - **UserOperation message signing compatibility** – `signMessage` in AA owner flow now follows viem EIP-191 semantics (`hashMessage(...)` then sign digest) to match permissionless/simple account UserOperation signature expectations.
19
+ - **7702 payload encoding alignment** – 7702 authorization signing uses Turnkey-specific parameters:
20
+ - `encoding: PAYLOAD_ENCODING_EIP7702_AUTHORIZATION`
21
+ - `hashFunction: HASH_FUNCTION_NOT_APPLICABLE`
14
22
 
15
- ### Fixed
23
+ ### Documentation
24
+
25
+ - **AA docs update** – Added `useSmartAccountOwner()` to hook list and expanded AA integration example in README.
26
+ - **Abstraxn infra example** – Updated README AA sample to use Abstraxn bundler/paymaster endpoints and dynamic chain-based URL construction.
27
+
28
+ ## [3.1.4] - 2026-04-06
29
+
30
+ ### Changed
31
+
32
+ - **Passkey completion via login code (`@abstraxn/signer-core`)** – Passkey signup and login now expect a `loginCode` (and optional `mfaRequired`) from the backend, then exchange it through `POST /auth/exchange` before tokens and user state are finalized. Passkey responses no longer persist access/refresh tokens directly from those calls.
33
+ - **Auth route prefixes** – OTP init/verify and passkey signup/login client paths use `/auth/otp/...` and `/auth/passkey/...` instead of `/login/otp/...` and `/login/passkey/...`.
34
+ - **Passkey login stamp** – The WebAuthn/Turnkey stamp is serialized and sent as a JSON string in the passkey login request, matching the updated API contract.
35
+ - **Passkey signup payload** – `targetPublicKey` is optional when a device public key is not available; session completion merges profile fields from the passkey API response where the JWT omits them (e.g. `isPolicy`, `publicKey`, `authProvider`).
36
+ - **MFA after passkey** – `AbstraxnWallet.loginWithPasskey` / `signupWithPasskey` and the React provider defer `connect()` when MFA is still required; `AuthManager` tracks `pendingSessionAuthMethod: 'passkey'` so post-MFA session setup uses the correct path.
37
+
38
+ ### Added
16
39
 
17
- - **`useExternalWalletInfo().switchChain`** – Returns the unified `switchChain` implementation (embedded + external) instead of only the external-wallet switcher, so chain changes behave consistently when external wallets are off.
40
+ - **`PasskeyFlowResult` and onboarding hooks** – `onPasskeySignup` / `onPasskeyLogin` may return `{ mfaRequired: true }`; `onPasskeyPendingMfa` lets UIs jump to the MFA step (`OnboardingUIReact`, `OnboardingUIWeb`, and `useAuthMethods`). `PasskeyFlowResult` is re-exported from `@abstraxn/signer-core`.
41
+ - **Server logout on full disconnect** – When the Abstraxn session disconnect runs, the SDK best-effort calls `POST /auth/logout` with the current access token (external-wallet-only disconnect continues to skip this).
18
42
 
19
43
  ## [3.1.2] - 2026-03-27
20
44
 
package/README.md CHANGED
@@ -105,6 +105,78 @@ function HookConnectButton() {
105
105
  - `useExportWallet()` - Export wallet (EVM or Solana, based on current chain)
106
106
  - `usePublicClient()` / `usePrepareRawTxn()` / `useSignTxn()` / `useSignAndSendTxn()` / `useWaitForTxnReceipt()` - EVM transaction flow (prepare → sign → send → confirm)
107
107
  - `useSolanaConnection()` / `useSolanaPublicKey()` / `usePrepareSolanaTxn()` / `useSignSolanaTxn()` / `useSignAndSendSolanaTxn()` / `useWaitForSolanaConfirmation()` - Solana transaction flow (prepare → sign → send → confirm)
108
+ - `useSmartAccountOwner()` - Get an AA-compatible owner account for permissionless-style account abstraction flows
109
+
110
+ ### Example: Account Abstraction owner (permissionless-compatible)
111
+
112
+ ```tsx
113
+ import { useMemo } from 'react';
114
+ import { useSmartAccountOwner } from '@abstraxn/signer-react';
115
+ import { createPublicClient, createWalletClient, http } from 'viem';
116
+ import { sepolia } from 'viem/chains';
117
+ import { toSimpleSmartAccount } from 'permissionless/accounts';
118
+ import {
119
+ createBundlerClient,
120
+ createPaymasterClient,
121
+ entryPoint07Address,
122
+ } from 'viem/account-abstraction';
123
+
124
+ export function useAaClient({
125
+ chain = sepolia,
126
+ rpcUrl = 'https://sepolia.rpc.thirdweb.com',
127
+ abstraxnApiKey,
128
+ }: {
129
+ chain?: Chain;
130
+ rpcUrl?: string;
131
+ abstraxnApiKey: string;
132
+ }) {
133
+ const { smartAccountOwner, isReady } = useSmartAccountOwner();
134
+
135
+ return useMemo(async () => {
136
+ if (!isReady || !smartAccountOwner) {
137
+ return null;
138
+ }
139
+
140
+ const bundlerUrl = `https://bundler.abstraxn.com/api/v1/${chainId}/?apikey=${abstraxnApiKey}`;
141
+ const paymasterUrl = `https://paymaster.abstraxn.com/api/v2/${chainId}/?apikey=${abstraxnApiKey}`;
142
+
143
+ const publicClient = createPublicClient({
144
+ chain,
145
+ transport: http(rpcUrl),
146
+ });
147
+
148
+ // permissionless expects a wallet client owner
149
+ const ownerWalletClient = createWalletClient({
150
+ account: smartAccountOwner,
151
+ chain,
152
+ transport: http(rpcUrl),
153
+ });
154
+
155
+ const paymasterClient = createPaymasterClient({
156
+ transport: http(paymasterUrl),
157
+ });
158
+
159
+ const simpleSmartAccount = await toSimpleSmartAccount({
160
+ owner: ownerWalletClient,
161
+ client: publicClient,
162
+ entryPoint: {
163
+ address: entryPoint07Address,
164
+ version: '0.7',
165
+ },
166
+ });
167
+
168
+ return createBundlerClient({
169
+ client: publicClient,
170
+ transport: http(bundlerUrl),
171
+ });
172
+ }, [isReady, smartAccountOwner, chain, rpcUrl, abstraxnApiKey]);
173
+ }
174
+ ```
175
+
176
+ Notes:
177
+ - `permissionless` and AA provider clients are app-level dependencies (not bundled in `@abstraxn/signer-react`).
178
+ - The owner returned by `useSmartAccountOwner()` is signer-only; you control chain, bundler, paymaster, and entry point in your app.
179
+ - After creating the `bundlerClient`, call `sendUserOperation({ account: simpleSmartAccount, calls: [...], paymaster: paymasterClient })`.
108
180
 
109
181
  ### Example: EVM → Solana flow after social/email login
110
182
 
@@ -531,6 +531,52 @@
531
531
  color: #6b7280;
532
532
  }
533
533
 
534
+ .wallet-modal-asset-balance-skeleton {
535
+ display: inline-block;
536
+ width: 140px;
537
+ max-width: 100%;
538
+ height: 14px;
539
+ border-radius: 999px;
540
+ background: linear-gradient(90deg, rgba(156, 163, 175, 0.22), rgba(156, 163, 175, 0.44), rgba(156, 163, 175, 0.22));
541
+ background-size: 200% 100%;
542
+ animation: wallet-modal-balance-shimmer 1.2s ease-in-out infinite;
543
+ }
544
+
545
+ .wallet-modal-theme-dark .wallet-modal-asset-balance-skeleton {
546
+ background: linear-gradient(90deg, rgba(75, 85, 99, 0.3), rgba(107, 114, 128, 0.55), rgba(75, 85, 99, 0.3));
547
+ background-size: 200% 100%;
548
+ }
549
+
550
+ .wallet-modal-asset-explorer-link {
551
+ display: inline-flex;
552
+ width: fit-content;
553
+ margin-top: 2px;
554
+ font-size: 12px;
555
+ font-weight: 500;
556
+ text-decoration: none;
557
+ }
558
+
559
+ .wallet-modal-theme-dark .wallet-modal-asset-explorer-link {
560
+ color: #a855f7;
561
+ }
562
+
563
+ .wallet-modal-theme-light .wallet-modal-asset-explorer-link {
564
+ color: #9333ea;
565
+ }
566
+
567
+ .wallet-modal-asset-explorer-link:hover {
568
+ text-decoration: underline;
569
+ }
570
+
571
+ @keyframes wallet-modal-balance-shimmer {
572
+ 0% {
573
+ background-position: 100% 0;
574
+ }
575
+ 100% {
576
+ background-position: -100% 0;
577
+ }
578
+ }
579
+
534
580
  .wallet-modal-asset-status {
535
581
  position: absolute;
536
582
  bottom: 0;
@@ -36,6 +36,7 @@ export function WalletModal({ isOpen, onClose, onRampUrl }) {
36
36
  const [showSendModal, setShowSendModal] = useState(false);
37
37
  const [showManageModal, setShowManageModal] = useState(false);
38
38
  const [showChainSelector, setShowChainSelector] = useState(false);
39
+ const [isBalanceRefreshing, setIsBalanceRefreshing] = useState(false);
39
40
  const [selectedChainType, setSelectedChainType] = useState("evm");
40
41
  // Export states
41
42
  const [showExportWarning, setShowExportWarning] = useState(false);
@@ -59,19 +60,30 @@ export function WalletModal({ isOpen, onClose, onRampUrl }) {
59
60
  // If no chain selected, show default
60
61
  if (!balance || balance === 0n)
61
62
  return "0";
62
- return formatBalance(balance, 18, "ETH");
63
+ return formatBalance(balance, 18, "ETH", 4);
63
64
  }
64
65
  const symbol = currentChain.nativeCurrency?.symbol || "ETH";
65
66
  const decimals = currentChain.nativeCurrency?.decimals || 18;
66
67
  if (!balance || balance === 0n) {
67
68
  return `0 ${symbol}`;
68
69
  }
69
- return formatBalance(balance, decimals, symbol);
70
+ return formatBalance(balance, decimals, symbol, 4);
70
71
  }, [currentChain, externalWalletBalance, walletBalance]);
72
+ const chainExplorer = useMemo(() => {
73
+ if (!currentChain)
74
+ return null;
75
+ const url = currentChain.blockExplorer?.url || currentChain.explorerUrl;
76
+ if (!url)
77
+ return null;
78
+ return {
79
+ url,
80
+ label: currentChain.blockExplorer?.name || "Explorer",
81
+ };
82
+ }, [currentChain]);
71
83
  // Refetch balance from RPC when UI screen changes (back button, modal open/close, etc.)
72
84
  useEffect(() => {
73
85
  if (isOpen && refetchBalance) {
74
- refetchBalance();
86
+ void refetchBalance();
75
87
  }
76
88
  }, [isOpen, showReceiveModal, showSendModal, showManageModal, showChainSelector, refetchBalance]);
77
89
  /** Load MFA status once per session (in-memory) when this wallet modal opens; not persisted. */
@@ -300,7 +312,7 @@ export function WalletModal({ isOpen, onClose, onRampUrl }) {
300
312
  return (_jsx(ManageWalletModal, { isOpen: true, onClose: () => setShowManageModal(false), displayAddress: displayAddress, userEmail: user?.email, organizationName: whoami?.organizationName, isExternalWalletConnected: isExternalWalletConnected || false, onExportPrivateKey: handleExportPrivateKey, theme: theme }));
301
313
  }
302
314
  // Main Modal
303
- return (_jsxs(_Fragment, { children: [_jsx("div", { "data-abstraxn-sdk-root": true, className: `wallet-modal-overlay wallet-modal-theme-${theme}`, onClick: showChainSelector ? undefined : onClose, style: showChainSelector ? { pointerEvents: "none" } : undefined, children: _jsxs("div", { className: `wallet-modal-content wallet-modal-theme-${theme} ${showChainSelector ? "selector-open" : ""}`, onClick: (e) => e.stopPropagation(), style: showChainSelector ? { pointerEvents: "auto" } : undefined, children: [_jsxs("div", { className: "wallet-modal-header", children: [_jsx("div", { className: "wallet-modal-header-left", children: _jsxs("div", { className: "wallet-modal-user-info", children: [_jsx("div", { className: "wallet-modal-avatar", children: _jsx(UserAvatar, { user: user, isExternalWalletConnected: isExternalWalletConnected || false }) }), _jsxs("div", { className: "wallet-modal-user-details", children: [_jsxs("div", { className: "wallet-modal-address", onClick: handleCopyAddress, children: [formatAddress(displayAddress || address), copied ? (_jsx("span", { className: "wallet-modal-copy-notification", children: "Copied!" })) : (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "wallet-modal-copy-icon", children: [_jsx("path", { d: "M5.5 3.5H3.5C2.67157 3.5 2 4.17157 2 5V12.5C2 13.3284 2.67157 14 3.5 14H11C11.8284 14 12.5 13.3284 12.5 12.5V10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M5.5 3.5C5.5 2.67157 6.17157 2 7 2H12.5C13.3284 2 14 2.67157 14 3.5V9C14 9.82843 13.3284 10.5 12.5 10.5H7C6.17157 10.5 5.5 9.82843 5.5 9V3.5Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }))] }), user?.email && (_jsx("div", { className: "wallet-modal-email", children: user.email }))] })] }) }), _jsx("div", { className: "wallet-modal-header-right", children: _jsx("button", { className: "wallet-modal-close", onClick: onClose, "aria-label": "Close", children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })] }), !isExternalWalletConnected && (_jsxs("div", { className: "wallet-modal-actions", children: [_jsxs("button", { className: "wallet-modal-action-btn", onClick: handleSend, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), _jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }), _jsx("span", { children: "Send" })] }), _jsxs("button", { className: "wallet-modal-action-btn", onClick: handleReceive, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), _jsx("polyline", { points: "7 10 12 15 17 10" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })] }), _jsx("span", { children: "Receive" })] }), _jsxs("button", { className: "wallet-modal-action-btn", onClick: handleBuy, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), _jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }), _jsx("span", { children: "Buy" })] })] })), !isExternalWalletConnected && currentChain && (_jsxs("div", { className: "wallet-modal-asset", onClick: () => setShowChainSelector(true), children: [_jsxs("div", { className: "wallet-modal-asset-info", children: [_jsxs("div", { className: "wallet-modal-asset-icon-wrapper", children: [currentChain.iconUrl ? (_jsx("img", { src: currentChain.iconUrl, alt: currentChain.displayName, className: "wallet-modal-asset-icon-img" })) : (_jsx("div", { className: "wallet-modal-asset-icon", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 2v20M2 12h20" })] }) })), _jsx("span", { className: "wallet-modal-asset-status" })] }), _jsxs("div", { className: "wallet-modal-asset-details", children: [_jsx("div", { className: "wallet-modal-asset-name", children: currentChain.displayName }), _jsx("div", { className: "wallet-modal-asset-balance", children: balanceDisplay })] })] }), _jsx("div", { className: "wallet-modal-asset-arrow", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "9 18 15 12 9 6" }) }) })] })), _jsxs("div", { className: "wallet-modal-menu", children: [_jsxs("button", { className: "wallet-modal-menu-item", onClick: handleManageWallet, children: [_jsx(LuWallet, { size: 20 }), _jsx("span", { children: "Manage Wallet" })] }), _jsxs("button", { className: "wallet-modal-menu-item wallet-modal-menu-item-disconnect", onClick: handleDisconnect, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [_jsx("path", { d: "M7 3H4C3.44772 3 3 3.44772 3 4V16C3 16.5523 3.44772 17 4 17H7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M13 7L17 10L13 13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M17 10H9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { children: "Disconnect" })] })] })] }) }), showChainSelector && (_jsx("div", { "data-abstraxn-sdk-root": true, className: `wallet-modal-overlay wallet-modal-theme-${theme}`, style: { zIndex: 10006, pointerEvents: "auto" }, onClick: (e) => {
315
+ return (_jsxs(_Fragment, { children: [_jsx("div", { "data-abstraxn-sdk-root": true, className: `wallet-modal-overlay wallet-modal-theme-${theme}`, onClick: showChainSelector ? undefined : onClose, style: showChainSelector ? { pointerEvents: "none" } : undefined, children: _jsxs("div", { className: `wallet-modal-content wallet-modal-theme-${theme} ${showChainSelector ? "selector-open" : ""}`, onClick: (e) => e.stopPropagation(), style: showChainSelector ? { pointerEvents: "auto" } : undefined, children: [_jsxs("div", { className: "wallet-modal-header", children: [_jsx("div", { className: "wallet-modal-header-left", children: _jsxs("div", { className: "wallet-modal-user-info", children: [_jsx("div", { className: "wallet-modal-avatar", children: _jsx(UserAvatar, { user: user, isExternalWalletConnected: isExternalWalletConnected || false }) }), _jsxs("div", { className: "wallet-modal-user-details", children: [_jsxs("div", { className: "wallet-modal-address", onClick: handleCopyAddress, children: [formatAddress(displayAddress || address), copied ? (_jsx("span", { className: "wallet-modal-copy-notification", children: "Copied!" })) : (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "wallet-modal-copy-icon", children: [_jsx("path", { d: "M5.5 3.5H3.5C2.67157 3.5 2 4.17157 2 5V12.5C2 13.3284 2.67157 14 3.5 14H11C11.8284 14 12.5 13.3284 12.5 12.5V10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M5.5 3.5C5.5 2.67157 6.17157 2 7 2H12.5C13.3284 2 14 2.67157 14 3.5V9C14 9.82843 13.3284 10.5 12.5 10.5H7C6.17157 10.5 5.5 9.82843 5.5 9V3.5Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }))] }), user?.email && (_jsx("div", { className: "wallet-modal-email", children: user.email }))] })] }) }), _jsx("div", { className: "wallet-modal-header-right", children: _jsx("button", { className: "wallet-modal-close", onClick: onClose, "aria-label": "Close", children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })] }), !isExternalWalletConnected && (_jsxs("div", { className: "wallet-modal-actions", children: [_jsxs("button", { className: "wallet-modal-action-btn", onClick: handleSend, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), _jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }), _jsx("span", { children: "Send" })] }), _jsxs("button", { className: "wallet-modal-action-btn", onClick: handleReceive, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), _jsx("polyline", { points: "7 10 12 15 17 10" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })] }), _jsx("span", { children: "Receive" })] }), _jsxs("button", { className: "wallet-modal-action-btn", onClick: handleBuy, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), _jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }), _jsx("span", { children: "Buy" })] })] })), !isExternalWalletConnected && currentChain && (_jsxs("div", { className: "wallet-modal-asset", onClick: () => setShowChainSelector(true), children: [_jsxs("div", { className: "wallet-modal-asset-info", children: [_jsxs("div", { className: "wallet-modal-asset-icon-wrapper", children: [currentChain.iconUrl ? (_jsx("img", { src: currentChain.iconUrl, alt: currentChain.displayName, className: "wallet-modal-asset-icon-img" })) : (_jsx("div", { className: "wallet-modal-asset-icon", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 2v20M2 12h20" })] }) })), _jsx("span", { className: "wallet-modal-asset-status" })] }), _jsxs("div", { className: "wallet-modal-asset-details", children: [_jsx("div", { className: "wallet-modal-asset-name", children: currentChain.displayName }), _jsx("div", { className: "wallet-modal-asset-balance", children: isBalanceRefreshing ? (_jsx("span", { className: "wallet-modal-asset-balance-skeleton", "aria-label": "Loading balance" })) : (balanceDisplay) }), chainExplorer?.url && (_jsx("a", { href: chainExplorer.url, target: "_blank", rel: "noopener noreferrer", className: "wallet-modal-asset-explorer-link", onClick: (e) => e.stopPropagation(), children: chainExplorer.label }))] })] }), _jsx("div", { className: "wallet-modal-asset-arrow", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "9 18 15 12 9 6" }) }) })] })), _jsxs("div", { className: "wallet-modal-menu", children: [_jsxs("button", { className: "wallet-modal-menu-item", onClick: handleManageWallet, children: [_jsx(LuWallet, { size: 20 }), _jsx("span", { children: "Manage Wallet" })] }), _jsxs("button", { className: "wallet-modal-menu-item wallet-modal-menu-item-disconnect", onClick: handleDisconnect, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [_jsx("path", { d: "M7 3H4C3.44772 3 3 3.44772 3 4V16C3 16.5523 3.44772 17 4 17H7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M13 7L17 10L13 13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M17 10H9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { children: "Disconnect" })] })] })] }) }), showChainSelector && (_jsx("div", { "data-abstraxn-sdk-root": true, className: `wallet-modal-overlay wallet-modal-theme-${theme}`, style: { zIndex: 10006, pointerEvents: "auto" }, onClick: (e) => {
304
316
  e.stopPropagation();
305
317
  setShowChainSelector(false);
306
318
  }, children: _jsx("div", { className: `wallet-modal-content wallet-modal-theme-${theme}`, onClick: (e) => e.stopPropagation(), children: _jsx(ChainSelector, { isOpen: showChainSelector, onClose: () => setShowChainSelector(false), currentChain: currentChain || null, availableChains: availableChains || [], onChainSelect: async (chainId) => {
@@ -317,7 +329,11 @@ export function WalletModal({ isOpen, onClose, onRampUrl }) {
317
329
  // Switch to a different chain
318
330
  if (switchChainEnhanced) {
319
331
  try {
332
+ setIsBalanceRefreshing(true);
320
333
  await switchChainEnhanced(chainId);
334
+ if (refetchBalance) {
335
+ await refetchBalance();
336
+ }
321
337
  // Only close if switch was successful
322
338
  setShowChainSelector(false);
323
339
  }
@@ -325,6 +341,9 @@ export function WalletModal({ isOpen, onClose, onRampUrl }) {
325
341
  console.error("Failed to switch chain:", error);
326
342
  // Don't close the selector on error so user can try again
327
343
  }
344
+ finally {
345
+ setIsBalanceRefreshing(false);
346
+ }
328
347
  }
329
348
  else {
330
349
  console.warn("switchChainEnhanced not available");
@@ -168,67 +168,38 @@ export function AbstraxnProvider({ config, children }) {
168
168
  externalSsr,
169
169
  config.wagmiConfig,
170
170
  ]);
171
- // Connector-free wagmi config so `useConfig` / `useAccount` / etc. never throw when
172
- // `externalWallets.enabled` is false (embedded email/social only). React Query is required
173
- // for WagmiProvider the same as for the full external-wallet tree.
174
- const { embedWagmiConfig, embedConfigError } = useMemo(() => {
175
- if (externalWalletsEnabled) {
176
- return { embedWagmiConfig: null, embedConfigError: null };
177
- }
178
- try {
179
- const cfg = createWagmiConfig(wagmiChains, undefined, undefined, wagmiThemeRef.current, externalSsr, true);
180
- return { embedWagmiConfig: cfg, embedConfigError: null };
181
- }
182
- catch (error) {
183
- console.error("Failed to create embed-only wagmi config:", error);
184
- return {
185
- embedWagmiConfig: null,
186
- embedConfigError: error instanceof Error
187
- ? error
188
- : new Error("Failed to create embed-only wagmi config"),
189
- };
190
- }
191
- }, [externalWalletsEnabled, chainsKey, externalSsr]);
192
- // Embedded-only mode: still mount WagmiProvider (no connectors) when React Query is available.
193
- if (!externalWalletsEnabled) {
171
+ // If external wallets are enabled, wrap with QueryClientProvider and WagmiProvider
172
+ if (externalWalletsEnabled) {
173
+ // Check if React Query is available BEFORE doing anything else
174
+ // This prevents errors from happening in the first place
194
175
  const queryCheck = canUseReactQuery();
195
176
  if (!queryCheck.canUse) {
196
- console.error("❌ Wagmi hooks require @tanstack/react-query. Install it or wrap your app with QueryClientProvider.\n" +
177
+ console.error("❌ External wallets are disabled because React Query is not available.\n" +
197
178
  `Reason: ${queryCheck.reason || "Unknown"}\n` +
198
- "Rendering without WagmiProvider; calls to useConfig may throw.");
179
+ "Please install @tanstack/react-query@^5.90.16 and ensure there is only one React instance.\n" +
180
+ "Falling back to Abstraxn wallet only.");
199
181
  return (_jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }));
200
182
  }
201
- if (embedConfigError || !embedWagmiConfig) {
202
- if (embedConfigError) {
203
- console.error("Failed to create embed-only wagmi config:", embedConfigError);
204
- }
183
+ if (configError) {
184
+ console.error("Failed to create wagmi config:", configError);
185
+ // Fallback to provider without wagmi if config creation fails
205
186
  return (_jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }));
206
187
  }
188
+ if (!wagmiConfig) {
189
+ // Show loading state while config is being created
190
+ // Don't render AbstraxnProviderInner until wagmiConfig is ready
191
+ return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: "none" }, children: "Loading wallet connectors..." }) }));
192
+ }
193
+ // ✅ For SSR mode, wait for hydration before rendering WagmiProvider
194
+ // This ensures injected wallets (Phantom, etc.) are detected properly
207
195
  if (externalSsr && !isHydrated) {
208
- return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: "none" }, children: "Hydrating..." }) }));
196
+ return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: "none" }, children: "Hydrating wallet connectors..." }) }));
209
197
  }
210
- return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx(WagmiProvider, { config: embedWagmiConfig, children: _jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }) }) }));
211
- }
212
- // External wallets enabled: full connectors + Solana adapter tree
213
- // Check if React Query is available BEFORE doing anything else
214
- const queryCheck = canUseReactQuery();
215
- if (!queryCheck.canUse) {
216
- console.error("❌ External wallets are disabled because React Query is not available.\n" +
217
- `Reason: ${queryCheck.reason || "Unknown"}\n` +
218
- "Please install @tanstack/react-query@^5.90.16 and ensure there is only one React instance.\n" +
219
- "Falling back to Abstraxn wallet only.");
220
- return (_jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }));
221
- }
222
- if (configError) {
223
- console.error("Failed to create wagmi config:", configError);
224
- return (_jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }));
225
- }
226
- if (!wagmiConfig) {
227
- return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: "none" }, children: "Loading wallet connectors..." }) }));
228
- }
229
- if (externalSsr && !isHydrated) {
230
- return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx("div", { style: { display: "none" }, children: "Hydrating wallet connectors..." }) }));
198
+ // Do NOT pass key to WagmiProvider. Remounting loses connection state and causes
199
+ // "Connector not connected" in Next.js (e.g. when config loads after hydration).
200
+ return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx(ConnectionProvider, { endpoint: config.chains?.solanaEndpoint || "https://api.mainnet-beta.solana.com", children: _jsx(WalletProvider, { wallets: solanaWallets, autoConnect: true, children: _jsx(WagmiProvider, { config: wagmiConfig, initialState: config.initialState, children: _jsx(AbstraxnProviderWithWagmi, { config: config, children: children }) }) }) }) }));
231
201
  }
232
- return (_jsx(QueryClientWrapper, { queryClient: queryClient, children: _jsx(ConnectionProvider, { endpoint: config.chains?.solanaEndpoint || "https://api.mainnet-beta.solana.com", children: _jsx(WalletProvider, { wallets: solanaWallets, autoConnect: true, children: _jsx(WagmiProvider, { config: wagmiConfig, initialState: config.initialState, children: _jsx(AbstraxnProviderWithWagmi, { config: config, children: children }) }) }) }) }));
202
+ // If external wallets are disabled, use the provider without wagmi
203
+ return (_jsx(AbstraxnProviderWithoutWagmi, { config: config, children: children }));
233
204
  }
234
205
  //# sourceMappingURL=AbstraxnProvider.js.map
@@ -766,10 +766,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
766
766
  }
767
767
  const authManager = walletInstance.getAuthManager();
768
768
  const user = await authManager.loginWithPasskey();
769
- // Set user immediately so onLoginSuccess can access it
770
769
  setUser(user);
771
- // Connect wallet after successful authentication
770
+ const lastAuthState = authManager.getLastAuthState();
771
+ if (lastAuthState?.mfaRequired === true) {
772
+ return { mfaRequired: true };
773
+ }
772
774
  await walletInstance.connect();
775
+ return undefined;
773
776
  },
774
777
  onPasskeySignup: async () => {
775
778
  if (!walletInstance)
@@ -784,11 +787,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
784
787
  }
785
788
  const authManager = walletInstance.getAuthManager();
786
789
  const user = await authManager.signupWithPasskey();
787
- // Clear any previous errors on successful verification
788
- // setError(null);
789
790
  setUser(user);
790
- // Connect wallet after successful authentication
791
+ const lastAuthState = authManager.getLastAuthState();
792
+ if (lastAuthState?.mfaRequired === true) {
793
+ return { mfaRequired: true };
794
+ }
791
795
  await walletInstance.connect();
796
+ return undefined;
792
797
  },
793
798
  onLoginSuccess: async (_data) => {
794
799
  // Clear any previous errors on successful login
@@ -1605,7 +1610,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1605
1610
  setLoading(false);
1606
1611
  }
1607
1612
  }, [showOnboarding]);
1608
- // Disconnect wallet
1613
+ // Full disconnect: when Abstraxn is connected, `wallet.disconnect()` runs and signer-core notifies POST /auth/logout; external-wallet-only flows use `disconnectExternalWallet` instead (no core disconnect).
1609
1614
  const disconnect = useCallback(async () => {
1610
1615
  setLoading(true);
1611
1616
  setDisconnecting(true);
@@ -2972,7 +2977,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
2972
2977
  wagmiDisconnect,
2973
2978
  wagmiAccount?.isConnected,
2974
2979
  ]);
2975
- // Disconnect external wallet
2980
+ // External wallet only — does not call AbstraxnWallet.disconnect (no server /auth/logout).
2976
2981
  const disconnectExternalWallet = useCallback(async () => {
2977
2982
  if (!externalWalletsEnabled || !wagmiDisconnect) {
2978
2983
  return;
@@ -3220,6 +3225,60 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3220
3225
  const currentChainId = isExternalWalletConnected && externalWalletChainId
3221
3226
  ? externalWalletChainId
3222
3227
  : chainId;
3228
+ // Custom chains passed via `config.chains` (array form) are not part of the static `EVM_CHAINS` map.
3229
+ // Normalize them into `ChainData` so the wallet UI can resolve chain + balance correctly.
3230
+ const customChainsById = useMemo(() => {
3231
+ const chainConfig = config.chains;
3232
+ const map = new Map();
3233
+ if (!Array.isArray(chainConfig) || chainConfig.length === 0)
3234
+ return map;
3235
+ for (const raw of chainConfig) {
3236
+ if (!raw || typeof raw.id !== "number" || !raw.nativeCurrency)
3237
+ continue;
3238
+ // Extract RPC URL (support viem shape + sdk-integrate custom shape)
3239
+ let rpcUrl;
3240
+ if (raw.rpcUrls?.default?.http) {
3241
+ rpcUrl = Array.isArray(raw.rpcUrls.default.http)
3242
+ ? raw.rpcUrls.default.http[0]
3243
+ : raw.rpcUrls.default.http;
3244
+ }
3245
+ else if (raw.rpcUrls?.public?.http) {
3246
+ rpcUrl = Array.isArray(raw.rpcUrls.public.http)
3247
+ ? raw.rpcUrls.public.http[0]
3248
+ : raw.rpcUrls.public.http;
3249
+ }
3250
+ else if (typeof raw.rpcUrl === "string") {
3251
+ rpcUrl = raw.rpcUrl;
3252
+ }
3253
+ if (!rpcUrl || rpcUrl.trim() === "")
3254
+ continue;
3255
+ const explorerUrl = raw.blockExplorers?.default?.url ||
3256
+ raw.explorerUrl || '';
3257
+ const displayName = raw.displayName || raw.name || `Chain ${raw.id}`;
3258
+ const name = (raw.name ? String(raw.name) : `chain-${raw.id}`)
3259
+ .toLowerCase()
3260
+ .replace(/\s+/g, "-");
3261
+ const isTestnet = raw.testnet === true ||
3262
+ raw.isTestnet === true ||
3263
+ // Heuristic fallback for unknown custom chains
3264
+ (raw.id !== 1 && raw.id !== 10 && raw.id !== 137 && raw.id !== 8453 && raw.id !== 42161 && raw.id !== 2741);
3265
+ const chainData = {
3266
+ id: raw.id,
3267
+ name,
3268
+ displayName,
3269
+ rpcUrl,
3270
+ explorerUrl,
3271
+ iconUrl: raw.iconUrl,
3272
+ icon: raw.icon,
3273
+ nativeCurrency: raw.nativeCurrency,
3274
+ type: "evm",
3275
+ isTestnet,
3276
+ blockExplorer: raw.blockExplorer,
3277
+ };
3278
+ map.set(chainData.id, chainData);
3279
+ }
3280
+ return map;
3281
+ }, [config.chains]);
3223
3282
  // Fetch balance for Abstraxn wallet (not external wallet) - reusable for on-demand refetch
3224
3283
  const refetchWalletBalance = useCallback(async () => {
3225
3284
  if (isExternalWalletConnected || !address || !currentChainId) {
@@ -3227,7 +3286,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3227
3286
  return;
3228
3287
  }
3229
3288
  try {
3230
- const currentChain = getChainById(currentChainId);
3289
+ const currentChain = getChainById(currentChainId) || customChainsById.get(currentChainId);
3231
3290
  if (!currentChain || currentChain.type !== "evm") {
3232
3291
  setWalletBalance(null);
3233
3292
  return;
@@ -3254,24 +3313,35 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3254
3313
  console.error("Failed to fetch balance:", error);
3255
3314
  setWalletBalance(null);
3256
3315
  }
3257
- }, [address, currentChainId, isExternalWalletConnected]);
3316
+ }, [address, currentChainId, isExternalWalletConnected, customChainsById]);
3258
3317
  useEffect(() => {
3259
3318
  refetchWalletBalance();
3260
3319
  }, [refetchWalletBalance]);
3261
3320
  // Single refetch for UI: Abstraxn wallet = RPC call, external wallet = wagmi refetch
3262
- const refetchBalance = useCallback(() => {
3321
+ const refetchBalance = useCallback(async () => {
3263
3322
  if (isExternalWalletConnected && wagmiBalance?.refetch) {
3264
- void wagmiBalance.refetch();
3323
+ await wagmiBalance.refetch();
3265
3324
  return;
3266
3325
  }
3267
- void refetchWalletBalance();
3326
+ await refetchWalletBalance();
3268
3327
  }, [isExternalWalletConnected, wagmiBalance, refetchWalletBalance]);
3269
3328
  // Compute available chains from config - supports both legacy and new format
3270
3329
  const availableChains = useMemo(() => {
3271
3330
  const chains = [];
3272
3331
  const chainConfig = config.chains;
3273
- // Priority 1: Check new chains config format
3274
- if (chainConfig?.supportedEvmChains &&
3332
+ // Priority 1: Check if chains is an array of viem/custom chain objects
3333
+ if (Array.isArray(chainConfig) && chainConfig.length > 0) {
3334
+ for (const raw of chainConfig) {
3335
+ const normalized = raw && typeof raw.id === "number"
3336
+ ? customChainsById.get(raw.id)
3337
+ : undefined;
3338
+ if (normalized)
3339
+ chains.push(normalized);
3340
+ }
3341
+ }
3342
+ // Priority 2: Check new chains config format
3343
+ if (!Array.isArray(chainConfig) &&
3344
+ chainConfig?.supportedEvmChains &&
3275
3345
  chainConfig.supportedEvmChains.length > 0) {
3276
3346
  // Use configured chains from new format
3277
3347
  chainConfig.supportedEvmChains.forEach((chainName) => {
@@ -3281,8 +3351,10 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3281
3351
  }
3282
3352
  });
3283
3353
  }
3284
- // Priority 2: Check legacy supportedChains format
3285
- else if (config.supportedChains && config.supportedChains.length > 0) {
3354
+ // Priority 3: Check legacy supportedChains format
3355
+ else if (!Array.isArray(chainConfig) &&
3356
+ config.supportedChains &&
3357
+ config.supportedChains.length > 0) {
3286
3358
  // Convert legacy Chain format to ChainData
3287
3359
  config.supportedChains.forEach((legacyChain) => {
3288
3360
  // Try to find matching chain by ID
@@ -3308,7 +3380,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3308
3380
  }
3309
3381
  });
3310
3382
  }
3311
- // Priority 3: Default chains only if nothing configured at all
3383
+ // Priority 4: Default chains only if nothing configured at all
3312
3384
  // Only show defaults if both config.chains and config.supportedChains are missing
3313
3385
  else if (!chainConfig && !config.supportedChains) {
3314
3386
  chains.push(EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base);
@@ -3325,13 +3397,15 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3325
3397
  : !chainConfig && !config.supportedChains
3326
3398
  ? [EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base]
3327
3399
  : [];
3328
- }, [config.chains, config.supportedChains]);
3400
+ }, [config.chains, config.supportedChains, customChainsById]);
3329
3401
  // Get current chain data
3330
3402
  const currentChain = useMemo(() => {
3331
3403
  if (!currentChainId)
3332
3404
  return null;
3333
- return getChainById(currentChainId) || null;
3334
- }, [currentChainId]);
3405
+ return (getChainById(currentChainId) ||
3406
+ customChainsById.get(currentChainId) ||
3407
+ null);
3408
+ }, [currentChainId, customChainsById]);
3335
3409
  // Enhanced switchChain that works for both EVM and Solana
3336
3410
  const switchChainEnhanced = useCallback(async (targetChainId) => {
3337
3411
  if (!walletRef.current) {
@@ -3342,7 +3416,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3342
3416
  if (!targetChainInAvailable) {
3343
3417
  throw new Error(`Chain with ID ${targetChainId} is not available. Please configure it in your SDK setup.`);
3344
3418
  }
3345
- const targetChain = getChainById(targetChainId);
3419
+ const targetChain = getChainById(targetChainId) || customChainsById.get(targetChainId);
3346
3420
  if (!targetChain) {
3347
3421
  throw new Error(`Chain with ID ${targetChainId} is not supported`);
3348
3422
  }
@@ -3402,7 +3476,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
3402
3476
  finally {
3403
3477
  setLoading(false);
3404
3478
  }
3405
- }, [isExternalWalletConnected, wagmiSwitchChain, switchChain, availableChains]);
3479
+ }, [
3480
+ isExternalWalletConnected,
3481
+ wagmiSwitchChain,
3482
+ switchChain,
3483
+ availableChains,
3484
+ customChainsById,
3485
+ ]);
3406
3486
  const setEmailForOtp = useCallback((email) => {
3407
3487
  setEmailForOtpState(email);
3408
3488
  }, []);
@@ -351,10 +351,13 @@ export function useWalletInitialization({ config, walletRef, onboardingRef, otpI
351
351
  }
352
352
  const authManager = walletInstance.getAuthManager();
353
353
  const user = await authManager.loginWithPasskey();
354
- // Set user immediately so onLoginSuccess can access it
355
354
  setUser(user);
356
- // Connect wallet after successful authentication
355
+ const lastAuthState = authManager.getLastAuthState();
356
+ if (lastAuthState?.mfaRequired === true) {
357
+ return { mfaRequired: true };
358
+ }
357
359
  await walletInstance.connect();
360
+ return undefined;
358
361
  },
359
362
  onPasskeySignup: async () => {
360
363
  if (!walletInstance)
@@ -369,11 +372,13 @@ export function useWalletInitialization({ config, walletRef, onboardingRef, otpI
369
372
  }
370
373
  const authManager = walletInstance.getAuthManager();
371
374
  const user = await authManager.signupWithPasskey();
372
- // Clear any previous errors on successful verification
373
- // setError(null);
374
375
  setUser(user);
375
- // Connect wallet after successful authentication
376
+ const lastAuthState = authManager.getLastAuthState();
377
+ if (lastAuthState?.mfaRequired === true) {
378
+ return { mfaRequired: true };
379
+ }
376
380
  await walletInstance.connect();
381
+ return undefined;
377
382
  },
378
383
  onLoginSuccess: async (_data) => {
379
384
  // Clear any previous errors on successful login