@openfort/react-native 0.1.20 → 0.1.22

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 (48) hide show
  1. package/dist/components/AuthBoundary.js +4 -1
  2. package/dist/core/index.js +1 -1
  3. package/dist/core/provider.js +20 -7
  4. package/dist/hooks/auth/useEmailAuth.js +108 -8
  5. package/dist/hooks/auth/useGuestAuth.js +16 -6
  6. package/dist/hooks/auth/useOAuth.js +14 -5
  7. package/dist/hooks/auth/useWalletAuth.js +29 -10
  8. package/dist/hooks/core/useOpenfort.js +3 -17
  9. package/dist/hooks/wallet/index.js +4 -2
  10. package/dist/hooks/wallet/solanaProvider.js +77 -0
  11. package/dist/hooks/wallet/useEmbeddedEthereumWallet.js +517 -0
  12. package/dist/hooks/wallet/useEmbeddedSolanaWallet.js +455 -0
  13. package/dist/hooks/wallet/utils.js +75 -0
  14. package/dist/lib/hookConsistency.js +6 -0
  15. package/dist/native/oauth.js +13 -0
  16. package/dist/native/storage.js +4 -0
  17. package/dist/native/webview.js +15 -1
  18. package/dist/types/components/AuthBoundary.d.ts +1 -0
  19. package/dist/types/core/index.d.ts +1 -1
  20. package/dist/types/core/provider.d.ts +20 -6
  21. package/dist/types/hooks/auth/useEmailAuth.d.ts +24 -12
  22. package/dist/types/hooks/auth/useGuestAuth.d.ts +17 -8
  23. package/dist/types/hooks/auth/useOAuth.d.ts +15 -7
  24. package/dist/types/hooks/auth/useWalletAuth.d.ts +29 -10
  25. package/dist/types/hooks/core/useOpenfort.d.ts +2 -13
  26. package/dist/types/hooks/wallet/index.d.ts +2 -1
  27. package/dist/types/hooks/wallet/solanaProvider.d.ts +75 -0
  28. package/dist/types/hooks/wallet/useEmbeddedEthereumWallet.d.ts +104 -0
  29. package/dist/types/hooks/wallet/useEmbeddedSolanaWallet.d.ts +111 -0
  30. package/dist/types/hooks/wallet/utils.d.ts +17 -0
  31. package/dist/types/index.js +1 -2
  32. package/dist/types/lib/hookConsistency.d.ts +6 -0
  33. package/dist/types/native/oauth.d.ts +13 -0
  34. package/dist/types/native/storage.d.ts +4 -0
  35. package/dist/types/native/webview.d.ts +14 -0
  36. package/dist/types/types/auth.d.ts +0 -41
  37. package/dist/types/types/index.d.ts +3 -30
  38. package/dist/types/types/oauth.d.ts +0 -38
  39. package/dist/types/types/wallet.d.ts +120 -216
  40. package/package.json +1 -1
  41. package/dist/hooks/auth/useCreateWalletPostAuth.js +0 -34
  42. package/dist/hooks/wallet/useWallets.js +0 -436
  43. package/dist/types/config.js +0 -1
  44. package/dist/types/hooks/auth/useCreateWalletPostAuth.d.ts +0 -1
  45. package/dist/types/hooks/wallet/useWallets.d.ts +0 -78
  46. package/dist/types/predicates.js +0 -120
  47. package/dist/types/types/config.d.ts +0 -39
  48. package/dist/types/types/predicates.d.ts +0 -118
@@ -0,0 +1,517 @@
1
+ import { AccountTypeEnum, ChainTypeEnum, EmbeddedState } from '@openfort/openfort-js';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useOpenfortContext } from '../../core/context';
4
+ import { onError, onSuccess } from '../../lib/hookConsistency';
5
+ import { logger } from '../../lib/logger';
6
+ import { OpenfortError, OpenfortErrorType } from '../../types/openfortError';
7
+ import { buildRecoveryParams } from './utils';
8
+ /**
9
+ * Hook for managing embedded Ethereum wallets.
10
+ *
11
+ * This hook provides comprehensive management of embedded Ethereum wallets including creation,
12
+ * recovery, activation, and EIP-1193 provider access. Returns a discriminated union state that
13
+ * enables type-safe wallet interactions based on connection status.
14
+ *
15
+ * **Wallet Types Supported:**
16
+ * - Smart Contract Accounts (Account Abstraction)
17
+ * - EOA (Externally Owned Accounts)
18
+ *
19
+ * **Recovery Methods:**
20
+ * - Automatic recovery (via encryption session)
21
+ * - Password-based recovery
22
+ *
23
+ * @param options - Configuration options including:
24
+ * - `chainId` - Default chain ID for wallet operations
25
+ * - `onCreateSuccess` - Callback when wallet is created
26
+ * - `onCreateError` - Callback when wallet creation fails
27
+ * - `onSetActiveSuccess` - Callback when wallet is activated/recovered
28
+ * - `onSetActiveError` - Callback when wallet activation fails
29
+ * - `onSetRecoverySuccess` - Callback when recovery method is updated
30
+ * - `onSetRecoveryError` - Callback when recovery update fails
31
+ *
32
+ * @returns Discriminated union state based on `status` field:
33
+ * - **'disconnected'**: No active wallet. Properties: `create`, `setActive`, `wallets`, `setRecovery`, `exportPrivateKey`
34
+ * - **'connecting'**: Activating wallet. Properties: same as disconnected + `activeWallet`
35
+ * - **'reconnecting'**: Reconnecting to wallet. Properties: same as connecting
36
+ * - **'creating'**: Creating new wallet. Properties: same as disconnected
37
+ * - **'needs-recovery'**: Recovery required. Properties: same as connecting
38
+ * - **'connected'**: Wallet ready. Properties: all + `provider` (EIP-1193 provider)
39
+ * - **'error'**: Operation failed. Properties: all + `error` message
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * import { useEmbeddedEthereumWallet } from '@openfort/react-native';
44
+ * import { ActivityIndicator } from 'react-native';
45
+ *
46
+ * function WalletComponent() {
47
+ * const ethereum = useEmbeddedEthereumWallet({
48
+ * chainId: 137, // Polygon
49
+ * onCreateSuccess: (account, provider) => {
50
+ * console.log('Wallet created:', account.address);
51
+ * },
52
+ * });
53
+ *
54
+ * // Handle loading states
55
+ * if (ethereum.status === 'creating' || ethereum.status === 'connecting') {
56
+ * return <ActivityIndicator />;
57
+ * }
58
+ *
59
+ * // Create first wallet
60
+ * if (ethereum.status === 'disconnected' && ethereum.wallets.length === 0) {
61
+ * return <Button onPress={() => ethereum.create()} title="Create Wallet" />;
62
+ * }
63
+ *
64
+ * // Activate existing wallet
65
+ * if (ethereum.status === 'disconnected' && ethereum.wallets.length > 0) {
66
+ * return (
67
+ * <Button
68
+ * onPress={() => ethereum.setActive({
69
+ * address: ethereum.wallets[0].address,
70
+ * recoveryPassword: 'optional-password'
71
+ * })}
72
+ * title="Connect Wallet"
73
+ * />
74
+ * );
75
+ * }
76
+ *
77
+ * // Use connected wallet
78
+ * if (ethereum.status === 'connected') {
79
+ * const sendTransaction = async () => {
80
+ * const tx = await ethereum.provider.request({
81
+ * method: 'eth_sendTransaction',
82
+ * params: [{
83
+ * from: ethereum.activeWallet.address,
84
+ * to: '0x...',
85
+ * value: '0x0'
86
+ * }]
87
+ * });
88
+ * console.log('Transaction hash:', tx);
89
+ * };
90
+ *
91
+ * return <Button onPress={sendTransaction} title="Send Transaction" />;
92
+ * }
93
+ *
94
+ * return null;
95
+ * }
96
+ * ```
97
+ */
98
+ export function useEmbeddedEthereumWallet(options = {}) {
99
+ const { client, supportedChains, walletConfig, embeddedState } = useOpenfortContext();
100
+ const [embeddedAccounts, setEmbeddedAccounts] = useState([]);
101
+ const [activeWalletId, setActiveWalletId] = useState(null);
102
+ const [activeAccount, setActiveAccount] = useState(null);
103
+ const [provider, setProvider] = useState(null);
104
+ const recoverPromiseRef = useRef(null);
105
+ const [status, setStatus] = useState({
106
+ status: 'idle',
107
+ });
108
+ // Fetch Ethereum embedded accounts
109
+ const fetchEmbeddedAccounts = useCallback(async () => {
110
+ if (!client || embeddedState === EmbeddedState.NONE || embeddedState === EmbeddedState.UNAUTHENTICATED) {
111
+ setEmbeddedAccounts([]);
112
+ return;
113
+ }
114
+ try {
115
+ const accounts = await client.embeddedWallet.list({
116
+ limit: 100,
117
+ chainType: ChainTypeEnum.EVM,
118
+ accountType: walletConfig?.accountType === AccountTypeEnum.EOA ? undefined : AccountTypeEnum.SMART_ACCOUNT,
119
+ });
120
+ // Filter for Ethereum accounts only
121
+ setEmbeddedAccounts(accounts);
122
+ }
123
+ catch {
124
+ setEmbeddedAccounts([]);
125
+ }
126
+ }, [client, embeddedState, walletConfig]);
127
+ useEffect(() => {
128
+ fetchEmbeddedAccounts();
129
+ }, [fetchEmbeddedAccounts]);
130
+ // Sync active wallet ID and account with client
131
+ useEffect(() => {
132
+ ;
133
+ (async () => {
134
+ try {
135
+ const embeddedAccount = await client.embeddedWallet.get();
136
+ if (embeddedAccount.chainType === ChainTypeEnum.EVM) {
137
+ setActiveWalletId(embeddedAccount.id);
138
+ setActiveAccount(embeddedAccount);
139
+ }
140
+ else {
141
+ setActiveWalletId(null);
142
+ setActiveAccount(null);
143
+ }
144
+ }
145
+ catch {
146
+ setActiveWalletId(null);
147
+ setActiveAccount(null);
148
+ }
149
+ })();
150
+ }, [client]);
151
+ // Get Ethereum provider
152
+ const getEthereumProvider = useCallback(async () => {
153
+ const resolvePolicy = () => {
154
+ const ethereumProviderPolicyId = walletConfig?.ethereumProviderPolicyId;
155
+ if (!ethereumProviderPolicyId)
156
+ return undefined;
157
+ if (typeof ethereumProviderPolicyId === 'string') {
158
+ return ethereumProviderPolicyId;
159
+ }
160
+ if (!options.chainId)
161
+ return undefined;
162
+ const policy = ethereumProviderPolicyId[options.chainId];
163
+ if (!policy) {
164
+ return undefined;
165
+ }
166
+ return policy;
167
+ };
168
+ return await client.embeddedWallet.getEthereumProvider({ announceProvider: false, policy: resolvePolicy() });
169
+ }, [client.embeddedWallet, walletConfig, options.chainId]);
170
+ // Initialize provider when recovering an active wallet on mount
171
+ useEffect(() => {
172
+ // Only initialize if we have an account but no provider
173
+ if (!activeAccount || provider) {
174
+ return;
175
+ }
176
+ // Don't interfere with user-initiated actions
177
+ if (['creating', 'connecting', 'reconnecting', 'loading'].includes(status.status)) {
178
+ return;
179
+ }
180
+ // Only initialize if embedded state is ready
181
+ if (embeddedState !== EmbeddedState.READY) {
182
+ return;
183
+ }
184
+ ;
185
+ (async () => {
186
+ try {
187
+ logger.info('Initializing provider for recovered Ethereum wallet session');
188
+ setStatus({ status: 'connecting' });
189
+ const ethProvider = await getEthereumProvider();
190
+ setProvider(ethProvider);
191
+ setStatus({ status: 'success' });
192
+ }
193
+ catch (e) {
194
+ const error = e instanceof OpenfortError
195
+ ? e
196
+ : new OpenfortError('Failed to initialize provider for active Ethereum wallet', OpenfortErrorType.WALLET_ERROR, { error: e });
197
+ logger.error('Ethereum provider initialization failed', error);
198
+ setStatus({ status: 'error', error });
199
+ }
200
+ })();
201
+ }, [activeAccount, provider, embeddedState, status.status, getEthereumProvider]);
202
+ // Build wallets list with deduplication logic
203
+ const wallets = useMemo(() => {
204
+ // Deduplicate accounts based on account type
205
+ const deduplicatedAccounts = embeddedAccounts.reduce((acc, account) => {
206
+ if (walletConfig?.accountType === AccountTypeEnum.EOA) {
207
+ // For EOAs, deduplicate by address only (EOAs work across all chains)
208
+ if (!acc.some((a) => a.address.toLowerCase() === account.address.toLowerCase())) {
209
+ acc.push(account);
210
+ }
211
+ }
212
+ else {
213
+ // For Smart Accounts, keep separate entries per chain (they're chain-specific)
214
+ // Only deduplicate exact matches (same address AND same chainId)
215
+ if (!acc.some((a) => a.address.toLowerCase() === account.address.toLowerCase() && a.chainId === account.chainId)) {
216
+ acc.push(account);
217
+ }
218
+ }
219
+ return acc;
220
+ }, []);
221
+ return deduplicatedAccounts.map((account, index) => ({
222
+ address: account.address,
223
+ ownerAddress: account.ownerAddress,
224
+ implementationType: account.implementationType,
225
+ chainType: ChainTypeEnum.EVM,
226
+ walletIndex: index,
227
+ getProvider: async () => await getEthereumProvider(),
228
+ }));
229
+ }, [embeddedAccounts, walletConfig?.accountType, getEthereumProvider]);
230
+ // Create wallet action
231
+ const create = useCallback(async (createOptions) => {
232
+ logger.info('Creating Ethereum wallet with options', createOptions);
233
+ try {
234
+ setStatus({ status: 'creating' });
235
+ // Validate chainId
236
+ let chainId;
237
+ if (createOptions?.chainId) {
238
+ if (!supportedChains || !supportedChains.some((chain) => chain.id === createOptions.chainId)) {
239
+ throw new OpenfortError(`Chain ID ${createOptions.chainId} is not supported. Supported chains: ${supportedChains?.map((c) => c.id).join(', ') || 'none'}`, OpenfortErrorType.WALLET_ERROR);
240
+ }
241
+ chainId = createOptions.chainId;
242
+ }
243
+ else if (options.chainId) {
244
+ chainId = options.chainId;
245
+ }
246
+ else if (supportedChains && supportedChains.length > 0) {
247
+ chainId = supportedChains[0].id;
248
+ }
249
+ else {
250
+ throw new OpenfortError('No supported chains available for wallet creation', OpenfortErrorType.WALLET_ERROR);
251
+ }
252
+ // Build recovery params
253
+ const recoveryParams = await buildRecoveryParams(createOptions, walletConfig);
254
+ const accountType = createOptions?.accountType || walletConfig?.accountType || AccountTypeEnum.SMART_ACCOUNT;
255
+ // Create embedded wallet
256
+ const embeddedAccount = await client.embeddedWallet.create({
257
+ chainId: accountType === AccountTypeEnum.EOA ? undefined : chainId,
258
+ accountType,
259
+ chainType: ChainTypeEnum.EVM,
260
+ recoveryParams,
261
+ });
262
+ logger.info('Embedded Ethereum wallet created');
263
+ // Get provider
264
+ const ethProvider = await getEthereumProvider();
265
+ setProvider(ethProvider);
266
+ // Refresh accounts and set as active
267
+ await fetchEmbeddedAccounts();
268
+ setActiveWalletId(embeddedAccount.id);
269
+ setActiveAccount(embeddedAccount);
270
+ setStatus({ status: 'success' });
271
+ onSuccess({
272
+ options: createOptions,
273
+ data: {
274
+ account: embeddedAccount,
275
+ provider: ethProvider,
276
+ },
277
+ });
278
+ if (createOptions?.onSuccess) {
279
+ createOptions.onSuccess({ account: embeddedAccount, provider: ethProvider });
280
+ }
281
+ if (options.onCreateSuccess) {
282
+ options.onCreateSuccess(embeddedAccount, ethProvider);
283
+ }
284
+ return embeddedAccount;
285
+ }
286
+ catch (e) {
287
+ const error = e instanceof OpenfortError
288
+ ? e
289
+ : new OpenfortError('Failed to create Ethereum wallet', OpenfortErrorType.WALLET_ERROR, { error: e });
290
+ setStatus({ status: 'error', error });
291
+ onError({
292
+ options: createOptions,
293
+ error,
294
+ });
295
+ if (createOptions?.onError) {
296
+ createOptions.onError(error);
297
+ }
298
+ if (options.onCreateError) {
299
+ options.onCreateError(error);
300
+ }
301
+ throw error;
302
+ }
303
+ }, [client, supportedChains, walletConfig, options, getEthereumProvider, fetchEmbeddedAccounts]);
304
+ // Set active wallet action
305
+ const setActive = useCallback(async (setActiveOptions) => {
306
+ // Prevent concurrent recoveries
307
+ if (recoverPromiseRef.current) {
308
+ await recoverPromiseRef.current;
309
+ return;
310
+ }
311
+ if (wallets.length === 0) {
312
+ const error = new OpenfortError('No embedded Ethereum wallets available to set as active', OpenfortErrorType.WALLET_ERROR);
313
+ onError({
314
+ options: setActiveOptions,
315
+ error,
316
+ });
317
+ if (setActiveOptions.onError) {
318
+ setActiveOptions.onError(error);
319
+ }
320
+ if (options.onSetActiveError) {
321
+ options.onSetActiveError(error);
322
+ }
323
+ throw error;
324
+ }
325
+ setStatus({ status: 'connecting' });
326
+ recoverPromiseRef.current = (async () => {
327
+ try {
328
+ // Validate chainId
329
+ let chainId;
330
+ if (setActiveOptions.chainId) {
331
+ if (!supportedChains || !supportedChains.some((chain) => chain.id === setActiveOptions.chainId)) {
332
+ throw new OpenfortError(`Chain ID ${setActiveOptions.chainId} is not supported. Supported chains: ${supportedChains?.map((c) => c.id).join(', ') || 'none'}`, OpenfortErrorType.WALLET_ERROR);
333
+ }
334
+ chainId = setActiveOptions.chainId;
335
+ }
336
+ else if (options.chainId) {
337
+ chainId = options.chainId;
338
+ }
339
+ else if (supportedChains && supportedChains.length > 0) {
340
+ chainId = supportedChains[0].id;
341
+ }
342
+ // Find account to recover
343
+ let embeddedAccountToRecover;
344
+ if (walletConfig?.accountType === AccountTypeEnum.EOA) {
345
+ // For EOAs, match only by address (EOAs work across all chains)
346
+ embeddedAccountToRecover = embeddedAccounts.find((account) => account.address.toLowerCase() === setActiveOptions.address.toLowerCase());
347
+ }
348
+ else {
349
+ // For Smart Accounts, match by both address and chainId (Smart Accounts are chain-specific)
350
+ embeddedAccountToRecover = embeddedAccounts.find((account) => account.chainId === chainId && account.address.toLowerCase() === setActiveOptions.address.toLowerCase());
351
+ }
352
+ if (!embeddedAccountToRecover) {
353
+ const errorMsg = walletConfig?.accountType === AccountTypeEnum.EOA
354
+ ? `No embedded EOA account found for address ${setActiveOptions.address}`
355
+ : `No embedded smart account found for address ${setActiveOptions.address} on chain ID ${chainId}`;
356
+ throw new OpenfortError(errorMsg, OpenfortErrorType.WALLET_ERROR);
357
+ }
358
+ // Build recovery params
359
+ const recoveryParams = await buildRecoveryParams(setActiveOptions, walletConfig);
360
+ // Recover the embedded wallet
361
+ const embeddedAccount = await client.embeddedWallet.recover({
362
+ account: embeddedAccountToRecover.id,
363
+ recoveryParams,
364
+ });
365
+ // Get provider
366
+ const ethProvider = await getEthereumProvider();
367
+ setProvider(ethProvider);
368
+ // Find the wallet index in the deduplicated accounts list
369
+ const walletIndex = embeddedAccounts.findIndex((acc) => acc.address.toLowerCase() === embeddedAccount.address.toLowerCase() &&
370
+ acc.chainId === embeddedAccount.chainId);
371
+ const wallet = {
372
+ address: embeddedAccount.address,
373
+ ownerAddress: embeddedAccount.ownerAddress,
374
+ implementationType: embeddedAccount.implementationType,
375
+ chainType: ChainTypeEnum.EVM,
376
+ walletIndex: walletIndex >= 0 ? walletIndex : 0,
377
+ getProvider: async () => ethProvider,
378
+ };
379
+ recoverPromiseRef.current = null;
380
+ setStatus({ status: 'success' });
381
+ setActiveWalletId(embeddedAccount.id);
382
+ setActiveAccount(embeddedAccount);
383
+ onSuccess({
384
+ options: setActiveOptions,
385
+ data: {
386
+ wallet,
387
+ provider: ethProvider,
388
+ },
389
+ });
390
+ if (setActiveOptions.onSuccess) {
391
+ setActiveOptions.onSuccess({ wallet, provider: ethProvider });
392
+ }
393
+ if (options.onSetActiveSuccess) {
394
+ options.onSetActiveSuccess(wallet, ethProvider);
395
+ }
396
+ return { wallet, provider: ethProvider };
397
+ }
398
+ catch (e) {
399
+ recoverPromiseRef.current = null;
400
+ const error = e instanceof OpenfortError
401
+ ? e
402
+ : new OpenfortError('Failed to set active Ethereum wallet', OpenfortErrorType.WALLET_ERROR);
403
+ setStatus({ status: 'error', error });
404
+ onError({
405
+ options: setActiveOptions,
406
+ error,
407
+ });
408
+ if (setActiveOptions.onError) {
409
+ setActiveOptions.onError(error);
410
+ }
411
+ if (options.onSetActiveError) {
412
+ options.onSetActiveError(error);
413
+ }
414
+ throw error;
415
+ }
416
+ })();
417
+ await recoverPromiseRef.current;
418
+ }, [client, supportedChains, walletConfig, embeddedAccounts, options, wallets.length, getEthereumProvider]);
419
+ // Set recovery method action
420
+ const setRecovery = useCallback(async (params) => {
421
+ try {
422
+ setStatus({ status: 'loading' });
423
+ await client.embeddedWallet.setRecoveryMethod(params.previousRecovery, params.newRecovery);
424
+ setStatus({ status: 'success' });
425
+ onSuccess({
426
+ options: params,
427
+ data: {},
428
+ });
429
+ if (params.onSuccess) {
430
+ params.onSuccess({});
431
+ }
432
+ if (options.onSetRecoverySuccess) {
433
+ options.onSetRecoverySuccess();
434
+ }
435
+ }
436
+ catch (e) {
437
+ const error = new OpenfortError('Failed to set wallet recovery', OpenfortErrorType.WALLET_ERROR, {
438
+ error: e instanceof Error ? e : new Error('Unknown error'),
439
+ });
440
+ setStatus({ status: 'error', error });
441
+ onError({
442
+ options: params,
443
+ error,
444
+ });
445
+ if (params.onError) {
446
+ params.onError(error);
447
+ }
448
+ if (options.onSetRecoveryError) {
449
+ options.onSetRecoveryError(error);
450
+ }
451
+ throw error;
452
+ }
453
+ }, [client, options]);
454
+ // Build active wallet from embeddedWallet.get()
455
+ const activeWallet = useMemo(() => {
456
+ if (!activeWalletId || !activeAccount)
457
+ return null;
458
+ // Find the wallet index in the accounts list
459
+ const accountIndex = embeddedAccounts.findIndex((acc) => acc.id === activeWalletId);
460
+ return {
461
+ address: activeAccount.address,
462
+ ownerAddress: activeAccount.ownerAddress,
463
+ implementationType: activeAccount.implementationType,
464
+ chainType: ChainTypeEnum.EVM,
465
+ walletIndex: accountIndex >= 0 ? accountIndex : 0,
466
+ getProvider: async () => await getEthereumProvider(),
467
+ };
468
+ }, [activeWalletId, activeAccount, embeddedAccounts, getEthereumProvider]);
469
+ // Build discriminated union state
470
+ const state = useMemo(() => {
471
+ const baseActions = {
472
+ create,
473
+ wallets,
474
+ setActive,
475
+ setRecovery,
476
+ exportPrivateKey: client.embeddedWallet.exportPrivateKey,
477
+ };
478
+ // Priority 1: Explicit action states (user-initiated operations)
479
+ if (status.status === 'creating') {
480
+ return { ...baseActions, status: 'creating', activeWallet: null };
481
+ }
482
+ if (status.status === 'connecting' || status.status === 'reconnecting' || status.status === 'loading') {
483
+ return { ...baseActions, status: 'connecting', activeWallet: activeWallet };
484
+ }
485
+ if (status.status === 'error') {
486
+ return { ...baseActions, status: 'error', activeWallet, error: status.error?.message || 'Unknown error' };
487
+ }
488
+ // Priority 2: Check authentication state from context
489
+ if (embeddedState !== EmbeddedState.READY && embeddedState !== EmbeddedState.CREATING_ACCOUNT) {
490
+ // Not authenticated or no embedded wallet capability
491
+ return { ...baseActions, status: 'disconnected', activeWallet: null };
492
+ }
493
+ // Priority 3: Data-driven connection state
494
+ if (activeWallet && provider) {
495
+ // Fully connected - have both wallet and provider
496
+ return { ...baseActions, status: 'connected', activeWallet, provider };
497
+ }
498
+ if (activeAccount && !provider) {
499
+ // Have wallet but provider not initialized yet (mount recovery in progress)
500
+ return { ...baseActions, status: 'connecting', activeWallet: activeWallet };
501
+ }
502
+ // Default: disconnected (authenticated but no wallet selected)
503
+ return { ...baseActions, status: 'disconnected', activeWallet: null };
504
+ }, [
505
+ status,
506
+ activeWallet,
507
+ activeAccount,
508
+ provider,
509
+ wallets,
510
+ embeddedState,
511
+ create,
512
+ setActive,
513
+ setRecovery,
514
+ client.embeddedWallet,
515
+ ]);
516
+ return state;
517
+ }