@abstraxn/signer-react 3.1.4 → 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 +20 -0
- package/README.md +72 -0
- package/dist/src/WalletModal.css +46 -0
- package/dist/src/WalletModal.js +23 -4
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +90 -15
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js +1 -1
- package/dist/src/components/WalletModal/utils/formatUtils.d.ts +1 -1
- package/dist/src/components/WalletModal/utils/formatUtils.js +3 -4
- package/dist/src/hooks.d.ts +37 -8
- package/dist/src/hooks.js +339 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/types.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ 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.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.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
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`
|
|
22
|
+
|
|
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
|
+
|
|
8
28
|
## [3.1.4] - 2026-04-06
|
|
9
29
|
|
|
10
30
|
### Changed
|
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
|
|
package/dist/src/WalletModal.css
CHANGED
|
@@ -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;
|
package/dist/src/WalletModal.js
CHANGED
|
@@ -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");
|
|
@@ -3225,6 +3225,60 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3225
3225
|
const currentChainId = isExternalWalletConnected && externalWalletChainId
|
|
3226
3226
|
? externalWalletChainId
|
|
3227
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]);
|
|
3228
3282
|
// Fetch balance for Abstraxn wallet (not external wallet) - reusable for on-demand refetch
|
|
3229
3283
|
const refetchWalletBalance = useCallback(async () => {
|
|
3230
3284
|
if (isExternalWalletConnected || !address || !currentChainId) {
|
|
@@ -3232,7 +3286,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3232
3286
|
return;
|
|
3233
3287
|
}
|
|
3234
3288
|
try {
|
|
3235
|
-
const currentChain = getChainById(currentChainId);
|
|
3289
|
+
const currentChain = getChainById(currentChainId) || customChainsById.get(currentChainId);
|
|
3236
3290
|
if (!currentChain || currentChain.type !== "evm") {
|
|
3237
3291
|
setWalletBalance(null);
|
|
3238
3292
|
return;
|
|
@@ -3259,24 +3313,35 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3259
3313
|
console.error("Failed to fetch balance:", error);
|
|
3260
3314
|
setWalletBalance(null);
|
|
3261
3315
|
}
|
|
3262
|
-
}, [address, currentChainId, isExternalWalletConnected]);
|
|
3316
|
+
}, [address, currentChainId, isExternalWalletConnected, customChainsById]);
|
|
3263
3317
|
useEffect(() => {
|
|
3264
3318
|
refetchWalletBalance();
|
|
3265
3319
|
}, [refetchWalletBalance]);
|
|
3266
3320
|
// Single refetch for UI: Abstraxn wallet = RPC call, external wallet = wagmi refetch
|
|
3267
|
-
const refetchBalance = useCallback(() => {
|
|
3321
|
+
const refetchBalance = useCallback(async () => {
|
|
3268
3322
|
if (isExternalWalletConnected && wagmiBalance?.refetch) {
|
|
3269
|
-
|
|
3323
|
+
await wagmiBalance.refetch();
|
|
3270
3324
|
return;
|
|
3271
3325
|
}
|
|
3272
|
-
|
|
3326
|
+
await refetchWalletBalance();
|
|
3273
3327
|
}, [isExternalWalletConnected, wagmiBalance, refetchWalletBalance]);
|
|
3274
3328
|
// Compute available chains from config - supports both legacy and new format
|
|
3275
3329
|
const availableChains = useMemo(() => {
|
|
3276
3330
|
const chains = [];
|
|
3277
3331
|
const chainConfig = config.chains;
|
|
3278
|
-
// Priority 1: Check
|
|
3279
|
-
if (chainConfig
|
|
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 &&
|
|
3280
3345
|
chainConfig.supportedEvmChains.length > 0) {
|
|
3281
3346
|
// Use configured chains from new format
|
|
3282
3347
|
chainConfig.supportedEvmChains.forEach((chainName) => {
|
|
@@ -3286,8 +3351,10 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3286
3351
|
}
|
|
3287
3352
|
});
|
|
3288
3353
|
}
|
|
3289
|
-
// Priority
|
|
3290
|
-
else if (
|
|
3354
|
+
// Priority 3: Check legacy supportedChains format
|
|
3355
|
+
else if (!Array.isArray(chainConfig) &&
|
|
3356
|
+
config.supportedChains &&
|
|
3357
|
+
config.supportedChains.length > 0) {
|
|
3291
3358
|
// Convert legacy Chain format to ChainData
|
|
3292
3359
|
config.supportedChains.forEach((legacyChain) => {
|
|
3293
3360
|
// Try to find matching chain by ID
|
|
@@ -3313,7 +3380,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3313
3380
|
}
|
|
3314
3381
|
});
|
|
3315
3382
|
}
|
|
3316
|
-
// Priority
|
|
3383
|
+
// Priority 4: Default chains only if nothing configured at all
|
|
3317
3384
|
// Only show defaults if both config.chains and config.supportedChains are missing
|
|
3318
3385
|
else if (!chainConfig && !config.supportedChains) {
|
|
3319
3386
|
chains.push(EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base);
|
|
@@ -3330,13 +3397,15 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3330
3397
|
: !chainConfig && !config.supportedChains
|
|
3331
3398
|
? [EVM_CHAINS.ethereum, EVM_CHAINS.polygon, EVM_CHAINS.base]
|
|
3332
3399
|
: [];
|
|
3333
|
-
}, [config.chains, config.supportedChains]);
|
|
3400
|
+
}, [config.chains, config.supportedChains, customChainsById]);
|
|
3334
3401
|
// Get current chain data
|
|
3335
3402
|
const currentChain = useMemo(() => {
|
|
3336
3403
|
if (!currentChainId)
|
|
3337
3404
|
return null;
|
|
3338
|
-
return getChainById(currentChainId) ||
|
|
3339
|
-
|
|
3405
|
+
return (getChainById(currentChainId) ||
|
|
3406
|
+
customChainsById.get(currentChainId) ||
|
|
3407
|
+
null);
|
|
3408
|
+
}, [currentChainId, customChainsById]);
|
|
3340
3409
|
// Enhanced switchChain that works for both EVM and Solana
|
|
3341
3410
|
const switchChainEnhanced = useCallback(async (targetChainId) => {
|
|
3342
3411
|
if (!walletRef.current) {
|
|
@@ -3347,7 +3416,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3347
3416
|
if (!targetChainInAvailable) {
|
|
3348
3417
|
throw new Error(`Chain with ID ${targetChainId} is not available. Please configure it in your SDK setup.`);
|
|
3349
3418
|
}
|
|
3350
|
-
const targetChain = getChainById(targetChainId);
|
|
3419
|
+
const targetChain = getChainById(targetChainId) || customChainsById.get(targetChainId);
|
|
3351
3420
|
if (!targetChain) {
|
|
3352
3421
|
throw new Error(`Chain with ID ${targetChainId} is not supported`);
|
|
3353
3422
|
}
|
|
@@ -3407,7 +3476,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
|
|
|
3407
3476
|
finally {
|
|
3408
3477
|
setLoading(false);
|
|
3409
3478
|
}
|
|
3410
|
-
}, [
|
|
3479
|
+
}, [
|
|
3480
|
+
isExternalWalletConnected,
|
|
3481
|
+
wagmiSwitchChain,
|
|
3482
|
+
switchChain,
|
|
3483
|
+
availableChains,
|
|
3484
|
+
customChainsById,
|
|
3485
|
+
]);
|
|
3411
3486
|
const setEmailForOtp = useCallback((email) => {
|
|
3412
3487
|
setEmailForOtpState(email);
|
|
3413
3488
|
}, []);
|
|
@@ -95,7 +95,7 @@ export function useSendTransaction({ sendTransaction, currentChain, fromAddress,
|
|
|
95
95
|
data: '0x',
|
|
96
96
|
chainId: currentChain.id,
|
|
97
97
|
gas: {
|
|
98
|
-
gasLimit,
|
|
98
|
+
gasLimit: gasLimit,
|
|
99
99
|
maxFeePerGas: gasFees.maxFeePerGas,
|
|
100
100
|
maxPriorityFeePerGas: gasFees.maxPriorityFeePerGas,
|
|
101
101
|
gasPrice: gasFees.gasPrice,
|
|
@@ -9,7 +9,7 @@ export declare function formatAddress(addr: string | null | undefined): string;
|
|
|
9
9
|
* Formats a balance with proper decimals and symbol
|
|
10
10
|
* Removes trailing zeros and unnecessary decimals
|
|
11
11
|
*/
|
|
12
|
-
export declare function formatBalance(balance: bigint | null | undefined, decimals: number, symbol: string): string;
|
|
12
|
+
export declare function formatBalance(balance: bigint | null | undefined, decimals: number, symbol: string, maxFractionDigits?: number): string;
|
|
13
13
|
/**
|
|
14
14
|
* Formats a balance value (number) with proper decimals
|
|
15
15
|
*/
|
|
@@ -15,13 +15,12 @@ export function formatAddress(addr) {
|
|
|
15
15
|
* Formats a balance with proper decimals and symbol
|
|
16
16
|
* Removes trailing zeros and unnecessary decimals
|
|
17
17
|
*/
|
|
18
|
-
export function formatBalance(balance, decimals, symbol) {
|
|
18
|
+
export function formatBalance(balance, decimals, symbol, maxFractionDigits = decimals) {
|
|
19
19
|
if (!balance || balance === 0n)
|
|
20
20
|
return `0 ${symbol}`;
|
|
21
21
|
const balanceValue = Number(balance) / Math.pow(10, decimals);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const formatted = balanceValue.toFixed(decimals);
|
|
22
|
+
const safeFractionDigits = Math.max(0, Math.min(maxFractionDigits, decimals));
|
|
23
|
+
const formatted = balanceValue.toFixed(safeFractionDigits);
|
|
25
24
|
// Remove trailing zeros and decimal point if not needed
|
|
26
25
|
const trimmed = formatted.replace(/\.?0+$/, '');
|
|
27
26
|
return `${trimmed} ${symbol}`;
|
package/dist/src/hooks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WhoamiResponse, TransactionRequest, EnableMfaResponse, VerifySetupMfaResponse, DisableMfaResponse } from '@abstraxn/signer-core';
|
|
1
|
+
import type { WhoamiResponse, TransactionRequest, EnableMfaResponse, VerifySetupMfaResponse, DisableMfaResponse, SupportedAuthMethod, LinkedAuthMethod } from '@abstraxn/signer-core';
|
|
2
2
|
import type { Chain, PublicClient, Address, Abi, GetContractReturnType, TransactionReceipt } from 'viem';
|
|
3
3
|
import { Connection as SolanaConnection, Transaction as SolanaTransaction, TransactionInstruction, type Commitment, type SendOptions } from '@solana/web3.js';
|
|
4
4
|
import type { TypedData, TypedDataDomain } from 'viem';
|
|
@@ -46,6 +46,20 @@ export { useAbstraxnWallet } from './AbstraxnProvider';
|
|
|
46
46
|
* Hook to get wallet instance (for advanced usage)
|
|
47
47
|
*/
|
|
48
48
|
export declare function useWallet(): import("@abstraxn/signer-core").AbstraxnWallet | null;
|
|
49
|
+
export interface SmartAccountOwnerOptions {
|
|
50
|
+
/** Override the signer address. By default, uses connected wallet address from context. */
|
|
51
|
+
address?: Address;
|
|
52
|
+
}
|
|
53
|
+
export interface SmartAccountOwnerResult {
|
|
54
|
+
smartAccountOwner: any | null;
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
address: Address | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Hook to get an AA-compatible owner account for permissionless-compatible SDKs.
|
|
60
|
+
* This does not include bundler/paymaster integration; apps can plug in any provider.
|
|
61
|
+
*/
|
|
62
|
+
export declare function useSmartAccountOwner(options?: SmartAccountOwnerOptions): SmartAccountOwnerResult;
|
|
49
63
|
/**
|
|
50
64
|
* Hook to export wallet private key
|
|
51
65
|
* Returns the export bundle containing the private key
|
|
@@ -127,6 +141,28 @@ export declare function useDisableMfa(): {
|
|
|
127
141
|
confirmDisable: () => Promise<DisableMfaResponse | null>;
|
|
128
142
|
reset: () => void;
|
|
129
143
|
};
|
|
144
|
+
export interface UseAuthMethodLinkingReturn {
|
|
145
|
+
supportedMethods: SupportedAuthMethod[];
|
|
146
|
+
linkedMethods: LinkedAuthMethod[];
|
|
147
|
+
loading: boolean;
|
|
148
|
+
actionProvider: string | null;
|
|
149
|
+
error: string | null;
|
|
150
|
+
refreshAuthMethods: (options?: {
|
|
151
|
+
force?: boolean;
|
|
152
|
+
}) => Promise<void>;
|
|
153
|
+
isLinked: (provider: string) => boolean;
|
|
154
|
+
linkProvider: (provider: 'google' | 'discord' | 'twitter' | 'x' | 'passkey') => Promise<void>;
|
|
155
|
+
requestEmailLinkOtp: (email: string) => Promise<{
|
|
156
|
+
otpId: string;
|
|
157
|
+
}>;
|
|
158
|
+
confirmEmailLink: (otpId: string, otpCode: string) => Promise<void>;
|
|
159
|
+
unlinkProvider: (provider: string) => Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Hook for linking/unlinking auth methods without any UI concerns.
|
|
163
|
+
* It exposes state and actions only; consumers own rendering and messaging.
|
|
164
|
+
*/
|
|
165
|
+
export declare function useAuthMethodLinking(): UseAuthMethodLinkingReturn;
|
|
130
166
|
/**
|
|
131
167
|
* Hook to access external wallet functionality
|
|
132
168
|
* Returns external wallet connection state and methods
|
|
@@ -270,7 +306,6 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
270
306
|
request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
|
|
271
307
|
} | undefined;
|
|
272
308
|
chain: Chain | undefined;
|
|
273
|
-
dataSuffix?: import("viem").DataSuffix | undefined;
|
|
274
309
|
experimental_blockTag?: import("viem").BlockTag | undefined;
|
|
275
310
|
key: string;
|
|
276
311
|
name: string;
|
|
@@ -453,7 +488,6 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
453
488
|
getChainId: () => Promise<import("viem").GetChainIdReturnType>;
|
|
454
489
|
getCode: (args: import("viem").GetBytecodeParameters) => Promise<import("viem").GetBytecodeReturnType>;
|
|
455
490
|
getContractEvents: <const abi extends Abi | readonly unknown[], eventName extends import("viem").ContractEventName<abi> | undefined = undefined, strict extends boolean | undefined = undefined, fromBlock extends import("viem").BlockNumber | import("viem").BlockTag | undefined = undefined, toBlock extends import("viem").BlockNumber | import("viem").BlockTag | undefined = undefined>(args: import("viem").GetContractEventsParameters<abi, eventName, strict, fromBlock, toBlock>) => Promise<import("viem").GetContractEventsReturnType<abi, eventName, strict, fromBlock, toBlock>>;
|
|
456
|
-
getDelegation: (args: import("viem").GetDelegationParameters) => Promise<import("viem").GetDelegationReturnType>;
|
|
457
491
|
getEip712Domain: (args: import("viem").GetEip712DomainParameters) => Promise<import("viem").GetEip712DomainReturnType>;
|
|
458
492
|
getEnsAddress: (args: import("viem").GetEnsAddressParameters) => Promise<import("viem").GetEnsAddressReturnType>;
|
|
459
493
|
getEnsAvatar: (args: import("viem").GetEnsAvatarParameters) => Promise<import("viem").GetEnsAvatarReturnType>;
|
|
@@ -3879,7 +3913,6 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
3879
3913
|
cacheTime?: undefined;
|
|
3880
3914
|
ccipRead?: undefined;
|
|
3881
3915
|
chain?: undefined;
|
|
3882
|
-
dataSuffix?: undefined;
|
|
3883
3916
|
experimental_blockTag?: undefined;
|
|
3884
3917
|
key?: undefined;
|
|
3885
3918
|
name?: undefined;
|
|
@@ -3929,7 +3962,6 @@ export declare function useWalletClient(chain: any, rpcUrl?: string): {
|
|
|
3929
3962
|
request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
|
|
3930
3963
|
} | undefined;
|
|
3931
3964
|
chain: Chain | undefined;
|
|
3932
|
-
dataSuffix?: import("viem").DataSuffix | undefined;
|
|
3933
3965
|
experimental_blockTag?: import("viem").BlockTag | undefined;
|
|
3934
3966
|
key: string;
|
|
3935
3967
|
name: string;
|
|
@@ -8341,7 +8373,6 @@ export declare function useWalletClient(chain: any, rpcUrl?: string): {
|
|
|
8341
8373
|
cacheTime?: undefined;
|
|
8342
8374
|
ccipRead?: undefined;
|
|
8343
8375
|
chain?: undefined;
|
|
8344
|
-
dataSuffix?: undefined;
|
|
8345
8376
|
experimental_blockTag?: undefined;
|
|
8346
8377
|
key?: undefined;
|
|
8347
8378
|
name?: undefined;
|
|
@@ -8868,7 +8899,6 @@ export declare function useExternalWalletClient(): {
|
|
|
8868
8899
|
request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
|
|
8869
8900
|
} | undefined;
|
|
8870
8901
|
chain: Chain;
|
|
8871
|
-
dataSuffix?: import("viem").DataSuffix | undefined;
|
|
8872
8902
|
experimental_blockTag?: import("viem").BlockTag | undefined;
|
|
8873
8903
|
key: string;
|
|
8874
8904
|
name: string;
|
|
@@ -13280,7 +13310,6 @@ export declare function useExternalWalletClient(): {
|
|
|
13280
13310
|
cacheTime?: undefined;
|
|
13281
13311
|
ccipRead?: undefined;
|
|
13282
13312
|
chain?: undefined;
|
|
13283
|
-
dataSuffix?: undefined;
|
|
13284
13313
|
experimental_blockTag?: undefined;
|
|
13285
13314
|
key?: undefined;
|
|
13286
13315
|
name?: undefined;
|
package/dist/src/hooks.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
5
5
|
import { useAbstraxnWallet } from './AbstraxnProvider';
|
|
6
6
|
import { InvalidDeviceIdError, SignMfaRequiredError } from '@abstraxn/signer-core';
|
|
7
|
-
import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, encodeFunctionData, hashTypedData } from 'viem';
|
|
7
|
+
import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, encodeFunctionData, hashTypedData, hashMessage, parseSignature } from 'viem';
|
|
8
|
+
import { toAccount } from 'viem/accounts';
|
|
8
9
|
import { Connection as SolanaConnection, Transaction as SolanaTransaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL, TransactionInstruction, } from '@solana/web3.js';
|
|
9
10
|
import { useWalletClient as useWagmiWalletClient, useAccount, useConfig, useChainId as useWagmiChainId, useSwitchChain as useWagmiSwitchChain, useSignMessage as useWagmiSignMessage, useReconnect as useWagmiReconnect } from 'wagmi';
|
|
10
11
|
import { getWalletClient, switchChain } from '@wagmi/core';
|
|
@@ -72,6 +73,67 @@ export function useWallet() {
|
|
|
72
73
|
const { wallet } = useAbstraxnWallet();
|
|
73
74
|
return wallet;
|
|
74
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Hook to get an AA-compatible owner account for permissionless-compatible SDKs.
|
|
78
|
+
* This does not include bundler/paymaster integration; apps can plug in any provider.
|
|
79
|
+
*/
|
|
80
|
+
export function useSmartAccountOwner(options = {}) {
|
|
81
|
+
const { isConnected, address: connectedAddress, signTransactionViaAPI, signTypedTxViaAPI, verifySignMfa, } = useAbstraxnWallet();
|
|
82
|
+
const ownerAddress = (options.address ?? connectedAddress ?? null);
|
|
83
|
+
const smartAccountOwner = useMemo(() => {
|
|
84
|
+
if (!isConnected || !ownerAddress)
|
|
85
|
+
return null;
|
|
86
|
+
return toAccount({
|
|
87
|
+
address: ownerAddress,
|
|
88
|
+
/**
|
|
89
|
+
* ERC-4337 SimpleAccount (permissionless) signs UserOperations via viem `signMessage` with
|
|
90
|
+
* `message: { raw: userOpHash }`. That requires the same digest as viem local accounts:
|
|
91
|
+
* EIP-191 `hashMessage(message)`, then ECDSA sign that 32-byte digest — not UTF-8 text hashing.
|
|
92
|
+
*/
|
|
93
|
+
async signMessage({ message }) {
|
|
94
|
+
const digest = hashMessage(message);
|
|
95
|
+
const signature = await runWithSignStepUpRetry(() => signTypedTxViaAPI(ownerAddress, digest, 'PAYLOAD_ENCODING_HEXADECIMAL', 'HASH_FUNCTION_NO_OP').then((res) => res.signature), verifySignMfa);
|
|
96
|
+
return (signature.startsWith('0x') ? signature : `0x${signature}`);
|
|
97
|
+
},
|
|
98
|
+
async signTransaction(transaction) {
|
|
99
|
+
const unsignedTransaction = serializeTransaction(transaction);
|
|
100
|
+
const result = await runWithSignStepUpRetry(() => signTransactionViaAPI(unsignedTransaction, ownerAddress), verifySignMfa);
|
|
101
|
+
return result.signedTransaction;
|
|
102
|
+
},
|
|
103
|
+
async signTypedData(typedData) {
|
|
104
|
+
const digest = hashTypedData(typedData);
|
|
105
|
+
const result = await runWithSignStepUpRetry(() => signTypedTxViaAPI(ownerAddress, digest, 'PAYLOAD_ENCODING_EIP712', 'HASH_FUNCTION_NO_OP'), verifySignMfa);
|
|
106
|
+
const signature = result.signature;
|
|
107
|
+
return (signature.startsWith('0x') ? signature : `0x${signature}`);
|
|
108
|
+
},
|
|
109
|
+
/** EIP-7702: sign delegation authorization (`keccak256(0x05 || rlp(...))`) via backend, same as raw ECDSA over digest. */
|
|
110
|
+
async signAuthorization(parameters) {
|
|
111
|
+
const authorizationAddress = parameters.contractAddress ?? parameters.address;
|
|
112
|
+
const authorizationPayload = JSON.stringify({
|
|
113
|
+
chainId: parameters.chainId,
|
|
114
|
+
address: authorizationAddress,
|
|
115
|
+
nonce: parameters.nonce,
|
|
116
|
+
});
|
|
117
|
+
const sig = await runWithSignStepUpRetry(() => signTypedTxViaAPI(ownerAddress, authorizationPayload, 'PAYLOAD_ENCODING_EIP7702_AUTHORIZATION', 'HASH_FUNCTION_NOT_APPLICABLE').then((res) => res.signature), verifySignMfa);
|
|
118
|
+
const hex = (sig.startsWith('0x') ? sig : `0x${sig}`);
|
|
119
|
+
const { r, s, yParity } = parseSignature(hex);
|
|
120
|
+
return {
|
|
121
|
+
address: authorizationAddress,
|
|
122
|
+
chainId: parameters.chainId,
|
|
123
|
+
nonce: parameters.nonce,
|
|
124
|
+
r,
|
|
125
|
+
s,
|
|
126
|
+
yParity,
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}, [isConnected, ownerAddress, signTransactionViaAPI, signTypedTxViaAPI, verifySignMfa]);
|
|
131
|
+
return {
|
|
132
|
+
smartAccountOwner,
|
|
133
|
+
isReady: !!smartAccountOwner,
|
|
134
|
+
address: ownerAddress,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
75
137
|
/**
|
|
76
138
|
* Hook to export wallet private key
|
|
77
139
|
* Returns the export bundle containing the private key
|
|
@@ -414,6 +476,282 @@ export function useDisableMfa() {
|
|
|
414
476
|
reset,
|
|
415
477
|
};
|
|
416
478
|
}
|
|
479
|
+
const AUTH_METHOD_LINK_PROVIDER_ALLOWLIST = new Set([
|
|
480
|
+
'google',
|
|
481
|
+
'discord',
|
|
482
|
+
'x',
|
|
483
|
+
'passkey',
|
|
484
|
+
]);
|
|
485
|
+
const AUTH_METHOD_UNLINK_PROVIDER_ALLOWLIST = new Set([
|
|
486
|
+
'google',
|
|
487
|
+
'discord',
|
|
488
|
+
'x',
|
|
489
|
+
'email',
|
|
490
|
+
'passkey',
|
|
491
|
+
]);
|
|
492
|
+
function normalizeAuthMethodProviderForApi(provider) {
|
|
493
|
+
const normalized = provider.trim().toLowerCase();
|
|
494
|
+
if (normalized === 'otp')
|
|
495
|
+
return 'email';
|
|
496
|
+
return normalized;
|
|
497
|
+
}
|
|
498
|
+
function normalizeAuthMethodProviderForMatch(provider) {
|
|
499
|
+
const normalized = normalizeAuthMethodProviderForApi(provider);
|
|
500
|
+
if (normalized === 'twitter' || normalized === 'x')
|
|
501
|
+
return 'x';
|
|
502
|
+
return normalized;
|
|
503
|
+
}
|
|
504
|
+
function normalizeAuthMethodErrorMessage(message) {
|
|
505
|
+
return message.replace(/^network error:\s*/i, '').trim();
|
|
506
|
+
}
|
|
507
|
+
function toAuthMethodError(error, fallbackMessage) {
|
|
508
|
+
if (error instanceof Error) {
|
|
509
|
+
return new Error(normalizeAuthMethodErrorMessage(error.message || fallbackMessage));
|
|
510
|
+
}
|
|
511
|
+
if (typeof error === 'string' && error.trim() !== '') {
|
|
512
|
+
return new Error(normalizeAuthMethodErrorMessage(error));
|
|
513
|
+
}
|
|
514
|
+
return new Error(fallbackMessage);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Hook for linking/unlinking auth methods without any UI concerns.
|
|
518
|
+
* It exposes state and actions only; consumers own rendering and messaging.
|
|
519
|
+
*/
|
|
520
|
+
export function useAuthMethodLinking() {
|
|
521
|
+
const { wallet, whoami, getSupportedAuthMethods, getLinkedAuthMethods, requestOtpForEmailLink, loginWithOTP, } = useAbstraxnWallet();
|
|
522
|
+
const [supportedMethods, setSupportedMethods] = useState([]);
|
|
523
|
+
const [linkedMethods, setLinkedMethods] = useState([]);
|
|
524
|
+
const [queryLoading, setQueryLoading] = useState(false);
|
|
525
|
+
const [actionProvider, setActionProvider] = useState(null);
|
|
526
|
+
const [error, setError] = useState(null);
|
|
527
|
+
const requireConnectedWallet = useCallback(() => {
|
|
528
|
+
if (!wallet?.isConnected) {
|
|
529
|
+
throw new Error('Wallet not connected');
|
|
530
|
+
}
|
|
531
|
+
return wallet;
|
|
532
|
+
}, [wallet]);
|
|
533
|
+
const refreshAuthMethods = useCallback(async (_options) => {
|
|
534
|
+
const activeWallet = requireConnectedWallet();
|
|
535
|
+
setQueryLoading(true);
|
|
536
|
+
setError(null);
|
|
537
|
+
try {
|
|
538
|
+
const supportedRequest = getSupportedAuthMethods != null
|
|
539
|
+
? getSupportedAuthMethods()
|
|
540
|
+
: activeWallet.getSupportedAuthMethods();
|
|
541
|
+
const linkedRequest = getLinkedAuthMethods != null
|
|
542
|
+
? getLinkedAuthMethods()
|
|
543
|
+
: activeWallet.getLinkedAuthMethods();
|
|
544
|
+
const [supportedResponse, linkedResponse] = await Promise.all([
|
|
545
|
+
supportedRequest,
|
|
546
|
+
linkedRequest,
|
|
547
|
+
]);
|
|
548
|
+
const loginProvider = normalizeAuthMethodProviderForMatch(String(whoami?.loginProvider ?? ''));
|
|
549
|
+
const allSupportedMethods = supportedResponse.supportedMethods ?? [];
|
|
550
|
+
const filteredMethods = loginProvider !== ''
|
|
551
|
+
? allSupportedMethods.filter((method) => normalizeAuthMethodProviderForMatch(method.provider) !== loginProvider)
|
|
552
|
+
: allSupportedMethods;
|
|
553
|
+
setSupportedMethods(filteredMethods);
|
|
554
|
+
setLinkedMethods(linkedResponse.linkedMethods ?? []);
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
const nextError = toAuthMethodError(err, 'Failed to load auth methods');
|
|
558
|
+
setSupportedMethods([]);
|
|
559
|
+
setLinkedMethods([]);
|
|
560
|
+
setError(nextError.message);
|
|
561
|
+
throw nextError;
|
|
562
|
+
}
|
|
563
|
+
finally {
|
|
564
|
+
setQueryLoading(false);
|
|
565
|
+
}
|
|
566
|
+
}, [getLinkedAuthMethods, getSupportedAuthMethods, requireConnectedWallet, whoami]);
|
|
567
|
+
const isLinked = useCallback((provider) => {
|
|
568
|
+
const normalizedProvider = normalizeAuthMethodProviderForMatch(provider);
|
|
569
|
+
return linkedMethods.some((method) => normalizeAuthMethodProviderForMatch(method.authProvider) === normalizedProvider);
|
|
570
|
+
}, [linkedMethods]);
|
|
571
|
+
const runOAuthPopupLink = useCallback(async (activeWallet, provider) => {
|
|
572
|
+
if (typeof window === 'undefined') {
|
|
573
|
+
throw new Error('OAuth linking requires a browser environment');
|
|
574
|
+
}
|
|
575
|
+
const { authUrl } = await activeWallet.initiateLinkAuth(provider);
|
|
576
|
+
const width = 500;
|
|
577
|
+
const height = 600;
|
|
578
|
+
const left = Math.round((window.screen.width - width) / 2);
|
|
579
|
+
const top = Math.round((window.screen.height - height) / 2);
|
|
580
|
+
const popup = window.open(authUrl, 'abstraxn-auth-link', `width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`);
|
|
581
|
+
if (!popup) {
|
|
582
|
+
throw new Error('Popup blocked. Please allow popups for this site.');
|
|
583
|
+
}
|
|
584
|
+
const timeoutMs = 120000;
|
|
585
|
+
await new Promise((resolve, reject) => {
|
|
586
|
+
let settled = false;
|
|
587
|
+
let closeCheckId = null;
|
|
588
|
+
let timeoutId = null;
|
|
589
|
+
const finalizeReject = (reason) => {
|
|
590
|
+
if (settled)
|
|
591
|
+
return;
|
|
592
|
+
settled = true;
|
|
593
|
+
cleanup();
|
|
594
|
+
reject(new Error(reason));
|
|
595
|
+
};
|
|
596
|
+
const cleanup = () => {
|
|
597
|
+
window.removeEventListener('message', onMessage);
|
|
598
|
+
if (closeCheckId != null)
|
|
599
|
+
clearInterval(closeCheckId);
|
|
600
|
+
if (timeoutId != null)
|
|
601
|
+
clearTimeout(timeoutId);
|
|
602
|
+
};
|
|
603
|
+
const onMessage = (event) => {
|
|
604
|
+
if (event.origin !== window.location.origin)
|
|
605
|
+
return;
|
|
606
|
+
if (event.source !== popup)
|
|
607
|
+
return;
|
|
608
|
+
const data = event.data;
|
|
609
|
+
if (!data?.type)
|
|
610
|
+
return;
|
|
611
|
+
const eventProvider = normalizeAuthMethodProviderForMatch(String(data.provider ?? ''));
|
|
612
|
+
const targetProvider = normalizeAuthMethodProviderForMatch(provider);
|
|
613
|
+
if (eventProvider !== targetProvider)
|
|
614
|
+
return;
|
|
615
|
+
if (data.type === 'abstraxn-auth-link-error') {
|
|
616
|
+
finalizeReject(normalizeAuthMethodErrorMessage(String(data.error ?? 'Failed to link auth method')));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (data.type === 'abstraxn-auth-link-done') {
|
|
620
|
+
if (!data.activityBody || data.activityBody.trim() === '') {
|
|
621
|
+
finalizeReject('Invalid linking response');
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
settled = true;
|
|
625
|
+
cleanup();
|
|
626
|
+
activeWallet
|
|
627
|
+
.submitLinkAuthStamped(provider, data.activityBody)
|
|
628
|
+
.then(resolve)
|
|
629
|
+
.catch((submitError) => {
|
|
630
|
+
reject(toAuthMethodError(submitError, 'Failed to finalize auth method link'));
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
window.addEventListener('message', onMessage);
|
|
635
|
+
closeCheckId = setInterval(() => {
|
|
636
|
+
if (popup.closed) {
|
|
637
|
+
finalizeReject('Linking was cancelled');
|
|
638
|
+
}
|
|
639
|
+
}, 200);
|
|
640
|
+
timeoutId = setTimeout(() => {
|
|
641
|
+
finalizeReject('Linking timed out. Please try again.');
|
|
642
|
+
}, timeoutMs);
|
|
643
|
+
});
|
|
644
|
+
}, [wallet]);
|
|
645
|
+
const linkProvider = useCallback(async (provider) => {
|
|
646
|
+
const activeWallet = requireConnectedWallet();
|
|
647
|
+
const normalizedProvider = provider.toLowerCase();
|
|
648
|
+
if (!AUTH_METHOD_LINK_PROVIDER_ALLOWLIST.has(normalizedProvider)) {
|
|
649
|
+
throw new Error(`Unsupported provider for linking: ${provider}`);
|
|
650
|
+
}
|
|
651
|
+
setActionProvider(normalizedProvider);
|
|
652
|
+
setError(null);
|
|
653
|
+
try {
|
|
654
|
+
if (normalizedProvider === 'passkey') {
|
|
655
|
+
await activeWallet.linkPasskey();
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
await runOAuthPopupLink(activeWallet, normalizedProvider);
|
|
659
|
+
}
|
|
660
|
+
await refreshAuthMethods({ force: true });
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
const nextError = toAuthMethodError(err, 'Failed to link auth method');
|
|
664
|
+
setError(nextError.message);
|
|
665
|
+
throw nextError;
|
|
666
|
+
}
|
|
667
|
+
finally {
|
|
668
|
+
setActionProvider(null);
|
|
669
|
+
}
|
|
670
|
+
}, [refreshAuthMethods, requireConnectedWallet, runOAuthPopupLink]);
|
|
671
|
+
const requestEmailLinkOtp = useCallback(async (email) => {
|
|
672
|
+
const activeWallet = requireConnectedWallet();
|
|
673
|
+
const trimmedEmail = email.trim();
|
|
674
|
+
if (!trimmedEmail) {
|
|
675
|
+
throw new Error('Email is required');
|
|
676
|
+
}
|
|
677
|
+
setActionProvider('email');
|
|
678
|
+
setError(null);
|
|
679
|
+
try {
|
|
680
|
+
if (requestOtpForEmailLink) {
|
|
681
|
+
return await requestOtpForEmailLink(trimmedEmail);
|
|
682
|
+
}
|
|
683
|
+
if (loginWithOTP) {
|
|
684
|
+
return await loginWithOTP(trimmedEmail);
|
|
685
|
+
}
|
|
686
|
+
// Fallback to core method directly if context methods are unavailable.
|
|
687
|
+
return await activeWallet.requestOtpForEmailLink(trimmedEmail);
|
|
688
|
+
}
|
|
689
|
+
catch (err) {
|
|
690
|
+
const nextError = toAuthMethodError(err, 'Failed to send email verification code');
|
|
691
|
+
setError(nextError.message);
|
|
692
|
+
throw nextError;
|
|
693
|
+
}
|
|
694
|
+
finally {
|
|
695
|
+
setActionProvider(null);
|
|
696
|
+
}
|
|
697
|
+
}, [loginWithOTP, requestOtpForEmailLink, requireConnectedWallet]);
|
|
698
|
+
const confirmEmailLink = useCallback(async (otpId, otpCode) => {
|
|
699
|
+
const activeWallet = requireConnectedWallet();
|
|
700
|
+
const trimmedOtpId = otpId.trim();
|
|
701
|
+
const trimmedOtpCode = otpCode.trim();
|
|
702
|
+
if (!trimmedOtpId || !trimmedOtpCode) {
|
|
703
|
+
throw new Error('otpId and otpCode are required');
|
|
704
|
+
}
|
|
705
|
+
setActionProvider('email');
|
|
706
|
+
setError(null);
|
|
707
|
+
try {
|
|
708
|
+
await activeWallet.linkEmail(trimmedOtpId, trimmedOtpCode);
|
|
709
|
+
await refreshAuthMethods({ force: true });
|
|
710
|
+
}
|
|
711
|
+
catch (err) {
|
|
712
|
+
const nextError = toAuthMethodError(err, 'Failed to link email');
|
|
713
|
+
setError(nextError.message);
|
|
714
|
+
throw nextError;
|
|
715
|
+
}
|
|
716
|
+
finally {
|
|
717
|
+
setActionProvider(null);
|
|
718
|
+
}
|
|
719
|
+
}, [refreshAuthMethods, requireConnectedWallet]);
|
|
720
|
+
const unlinkProvider = useCallback(async (provider) => {
|
|
721
|
+
const activeWallet = requireConnectedWallet();
|
|
722
|
+
const normalizedProvider = normalizeAuthMethodProviderForApi(provider);
|
|
723
|
+
if (!AUTH_METHOD_UNLINK_PROVIDER_ALLOWLIST.has(normalizedProvider)) {
|
|
724
|
+
throw new Error(`Unsupported provider for unlinking: ${provider}`);
|
|
725
|
+
}
|
|
726
|
+
setActionProvider(normalizedProvider);
|
|
727
|
+
setError(null);
|
|
728
|
+
try {
|
|
729
|
+
await activeWallet.unlinkAuthMethod(normalizedProvider);
|
|
730
|
+
await refreshAuthMethods({ force: true });
|
|
731
|
+
}
|
|
732
|
+
catch (err) {
|
|
733
|
+
const nextError = toAuthMethodError(err, 'Failed to unlink auth method');
|
|
734
|
+
setError(nextError.message);
|
|
735
|
+
throw nextError;
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
setActionProvider(null);
|
|
739
|
+
}
|
|
740
|
+
}, [refreshAuthMethods, requireConnectedWallet]);
|
|
741
|
+
return {
|
|
742
|
+
supportedMethods,
|
|
743
|
+
linkedMethods,
|
|
744
|
+
loading: queryLoading || actionProvider !== null,
|
|
745
|
+
actionProvider,
|
|
746
|
+
error,
|
|
747
|
+
refreshAuthMethods,
|
|
748
|
+
isLinked,
|
|
749
|
+
linkProvider,
|
|
750
|
+
requestEmailLinkOtp,
|
|
751
|
+
confirmEmailLink,
|
|
752
|
+
unlinkProvider,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
417
755
|
/**
|
|
418
756
|
* Hook to access external wallet functionality
|
|
419
757
|
* Returns external wallet connection state and methods
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* when used in a React project
|
|
5
5
|
*/
|
|
6
6
|
export { AbstraxnProvider, useAbstraxnWallet } from './AbstraxnProvider';
|
|
7
|
-
export { useIsConnected, useAddress, useAuthContext, useChainId, useError, useEnableMfa, useDisableMfa, useEmailForOtp, useWallet, useExportWallet, useWhoami, useExternalWallet, useExternalWalletBalance, useExternalWalletChain, useExternalWalletInfo, usePublicClient, useWalletClient, useContract, usePrepareRawTxn, useSignTxn, useSignTypedTx, useSignAndSendTxn, useWaitForTxnReceipt, useReadContract, useExternalWalletClient, useWriteContract, useConnectionType, useSwitchChain, useSignMessage, useEstimateGas, useGetGasPrice, useSolanaConnection, useSolanaPublicKey, useSolanaBalance, usePrepareSolanaTxn, useSolanaExternalWallet, usePrepareSolanaProgramTxn, useSolanaProgramTransaction, useSignSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, } from './hooks';
|
|
7
|
+
export { useIsConnected, useAddress, useAuthContext, useChainId, useError, useEnableMfa, useDisableMfa, useAuthMethodLinking, useEmailForOtp, useWallet, useSmartAccountOwner, useExportWallet, useWhoami, useExternalWallet, useExternalWalletBalance, useExternalWalletChain, useExternalWalletInfo, usePublicClient, useWalletClient, useContract, usePrepareRawTxn, useSignTxn, useSignTypedTx, useSignAndSendTxn, useWaitForTxnReceipt, useReadContract, useExternalWalletClient, useWriteContract, useConnectionType, useSwitchChain, useSignMessage, useEstimateGas, useGetGasPrice, useSolanaConnection, useSolanaPublicKey, useSolanaBalance, usePrepareSolanaTxn, useSolanaExternalWallet, usePrepareSolanaProgramTxn, useSolanaProgramTransaction, useSignSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, } from './hooks';
|
|
8
8
|
export { ConnectButton } from './ConnectButton';
|
|
9
9
|
export type { ConnectButtonProps, ConnectButtonVariant, ConnectButtonSize } from './ConnectButton';
|
|
10
10
|
export { WalletModal } from './WalletModal';
|
|
11
11
|
export type { WalletModalProps } from './WalletModal';
|
|
12
12
|
export { OnboardingUIComponent as OnboardingUI } from './OnboardingUI';
|
|
13
13
|
export type { AbstraxnProviderConfig, AbstraxnUIConfig, AbstraxnContextValue, ChainConfig, } from './types';
|
|
14
|
-
export type { PayloadEncoding, HashFunction, SignTypedTxParams, SolanaAccountMetaInput, SolanaInstructionDataInput, } from './hooks';
|
|
14
|
+
export type { PayloadEncoding, HashFunction, SignTypedTxParams, SmartAccountOwnerOptions, SmartAccountOwnerResult, SolanaAccountMetaInput, SolanaInstructionDataInput, } from './hooks';
|
|
15
15
|
export type { OnboardingUIComponentProps, OnboardingUIComponentRef } from './OnboardingUI';
|
|
16
16
|
export { ALL_CHAINS, EVM_CHAINS, SOLANA_CHAINS, getChainById, getChainsByType, getDefaultChains, toCoreChain, type ChainData, } from './chains';
|
|
17
17
|
export { CONNECTORS, getConnectorMeta, isOAuthConnector, isWalletConnector, isAuthConnector, type ConnectorType, type ConnectorMeta, } from './connectors';
|
package/dist/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* when used in a React project
|
|
5
5
|
*/
|
|
6
6
|
export { AbstraxnProvider, useAbstraxnWallet } from './AbstraxnProvider';
|
|
7
|
-
export { useIsConnected, useAddress, useAuthContext, useChainId, useError, useEnableMfa, useDisableMfa, useEmailForOtp, useWallet, useExportWallet, useWhoami, useExternalWallet, useExternalWalletBalance, useExternalWalletChain, useExternalWalletInfo, usePublicClient, useWalletClient, useContract, usePrepareRawTxn, useSignTxn, useSignTypedTx, useSignAndSendTxn, useWaitForTxnReceipt, useReadContract, useExternalWalletClient, useWriteContract, useConnectionType, useSwitchChain, useSignMessage, useEstimateGas, useGetGasPrice, useSolanaConnection, useSolanaPublicKey, useSolanaBalance, usePrepareSolanaTxn, useSolanaExternalWallet, usePrepareSolanaProgramTxn, useSolanaProgramTransaction, useSignSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, } from './hooks';
|
|
7
|
+
export { useIsConnected, useAddress, useAuthContext, useChainId, useError, useEnableMfa, useDisableMfa, useAuthMethodLinking, useEmailForOtp, useWallet, useSmartAccountOwner, useExportWallet, useWhoami, useExternalWallet, useExternalWalletBalance, useExternalWalletChain, useExternalWalletInfo, usePublicClient, useWalletClient, useContract, usePrepareRawTxn, useSignTxn, useSignTypedTx, useSignAndSendTxn, useWaitForTxnReceipt, useReadContract, useExternalWalletClient, useWriteContract, useConnectionType, useSwitchChain, useSignMessage, useEstimateGas, useGetGasPrice, useSolanaConnection, useSolanaPublicKey, useSolanaBalance, usePrepareSolanaTxn, useSolanaExternalWallet, usePrepareSolanaProgramTxn, useSolanaProgramTransaction, useSignSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, } from './hooks';
|
|
8
8
|
export { ConnectButton } from './ConnectButton';
|
|
9
9
|
export { WalletModal } from './WalletModal';
|
|
10
10
|
export { OnboardingUIComponent as OnboardingUI } from './OnboardingUI';
|
package/dist/src/types.d.ts
CHANGED
|
@@ -273,7 +273,7 @@ export interface AbstraxnContextValue {
|
|
|
273
273
|
availableChains?: import("./chains").ChainData[];
|
|
274
274
|
walletBalance?: bigint | null;
|
|
275
275
|
/** Refetch wallet balance from RPC (Abstraxn or external). Call when UI screen changes (e.g. back button, modal open/close). */
|
|
276
|
-
refetchBalance?: () => void
|
|
276
|
+
refetchBalance?: () => Promise<void>;
|
|
277
277
|
connectionType?: string | null;
|
|
278
278
|
emailForOtp?: string;
|
|
279
279
|
setEmailForOtp?: (email: string) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abstraxn/signer-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "React SDK for Abstraxn Wallet - React components, hooks, and providers for seamless Web3 wallet integration",
|
|
5
5
|
"main": "./dist/src/index.js",
|
|
6
6
|
"module": "./dist/src/index.js",
|