@abstraxn/signer-react 1.0.15 → 2.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.
- package/CHANGELOG.md +6 -0
- package/README.md +101 -5
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +124 -8
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -1
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js +36 -7
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js.map +1 -1
- package/dist/src/hooks.d.ts +131 -0
- package/dist/src/hooks.js +275 -0
- package/dist/src/hooks.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +3 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ 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
|
+
## [1.0.16] - 2026-02-23
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **External wallet disconnect from extension** – When the user disconnects from an external wallet (e.g. MetaMask) via the extension ("Disconnect this site"), the app now updates `isConnected` to false immediately without requiring a page refresh. Previously the sync effect returned early when the user had explicitly connected, so state was never reset until refresh. The fix: (1) allow the sync effect to run the existing reset when wagmi reports disconnected; (2) use wagmi’s `useConnectionEffect(onDisconnect)` to clear external wallet state as soon as wagmi reports disconnect; (3) subscribe to the connector provider’s `accountsChanged` and `disconnect` events so state clears even if wagmi is slow or stale.
|
|
13
|
+
|
|
8
14
|
## [1.0.15] - 2026-02-20
|
|
9
15
|
|
|
10
16
|
### Added
|
package/README.md
CHANGED
|
@@ -98,11 +98,107 @@ function HookConnectButton() {
|
|
|
98
98
|
|
|
99
99
|
- `useAbstraxnWallet()` - Main wallet hook
|
|
100
100
|
- `useIsConnected()` - Check connection status
|
|
101
|
-
- `useAddress()` - Get wallet address
|
|
102
|
-
- `useWhoami()` - Get user information
|
|
103
|
-
- `useExternalWalletInfo()` - External wallet information
|
|
104
|
-
- `
|
|
105
|
-
- `
|
|
101
|
+
- `useAddress()` - Get wallet address (EVM)
|
|
102
|
+
- `useWhoami()` - Get user information (includes `solanaAddress` when available)
|
|
103
|
+
- `useExternalWalletInfo()` - External wallet information (EVM)
|
|
104
|
+
- `useExportWallet()` - Export wallet (EVM or Solana, based on current chain)
|
|
105
|
+
- `usePublicClient()` / `usePrepareRawTxn()` / `useSignTxn()` / `useSignAndSendTxn()` / `useWaitForTxnReceipt()` - EVM transaction flow (prepare → sign → send → confirm)
|
|
106
|
+
- `useSolanaConnection()` / `useSolanaPublicKey()` / `usePrepareSolanaTxn()` / `useSignSolanaTxn()` / `useSignAndSendSolanaTxn()` / `useWaitForSolanaConfirmation()` - Solana transaction flow (prepare → sign → send → confirm)
|
|
107
|
+
|
|
108
|
+
### Example: EVM → Solana flow after social/email login
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import {
|
|
112
|
+
useAbstraxnWallet,
|
|
113
|
+
usePublicClient,
|
|
114
|
+
usePrepareRawTxn,
|
|
115
|
+
useEstimateGas,
|
|
116
|
+
useGetGasPrice,
|
|
117
|
+
useSignAndSendTxn,
|
|
118
|
+
useWaitForTxnReceipt,
|
|
119
|
+
useSolanaConnection,
|
|
120
|
+
useSolanaPublicKey,
|
|
121
|
+
usePrepareSolanaTxn,
|
|
122
|
+
useSignAndSendSolanaTxn,
|
|
123
|
+
useWaitForSolanaConfirmation,
|
|
124
|
+
} from '@abstraxn/signer-react';
|
|
125
|
+
import { polygonAmoy } from 'viem/chains';
|
|
126
|
+
|
|
127
|
+
function CrossChainExample() {
|
|
128
|
+
const { isConnected } = useAbstraxnWallet();
|
|
129
|
+
|
|
130
|
+
// EVM clients & hooks
|
|
131
|
+
const { publicClient } = usePublicClient(
|
|
132
|
+
polygonAmoy,
|
|
133
|
+
'https://rpc-amoy.polygon.technology'
|
|
134
|
+
);
|
|
135
|
+
const { prepareRawTxn } = usePrepareRawTxn(publicClient);
|
|
136
|
+
const { estimateGas } = useEstimateGas(publicClient);
|
|
137
|
+
const { getGasPrice } = useGetGasPrice(publicClient);
|
|
138
|
+
const { signAndSendTxn } = useSignAndSendTxn(publicClient);
|
|
139
|
+
const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
|
|
140
|
+
|
|
141
|
+
// Solana clients & hooks
|
|
142
|
+
const { connection } = useSolanaConnection('https://api.devnet.solana.com');
|
|
143
|
+
const { solanaAddress } = useSolanaPublicKey();
|
|
144
|
+
const { prepareSolanaTransfer } = usePrepareSolanaTxn(connection);
|
|
145
|
+
const { signAndSendSolanaTxn } = useSignAndSendSolanaTxn(connection);
|
|
146
|
+
const { waitForSolanaConfirmation } = useWaitForSolanaConfirmation(connection);
|
|
147
|
+
|
|
148
|
+
const handleCrossChain = async () => {
|
|
149
|
+
if (!isConnected) throw new Error('Please login first');
|
|
150
|
+
|
|
151
|
+
// 1) EVM tx (e.g. on Polygon Amoy)
|
|
152
|
+
const from = '0x...'; // current EVM address
|
|
153
|
+
const rawTx = await prepareRawTxn({
|
|
154
|
+
from,
|
|
155
|
+
to: '0x...',
|
|
156
|
+
value: '0.001',
|
|
157
|
+
});
|
|
158
|
+
const { gasLimit } = await estimateGas({
|
|
159
|
+
account: from,
|
|
160
|
+
to: rawTx.to,
|
|
161
|
+
data: rawTx.data,
|
|
162
|
+
value: rawTx.value,
|
|
163
|
+
});
|
|
164
|
+
const fees = await getGasPrice();
|
|
165
|
+
|
|
166
|
+
const evmResult = await signAndSendTxn({
|
|
167
|
+
from,
|
|
168
|
+
...rawTx,
|
|
169
|
+
gas: {
|
|
170
|
+
gasLimit,
|
|
171
|
+
maxFeePerGas: fees.maxFeePerGas!,
|
|
172
|
+
maxPriorityFeePerGas: fees.maxPriorityFeePerGas!,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await waitForTxnReceipt({ hash: evmResult.hash, confirmations: 1 });
|
|
177
|
+
|
|
178
|
+
// 2) After EVM confirmation, send SOL
|
|
179
|
+
if (!solanaAddress) throw new Error('Solana wallet not provisioned');
|
|
180
|
+
|
|
181
|
+
const { transaction } = await prepareSolanaTransfer({
|
|
182
|
+
fromPubkey: solanaAddress,
|
|
183
|
+
toPubkey: 'TargetSolanaAddressHere',
|
|
184
|
+
amountInSol: 0.01,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const solanaResult = await signAndSendSolanaTxn({
|
|
188
|
+
transaction,
|
|
189
|
+
fromPubkey: solanaAddress,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
await waitForSolanaConfirmation({ signature: solanaResult.signature });
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<button onClick={handleCrossChain}>
|
|
197
|
+
Run EVM → Solana Flow
|
|
198
|
+
</button>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
106
202
|
|
|
107
203
|
## 🔗 Related Packages
|
|
108
204
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* AbstraxnProviderInner - Core provider logic
|
|
4
4
|
* This component contains all the wallet state management and operations
|
|
@@ -6,15 +6,22 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
import { useEffect, useState, useRef, useCallback, useMemo, } from "react";
|
|
7
7
|
import { AbstraxnWallet, AuthenticationError, } from "@abstraxn/signer-core";
|
|
8
8
|
import { OnboardingUIWeb } from "../OnboardingUI";
|
|
9
|
+
import { useConnectionEffect, } from "wagmi";
|
|
9
10
|
import { parseEther, createPublicClient, http } from "viem";
|
|
10
11
|
import { ExternalWalletButtons } from "../../ExternalWalletButtons";
|
|
11
12
|
import { EVM_CHAINS, SOLANA_CHAINS, getChainById, toCoreChain, } from "../../chains";
|
|
12
13
|
import { AbstraxnContext } from "./context";
|
|
14
|
+
/** Syncs wagmi disconnect to external wallet state; only rendered when wagmi is available (under WagmiProvider). */
|
|
15
|
+
function WagmiConnectionEffectSync({ onDisconnect, }) {
|
|
16
|
+
useConnectionEffect({ onDisconnect });
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
13
19
|
export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
14
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;
|
|
15
21
|
const externalWalletsEnabled = config.externalWallets?.enabled ?? false;
|
|
16
22
|
// Keep a ref to avoid re-creating callbacks when toggling config (prevents flicker)
|
|
17
23
|
const externalWalletsEnabledRef = useRef(externalWalletsEnabled);
|
|
24
|
+
const isExternalWalletConnectedRef = useRef(false);
|
|
18
25
|
useEffect(() => {
|
|
19
26
|
externalWalletsEnabledRef.current = externalWalletsEnabled;
|
|
20
27
|
}, [externalWalletsEnabled]);
|
|
@@ -37,6 +44,9 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
37
44
|
const [emailForOtp, setEmailForOtpState] = useState("");
|
|
38
45
|
const explicitConnectionRef = useRef(false);
|
|
39
46
|
const autoDisconnectHandledRef = useRef(false);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
isExternalWalletConnectedRef.current = isExternalWalletConnected;
|
|
49
|
+
}, [isExternalWalletConnected]);
|
|
40
50
|
// Track when we last connected to prevent premature reset
|
|
41
51
|
const lastConnectionTimeRef = useRef(0);
|
|
42
52
|
// Refs to track previous values and prevent unnecessary updates
|
|
@@ -1715,6 +1725,28 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
1715
1725
|
setLoading(false);
|
|
1716
1726
|
}
|
|
1717
1727
|
}, []);
|
|
1728
|
+
// Sign Solana transaction via API (returns signed transaction)
|
|
1729
|
+
const signSolanaTransactionViaAPI = useCallback(async (unsignedTransaction, fromAddress) => {
|
|
1730
|
+
if (!walletRef.current)
|
|
1731
|
+
throw new Error("Wallet not initialized");
|
|
1732
|
+
setLoading(true);
|
|
1733
|
+
setError(null);
|
|
1734
|
+
try {
|
|
1735
|
+
const walletAny = walletRef.current;
|
|
1736
|
+
if (typeof walletAny.signSolanaTransactionViaAPI !== "function") {
|
|
1737
|
+
throw new Error("Solana transaction signing is not supported by this wallet");
|
|
1738
|
+
}
|
|
1739
|
+
return await walletAny.signSolanaTransactionViaAPI(unsignedTransaction, fromAddress);
|
|
1740
|
+
}
|
|
1741
|
+
catch (err) {
|
|
1742
|
+
const error = err instanceof Error ? err : new Error("Failed to sign Solana transaction");
|
|
1743
|
+
setError(error);
|
|
1744
|
+
throw error;
|
|
1745
|
+
}
|
|
1746
|
+
finally {
|
|
1747
|
+
setLoading(false);
|
|
1748
|
+
}
|
|
1749
|
+
}, []);
|
|
1718
1750
|
// Sign typed data / raw payload via API (same flow as signTransactionViaAPI, payload: from, unsignedTransaction, encoding, hashFunction)
|
|
1719
1751
|
const signTypedTxViaAPI = useCallback(async (fromAddress, unsignedTransaction, encoding, hashFunction) => {
|
|
1720
1752
|
if (!walletRef.current)
|
|
@@ -1984,12 +2016,6 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
1984
2016
|
isUpdatingRef.current = false;
|
|
1985
2017
|
return;
|
|
1986
2018
|
}
|
|
1987
|
-
// If we're not connected but explicitConnectionRef is true, it means we're trying to reconnect
|
|
1988
|
-
// Don't reset state in this case - wait for the connection to complete
|
|
1989
|
-
if (!wagmiAccount.isConnected && explicitConnectionRef.current) {
|
|
1990
|
-
// Connection is in progress, don't interfere
|
|
1991
|
-
return;
|
|
1992
|
-
}
|
|
1993
2019
|
}
|
|
1994
2020
|
// Check if external wallet is connected
|
|
1995
2021
|
if (wagmiAccount.isConnected && wagmiAccount.address) {
|
|
@@ -2483,6 +2509,95 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
2483
2509
|
setLoading(false);
|
|
2484
2510
|
}
|
|
2485
2511
|
}, [externalWalletsEnabled, wagmiDisconnect, wagmiAccount?.connector]);
|
|
2512
|
+
// When wagmi reports disconnect (e.g. user disconnected in extension), clear external wallet state immediately
|
|
2513
|
+
const handleWagmiDisconnect = useCallback(() => {
|
|
2514
|
+
if (!externalWalletsEnabledRef.current)
|
|
2515
|
+
return;
|
|
2516
|
+
setIsExternalWalletConnected(false);
|
|
2517
|
+
setExternalWalletAddress(null);
|
|
2518
|
+
setExternalWalletChainId(null);
|
|
2519
|
+
setConnectionType(null);
|
|
2520
|
+
try {
|
|
2521
|
+
localStorage.removeItem("abstraxn_connection_type");
|
|
2522
|
+
}
|
|
2523
|
+
catch (e) {
|
|
2524
|
+
// Ignore
|
|
2525
|
+
}
|
|
2526
|
+
explicitConnectionRef.current = false;
|
|
2527
|
+
lastConnectionTimeRef.current = 0;
|
|
2528
|
+
lastAddressRef.current = null;
|
|
2529
|
+
lastChainIdRef.current = null;
|
|
2530
|
+
if (!walletRef.current?.isConnected) {
|
|
2531
|
+
setIsConnected(false);
|
|
2532
|
+
setAddress(null);
|
|
2533
|
+
setChainId(null);
|
|
2534
|
+
}
|
|
2535
|
+
}, [
|
|
2536
|
+
setIsExternalWalletConnected,
|
|
2537
|
+
setExternalWalletAddress,
|
|
2538
|
+
setExternalWalletChainId,
|
|
2539
|
+
setConnectionType,
|
|
2540
|
+
setIsConnected,
|
|
2541
|
+
setAddress,
|
|
2542
|
+
setChainId,
|
|
2543
|
+
]);
|
|
2544
|
+
// When user disconnects in the extension, provider may emit before wagmi updates; listen so we clear state even if wagmi is stale
|
|
2545
|
+
const providerListenerCleanupRef = useRef(null);
|
|
2546
|
+
useEffect(() => {
|
|
2547
|
+
if (!externalWalletsEnabled ||
|
|
2548
|
+
!isExternalWalletConnected ||
|
|
2549
|
+
!wagmiAccount?.connector) {
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
const connector = wagmiAccount.connector;
|
|
2553
|
+
let mounted = true;
|
|
2554
|
+
const setup = async () => {
|
|
2555
|
+
try {
|
|
2556
|
+
const getProvider = connector.getProvider ?? connector.getEthereumProvider;
|
|
2557
|
+
if (typeof getProvider !== "function")
|
|
2558
|
+
return;
|
|
2559
|
+
const p = await getProvider();
|
|
2560
|
+
if (!mounted || !p)
|
|
2561
|
+
return;
|
|
2562
|
+
const onDisconnectFromProvider = () => {
|
|
2563
|
+
if (!isExternalWalletConnectedRef.current)
|
|
2564
|
+
return;
|
|
2565
|
+
handleWagmiDisconnect();
|
|
2566
|
+
};
|
|
2567
|
+
const onAccountsChanged = (accounts) => {
|
|
2568
|
+
const list = Array.isArray(accounts) ? accounts : [];
|
|
2569
|
+
if (list.length === 0)
|
|
2570
|
+
onDisconnectFromProvider();
|
|
2571
|
+
};
|
|
2572
|
+
p.on?.("accountsChanged", onAccountsChanged);
|
|
2573
|
+
p.on?.("disconnect", onDisconnectFromProvider);
|
|
2574
|
+
return () => {
|
|
2575
|
+
p.removeListener?.("accountsChanged", onAccountsChanged);
|
|
2576
|
+
p.removeListener?.("disconnect", onDisconnectFromProvider);
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
catch {
|
|
2580
|
+
// Connector may not support getProvider (e.g. some injected)
|
|
2581
|
+
return undefined;
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
setup().then((cleanup) => {
|
|
2585
|
+
if (mounted)
|
|
2586
|
+
providerListenerCleanupRef.current = cleanup ?? null;
|
|
2587
|
+
});
|
|
2588
|
+
return () => {
|
|
2589
|
+
mounted = false;
|
|
2590
|
+
const cleanup = providerListenerCleanupRef.current;
|
|
2591
|
+
providerListenerCleanupRef.current = null;
|
|
2592
|
+
if (typeof cleanup === "function")
|
|
2593
|
+
cleanup();
|
|
2594
|
+
};
|
|
2595
|
+
}, [
|
|
2596
|
+
externalWalletsEnabled,
|
|
2597
|
+
isExternalWalletConnected,
|
|
2598
|
+
wagmiAccount?.connector,
|
|
2599
|
+
handleWagmiDisconnect,
|
|
2600
|
+
]);
|
|
2486
2601
|
// Helper function to get network name from chain ID
|
|
2487
2602
|
const getNetworkName = useCallback((chainId) => {
|
|
2488
2603
|
const chainNames = {
|
|
@@ -2762,6 +2877,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
2762
2877
|
signTransaction,
|
|
2763
2878
|
sendTransaction,
|
|
2764
2879
|
signTransactionViaAPI,
|
|
2880
|
+
signSolanaTransactionViaAPI,
|
|
2765
2881
|
signTypedTxViaAPI,
|
|
2766
2882
|
signAndSendTransaction,
|
|
2767
2883
|
loginWithOTP,
|
|
@@ -3351,6 +3467,6 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, }) {
|
|
|
3351
3467
|
console.warn("Failed to update ExternalWalletButtons:", error);
|
|
3352
3468
|
}
|
|
3353
3469
|
}, [value, externalWalletsEnabled]);
|
|
3354
|
-
return (
|
|
3470
|
+
return (_jsxs(AbstraxnContext.Provider, { value: value, children: [wagmi ? (_jsx(WagmiConnectionEffectSync, { onDisconnect: handleWagmiDisconnect })) : null, children] }));
|
|
3355
3471
|
}
|
|
3356
3472
|
//# sourceMappingURL=AbstraxnProviderInner.js.map
|