@abstraxn/signer-react 2.0.1 → 2.1.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.
@@ -16,7 +16,7 @@ function WagmiConnectionEffectSync({ onDisconnect, }) {
16
16
  useConnectionEffect({ onDisconnect });
17
17
  return null;
18
18
  }
19
- export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
19
+ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }) {
20
20
  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;
21
21
  const externalWalletsEnabled = config.externalWallets?.enabled ?? false;
22
22
  // Keep a ref to avoid re-creating callbacks when toggling config (prevents flicker)
@@ -47,6 +47,99 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
47
47
  useEffect(() => {
48
48
  isExternalWalletConnectedRef.current = isExternalWalletConnected;
49
49
  }, [isExternalWalletConnected]);
50
+ // Track Solana state changes
51
+ useEffect(() => {
52
+ if (solana?.wallet?.connected && solana?.wallet?.publicKey) {
53
+ // For wallets that support both (like MetaMask via snaps), if we explicitly chose "solana", force it
54
+ // The wallet adapter usually handles this, but we want our internal state to reflect the Solana connection
55
+ const pubKey = solana.wallet.publicKey.toBase58();
56
+ setIsExternalWalletConnected(true);
57
+ setExternalWalletAddress(pubKey);
58
+ setIsConnected(true);
59
+ setAddress(pubKey);
60
+ // Use a real Solana chainId so `currentChain` resolves from SOLANA_CHAINS.
61
+ const cluster = String(config?.solanaConfig?.cluster || '').toLowerCase();
62
+ const chainIdForSolana = cluster === 'devnet'
63
+ ? SOLANA_CHAINS.solanaDevnet.id
64
+ : SOLANA_CHAINS.solana.id;
65
+ setExternalWalletChainId(chainIdForSolana);
66
+ setChainId(chainIdForSolana);
67
+ const walletName = solana.wallet.wallet?.adapter.name?.toLowerCase() || 'solana';
68
+ setConnectionType(walletName);
69
+ localStorage.setItem("abstraxn_connection_type", walletName);
70
+ localStorage.setItem("abstraxn_chain_type", "solana");
71
+ explicitConnectionRef.current = true;
72
+ autoDisconnectHandledRef.current = false;
73
+ // Close the onboarding modal after successful connection
74
+ if (onboardingRef.current) {
75
+ const onboardingAny = onboardingRef.current;
76
+ if (onboardingAny.modalOverlay) {
77
+ setTimeout(() => {
78
+ if (onboardingAny.modalOverlay) {
79
+ onboardingAny.modalOverlay.classList.remove("onboarding-modal-open");
80
+ onboardingAny.modalOverlay.classList.add("onboarding-modal-closing");
81
+ setTimeout(() => {
82
+ if (onboardingAny.modalOverlay) {
83
+ onboardingAny.modalOverlay.style.display = "none";
84
+ }
85
+ }, 200);
86
+ document.body.style.overflow = "";
87
+ }
88
+ }, 100);
89
+ }
90
+ }
91
+ }
92
+ else if (solana?.wallet && !solana.wallet.connected) {
93
+ // If the user disconnected from the Solana wallet (e.g. Phantom UI), ensure our state resets.
94
+ // Without this, the UI can stay "connected" and block reconnects until refresh.
95
+ if (disconnectingRef.current)
96
+ return;
97
+ const chainTypePref = typeof window !== "undefined"
98
+ ? localStorage.getItem("abstraxn_chain_type")
99
+ : null;
100
+ const ct = (connectionType || "").toLowerCase();
101
+ const looksLikeSolana = chainTypePref === "solana" ||
102
+ ct === "phantom" ||
103
+ ct === "solflare" ||
104
+ ct === "solana";
105
+ if (looksLikeSolana && (isExternalWalletConnectedRef.current || externalWalletAddress)) {
106
+ setIsExternalWalletConnected(false);
107
+ setExternalWalletAddress(null);
108
+ setExternalWalletChainId(null);
109
+ setConnectionType(null);
110
+ // Only clear main state if Abstraxn embedded wallet isn't connected
111
+ if (!walletRef.current?.isConnected) {
112
+ setIsConnected(false);
113
+ setAddress(null);
114
+ setChainId(null);
115
+ }
116
+ explicitConnectionRef.current = false;
117
+ lastConnectionTimeRef.current = 0;
118
+ lastAddressRef.current = null;
119
+ lastChainIdRef.current = null;
120
+ try {
121
+ localStorage.removeItem("abstraxn_connection_type");
122
+ localStorage.removeItem("abstraxn_chain_type");
123
+ localStorage.removeItem("walletName"); // wallet-adapter-react selection key
124
+ }
125
+ catch (e) {
126
+ // Ignore
127
+ }
128
+ // Clear selection so a subsequent connect starts cleanly
129
+ try {
130
+ solana.wallet.select?.(null);
131
+ }
132
+ catch (e) {
133
+ // Ignore
134
+ }
135
+ }
136
+ }
137
+ }, [
138
+ solana?.wallet?.connected,
139
+ solana?.wallet?.publicKey?.toBase58(),
140
+ connectionType,
141
+ externalWalletAddress,
142
+ ]);
50
143
  // Track when we last connected to prevent premature reset
51
144
  const lastConnectionTimeRef = useRef(0);
52
145
  // Refs to track previous values and prevent unnecessary updates
@@ -99,6 +192,11 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
99
192
  // Check if wagmi has already restored a connection from localStorage
100
193
  // This happens automatically with wagmi's persistence when storage is configured
101
194
  const checkAndRestore = () => {
195
+ // If we explicitly connected to Solana, do not restore Wagmi EVM state
196
+ const chainTypePref = typeof window !== 'undefined' ? localStorage.getItem("abstraxn_chain_type") : null;
197
+ if (chainTypePref === "solana") {
198
+ return false;
199
+ }
102
200
  if (wagmiAccount.isConnected &&
103
201
  wagmiAccount.address &&
104
202
  !isExternalWalletConnected) {
@@ -345,6 +443,8 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
345
443
  // Clear from localStorage
346
444
  try {
347
445
  localStorage.removeItem("abstraxn_connection_type");
446
+ localStorage.removeItem("abstraxn_chain_type");
447
+ localStorage.removeItem("walletName"); // Clear wallet adapter state
348
448
  }
349
449
  catch (e) {
350
450
  // Ignore localStorage errors
@@ -1321,8 +1421,34 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
1321
1421
  // If external wallet is connected, disconnect it first
1322
1422
  if (wasExternalWalletConnected &&
1323
1423
  externalWalletsEnabled &&
1324
- wagmiDisconnect) {
1325
- try {
1424
+ (wagmiDisconnect || solana?.wallet)) {
1425
+ // Disconnect Solana adapter first (Phantom/Solflare), otherwise reconnect can get stuck until refresh.
1426
+ const chainTypePref = typeof window !== "undefined"
1427
+ ? localStorage.getItem("abstraxn_chain_type")
1428
+ : null;
1429
+ const ct = (connectionType || "").toLowerCase();
1430
+ const looksLikeSolana = chainTypePref === "solana" ||
1431
+ ct === "phantom" ||
1432
+ ct === "solflare" ||
1433
+ ct === "solana";
1434
+ if (solana?.wallet && (solana.wallet.connected || looksLikeSolana)) {
1435
+ try {
1436
+ if (solana.wallet.connected) {
1437
+ await solana.wallet.disconnect();
1438
+ }
1439
+ }
1440
+ catch (e) {
1441
+ // Ignore
1442
+ }
1443
+ try {
1444
+ solana.wallet.select?.(null);
1445
+ }
1446
+ catch (e) {
1447
+ // Ignore
1448
+ }
1449
+ }
1450
+ // Disconnect wagmi connector only if it's actually connected (avoid no-op errors for Solana-only connections)
1451
+ if (wagmiDisconnect && wagmiAccount?.isConnected) {
1326
1452
  // For WalletConnect: disconnect provider first so session is fully torn down on first click
1327
1453
  const connector = wagmiAccount?.connector;
1328
1454
  const isWalletConnect = connector &&
@@ -1339,36 +1465,40 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
1339
1465
  // Ignore - wagmi disconnect will still run
1340
1466
  }
1341
1467
  }
1342
- await wagmiDisconnect.disconnect();
1343
- // Wait for wagmi state to update (WalletConnect can be slow)
1344
- await new Promise((resolve) => setTimeout(resolve, 200));
1345
- // Reset external wallet state immediately
1346
- setIsExternalWalletConnected(false);
1347
- setExternalWalletAddress(null);
1348
- setExternalWalletChainId(null);
1349
- setConnectionType(null);
1350
- // Clear refs so useEffect doesn't think we're still connected (fixes "disconnect twice")
1351
- lastAddressRef.current = null;
1352
- lastChainIdRef.current = null;
1353
- // Clear from localStorage
1354
1468
  try {
1355
- localStorage.removeItem("abstraxn_connection_type");
1469
+ await wagmiDisconnect.disconnect();
1470
+ // Wait for wagmi state to update (WalletConnect can be slow)
1471
+ await new Promise((resolve) => setTimeout(resolve, 200));
1356
1472
  }
1357
1473
  catch (e) {
1358
- // Ignore localStorage errors
1474
+ // Ignore wagmi disconnect failures; we still reset our state below.
1359
1475
  }
1360
- explicitConnectionRef.current = false;
1361
- lastConnectionTimeRef.current = 0;
1362
- // Always reset main isConnected when disconnecting external wallet
1363
- // Since the context value is computed as isExternalWalletConnected || isConnected,
1364
- // we need to set isConnected to false so the overall isConnected becomes false
1365
- setIsConnected(false);
1366
- setAddress(null);
1367
- setChainId(null);
1368
1476
  }
1369
- catch (err) {
1370
- throw err;
1477
+ // Reset external wallet state immediately
1478
+ setIsExternalWalletConnected(false);
1479
+ setExternalWalletAddress(null);
1480
+ setExternalWalletChainId(null);
1481
+ setConnectionType(null);
1482
+ // Clear refs so useEffect doesn't think we're still connected (fixes "disconnect twice")
1483
+ lastAddressRef.current = null;
1484
+ lastChainIdRef.current = null;
1485
+ // Clear from localStorage
1486
+ try {
1487
+ localStorage.removeItem("abstraxn_connection_type");
1488
+ localStorage.removeItem("abstraxn_chain_type");
1489
+ localStorage.removeItem("walletName");
1490
+ }
1491
+ catch (e) {
1492
+ // Ignore localStorage errors
1371
1493
  }
1494
+ explicitConnectionRef.current = false;
1495
+ lastConnectionTimeRef.current = 0;
1496
+ // Always reset main isConnected when disconnecting external wallet
1497
+ // Since the context value is computed as isExternalWalletConnected || isConnected,
1498
+ // we need to set isConnected to false so the overall isConnected becomes false
1499
+ setIsConnected(false);
1500
+ setAddress(null);
1501
+ setChainId(null);
1372
1502
  }
1373
1503
  // Disconnect Abstraxn wallet if it's connected
1374
1504
  if (wasAbstraxnWalletConnected && walletRef.current) {
@@ -1384,6 +1514,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
1384
1514
  // Clear from localStorage
1385
1515
  try {
1386
1516
  localStorage.removeItem("abstraxn_connection_type");
1517
+ localStorage.removeItem("walletName");
1387
1518
  }
1388
1519
  catch (e) {
1389
1520
  // Ignore localStorage errors
@@ -1971,6 +2102,12 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
1971
2102
  if (!externalWalletsEnabled || !wagmiAccount) {
1972
2103
  return;
1973
2104
  }
2105
+ // Check if we explicitly connected to a Solana wallet and it is currently connected.
2106
+ // If so, we should ignore Wagmi's connection state to prevent EVM from overriding Solana.
2107
+ const chainTypePref = typeof window !== 'undefined' ? localStorage.getItem("abstraxn_chain_type") : null;
2108
+ if (chainTypePref === "solana" && solana?.wallet?.connected) {
2109
+ return;
2110
+ }
1974
2111
  // Prevent auto-connect (but allow restoration from persistence)
1975
2112
  // Only auto-disconnect if:
1976
2113
  // 1. wagmiAccount is connected
@@ -2333,6 +2470,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2333
2470
  setConnectionType(connectionTypeValue);
2334
2471
  try {
2335
2472
  localStorage.setItem("abstraxn_connection_type", connectionTypeValue);
2473
+ localStorage.setItem("abstraxn_chain_type", "evm");
2336
2474
  }
2337
2475
  catch (e) { }
2338
2476
  // Update refs
@@ -2466,6 +2604,19 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2466
2604
  }
2467
2605
  }
2468
2606
  // Await the disconnect to ensure it completes
2607
+ if (solana?.wallet?.connected) {
2608
+ try {
2609
+ await solana?.wallet?.disconnect();
2610
+ }
2611
+ catch (e) { }
2612
+ }
2613
+ // Clear Solana wallet selection so reconnect starts cleanly
2614
+ if (solana?.wallet) {
2615
+ try {
2616
+ solana.wallet.select?.(null);
2617
+ }
2618
+ catch (e) { }
2619
+ }
2469
2620
  await wagmiDisconnect.disconnect();
2470
2621
  // Wait for wagmi state to update (WalletConnect can be slow)
2471
2622
  await new Promise((resolve) => setTimeout(resolve, 200));
@@ -2477,6 +2628,8 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2477
2628
  // Clear from localStorage
2478
2629
  try {
2479
2630
  localStorage.removeItem("abstraxn_connection_type");
2631
+ localStorage.removeItem("abstraxn_chain_type");
2632
+ localStorage.removeItem("walletName");
2480
2633
  }
2481
2634
  catch (e) {
2482
2635
  // Ignore localStorage errors
@@ -2508,7 +2661,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2508
2661
  }, 200);
2509
2662
  setLoading(false);
2510
2663
  }
2511
- }, [externalWalletsEnabled, wagmiDisconnect, wagmiAccount?.connector]);
2664
+ }, [externalWalletsEnabled, wagmiDisconnect, wagmiAccount?.connector, solana?.wallet]);
2512
2665
  // When wagmi reports disconnect (e.g. user disconnected in extension), clear external wallet state immediately
2513
2666
  const handleWagmiDisconnect = useCallback(() => {
2514
2667
  if (!externalWalletsEnabledRef.current)
@@ -2519,6 +2672,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2519
2672
  setConnectionType(null);
2520
2673
  try {
2521
2674
  localStorage.removeItem("abstraxn_connection_type");
2675
+ localStorage.removeItem("abstraxn_chain_type");
2522
2676
  }
2523
2677
  catch (e) {
2524
2678
  // Ignore
@@ -2862,6 +3016,8 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
2862
3016
  loading,
2863
3017
  resending: resending ?? false,
2864
3018
  disconnecting: disconnecting ?? false,
3019
+ solanaWallet: solana?.wallet,
3020
+ solanaConnection: solana?.connection,
2865
3021
  init,
2866
3022
  connect,
2867
3023
  disconnect,