@abstraxn/signer-react 1.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.
Files changed (178) hide show
  1. package/README.md +114 -0
  2. package/dist/src/AbstraxnProvider.d.ts +9 -0
  3. package/dist/src/AbstraxnProvider.js +21 -0
  4. package/dist/src/AbstraxnProvider.js.map +1 -0
  5. package/dist/src/ConnectButton.css +217 -0
  6. package/dist/src/ConnectButton.d.ts +71 -0
  7. package/dist/src/ConnectButton.js +102 -0
  8. package/dist/src/ConnectButton.js.map +1 -0
  9. package/dist/src/ExternalWalletButtons.css +319 -0
  10. package/dist/src/ExternalWalletButtons.d.ts +56 -0
  11. package/dist/src/ExternalWalletButtons.js +272 -0
  12. package/dist/src/ExternalWalletButtons.js.map +1 -0
  13. package/dist/src/OnboardingUI.d.ts +63 -0
  14. package/dist/src/OnboardingUI.js +66 -0
  15. package/dist/src/OnboardingUI.js.map +1 -0
  16. package/dist/src/WalletModal.css +2319 -0
  17. package/dist/src/WalletModal.d.ts +7 -0
  18. package/dist/src/WalletModal.js +322 -0
  19. package/dist/src/WalletModal.js.map +1 -0
  20. package/dist/src/chains.d.ts +56 -0
  21. package/dist/src/chains.js +291 -0
  22. package/dist/src/chains.js.map +1 -0
  23. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.d.ts +12 -0
  24. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js +146 -0
  25. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js.map +1 -0
  26. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.d.ts +25 -0
  27. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +3086 -0
  28. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -0
  29. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.d.ts +8 -0
  30. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js +46 -0
  31. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js.map +1 -0
  32. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.d.ts +8 -0
  33. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js +12 -0
  34. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js.map +1 -0
  35. package/dist/src/components/AbstraxnProvider/context.d.ts +2 -0
  36. package/dist/src/components/AbstraxnProvider/context.js +6 -0
  37. package/dist/src/components/AbstraxnProvider/context.js.map +1 -0
  38. package/dist/src/components/AbstraxnProvider/index.d.ts +6 -0
  39. package/dist/src/components/AbstraxnProvider/index.js +7 -0
  40. package/dist/src/components/AbstraxnProvider/index.js.map +1 -0
  41. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +30 -0
  42. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js +49 -0
  43. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js.map +1 -0
  44. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.d.ts +2 -0
  45. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js +13 -0
  46. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js.map +1 -0
  47. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.d.ts +22 -0
  48. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js +242 -0
  49. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js.map +1 -0
  50. package/dist/src/components/AbstraxnProvider/useWalletInitialization.d.ts +25 -0
  51. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +539 -0
  52. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js.map +1 -0
  53. package/dist/src/components/AbstraxnProvider/utils.d.ts +41 -0
  54. package/dist/src/components/AbstraxnProvider/utils.js +139 -0
  55. package/dist/src/components/AbstraxnProvider/utils.js.map +1 -0
  56. package/dist/src/components/OnboardingUI/OnboardingUI.css +1062 -0
  57. package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +15 -0
  58. package/dist/src/components/OnboardingUI/OnboardingUIReact.js +318 -0
  59. package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -0
  60. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +265 -0
  61. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +3782 -0
  62. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -0
  63. package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +16 -0
  64. package/dist/src/components/OnboardingUI/components/EmailForm.js +27 -0
  65. package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -0
  66. package/dist/src/components/OnboardingUI/components/Modal.d.ts +14 -0
  67. package/dist/src/components/OnboardingUI/components/Modal.js +61 -0
  68. package/dist/src/components/OnboardingUI/components/Modal.js.map +1 -0
  69. package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +20 -0
  70. package/dist/src/components/OnboardingUI/components/OtpForm.js +72 -0
  71. package/dist/src/components/OnboardingUI/components/OtpForm.js.map +1 -0
  72. package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +14 -0
  73. package/dist/src/components/OnboardingUI/components/PasskeyButton.js +22 -0
  74. package/dist/src/components/OnboardingUI/components/PasskeyButton.js.map +1 -0
  75. package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +15 -0
  76. package/dist/src/components/OnboardingUI/components/SocialButtons.js +20 -0
  77. package/dist/src/components/OnboardingUI/components/SocialButtons.js.map +1 -0
  78. package/dist/src/components/OnboardingUI/components/index.d.ts +13 -0
  79. package/dist/src/components/OnboardingUI/components/index.js +9 -0
  80. package/dist/src/components/OnboardingUI/components/index.js.map +1 -0
  81. package/dist/src/components/OnboardingUI/hooks/index.d.ts +7 -0
  82. package/dist/src/components/OnboardingUI/hooks/index.js +6 -0
  83. package/dist/src/components/OnboardingUI/hooks/index.js.map +1 -0
  84. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.d.ts +11 -0
  85. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js +243 -0
  86. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js.map +1 -0
  87. package/dist/src/components/OnboardingUI/hooks/useOnboarding.d.ts +21 -0
  88. package/dist/src/components/OnboardingUI/hooks/useOnboarding.js +153 -0
  89. package/dist/src/components/OnboardingUI/hooks/useOnboarding.js.map +1 -0
  90. package/dist/src/components/OnboardingUI/index.d.ts +12 -0
  91. package/dist/src/components/OnboardingUI/index.js +15 -0
  92. package/dist/src/components/OnboardingUI/index.js.map +1 -0
  93. package/dist/src/components/QRCode.d.ts +13 -0
  94. package/dist/src/components/QRCode.js +6 -0
  95. package/dist/src/components/QRCode.js.map +1 -0
  96. package/dist/src/components/WalletModal/components/ChainSelector.css +327 -0
  97. package/dist/src/components/WalletModal/components/ChainSelector.d.ts +11 -0
  98. package/dist/src/components/WalletModal/components/ChainSelector.js +75 -0
  99. package/dist/src/components/WalletModal/components/ChainSelector.js.map +1 -0
  100. package/dist/src/components/WalletModal/components/ExportKeyModal.css +134 -0
  101. package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +14 -0
  102. package/dist/src/components/WalletModal/components/ExportKeyModal.js +26 -0
  103. package/dist/src/components/WalletModal/components/ExportKeyModal.js.map +1 -0
  104. package/dist/src/components/WalletModal/components/ExportWarningModal.css +107 -0
  105. package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +17 -0
  106. package/dist/src/components/WalletModal/components/ExportWarningModal.js +20 -0
  107. package/dist/src/components/WalletModal/components/ExportWarningModal.js.map +1 -0
  108. package/dist/src/components/WalletModal/components/ManageWalletModal.css +246 -0
  109. package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +12 -0
  110. package/dist/src/components/WalletModal/components/ManageWalletModal.js +36 -0
  111. package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -0
  112. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +127 -0
  113. package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +17 -0
  114. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +10 -0
  115. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js.map +1 -0
  116. package/dist/src/components/WalletModal/components/ReceiveModal.css +136 -0
  117. package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +8 -0
  118. package/dist/src/components/WalletModal/components/ReceiveModal.js +22 -0
  119. package/dist/src/components/WalletModal/components/ReceiveModal.js.map +1 -0
  120. package/dist/src/components/WalletModal/components/SendModal.css +277 -0
  121. package/dist/src/components/WalletModal/components/SendModal.d.ts +16 -0
  122. package/dist/src/components/WalletModal/components/SendModal.js +219 -0
  123. package/dist/src/components/WalletModal/components/SendModal.js.map +1 -0
  124. package/dist/src/components/WalletModal/components/SuccessModal.css +85 -0
  125. package/dist/src/components/WalletModal/components/SuccessModal.d.ts +13 -0
  126. package/dist/src/components/WalletModal/components/SuccessModal.js +8 -0
  127. package/dist/src/components/WalletModal/components/SuccessModal.js.map +1 -0
  128. package/dist/src/components/WalletModal/components/TokenSelectorModal.css +240 -0
  129. package/dist/src/components/WalletModal/components/TokenSelectorModal.d.ts +21 -0
  130. package/dist/src/components/WalletModal/components/TokenSelectorModal.js +44 -0
  131. package/dist/src/components/WalletModal/components/TokenSelectorModal.js.map +1 -0
  132. package/dist/src/components/WalletModal/components/UserAvatar.d.ts +9 -0
  133. package/dist/src/components/WalletModal/components/UserAvatar.js +31 -0
  134. package/dist/src/components/WalletModal/components/UserAvatar.js.map +1 -0
  135. package/dist/src/components/WalletModal/components/index.d.ts +23 -0
  136. package/dist/src/components/WalletModal/components/index.js +14 -0
  137. package/dist/src/components/WalletModal/components/index.js.map +1 -0
  138. package/dist/src/components/WalletModal/hooks/index.d.ts +6 -0
  139. package/dist/src/components/WalletModal/hooks/index.js +7 -0
  140. package/dist/src/components/WalletModal/hooks/index.js.map +1 -0
  141. package/dist/src/components/WalletModal/hooks/useAddressValidation.d.ts +4 -0
  142. package/dist/src/components/WalletModal/hooks/useAddressValidation.js +17 -0
  143. package/dist/src/components/WalletModal/hooks/useAddressValidation.js.map +1 -0
  144. package/dist/src/components/WalletModal/hooks/useAmountValidation.d.ts +4 -0
  145. package/dist/src/components/WalletModal/hooks/useAmountValidation.js +29 -0
  146. package/dist/src/components/WalletModal/hooks/useAmountValidation.js.map +1 -0
  147. package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +20 -0
  148. package/dist/src/components/WalletModal/hooks/useSendTransaction.js +55 -0
  149. package/dist/src/components/WalletModal/hooks/useSendTransaction.js.map +1 -0
  150. package/dist/src/components/WalletModal/index.d.ts +5 -0
  151. package/dist/src/components/WalletModal/index.js +7 -0
  152. package/dist/src/components/WalletModal/index.js.map +1 -0
  153. package/dist/src/components/WalletModal/utils/addressUtils.d.ts +19 -0
  154. package/dist/src/components/WalletModal/utils/addressUtils.js +62 -0
  155. package/dist/src/components/WalletModal/utils/addressUtils.js.map +1 -0
  156. package/dist/src/components/WalletModal/utils/formatUtils.d.ts +20 -0
  157. package/dist/src/components/WalletModal/utils/formatUtils.js +47 -0
  158. package/dist/src/components/WalletModal/utils/formatUtils.js.map +1 -0
  159. package/dist/src/components/WalletModal/utils/index.d.ts +5 -0
  160. package/dist/src/components/WalletModal/utils/index.js +6 -0
  161. package/dist/src/components/WalletModal/utils/index.js.map +1 -0
  162. package/dist/src/connectors.d.ts +27 -0
  163. package/dist/src/connectors.js +70 -0
  164. package/dist/src/connectors.js.map +1 -0
  165. package/dist/src/hooks.d.ts +13136 -0
  166. package/dist/src/hooks.js +1358 -0
  167. package/dist/src/hooks.js.map +1 -0
  168. package/dist/src/index.d.ts +17 -0
  169. package/dist/src/index.js +14 -0
  170. package/dist/src/index.js.map +1 -0
  171. package/dist/src/types.d.ts +224 -0
  172. package/dist/src/types.js +2 -0
  173. package/dist/src/types.js.map +1 -0
  174. package/dist/src/wagmiConfig.d.ts +16 -0
  175. package/dist/src/wagmiConfig.js +103 -0
  176. package/dist/src/wagmiConfig.js.map +1 -0
  177. package/dist/tsconfig.tsbuildinfo +1 -0
  178. package/package.json +70 -0
@@ -0,0 +1,1358 @@
1
+ /**
2
+ * React Hooks for Abstraxn Wallet SDK
3
+ */
4
+ import { useState, useCallback, useEffect, useMemo } from 'react';
5
+ import { useAbstraxnWallet } from './AbstraxnProvider';
6
+ import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, parseEther, encodeFunctionData } from 'viem';
7
+ import { useWalletClient as useWagmiWalletClient, useAccount, useConfig, useChainId as useWagmiChainId, useSwitchChain as useWagmiSwitchChain, useSignMessage as useWagmiSignMessage } from 'wagmi';
8
+ import { getWalletClient, switchChain } from '@wagmi/core';
9
+ import { getConnectorMeta } from './connectors';
10
+ import { getChainById } from './chains';
11
+ /**
12
+ * Hook to check if wallet is connected
13
+ */
14
+ export function useIsConnected() {
15
+ const { isConnected } = useAbstraxnWallet();
16
+ return isConnected;
17
+ }
18
+ /**
19
+ * Hook to get wallet address
20
+ */
21
+ export function useAddress() {
22
+ const { address } = useAbstraxnWallet();
23
+ return address;
24
+ }
25
+ /**
26
+ * Hook to get current user and whoami information
27
+ * Returns an object with both user and whoami data
28
+ */
29
+ export function useAuthContext() {
30
+ const { user, whoami } = useAbstraxnWallet();
31
+ return { user, whoami };
32
+ }
33
+ /**
34
+ * Hook to get current chain ID
35
+ */
36
+ export function useChainId() {
37
+ const { chainId } = useAbstraxnWallet();
38
+ return chainId;
39
+ }
40
+ /**
41
+ * Hook to get error state
42
+ */
43
+ export function useError() {
44
+ const { error } = useAbstraxnWallet();
45
+ return error;
46
+ }
47
+ // Re-export core hook for modules that import from './hooks'
48
+ export { useAbstraxnWallet } from './AbstraxnProvider';
49
+ /**
50
+ * Hook to get wallet instance (for advanced usage)
51
+ */
52
+ export function useWallet() {
53
+ const { wallet } = useAbstraxnWallet();
54
+ return wallet;
55
+ }
56
+ /**
57
+ * Hook to export wallet private key
58
+ * Returns the export bundle containing the private key
59
+ * Automatically picks the connected wallet address (EVM or Solana) from whoami
60
+ */
61
+ export function useExportWallet() {
62
+ const { wallet, isConnected, address } = useAbstraxnWallet();
63
+ const exportWallet = async (targetPublicKey, privateKey, blockchain) => {
64
+ if (!isConnected || !wallet) {
65
+ throw new Error('Wallet is not connected');
66
+ }
67
+ // Pass blockchain type to wallet.exportWallet, which will load address from whoami
68
+ return await wallet.exportWallet(targetPublicKey, privateKey, blockchain);
69
+ };
70
+ return { exportWallet, isConnected, address };
71
+ }
72
+ /**
73
+ * Hook to call whoami API
74
+ * Returns whoami data, loading state, error, and a refresh function
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * const { whoami, loading, error, refreshWhoami } = useWhoami();
79
+ *
80
+ * useEffect(() => {
81
+ * refreshWhoami();
82
+ * }, []);
83
+ * ```
84
+ */
85
+ export function useWhoami() {
86
+ const { isConnected, whoami: cachedWhoami, refreshWhoami: refreshWhoamiFromContext, loading: contextLoading, error: contextError } = useAbstraxnWallet();
87
+ const [whoami, setWhoami] = useState(cachedWhoami);
88
+ const [loading, setLoading] = useState(false);
89
+ const [error, setError] = useState(null);
90
+ const refreshWhoami = useCallback(async () => {
91
+ if (!isConnected) {
92
+ setError(new Error('Wallet is not connected'));
93
+ return null;
94
+ }
95
+ setLoading(true);
96
+ setError(null);
97
+ try {
98
+ // Use refreshWhoami from context which updates both API and context state
99
+ // This will handle the check for external wallets and connection status internally
100
+ const whoamiData = await refreshWhoamiFromContext();
101
+ setWhoami(whoamiData);
102
+ return whoamiData;
103
+ }
104
+ catch (err) {
105
+ // Don't throw error if wallet is not connected (might be during disconnect)
106
+ const error = err instanceof Error ? err : new Error('Failed to refresh whoami information');
107
+ // Only set error if it's not a connection error
108
+ if (!error.message.includes('not connected') && !error.message.includes('Wallet is not connected')) {
109
+ setError(error);
110
+ }
111
+ return null;
112
+ }
113
+ finally {
114
+ setLoading(false);
115
+ }
116
+ }, [isConnected, refreshWhoamiFromContext]);
117
+ // Update whoami when cached value changes
118
+ useEffect(() => {
119
+ if (cachedWhoami) {
120
+ setWhoami(cachedWhoami);
121
+ }
122
+ }, [cachedWhoami]);
123
+ // Use context loading/error if hook is not in loading state
124
+ const displayLoading = loading || contextLoading;
125
+ const displayError = error || contextError;
126
+ return {
127
+ whoami,
128
+ loading: displayLoading,
129
+ error: displayError,
130
+ refreshWhoami,
131
+ isConnected,
132
+ };
133
+ }
134
+ /**
135
+ * Hook to access external wallet functionality
136
+ * Returns external wallet connection state and methods
137
+ */
138
+ export function useExternalWallet() {
139
+ const { connectExternalWallet, disconnectExternalWallet, isExternalWalletConnected, externalWalletAddress, externalWalletChainId, externalWalletBalance, externalWalletNetwork, availableConnectors, switchExternalWalletChain, } = useAbstraxnWallet();
140
+ return {
141
+ connectExternalWallet: connectExternalWallet || (async () => {
142
+ throw new Error('External wallets are not enabled');
143
+ }),
144
+ disconnectExternalWallet: disconnectExternalWallet || (async () => {
145
+ throw new Error('External wallets are not enabled');
146
+ }),
147
+ isExternalWalletConnected: isExternalWalletConnected ?? false,
148
+ externalWalletAddress: externalWalletAddress ?? null,
149
+ externalWalletChainId: externalWalletChainId ?? null,
150
+ externalWalletBalance: externalWalletBalance ?? null,
151
+ externalWalletNetwork: externalWalletNetwork ?? null,
152
+ availableConnectors: availableConnectors ?? [],
153
+ switchExternalWalletChain: switchExternalWalletChain || (async () => {
154
+ throw new Error('External wallets are not enabled');
155
+ }),
156
+ };
157
+ }
158
+ /**
159
+ * Hook to get external wallet balance
160
+ * Returns balance in wei (BigInt) and formatted balance
161
+ */
162
+ export function useExternalWalletBalance() {
163
+ const { externalWalletBalance, externalWalletAddress, isExternalWalletConnected } = useExternalWallet();
164
+ const [formattedBalance, setFormattedBalance] = useState('0');
165
+ useEffect(() => {
166
+ if (externalWalletBalance && externalWalletBalance > 0n) {
167
+ // Convert from wei to ETH (18 decimals)
168
+ const balanceInEth = Number(externalWalletBalance) / 1e18;
169
+ setFormattedBalance(balanceInEth.toFixed(6));
170
+ }
171
+ else {
172
+ setFormattedBalance('0');
173
+ }
174
+ }, [externalWalletBalance]);
175
+ return {
176
+ balance: externalWalletBalance,
177
+ formattedBalance,
178
+ address: externalWalletAddress,
179
+ isConnected: isExternalWalletConnected,
180
+ };
181
+ }
182
+ /**
183
+ * Hook to get external wallet chain information
184
+ * Returns chainId, network name, and switch chain function
185
+ */
186
+ export function useExternalWalletChain() {
187
+ const { externalWalletChainId, externalWalletNetwork, switchExternalWalletChain, isExternalWalletConnected } = useExternalWallet();
188
+ return {
189
+ chainId: externalWalletChainId,
190
+ network: externalWalletNetwork,
191
+ switchChain: switchExternalWalletChain,
192
+ isConnected: isExternalWalletConnected,
193
+ };
194
+ }
195
+ /**
196
+ * Comprehensive hook for external wallet information
197
+ * Returns chainId, network, balance, switchChain function, connection status, and walletClient
198
+ * This is the main hook to use for external wallet operations
199
+ *
200
+ * @example
201
+ * ```tsx
202
+ * const { chainId, network, balance, formattedBalance, switchChain, isConnected, address, walletClient } = useExternalWalletInfo();
203
+ *
204
+ * // Switch chain
205
+ * await switchChain(137); // Switch to Polygon
206
+ *
207
+ * // Send transaction using walletClient
208
+ * const txHash = await walletClient.sendTransaction({
209
+ * to: '0x...',
210
+ * value: '0.1',
211
+ * });
212
+ *
213
+ * // Sign message using walletClient
214
+ * const signature = await walletClient.signMessage('Hello World');
215
+ *
216
+ * // Display info
217
+ * <div>
218
+ * <p>Network: {network}</p>
219
+ * <p>Balance: {formattedBalance} ETH</p>
220
+ * </div>
221
+ * ```
222
+ */
223
+ export function useExternalWalletInfo() {
224
+ const { externalWalletChainId, externalWalletNetwork, switchExternalWalletChain, isExternalWalletConnected, externalWalletAddress, externalWalletBalance, } = useExternalWallet();
225
+ const { sendTransaction, signMessage, signTransaction, switchChain, } = useAbstraxnWallet();
226
+ const [formattedBalance, setFormattedBalance] = useState('0');
227
+ useEffect(() => {
228
+ if (externalWalletBalance && externalWalletBalance > 0n) {
229
+ // Convert from wei to ETH (18 decimals)
230
+ const balanceInEth = Number(externalWalletBalance) / 1e18;
231
+ setFormattedBalance(balanceInEth.toFixed(6));
232
+ }
233
+ else {
234
+ setFormattedBalance('0');
235
+ }
236
+ }, [externalWalletBalance]);
237
+ // Create walletClient object for external wallet transactions
238
+ const walletClient = {
239
+ /**
240
+ * Send a transaction using external wallet
241
+ * @param tx - Transaction request object
242
+ * @returns Promise resolving to transaction response with hash
243
+ */
244
+ sendTransaction: async (tx) => {
245
+ if (!isExternalWalletConnected) {
246
+ throw new Error('External wallet is not connected');
247
+ }
248
+ return await sendTransaction(tx);
249
+ },
250
+ /**
251
+ * Sign a message using external wallet
252
+ * @param message - Message string to sign
253
+ * @returns Promise resolving to signature string
254
+ */
255
+ signMessage: async (message) => {
256
+ if (!isExternalWalletConnected) {
257
+ throw new Error('External wallet is not connected');
258
+ }
259
+ return await signMessage(message);
260
+ },
261
+ /**
262
+ * Sign a transaction using external wallet
263
+ * @param tx - Transaction request object
264
+ * @returns Promise resolving to signed transaction string
265
+ */
266
+ signTransaction: async (tx) => {
267
+ if (!isExternalWalletConnected) {
268
+ throw new Error('External wallet is not connected');
269
+ }
270
+ return await signTransaction(tx);
271
+ },
272
+ /**
273
+ * Switch chain on external wallet
274
+ * @param chainId - Chain ID to switch to
275
+ * @returns Promise that resolves when chain is switched
276
+ */
277
+ switchChain: async (chainId) => {
278
+ if (!isExternalWalletConnected) {
279
+ throw new Error('External wallet is not connected');
280
+ }
281
+ return await switchChain(chainId);
282
+ },
283
+ };
284
+ return {
285
+ // Chain information
286
+ chainId: externalWalletChainId,
287
+ network: externalWalletNetwork,
288
+ // Balance information
289
+ balance: externalWalletBalance, // BigInt in wei
290
+ formattedBalance, // String formatted as ETH (e.g., "0.123456")
291
+ // Chain switching
292
+ switchChain: switchExternalWalletChain,
293
+ // Connection status
294
+ isConnected: isExternalWalletConnected,
295
+ address: externalWalletAddress,
296
+ // Wallet client for executing transactions
297
+ walletClient,
298
+ };
299
+ }
300
+ /**
301
+ * Hook to create a publicClient using viem
302
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
303
+ *
304
+ * @param chain - Viem chain object (can use from 'viem/chains' or create custom)
305
+ * @param rpcUrl - RPC URL for the chain
306
+ * @returns Object with publicClient instance from viem
307
+ *
308
+ * @example
309
+ * ```tsx
310
+ * import { usePublicClient } from '@abstraxn/signer-react';
311
+ * import { polygonAmoy } from 'viem/chains';
312
+ *
313
+ * function MyComponent() {
314
+ * const { publicClient } = usePublicClient(
315
+ * polygonAmoy,
316
+ * 'https://rpc-amoy.polygon.technology'
317
+ * );
318
+ *
319
+ * // Read operations
320
+ * const balance = await publicClient.getBalance({ address: '0x...' });
321
+ * const tokenBalance = await publicClient.readContract({
322
+ * address: '0x...',
323
+ * abi: erc20Abi,
324
+ * functionName: 'balanceOf',
325
+ * args: ['0x...'],
326
+ * });
327
+ * }
328
+ * ```
329
+ */
330
+ export function usePublicClient(chain, rpcUrl) {
331
+ const publicClient = useMemo(() => {
332
+ if (!chain || !rpcUrl) {
333
+ throw new Error('Chain and RPC URL are required');
334
+ }
335
+ return createPublicClient({
336
+ chain,
337
+ transport: http(rpcUrl),
338
+ });
339
+ }, [chain, rpcUrl]);
340
+ return { publicClient };
341
+ }
342
+ /**
343
+ * Hook to create a walletClient using viem
344
+ * Used ONLY to broadcast signed transactions. No private key involved.
345
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
346
+ *
347
+ * @param chain - Viem chain object (can use from 'viem/chains' or create custom)
348
+ * @param rpcUrl - RPC URL for the chain (optional, will use chain's default RPC if not provided)
349
+ * @returns Object with walletClient instance from viem
350
+ *
351
+ * @example
352
+ * ```tsx
353
+ * import { useWalletClient } from '@abstraxn/signer-react';
354
+ * import { polygonAmoy } from 'viem/chains';
355
+ *
356
+ * function MyComponent() {
357
+ * const { walletClient } = useWalletClient(
358
+ * polygonAmoy,
359
+ * 'https://rpc-amoy.polygon.technology'
360
+ * );
361
+ *
362
+ * // Broadcast signed transaction
363
+ * const hash = await walletClient.sendRawTransaction({
364
+ * serializedTransaction: signedTx,
365
+ * });
366
+ * }
367
+ * ```
368
+ */
369
+ export function useWalletClient(chain, rpcUrl) {
370
+ const walletClient = useMemo(() => {
371
+ if (!chain) {
372
+ throw new Error('Chain is required');
373
+ }
374
+ // Extract RPC URL from chain if not provided
375
+ let finalRpcUrl = rpcUrl;
376
+ if (!finalRpcUrl) {
377
+ if (chain.rpcUrls?.default?.http) {
378
+ finalRpcUrl = Array.isArray(chain.rpcUrls.default.http)
379
+ ? chain.rpcUrls.default.http[0]
380
+ : chain.rpcUrls.default.http;
381
+ }
382
+ else if (chain.rpcUrls?.public?.http) {
383
+ finalRpcUrl = Array.isArray(chain.rpcUrls.public.http)
384
+ ? chain.rpcUrls.public.http[0]
385
+ : chain.rpcUrls.public.http;
386
+ }
387
+ }
388
+ if (!finalRpcUrl) {
389
+ throw new Error('RPC URL is required. Either provide it as a parameter or ensure the chain has an RPC URL configured.');
390
+ }
391
+ return createWalletClient({
392
+ chain: chain,
393
+ transport: http(finalRpcUrl),
394
+ });
395
+ }, [chain, rpcUrl]);
396
+ return { walletClient };
397
+ }
398
+ /**
399
+ * Hook to create a contract instance using viem
400
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
401
+ *
402
+ * @param address - Contract address
403
+ * @param abi - Contract ABI
404
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
405
+ * @returns Object with contract instance from viem
406
+ *
407
+ * @example
408
+ * ```tsx
409
+ * import { useContract, usePublicClient } from '@abstraxn/signer-react';
410
+ * import { polygonAmoy } from 'viem/chains';
411
+ * import erc20Abi from './erc20Abi.json';
412
+ *
413
+ * function MyComponent() {
414
+ * const { publicClient } = usePublicClient(
415
+ * polygonAmoy,
416
+ * 'https://rpc-amoy.polygon.technology'
417
+ * );
418
+ *
419
+ * const { contract } = useContract({
420
+ * address: '0x...',
421
+ * abi: erc20Abi,
422
+ * provider: publicClient,
423
+ * });
424
+ *
425
+ * // Read from contract
426
+ * const balance = await contract.read.balanceOf(['0x...']);
427
+ *
428
+ * // Write to contract (requires walletClient)
429
+ * const hash = await contract.write.transfer(['0x...', '1000000000000000000']);
430
+ * }
431
+ * ```
432
+ */
433
+ export function useContract({ address, abi, provider, }) {
434
+ const contract = useMemo(() => {
435
+ if (!address) {
436
+ throw new Error('Contract address is required');
437
+ }
438
+ if (!abi) {
439
+ throw new Error('Contract ABI is required');
440
+ }
441
+ if (!provider) {
442
+ throw new Error('Provider (publicClient) is required');
443
+ }
444
+ return getContract({
445
+ address,
446
+ abi,
447
+ client: provider,
448
+ });
449
+ }, [address, abi, provider]);
450
+ return { contract };
451
+ }
452
+ /**
453
+ * Hook to prepare raw transaction data with encoding support
454
+ * Returns { to, value, data } for both native transfers and contract calls
455
+ * Can encode function data internally when ABI, functionName, and args are provided
456
+ *
457
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
458
+ * @returns Object with prepareRawTxn function
459
+ *
460
+ * @example
461
+ * ```tsx
462
+ * // Native transfer
463
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
464
+ * const nativeTx = await prepareRawTxn({
465
+ * from: address!,
466
+ * to: '0x...',
467
+ * value: '0.001', // ETH amount
468
+ * });
469
+ * // Returns: { to: '0x...', value: '0x...', data: '0x' }
470
+ *
471
+ * // Contract call with encoding (recommended)
472
+ * const contractTx = await prepareRawTxn({
473
+ * from: address!,
474
+ * to: '0x...', // Contract address
475
+ * abi: erc20Abi,
476
+ * functionName: 'transfer',
477
+ * args: ['0x...', '0.001'], // Automatically converts ETH to wei
478
+ * value: '0', // Optional ETH value
479
+ * });
480
+ * // Returns: { to: '0x...', value: '0x', data: '0xa9059cbb...' }
481
+ *
482
+ * // Contract call with pre-encoded data
483
+ * const contractTx2 = await prepareRawTxn({
484
+ * from: address!,
485
+ * to: '0x...',
486
+ * data: '0xa9059cbb...', // Already encoded
487
+ * });
488
+ * // Returns: { to: '0x...', value: '0x', data: '0xa9059cbb...' }
489
+ * ```
490
+ */
491
+ export function usePrepareRawTxn(provider) {
492
+ const prepareRawTxn = useCallback(async ({ from, to, value, data, abi, functionName, args, }) => {
493
+ if (!provider) {
494
+ throw new Error('Provider (publicClient) is required');
495
+ }
496
+ if (!from) {
497
+ throw new Error('From address is required');
498
+ }
499
+ if (!to) {
500
+ throw new Error('To address is required');
501
+ }
502
+ let finalValue;
503
+ let finalData;
504
+ // Check if we need to encode function data
505
+ const needsEncoding = abi && functionName;
506
+ const hasPreEncodedData = data && data !== '0x';
507
+ if (needsEncoding && hasPreEncodedData) {
508
+ throw new Error('Cannot provide both encoded data and encoding parameters (abi/functionName). Use one or the other.');
509
+ }
510
+ // Determine if this is a native transfer
511
+ const isNativeTransfer = !needsEncoding && !hasPreEncodedData;
512
+ if (isNativeTransfer) {
513
+ // Native transfer: convert value to wei and set data to '0x'
514
+ if (!value || value === '0' || value === 0) {
515
+ throw new Error('Value is required for native transfer');
516
+ }
517
+ // Convert value to wei
518
+ const valueStr = typeof value === 'string' ? value.trim() : String(value);
519
+ let valueInWei;
520
+ try {
521
+ valueInWei = parseEther(valueStr);
522
+ }
523
+ catch (error) {
524
+ throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
525
+ }
526
+ // Convert to hex string
527
+ finalValue = `0x${valueInWei.toString(16)}`;
528
+ finalData = '0x';
529
+ }
530
+ else {
531
+ // Contract call: encode data if needed, or use provided data
532
+ if (needsEncoding) {
533
+ // Encode function data
534
+ if (!abi) {
535
+ throw new Error('ABI is required for encoding function data');
536
+ }
537
+ if (!functionName) {
538
+ throw new Error('Function name is required for encoding function data');
539
+ }
540
+ // Process args to convert ETH amounts to wei for transfer functions
541
+ const processedArgs = (args || []).map((arg, index) => {
542
+ // Check if this is likely an amount parameter for transfer/transferFrom functions
543
+ const isAmountParam = (functionName === 'transfer' || functionName === 'transferFrom') && index === 1;
544
+ if (isAmountParam) {
545
+ // If arg is a string or number that looks like ETH (has decimal point)
546
+ if (typeof arg === 'string' && arg.includes('.')) {
547
+ try {
548
+ // Convert ETH to wei
549
+ const weiAmount = parseEther(arg);
550
+ return weiAmount.toString();
551
+ }
552
+ catch (error) {
553
+ throw new Error(`Failed to convert amount "${arg}" to wei. Make sure it's a valid number string (e.g., "0.001").`);
554
+ }
555
+ }
556
+ else if (typeof arg === 'number' && arg % 1 !== 0) {
557
+ // Number with decimal places - convert to wei
558
+ try {
559
+ const weiAmount = parseEther(arg.toString());
560
+ return weiAmount.toString();
561
+ }
562
+ catch (error) {
563
+ throw new Error(`Failed to convert amount ${arg} to wei. Make sure it's a valid number.`);
564
+ }
565
+ }
566
+ }
567
+ // For other args, just return as-is
568
+ return arg;
569
+ });
570
+ try {
571
+ finalData = encodeFunctionData({
572
+ abi: abi,
573
+ functionName: functionName,
574
+ args: processedArgs,
575
+ });
576
+ }
577
+ catch (error) {
578
+ if (error instanceof Error && error.message.includes('BigInt')) {
579
+ throw new Error(`Failed to encode function data: ${error.message}. Make sure all numeric arguments are in the correct format (e.g., use wei for amounts, not ETH).`);
580
+ }
581
+ throw error;
582
+ }
583
+ }
584
+ else if (hasPreEncodedData) {
585
+ // Use provided encoded data
586
+ finalData = data;
587
+ }
588
+ else {
589
+ throw new Error('Either provide encoded data, or provide abi/functionName/args for encoding.');
590
+ }
591
+ // Handle value for contract calls
592
+ if (value && value !== '0' && value !== 0) {
593
+ // Convert value to wei if provided
594
+ const valueStr = typeof value === 'string' ? value.trim() : String(value);
595
+ let valueInWei;
596
+ try {
597
+ valueInWei = parseEther(valueStr);
598
+ }
599
+ catch (error) {
600
+ throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
601
+ }
602
+ finalValue = `0x${valueInWei.toString(16)}`;
603
+ }
604
+ else {
605
+ finalValue = '0x0';
606
+ }
607
+ }
608
+ return {
609
+ to,
610
+ value: finalValue,
611
+ data: finalData,
612
+ };
613
+ }, [provider]);
614
+ return { prepareRawTxn };
615
+ }
616
+ /**
617
+ * Hook to sign transaction using Turnkey API
618
+ * Accepts raw transaction data from usePrepareRawTxn, serializes it, and signs via API
619
+ *
620
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
621
+ * @returns Object with signTxn function
622
+ *
623
+ * @example
624
+ * ```tsx
625
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
626
+ * const { signTxn } = useSignTxn(publicClient);
627
+ *
628
+ * // Prepare transaction
629
+ * const rawTx = await prepareRawTxn({
630
+ * from: address!,
631
+ * to: '0x...',
632
+ * value: '0.001',
633
+ * });
634
+ *
635
+ * // Sign transaction
636
+ * const signedTx = await signTxn({
637
+ * from: address!,
638
+ * ...rawTx, // Spread to, value, data from prepareRawTxn
639
+ * });
640
+ * ```
641
+ */
642
+ export function useSignTxn(provider) {
643
+ const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
644
+ const signTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
645
+ if (!isConnected || !wallet) {
646
+ throw new Error('Wallet is not connected');
647
+ }
648
+ if (!provider) {
649
+ throw new Error('Provider (publicClient) is required');
650
+ }
651
+ if (!from) {
652
+ throw new Error('From address is required');
653
+ }
654
+ if (!to) {
655
+ throw new Error('To address is required');
656
+ }
657
+ // Get chain ID from provider or use provided one
658
+ const targetChainId = chainId || provider.chain?.id;
659
+ if (!targetChainId) {
660
+ throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
661
+ }
662
+ // Get nonce
663
+ const nonce = await provider.getTransactionCount({ address: from });
664
+ // Convert hex value to bigint
665
+ // Handle empty hex string "0x" by converting to "0x0"
666
+ const normalizedValue = value === '0x' ? '0x0' : value;
667
+ const valueInWei = BigInt(normalizedValue);
668
+ // Estimate gas
669
+ const gas = await provider.estimateGas({
670
+ account: from,
671
+ to,
672
+ data,
673
+ value: valueInWei,
674
+ });
675
+ // Estimate fees
676
+ const fee = await provider.estimateFeesPerGas();
677
+ // Create unsigned transaction
678
+ const unsignedTx = {
679
+ chainId: targetChainId,
680
+ from,
681
+ to,
682
+ data,
683
+ value: valueInWei,
684
+ nonce,
685
+ gas,
686
+ maxFeePerGas: fee.maxFeePerGas,
687
+ maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
688
+ type: 'eip1559',
689
+ };
690
+ // Serialize transaction
691
+ const serializedTx = serializeTransaction(unsignedTx);
692
+ // Sign transaction via Turnkey API
693
+ const signResult = await signTransactionViaAPI(serializedTx, from);
694
+ return {
695
+ unsignedTransaction: serializedTx,
696
+ signedTransaction: signResult.signedTransaction,
697
+ };
698
+ }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
699
+ return { signTxn, isConnected, address };
700
+ }
701
+ /**
702
+ * Hook to sign and send transaction using Turnkey API
703
+ * Same as useSignTxn but also sends the transaction on blockchain using publicClient
704
+ *
705
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
706
+ * @returns Object with signAndSendTxn function
707
+ *
708
+ * @example
709
+ * ```tsx
710
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
711
+ * const { signAndSendTxn } = useSignAndSendTxn(publicClient);
712
+ *
713
+ * // Prepare transaction
714
+ * const rawTx = await prepareRawTxn({
715
+ * from: address!,
716
+ * to: '0x...',
717
+ * value: '0.001',
718
+ * });
719
+ *
720
+ * // Sign and send transaction
721
+ * const result = await signAndSendTxn({
722
+ * from: address!,
723
+ * ...rawTx, // Spread to, value, data from prepareRawTxn
724
+ * });
725
+ *
726
+ * console.log('Transaction hash:', result.hash);
727
+ * ```
728
+ */
729
+ export function useSignAndSendTxn(provider) {
730
+ const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
731
+ const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
732
+ if (!isConnected || !wallet) {
733
+ throw new Error('Wallet is not connected');
734
+ }
735
+ if (!provider) {
736
+ throw new Error('Provider (publicClient) is required');
737
+ }
738
+ if (!from) {
739
+ throw new Error('From address is required');
740
+ }
741
+ if (!to) {
742
+ throw new Error('To address is required');
743
+ }
744
+ // Get chain ID from provider or use provided one
745
+ const targetChainId = chainId || provider.chain?.id;
746
+ if (!targetChainId) {
747
+ throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
748
+ }
749
+ // Get nonce
750
+ const nonce = await provider.getTransactionCount({ address: from });
751
+ // Convert hex value to bigint
752
+ // Handle empty hex string "0x" by converting to "0x0"
753
+ const normalizedValue = value === '0x' ? '0x0' : value;
754
+ const valueInWei = BigInt(normalizedValue);
755
+ // Estimate gas
756
+ const gas = await provider.estimateGas({
757
+ account: from,
758
+ to,
759
+ data,
760
+ value: valueInWei,
761
+ });
762
+ // Estimate fees
763
+ const fee = await provider.estimateFeesPerGas();
764
+ // Create unsigned transaction
765
+ const unsignedTx = {
766
+ chainId: targetChainId,
767
+ from,
768
+ to,
769
+ data,
770
+ value: valueInWei,
771
+ nonce,
772
+ gas,
773
+ maxFeePerGas: fee.maxFeePerGas,
774
+ maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
775
+ type: 'eip1559',
776
+ };
777
+ // Serialize transaction
778
+ const serializedTx = serializeTransaction(unsignedTx);
779
+ // Sign transaction via Turnkey API
780
+ const signResult = await signTransactionViaAPI(serializedTx, from);
781
+ // Send the signed transaction
782
+ const hash = await provider.sendRawTransaction({
783
+ serializedTransaction: signResult.signedTransaction,
784
+ });
785
+ return {
786
+ hash,
787
+ unsignedTransaction: serializedTx,
788
+ signedTransaction: signResult.signedTransaction,
789
+ };
790
+ }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
791
+ return { signAndSendTxn, isConnected, address };
792
+ }
793
+ /**
794
+ * Hook to wait for transaction receipt
795
+ * Waits for a transaction to be mined and returns the receipt
796
+ *
797
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
798
+ * @returns Object with waitForTxnReceipt function
799
+ *
800
+ * @example
801
+ * ```tsx
802
+ * const { publicClient } = usePublicClient(
803
+ * polygonAmoy,
804
+ * 'https://rpc-amoy.polygon.technology'
805
+ * );
806
+ * const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
807
+ *
808
+ * // After sending a transaction
809
+ * const hash = '0x...'; // Transaction hash
810
+ *
811
+ * // Wait for receipt
812
+ * const receipt = await waitForTxnReceipt({
813
+ * hash,
814
+ * confirmations: 1, // Optional: number of confirmations to wait for
815
+ * });
816
+ *
817
+ * console.log('Transaction confirmed:', receipt);
818
+ * ```
819
+ */
820
+ export function useWaitForTxnReceipt(provider) {
821
+ const waitForTxnReceipt = useCallback(async ({ hash, confirmations, timeout, }) => {
822
+ if (!provider) {
823
+ throw new Error('Provider (publicClient) is required');
824
+ }
825
+ if (!hash) {
826
+ throw new Error('Transaction hash is required');
827
+ }
828
+ try {
829
+ // waitForTransactionReceipt is a method on PublicClient
830
+ const receipt = await provider.waitForTransactionReceipt({
831
+ hash,
832
+ confirmations,
833
+ timeout,
834
+ });
835
+ return receipt;
836
+ }
837
+ catch (error) {
838
+ if (error instanceof Error) {
839
+ throw new Error(`Failed to wait for transaction receipt: ${error.message}`);
840
+ }
841
+ throw error;
842
+ }
843
+ }, [provider]);
844
+ return { waitForTxnReceipt };
845
+ }
846
+ /**
847
+ * Hook to estimate gas price
848
+ * Estimates gas price and fees for transactions
849
+ *
850
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
851
+ * @returns Object with estimateGas function
852
+ *
853
+ * @example
854
+ * ```tsx
855
+ * const { publicClient } = usePublicClient(
856
+ * polygonAmoy,
857
+ * 'https://rpc-amoy.polygon.technology'
858
+ * );
859
+ * const { estimateGas } = useEstimateGas(publicClient);
860
+ *
861
+ * // Estimate gas price
862
+ * const gasPrice = await estimateGas();
863
+ *
864
+ * // Returns:
865
+ * // {
866
+ * // gasPrice?: bigint, // Legacy gas price (for non-EIP-1559 chains)
867
+ * // maxFeePerGas?: bigint, // EIP-1559 max fee per gas
868
+ * // maxPriorityFeePerGas?: bigint // EIP-1559 max priority fee per gas
869
+ * // }
870
+ * ```
871
+ */
872
+ export function useEstimateGas(provider) {
873
+ const estimateGas = useCallback(async () => {
874
+ if (!provider) {
875
+ throw new Error('Provider (publicClient) is required');
876
+ }
877
+ try {
878
+ // Try to get EIP-1559 fees first (for modern chains)
879
+ const fees = await provider.estimateFeesPerGas();
880
+ return {
881
+ maxFeePerGas: fees.maxFeePerGas,
882
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
883
+ // Also get legacy gas price as fallback
884
+ gasPrice: fees.gasPrice,
885
+ };
886
+ }
887
+ catch (error) {
888
+ // If EIP-1559 fails, try legacy gas price
889
+ try {
890
+ const gasPrice = await provider.getGasPrice();
891
+ return {
892
+ gasPrice,
893
+ };
894
+ }
895
+ catch (legacyError) {
896
+ if (error instanceof Error) {
897
+ throw new Error(`Failed to estimate gas price: ${error.message}`);
898
+ }
899
+ throw error;
900
+ }
901
+ }
902
+ }, [provider]);
903
+ return { estimateGas };
904
+ }
905
+ /**
906
+ * Hook to read from contract
907
+ * Reads contract data using publicClient.readContract
908
+ *
909
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
910
+ * @returns Object with readContract function
911
+ *
912
+ * @example
913
+ * ```tsx
914
+ * const { publicClient } = usePublicClient(
915
+ * polygonAmoy,
916
+ * 'https://rpc-amoy.polygon.technology'
917
+ * );
918
+ * const { readContract } = useReadContract(publicClient);
919
+ *
920
+ * // Read contract data
921
+ * const balance = await readContract({
922
+ * address: '0x...',
923
+ * abi: erc20Abi,
924
+ * functionName: 'balanceOf',
925
+ * args: ['0x...'],
926
+ * });
927
+ *
928
+ * const name = await readContract({
929
+ * address: '0x...',
930
+ * abi: erc20Abi,
931
+ * functionName: 'name',
932
+ * });
933
+ * ```
934
+ */
935
+ export function useReadContract(provider) {
936
+ const readContract = useCallback(({ address, abi, functionName, args, }) => {
937
+ if (!provider) {
938
+ throw new Error('Provider (publicClient) is required');
939
+ }
940
+ if (!address) {
941
+ throw new Error('Contract address is required');
942
+ }
943
+ if (!abi) {
944
+ throw new Error('Contract ABI is required');
945
+ }
946
+ if (!functionName) {
947
+ throw new Error('Function name is required');
948
+ }
949
+ try {
950
+ return provider.readContract({
951
+ address,
952
+ abi: abi,
953
+ functionName: functionName,
954
+ args: args,
955
+ });
956
+ }
957
+ catch (error) {
958
+ if (error instanceof Error) {
959
+ throw new Error(`Failed to read contract: ${error.message}`);
960
+ }
961
+ throw error;
962
+ }
963
+ }, [provider]);
964
+ return { readContract };
965
+ }
966
+ /**
967
+ * Hook to get walletClient for external wallets (MetaMask, WalletConnect, etc.)
968
+ * Uses wagmi's useConnectorClient to get the walletClient
969
+ *
970
+ * @returns Object with walletClient instance (null if not connected)
971
+ *
972
+ * @example
973
+ * ```tsx
974
+ * import { useExternalWalletClient } from '@abstraxn/signer-react';
975
+ *
976
+ * function MyComponent() {
977
+ * const { walletClient, isConnected } = useExternalWalletClient();
978
+ *
979
+ * if (!walletClient || !isConnected) {
980
+ * return <div>Please connect your wallet</div>;
981
+ * }
982
+ *
983
+ * // Use walletClient for transactions
984
+ * }
985
+ * ```
986
+ */
987
+ export function useExternalWalletClient() {
988
+ const { isExternalWalletConnected } = useExternalWallet();
989
+ const { data: walletClient, isPending, isError, error } = useWagmiWalletClient();
990
+ const { isConnected: wagmiIsConnected, connector } = useAccount();
991
+ // Check both our internal state and wagmi's state
992
+ // useConnectorClient might return undefined even when connected, so we check both
993
+ // Edge case: connectorClient can be undefined/pending even when wallet appears connected
994
+ // This happens during:
995
+ // 1. Initial connection phase (connector is still initializing)
996
+ // 2. Chain switching (connector needs to re-initialize for new chain)
997
+ // 3. Connector re-initialization after page refresh/reconnect
998
+ const isActuallyConnected = isExternalWalletConnected && wagmiIsConnected;
999
+ // Return walletClient if available, even if isPending (might be refetching)
1000
+ // Only return null if walletClient is actually undefined
1001
+ const finalClient = (isActuallyConnected && walletClient) ? walletClient : null;
1002
+ return {
1003
+ walletClient: finalClient,
1004
+ // isConnected should be true only when both states agree AND walletClient is available
1005
+ // Note: isPending might be true even when walletClient exists (during refetch)
1006
+ isConnected: isActuallyConnected && !!walletClient,
1007
+ isPending,
1008
+ isError,
1009
+ error,
1010
+ // Additional info for debugging edge cases
1011
+ connector,
1012
+ };
1013
+ }
1014
+ /**
1015
+ * Hook to write to contract (for external wallets like MetaMask, WalletConnect, etc.)
1016
+ * Uses walletClient from useExternalWalletClient to send transactions
1017
+ *
1018
+ * @returns Object with writeContract function
1019
+ *
1020
+ * @example
1021
+ * ```tsx
1022
+ * import { useWriteContract } from '@abstraxn/signer-react';
1023
+ * import erc20Abi from './erc20Abi.json';
1024
+ *
1025
+ * function MyComponent() {
1026
+ * const { writeContract } = useWriteContract();
1027
+ *
1028
+ * const handleTransfer = async () => {
1029
+ * try {
1030
+ * // Write to contract
1031
+ * const hash = await writeContract({
1032
+ * address: '0x...', // Contract address
1033
+ * abi: erc20Abi,
1034
+ * functionName: 'transfer',
1035
+ * args: ['0x...', '1000000000000000000'], // to, amount in wei
1036
+ * });
1037
+ *
1038
+ * console.log('Transaction hash:', hash);
1039
+ * } catch (error) {
1040
+ * console.error('Transaction failed:', error);
1041
+ * }
1042
+ * };
1043
+ *
1044
+ * // With ETH value
1045
+ * const handleDeposit = async () => {
1046
+ * const hash = await writeContract({
1047
+ * address: '0x...',
1048
+ * abi: contractAbi,
1049
+ * functionName: 'deposit',
1050
+ * value: parseEther('0.1'), // Send 0.1 ETH with the transaction
1051
+ * });
1052
+ * };
1053
+ *
1054
+ * return <button onClick={handleTransfer}>Transfer Tokens</button>;
1055
+ * }
1056
+ * ```
1057
+ */
1058
+ // ... (keep existing code)
1059
+ // ... (keep existing code)
1060
+ export function useWriteContract() {
1061
+ const { walletClient: hookClient, isPending, isError, error, isConnected } = useExternalWalletClient();
1062
+ const config = useConfig();
1063
+ const currentChainId = useWagmiChainId();
1064
+ const writeContract = useCallback(({ address, abi, functionName, args, value, account, gas, gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas, nonce, }) => {
1065
+ return (async () => {
1066
+ let activeClient = hookClient;
1067
+ // If hook client is missing or in error, try to recover
1068
+ if (!activeClient || isError) {
1069
+ // Check if the initial error was a chain mismatch
1070
+ const isMismatch = isError && (error?.name === 'ConnectorChainMismatchError' || error?.message?.includes('ChainMismatch'));
1071
+ if (isMismatch) {
1072
+ try {
1073
+ if (!currentChainId)
1074
+ throw new Error('Current chain ID is not available');
1075
+ await switchChain(config, { chainId: currentChainId });
1076
+ // Retry getting client after switch
1077
+ activeClient = await getWalletClient(config);
1078
+ }
1079
+ catch (switchErr) {
1080
+ console.error("Failed to auto-switch chain:", switchErr);
1081
+ // Fall through to try getWalletClient directly or throw
1082
+ }
1083
+ }
1084
+ // If we still don't have a client (or switch failed), try to get it directly
1085
+ if (!activeClient) {
1086
+ try {
1087
+ // Try to get client directly
1088
+ activeClient = await getWalletClient(config);
1089
+ }
1090
+ catch (err) {
1091
+ // If mismatch error (and we haven't tried switching yet or it failed differently), try to switch chain
1092
+ // This catches cases where getWalletClient throws mismatch but the hook error wasn't mismatch (unlikely but possible)
1093
+ if (err?.name === 'ConnectorChainMismatchError' || err?.message?.includes('ChainMismatch')) {
1094
+ try {
1095
+ if (!currentChainId)
1096
+ throw new Error('Current chain ID is not available');
1097
+ await switchChain(config, { chainId: currentChainId });
1098
+ // Retry getting client
1099
+ activeClient = await getWalletClient(config);
1100
+ }
1101
+ catch (switchErr) {
1102
+ throw new Error(`Failed to switch chain: ${switchErr instanceof Error ? switchErr.message : 'Unknown error'}`);
1103
+ }
1104
+ }
1105
+ else {
1106
+ // Re-throw if it's not a mismatch we can handle
1107
+ if (isError && error) {
1108
+ console.error("WalletClient Initialization Error:", error);
1109
+ throw new Error(`Failed to initialize WalletClient: ${error.message}. Please try reconnecting your wallet.`);
1110
+ }
1111
+ throw err;
1112
+ }
1113
+ }
1114
+ }
1115
+ }
1116
+ if (!activeClient) {
1117
+ throw new Error('WalletClient is not available. Please connect an external wallet.');
1118
+ }
1119
+ // Cast to any to avoid type issues with different viem versions
1120
+ const walletClientAny = activeClient;
1121
+ // Try using writeContract method directly if available
1122
+ if (walletClientAny && typeof walletClientAny.writeContract === 'function') {
1123
+ return walletClientAny.writeContract({
1124
+ address,
1125
+ abi: abi,
1126
+ functionName: functionName,
1127
+ args: args,
1128
+ value,
1129
+ account,
1130
+ gas,
1131
+ gasPrice,
1132
+ maxFeePerGas,
1133
+ maxPriorityFeePerGas,
1134
+ nonce,
1135
+ chain: null, // Let the client determine the chain (or use current)
1136
+ });
1137
+ }
1138
+ // If writeContract is not available, use getContract to create a contract instance
1139
+ // This works with wagmi's connectorClient
1140
+ const contract = getContract({
1141
+ address,
1142
+ abi: abi,
1143
+ client: walletClientAny,
1144
+ });
1145
+ // Get the write function from the contract
1146
+ const writeFunction = contract.write?.[functionName];
1147
+ if (!writeFunction || typeof writeFunction !== 'function') {
1148
+ throw new Error(`Function "${functionName}" not found in contract ABI or is not a write function. ` +
1149
+ 'Make sure the function exists in the ABI and is a state-changing function.');
1150
+ }
1151
+ // Prepare the call options (viem contract write methods accept options as the last parameter)
1152
+ const hasOptions = value !== undefined || account !== undefined || gas !== undefined || gasLimit !== undefined ||
1153
+ gasPrice !== undefined || maxFeePerGas !== undefined ||
1154
+ maxPriorityFeePerGas !== undefined || nonce !== undefined;
1155
+ // Viem contract write methods expect args as an array, not spread
1156
+ const functionArgs = args || [];
1157
+ if (hasOptions) {
1158
+ const callOptions = {};
1159
+ if (value !== undefined)
1160
+ callOptions.value = value;
1161
+ if (account !== undefined)
1162
+ callOptions.account = account;
1163
+ // `gas` in viem is the gas *limit*. Allow either `gas` or `gasLimit` from the hook caller.
1164
+ if (gas !== undefined) {
1165
+ callOptions.gas = gas;
1166
+ }
1167
+ else if (gasLimit !== undefined) {
1168
+ callOptions.gas = gasLimit;
1169
+ }
1170
+ if (gasPrice !== undefined)
1171
+ callOptions.gasPrice = gasPrice;
1172
+ if (maxFeePerGas !== undefined)
1173
+ callOptions.maxFeePerGas = maxFeePerGas;
1174
+ if (maxPriorityFeePerGas !== undefined)
1175
+ callOptions.maxPriorityFeePerGas = maxPriorityFeePerGas;
1176
+ if (nonce !== undefined)
1177
+ callOptions.nonce = nonce;
1178
+ // Call the write function with args array and options object
1179
+ return writeFunction(functionArgs, callOptions);
1180
+ }
1181
+ else {
1182
+ // Call the write function with args array only
1183
+ return writeFunction(functionArgs);
1184
+ }
1185
+ })();
1186
+ }, [hookClient, isPending, isError, error, isConnected, config, currentChainId]);
1187
+ return { writeContract };
1188
+ }
1189
+ /**
1190
+ * Hook to get the connection type (how the user connected)
1191
+ * Returns the method used to connect: 'google', 'x', 'discord', 'metamask', 'walletconnect',
1192
+ * 'email', 'passkey', or null if not connected
1193
+ *
1194
+ * @returns Object with connectionType and connector metadata
1195
+ *
1196
+ * @example
1197
+ * ```tsx
1198
+ * import { useConnectionType } from '@abstraxn/signer-react';
1199
+ *
1200
+ * function MyComponent() {
1201
+ * const { connectionType, connectorMeta } = useConnectionType();
1202
+ *
1203
+ * if (!connectionType) {
1204
+ * return <div>Not connected</div>;
1205
+ * }
1206
+ *
1207
+ * return (
1208
+ * <div>
1209
+ * Connected via: {connectorMeta?.name || connectionType}
1210
+ * {connectionType === 'google' && <div>Welcome Google user!</div>}
1211
+ * {connectionType === 'metamask' && <div>MetaMask wallet connected</div>}
1212
+ * </div>
1213
+ * );
1214
+ * }
1215
+ * ```
1216
+ */
1217
+ export function useConnectionType() {
1218
+ const { connectionType } = useAbstraxnWallet();
1219
+ // Normalize connection type to match ConnectorType
1220
+ const normalizedType = connectionType ? normalizeConnectionType(connectionType) : null;
1221
+ const connectorMeta = getConnectorMeta(normalizedType);
1222
+ return {
1223
+ connectionType: normalizedType,
1224
+ connectorMeta,
1225
+ };
1226
+ }
1227
+ /**
1228
+ * Normalize connection type string to match ConnectorType
1229
+ */
1230
+ function normalizeConnectionType(type) {
1231
+ const lower = type.toLowerCase();
1232
+ // Map to standard connector types
1233
+ if (lower.includes('google'))
1234
+ return 'google';
1235
+ if (lower.includes('twitter') || lower === 'x')
1236
+ return 'x';
1237
+ if (lower.includes('discord'))
1238
+ return 'discord';
1239
+ if (lower.includes('metamask') || lower.includes('io.metamask'))
1240
+ return 'metamask';
1241
+ if (lower.includes('walletconnect') || lower.includes('wallet_connect'))
1242
+ return 'walletconnect';
1243
+ if (lower.includes('email') || lower.includes('otp'))
1244
+ return 'email';
1245
+ if (lower.includes('passkey'))
1246
+ return 'passkey';
1247
+ // Return original if no match (for other wallet types like coinbase, phantom, etc.)
1248
+ return type;
1249
+ }
1250
+ /**
1251
+ * Hook to switch chain
1252
+ * Handles both internal (social) and external wallets
1253
+ */
1254
+ export function useSwitchChain() {
1255
+ const { wallet, connectionType } = useAbstraxnWallet();
1256
+ const { switchChainAsync } = useWagmiSwitchChain();
1257
+ const { isConnected: isWagmiConnected } = useAccount();
1258
+ const config = useConfig();
1259
+ const switchChain = async (chainId) => {
1260
+ // Normalize connection type
1261
+ const type = connectionType ? connectionType.toLowerCase() : '';
1262
+ // Check if it's a social login
1263
+ const isSocial = ['google', 'email', 'otp', 'x', 'discord', 'passkey'].some(t => type.includes(t));
1264
+ if (isSocial) {
1265
+ if (!wallet)
1266
+ throw new Error('Wallet not initialized');
1267
+ // Use internal wallet switch
1268
+ await wallet.switchChain(chainId);
1269
+ }
1270
+ else if (isWagmiConnected) {
1271
+ // Use wagmi for external wallets
1272
+ try {
1273
+ await switchChainAsync({ chainId });
1274
+ }
1275
+ catch (error) {
1276
+ // Check if error is related to chain not being added or RPC URL missing
1277
+ // "UserRejectedRequestError" with "chain.rpcUrls is undefined" often means the chain isn't known to the wallet/viem
1278
+ // Try to add the chain if we have its details
1279
+ let chainParams = null;
1280
+ // 1. Try to get from internal SDK chains
1281
+ const chainData = getChainById(chainId);
1282
+ if (chainData && chainData.type === 'evm') {
1283
+ chainParams = {
1284
+ chainName: chainData.displayName,
1285
+ nativeCurrency: chainData.nativeCurrency,
1286
+ rpcUrls: [chainData.rpcUrl],
1287
+ blockExplorerUrls: chainData.blockExplorer ? [chainData.blockExplorer.url] : undefined,
1288
+ };
1289
+ }
1290
+ // 2. If not found, try to get from wagmi config (includes custom chains passed to provider)
1291
+ if (!chainParams) {
1292
+ const wagmiChain = config.chains.find(c => c.id === chainId);
1293
+ if (wagmiChain) {
1294
+ // Handle both viem Chain (rpcUrls) and CoreChain (rpcUrl) formats
1295
+ // The chain object in config might be a CoreChain if it wasn't converted correctly
1296
+ const rpcUrls = wagmiChain.rpcUrls?.default?.http || (wagmiChain.rpcUrl ? [wagmiChain.rpcUrl] : []);
1297
+ const blockExplorerUrls = wagmiChain.blockExplorers?.default?.url
1298
+ ? [wagmiChain.blockExplorers.default.url]
1299
+ : (wagmiChain.explorerUrl ? [wagmiChain.explorerUrl] : undefined);
1300
+ chainParams = {
1301
+ chainName: wagmiChain.name,
1302
+ nativeCurrency: wagmiChain.nativeCurrency,
1303
+ rpcUrls: rpcUrls,
1304
+ blockExplorerUrls: blockExplorerUrls,
1305
+ };
1306
+ }
1307
+ }
1308
+ if (chainParams) {
1309
+ try {
1310
+ await switchChainAsync({
1311
+ chainId,
1312
+ addEthereumChainParameter: chainParams
1313
+ });
1314
+ }
1315
+ catch (retryError) {
1316
+ console.error('Failed to add and switch chain:', retryError);
1317
+ throw retryError;
1318
+ }
1319
+ }
1320
+ else {
1321
+ // If we don't have chain data, rethrow the original error
1322
+ throw error;
1323
+ }
1324
+ }
1325
+ }
1326
+ else {
1327
+ throw new Error('No active connection to switch chain');
1328
+ }
1329
+ };
1330
+ return { switchChain };
1331
+ }
1332
+ /**
1333
+ * Hook to sign a message
1334
+ * Handles both internal (social) and external wallets
1335
+ */
1336
+ export function useSignMessage() {
1337
+ const { wallet, connectionType } = useAbstraxnWallet();
1338
+ const { signMessageAsync } = useWagmiSignMessage();
1339
+ const { isConnected: isWagmiConnected } = useAccount();
1340
+ const signMessage = async (message) => {
1341
+ // Normalize connection type
1342
+ const type = connectionType ? connectionType.toLowerCase() : '';
1343
+ // Check if it's a social login
1344
+ const isSocial = ['google', 'email', 'otp', 'x', 'discord', 'passkey'].some(t => type.includes(t));
1345
+ if (isSocial) {
1346
+ throw new Error('Signing messages is not supported for embedded wallets');
1347
+ }
1348
+ else if (isWagmiConnected) {
1349
+ // Use wagmi for external wallets
1350
+ return await signMessageAsync({ message });
1351
+ }
1352
+ else {
1353
+ throw new Error('No active connection to sign message');
1354
+ }
1355
+ };
1356
+ return { signMessage };
1357
+ }
1358
+ //# sourceMappingURL=hooks.js.map