signer-test-sdk-react 0.0.22 → 0.0.24
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 +29 -0
- package/README.md +4 -0
- package/dist/src/AbstraxnProvider.d.ts +5 -13
- package/dist/src/AbstraxnProvider.js +14 -3126
- package/dist/src/AbstraxnProvider.js.map +1 -1
- package/dist/src/ConnectButton.css +1 -1
- package/dist/src/ExternalWalletButtons.css +1 -1
- package/dist/src/ExternalWalletButtons.js +2 -2
- package/dist/src/ExternalWalletButtons.js.map +1 -1
- package/dist/src/OnboardingUI.d.ts +1 -1
- package/dist/src/WalletModal.css +193 -373
- package/dist/src/WalletModal.d.ts +1 -1
- package/dist/src/WalletModal.js +108 -45
- package/dist/src/WalletModal.js.map +1 -1
- package/dist/src/chains.d.ts +4 -3
- package/dist/src/chains.js +154 -84
- package/dist/src/chains.js.map +1 -1
- package/dist/src/components/AbstraxnProvider/AbstraxnProvider.d.ts +12 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js +146 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.d.ts +25 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +3086 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.d.ts +8 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js +46 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.d.ts +8 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js +12 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/context.d.ts +2 -0
- package/dist/src/components/AbstraxnProvider/context.js +6 -0
- package/dist/src/components/AbstraxnProvider/context.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/index.d.ts +6 -0
- package/dist/src/components/AbstraxnProvider/index.js +7 -0
- package/dist/src/components/AbstraxnProvider/index.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +30 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js +49 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.d.ts +2 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js +13 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.d.ts +22 -0
- package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js +242 -0
- package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.d.ts +25 -0
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +539 -0
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.js.map +1 -0
- package/dist/src/components/AbstraxnProvider/utils.d.ts +41 -0
- package/dist/src/components/AbstraxnProvider/utils.js +139 -0
- package/dist/src/components/AbstraxnProvider/utils.js.map +1 -0
- package/dist/src/components/OnboardingUI/OnboardingUI.css +8 -5
- package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +1 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +1 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +7 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +1 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.js +1 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +1 -1
- package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +1 -1
- package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +1 -1
- package/dist/src/components/OnboardingUI/hooks/useAuthMethods.d.ts +1 -1
- package/dist/src/components/OnboardingUI/hooks/useOnboarding.d.ts +1 -1
- package/dist/src/components/OnboardingUI/index.d.ts +1 -1
- package/dist/src/components/WalletModal/components/ChainSelector.css +249 -102
- package/dist/src/components/WalletModal/components/ChainSelector.d.ts +7 -6
- package/dist/src/components/WalletModal/components/ChainSelector.js +68 -27
- package/dist/src/components/WalletModal/components/ChainSelector.js.map +1 -1
- package/dist/src/components/WalletModal/components/ExportKeyModal.css +89 -88
- package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +6 -1
- package/dist/src/components/WalletModal/components/ExportKeyModal.js +6 -11
- package/dist/src/components/WalletModal/components/ExportKeyModal.js.map +1 -1
- package/dist/src/components/WalletModal/components/ExportWarningModal.css +107 -2
- package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +7 -1
- package/dist/src/components/WalletModal/components/ExportWarningModal.js +5 -3
- package/dist/src/components/WalletModal/components/ExportWarningModal.js.map +1 -1
- package/dist/src/components/WalletModal/components/ManageWalletModal.css +90 -4
- package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +3 -3
- package/dist/src/components/WalletModal/components/ManageWalletModal.js +28 -13
- package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -1
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +3 -4
- package/dist/src/components/WalletModal/components/ReceiveModal.css +93 -58
- package/dist/src/components/WalletModal/components/ReceiveModal.js +1 -1
- package/dist/src/components/WalletModal/components/ReceiveModal.js.map +1 -1
- package/dist/src/components/WalletModal/components/SendModal.css +170 -127
- package/dist/src/components/WalletModal/components/SendModal.d.ts +4 -6
- package/dist/src/components/WalletModal/components/SendModal.js +131 -39
- package/dist/src/components/WalletModal/components/SendModal.js.map +1 -1
- package/dist/src/components/WalletModal/components/SuccessModal.css +7 -8
- package/dist/src/components/WalletModal/components/TokenSelectorModal.css +240 -0
- package/dist/src/components/WalletModal/components/TokenSelectorModal.d.ts +21 -0
- package/dist/src/components/WalletModal/components/TokenSelectorModal.js +44 -0
- package/dist/src/components/WalletModal/components/TokenSelectorModal.js.map +1 -0
- package/dist/src/components/WalletModal/components/UserAvatar.d.ts +1 -1
- package/dist/src/components/WalletModal/components/index.d.ts +2 -0
- package/dist/src/components/WalletModal/components/index.js +1 -0
- package/dist/src/components/WalletModal/components/index.js.map +1 -1
- package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +1 -1
- package/dist/src/hooks.d.ts +402 -362
- package/dist/src/hooks.js +486 -244
- package/dist/src/hooks.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +6 -6
- package/dist/src/wagmiConfig.d.ts +2 -2
- package/dist/src/wagmiConfig.js +34 -21
- package/dist/src/wagmiConfig.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
package/dist/src/hooks.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
5
5
|
import { useAbstraxnWallet } from './AbstraxnProvider';
|
|
6
|
-
import { createPublicClient, createWalletClient, http, getContract, serializeTransaction,
|
|
7
|
-
import { useWalletClient as useWagmiWalletClient, useAccount, useConfig, useChainId as useWagmiChainId } from 'wagmi';
|
|
6
|
+
import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, encodeFunctionData } from 'viem';
|
|
7
|
+
import { useWalletClient as useWagmiWalletClient, useAccount, useConfig, useChainId as useWagmiChainId, useSwitchChain as useWagmiSwitchChain, useSignMessage as useWagmiSignMessage } from 'wagmi';
|
|
8
8
|
import { getWalletClient, switchChain } from '@wagmi/core';
|
|
9
9
|
import { getConnectorMeta } from './connectors';
|
|
10
|
+
import { getChainById } from './chains';
|
|
10
11
|
/**
|
|
11
12
|
* Hook to check if wallet is connected
|
|
12
13
|
*/
|
|
@@ -25,7 +26,7 @@ export function useAddress() {
|
|
|
25
26
|
* Hook to get current user and whoami information
|
|
26
27
|
* Returns an object with both user and whoami data
|
|
27
28
|
*/
|
|
28
|
-
export function
|
|
29
|
+
export function useAuthContext() {
|
|
29
30
|
const { user, whoami } = useAbstraxnWallet();
|
|
30
31
|
return { user, whoami };
|
|
31
32
|
}
|
|
@@ -36,20 +37,6 @@ export function useChainId() {
|
|
|
36
37
|
const { chainId } = useAbstraxnWallet();
|
|
37
38
|
return chainId;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Hook to check if wallet is initialized
|
|
41
|
-
*/
|
|
42
|
-
export function useIsInitialized() {
|
|
43
|
-
const { isInitialized } = useAbstraxnWallet();
|
|
44
|
-
return isInitialized;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Hook to get loading state
|
|
48
|
-
*/
|
|
49
|
-
export function useLoading() {
|
|
50
|
-
const { loading } = useAbstraxnWallet();
|
|
51
|
-
return loading;
|
|
52
|
-
}
|
|
53
40
|
/**
|
|
54
41
|
* Hook to get error state
|
|
55
42
|
*/
|
|
@@ -66,91 +53,6 @@ export function useWallet() {
|
|
|
66
53
|
const { wallet } = useAbstraxnWallet();
|
|
67
54
|
return wallet;
|
|
68
55
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Hook to prepare and sign transaction
|
|
71
|
-
* Prepares the unsigned transaction, calls the sign API, and returns the signed transaction
|
|
72
|
-
* User can execute this signed transaction themselves
|
|
73
|
-
*/
|
|
74
|
-
export function usePrepareTransaction() {
|
|
75
|
-
const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
76
|
-
const prepareTransaction = async (to, value, rpcUrl, chainId) => {
|
|
77
|
-
if (!isConnected || !wallet || !address) {
|
|
78
|
-
throw new Error('Wallet is not connected');
|
|
79
|
-
}
|
|
80
|
-
// Import viem functions dynamically
|
|
81
|
-
const { createPublicClient, http, serializeTransaction } = await import('viem');
|
|
82
|
-
const { polygonAmoy } = await import('viem/chains');
|
|
83
|
-
const targetRpcUrl = rpcUrl || 'https://rpc-amoy.polygon.technology';
|
|
84
|
-
const targetChainId = chainId || polygonAmoy.id;
|
|
85
|
-
// Create public client
|
|
86
|
-
const publicClient = createPublicClient({
|
|
87
|
-
chain: polygonAmoy,
|
|
88
|
-
transport: http(targetRpcUrl),
|
|
89
|
-
});
|
|
90
|
-
// Get nonce
|
|
91
|
-
const nonce = await publicClient.getTransactionCount({ address: address });
|
|
92
|
-
// Get gas price
|
|
93
|
-
const gasPrice = await publicClient.getGasPrice();
|
|
94
|
-
// Convert amount to wei (18 decimals)
|
|
95
|
-
const valueInWei = BigInt(Math.floor(Number(value) * 10 ** 18));
|
|
96
|
-
// Create unsigned transaction
|
|
97
|
-
const unsignedTx = {
|
|
98
|
-
to: to,
|
|
99
|
-
value: valueInWei,
|
|
100
|
-
gas: 21000n,
|
|
101
|
-
nonce: nonce,
|
|
102
|
-
gasPrice: gasPrice,
|
|
103
|
-
chainId: targetChainId,
|
|
104
|
-
data: '0x',
|
|
105
|
-
};
|
|
106
|
-
// Serialize transaction
|
|
107
|
-
const serializedTx = serializeTransaction(unsignedTx);
|
|
108
|
-
// Sign transaction via API
|
|
109
|
-
const signResult = await signTransactionViaAPI(serializedTx, address);
|
|
110
|
-
return {
|
|
111
|
-
unsignedTransaction: serializedTx,
|
|
112
|
-
signedTransaction: signResult.signedTransaction,
|
|
113
|
-
};
|
|
114
|
-
};
|
|
115
|
-
return { prepareTransaction, isConnected, address };
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Hook to sign transaction via API
|
|
119
|
-
* Returns the signed transaction that user can execute themselves
|
|
120
|
-
*/
|
|
121
|
-
export function useSignTransaction() {
|
|
122
|
-
const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
123
|
-
const signTransaction = async (unsignedTransaction, fromAddress) => {
|
|
124
|
-
if (!isConnected || !wallet) {
|
|
125
|
-
throw new Error('Wallet is not connected');
|
|
126
|
-
}
|
|
127
|
-
const targetAddress = fromAddress || address;
|
|
128
|
-
if (!targetAddress) {
|
|
129
|
-
throw new Error('Address is required');
|
|
130
|
-
}
|
|
131
|
-
return await signTransactionViaAPI(unsignedTransaction, targetAddress);
|
|
132
|
-
};
|
|
133
|
-
return { signTransaction, isConnected, address };
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Hook to sign and send transaction
|
|
137
|
-
* Signs transaction via API and then executes it internally
|
|
138
|
-
* Returns transaction hash
|
|
139
|
-
*/
|
|
140
|
-
export function useSignAndSendTransaction() {
|
|
141
|
-
const { wallet, isConnected, address, signAndSendTransaction } = useAbstraxnWallet();
|
|
142
|
-
const signAndSend = async (unsignedTransaction, fromAddress, rpcUrl) => {
|
|
143
|
-
if (!isConnected || !wallet) {
|
|
144
|
-
throw new Error('Wallet is not connected');
|
|
145
|
-
}
|
|
146
|
-
const targetAddress = fromAddress || address;
|
|
147
|
-
if (!targetAddress) {
|
|
148
|
-
throw new Error('Address is required');
|
|
149
|
-
}
|
|
150
|
-
return await signAndSendTransaction(unsignedTransaction, targetAddress, rpcUrl);
|
|
151
|
-
};
|
|
152
|
-
return { signAndSendTransaction: signAndSend, isConnected, address };
|
|
153
|
-
}
|
|
154
56
|
/**
|
|
155
57
|
* Hook to export wallet private key
|
|
156
58
|
* Returns the export bundle containing the private key
|
|
@@ -405,7 +307,7 @@ export function useExternalWalletInfo() {
|
|
|
405
307
|
*
|
|
406
308
|
* @example
|
|
407
309
|
* ```tsx
|
|
408
|
-
* import { usePublicClient } from 'signer-
|
|
310
|
+
* import { usePublicClient } from '@abstraxn/signer-react';
|
|
409
311
|
* import { polygonAmoy } from 'viem/chains';
|
|
410
312
|
*
|
|
411
313
|
* function MyComponent() {
|
|
@@ -448,7 +350,7 @@ export function usePublicClient(chain, rpcUrl) {
|
|
|
448
350
|
*
|
|
449
351
|
* @example
|
|
450
352
|
* ```tsx
|
|
451
|
-
* import { useWalletClient } from 'signer-
|
|
353
|
+
* import { useWalletClient } from '@abstraxn/signer-react';
|
|
452
354
|
* import { polygonAmoy } from 'viem/chains';
|
|
453
355
|
*
|
|
454
356
|
* function MyComponent() {
|
|
@@ -504,7 +406,7 @@ export function useWalletClient(chain, rpcUrl) {
|
|
|
504
406
|
*
|
|
505
407
|
* @example
|
|
506
408
|
* ```tsx
|
|
507
|
-
* import { useContract, usePublicClient } from 'signer-
|
|
409
|
+
* import { useContract, usePublicClient } from '@abstraxn/signer-react';
|
|
508
410
|
* import { polygonAmoy } from 'viem/chains';
|
|
509
411
|
* import erc20Abi from './erc20Abi.json';
|
|
510
412
|
*
|
|
@@ -608,21 +510,11 @@ export function usePrepareRawTxn(provider) {
|
|
|
608
510
|
// Determine if this is a native transfer
|
|
609
511
|
const isNativeTransfer = !needsEncoding && !hasPreEncodedData;
|
|
610
512
|
if (isNativeTransfer) {
|
|
611
|
-
// Native transfer:
|
|
612
|
-
if (
|
|
513
|
+
// Native transfer: pass value through as-is (no hex conversion)
|
|
514
|
+
if (value === undefined || value === null || value === '' || value === '0' || value === 0) {
|
|
613
515
|
throw new Error('Value is required for native transfer');
|
|
614
516
|
}
|
|
615
|
-
|
|
616
|
-
const valueStr = typeof value === 'string' ? value.trim() : String(value);
|
|
617
|
-
let valueInWei;
|
|
618
|
-
try {
|
|
619
|
-
valueInWei = parseEther(valueStr);
|
|
620
|
-
}
|
|
621
|
-
catch (error) {
|
|
622
|
-
throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
|
|
623
|
-
}
|
|
624
|
-
// Convert to hex string
|
|
625
|
-
finalValue = `0x${valueInWei.toString(16)}`;
|
|
517
|
+
finalValue = typeof value === 'bigint' ? value : (typeof value === 'string' ? value.trim() : String(value));
|
|
626
518
|
finalData = '0x';
|
|
627
519
|
}
|
|
628
520
|
else {
|
|
@@ -635,36 +527,8 @@ export function usePrepareRawTxn(provider) {
|
|
|
635
527
|
if (!functionName) {
|
|
636
528
|
throw new Error('Function name is required for encoding function data');
|
|
637
529
|
}
|
|
638
|
-
//
|
|
639
|
-
const processedArgs =
|
|
640
|
-
// Check if this is likely an amount parameter for transfer/transferFrom functions
|
|
641
|
-
const isAmountParam = (functionName === 'transfer' || functionName === 'transferFrom') && index === 1;
|
|
642
|
-
if (isAmountParam) {
|
|
643
|
-
// If arg is a string or number that looks like ETH (has decimal point)
|
|
644
|
-
if (typeof arg === 'string' && arg.includes('.')) {
|
|
645
|
-
try {
|
|
646
|
-
// Convert ETH to wei
|
|
647
|
-
const weiAmount = parseEther(arg);
|
|
648
|
-
return weiAmount.toString();
|
|
649
|
-
}
|
|
650
|
-
catch (error) {
|
|
651
|
-
throw new Error(`Failed to convert amount "${arg}" to wei. Make sure it's a valid number string (e.g., "0.001").`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
else if (typeof arg === 'number' && arg % 1 !== 0) {
|
|
655
|
-
// Number with decimal places - convert to wei
|
|
656
|
-
try {
|
|
657
|
-
const weiAmount = parseEther(arg.toString());
|
|
658
|
-
return weiAmount.toString();
|
|
659
|
-
}
|
|
660
|
-
catch (error) {
|
|
661
|
-
throw new Error(`Failed to convert amount ${arg} to wei. Make sure it's a valid number.`);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
// For other args, just return as-is
|
|
666
|
-
return arg;
|
|
667
|
-
});
|
|
530
|
+
// Pass args through as-is (no conversion); caller must pass amounts in wei / correct format
|
|
531
|
+
const processedArgs = args ?? [];
|
|
668
532
|
try {
|
|
669
533
|
finalData = encodeFunctionData({
|
|
670
534
|
abi: abi,
|
|
@@ -686,18 +550,9 @@ export function usePrepareRawTxn(provider) {
|
|
|
686
550
|
else {
|
|
687
551
|
throw new Error('Either provide encoded data, or provide abi/functionName/args for encoding.');
|
|
688
552
|
}
|
|
689
|
-
// Handle value for contract calls
|
|
553
|
+
// Handle value for contract calls: pass through as-is (no hex conversion)
|
|
690
554
|
if (value && value !== '0' && value !== 0) {
|
|
691
|
-
|
|
692
|
-
const valueStr = typeof value === 'string' ? value.trim() : String(value);
|
|
693
|
-
let valueInWei;
|
|
694
|
-
try {
|
|
695
|
-
valueInWei = parseEther(valueStr);
|
|
696
|
-
}
|
|
697
|
-
catch (error) {
|
|
698
|
-
throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
|
|
699
|
-
}
|
|
700
|
-
finalValue = `0x${valueInWei.toString(16)}`;
|
|
555
|
+
finalValue = typeof value === 'bigint' ? value : (typeof value === 'string' ? value.trim() : String(value));
|
|
701
556
|
}
|
|
702
557
|
else {
|
|
703
558
|
finalValue = '0x0';
|
|
@@ -724,22 +579,108 @@ export function usePrepareRawTxn(provider) {
|
|
|
724
579
|
* const { signTxn } = useSignTxn(publicClient);
|
|
725
580
|
*
|
|
726
581
|
* // Prepare transaction
|
|
727
|
-
* const rawTx = await prepareRawTxn({
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
* // Sign transaction
|
|
582
|
+
* const rawTx = await prepareRawTxn({ from: address!, to: '0x...', value: '0.001' });
|
|
583
|
+
* const { estimateGas } = useEstimateGas(publicClient);
|
|
584
|
+
* const { getGasPrice } = useGetGasPrice(publicClient);
|
|
585
|
+
* const { gasLimit } = await estimateGas({ account: address!, to: rawTx.to, data: rawTx.data, value: rawTx.value });
|
|
586
|
+
* const fees = await getGasPrice();
|
|
734
587
|
* const signedTx = await signTxn({
|
|
735
588
|
* from: address!,
|
|
736
|
-
* ...rawTx,
|
|
589
|
+
* ...rawTx,
|
|
590
|
+
* gas: { gasLimit, maxFeePerGas: fees.maxFeePerGas!, maxPriorityFeePerGas: fees.maxPriorityFeePerGas! },
|
|
737
591
|
* });
|
|
738
592
|
* ```
|
|
739
593
|
*/
|
|
594
|
+
// export function useSignTxn(provider: PublicClient) {
|
|
595
|
+
// const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
596
|
+
// const signTxn = useCallback(async ({
|
|
597
|
+
// from,
|
|
598
|
+
// to,
|
|
599
|
+
// value,
|
|
600
|
+
// data,
|
|
601
|
+
// chainId,
|
|
602
|
+
// gas,
|
|
603
|
+
// }: {
|
|
604
|
+
// from: Address;
|
|
605
|
+
// to: Address;
|
|
606
|
+
// value: string | bigint;
|
|
607
|
+
// data: `0x${string}`;
|
|
608
|
+
// chainId?: number;
|
|
609
|
+
// /** Gas: pass gasLimit + (maxFeePerGas & maxPriorityFeePerGas for EIP-1559) or (gasPrice for legacy). Use useEstimateGas and useGetGasPrice. */
|
|
610
|
+
// gas: {
|
|
611
|
+
// gasLimit: bigint;
|
|
612
|
+
// gasPrice?: bigint;
|
|
613
|
+
// maxFeePerGas?: bigint;
|
|
614
|
+
// maxPriorityFeePerGas?: bigint;
|
|
615
|
+
// };
|
|
616
|
+
// }) => {
|
|
617
|
+
// if (!isConnected || !wallet) {
|
|
618
|
+
// throw new Error('Wallet is not connected');
|
|
619
|
+
// }
|
|
620
|
+
// if (!provider) {
|
|
621
|
+
// throw new Error('Provider (publicClient) is required');
|
|
622
|
+
// }
|
|
623
|
+
// if (!from) {
|
|
624
|
+
// throw new Error('From address is required');
|
|
625
|
+
// }
|
|
626
|
+
// if (!to) {
|
|
627
|
+
// throw new Error('To address is required');
|
|
628
|
+
// }
|
|
629
|
+
// if (gas.gasLimit === undefined) {
|
|
630
|
+
// throw new Error('gas.gasLimit is required. Use useEstimateGas to get values.');
|
|
631
|
+
// }
|
|
632
|
+
// const isLegacy = gas.gasPrice !== undefined;
|
|
633
|
+
// if (!isLegacy) {
|
|
634
|
+
// if (gas.maxFeePerGas === undefined || gas.maxPriorityFeePerGas === undefined) {
|
|
635
|
+
// throw new Error('gas.maxFeePerGas and gas.maxPriorityFeePerGas are required for EIP-1559, or pass gas.gasPrice for legacy.');
|
|
636
|
+
// }
|
|
637
|
+
// }
|
|
638
|
+
// // Get chain ID from provider or use provided one
|
|
639
|
+
// const targetChainId = chainId || provider.chain?.id;
|
|
640
|
+
// if (!targetChainId) {
|
|
641
|
+
// throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
|
|
642
|
+
// }
|
|
643
|
+
// // Get nonce
|
|
644
|
+
// const nonce = await provider.getTransactionCount({ address: from });
|
|
645
|
+
// // Value in wei (pass-through from prepareRawTxn, no hex conversion)
|
|
646
|
+
// const valueInWei = typeof value === 'bigint' ? value : BigInt(value === '0x' || value === '' ? '0' : value);
|
|
647
|
+
// // Build unsigned tx from user gas (no estimation in hook)
|
|
648
|
+
// const unsignedTx = isLegacy
|
|
649
|
+
// ? {
|
|
650
|
+
// chainId: targetChainId,
|
|
651
|
+
// from,
|
|
652
|
+
// to,
|
|
653
|
+
// data,
|
|
654
|
+
// value: valueInWei,
|
|
655
|
+
// nonce,
|
|
656
|
+
// gas: gas.gasLimit,
|
|
657
|
+
// gasPrice: gas.gasPrice,
|
|
658
|
+
// }
|
|
659
|
+
// : {
|
|
660
|
+
// chainId: targetChainId,
|
|
661
|
+
// from,
|
|
662
|
+
// to,
|
|
663
|
+
// data,
|
|
664
|
+
// value: valueInWei,
|
|
665
|
+
// nonce,
|
|
666
|
+
// gas: gas.gasLimit,
|
|
667
|
+
// maxFeePerGas: gas.maxFeePerGas!,
|
|
668
|
+
// maxPriorityFeePerGas: gas.maxPriorityFeePerGas!,
|
|
669
|
+
// };
|
|
670
|
+
// // Serialize transaction
|
|
671
|
+
// const serializedTx = serializeTransaction(unsignedTx);
|
|
672
|
+
// // Sign transaction via Turnkey API
|
|
673
|
+
// const signResult = await signTransactionViaAPI(serializedTx, from);
|
|
674
|
+
// return {
|
|
675
|
+
// unsignedTransaction: serializedTx,
|
|
676
|
+
// signedTransaction: signResult.signedTransaction,
|
|
677
|
+
// };
|
|
678
|
+
// }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
|
|
679
|
+
// return { signTxn, isConnected, address };
|
|
680
|
+
// }
|
|
740
681
|
export function useSignTxn(provider) {
|
|
741
682
|
const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
742
|
-
const signTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
|
|
683
|
+
const signTxn = useCallback(async ({ from, to, value, data, chainId, gas, }) => {
|
|
743
684
|
if (!isConnected || !wallet) {
|
|
744
685
|
throw new Error('Wallet is not connected');
|
|
745
686
|
}
|
|
@@ -759,32 +700,31 @@ export function useSignTxn(provider) {
|
|
|
759
700
|
}
|
|
760
701
|
// Get nonce
|
|
761
702
|
const nonce = await provider.getTransactionCount({ address: from });
|
|
762
|
-
//
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
// Estimate gas
|
|
767
|
-
const gas = await provider.estimateGas({
|
|
768
|
-
account: from,
|
|
769
|
-
to,
|
|
770
|
-
data,
|
|
771
|
-
value: valueInWei,
|
|
772
|
-
});
|
|
773
|
-
// Estimate fees
|
|
774
|
-
const fee = await provider.estimateFeesPerGas();
|
|
775
|
-
// Create unsigned transaction
|
|
776
|
-
const unsignedTx = {
|
|
703
|
+
// Value in wei (pass-through from prepareRawTxn, no hex conversion)
|
|
704
|
+
const valueInWei = typeof value === 'bigint' ? value : BigInt(value === '0x' || value === '' ? '0' : value);
|
|
705
|
+
// Build base transaction object
|
|
706
|
+
const baseTx = {
|
|
777
707
|
chainId: targetChainId,
|
|
778
708
|
from,
|
|
779
709
|
to,
|
|
780
710
|
data,
|
|
781
711
|
value: valueInWei,
|
|
782
712
|
nonce,
|
|
783
|
-
gas,
|
|
784
|
-
maxFeePerGas: fee.maxFeePerGas,
|
|
785
|
-
maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
|
|
786
|
-
type: 'eip1559',
|
|
787
713
|
};
|
|
714
|
+
// Add gas properties if they exist
|
|
715
|
+
const unsignedTx = { ...baseTx };
|
|
716
|
+
if (gas?.gasLimit !== undefined) {
|
|
717
|
+
unsignedTx.gas = gas.gasLimit;
|
|
718
|
+
}
|
|
719
|
+
if (gas?.gasPrice !== undefined) {
|
|
720
|
+
unsignedTx.gasPrice = gas.gasPrice;
|
|
721
|
+
}
|
|
722
|
+
if (gas?.maxFeePerGas !== undefined) {
|
|
723
|
+
unsignedTx.maxFeePerGas = gas.maxFeePerGas;
|
|
724
|
+
}
|
|
725
|
+
if (gas?.maxPriorityFeePerGas !== undefined) {
|
|
726
|
+
unsignedTx.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
|
|
727
|
+
}
|
|
788
728
|
// Serialize transaction
|
|
789
729
|
const serializedTx = serializeTransaction(unsignedTx);
|
|
790
730
|
// Sign transaction via Turnkey API
|
|
@@ -809,24 +749,114 @@ export function useSignTxn(provider) {
|
|
|
809
749
|
* const { signAndSendTxn } = useSignAndSendTxn(publicClient);
|
|
810
750
|
*
|
|
811
751
|
* // Prepare transaction
|
|
812
|
-
* const rawTx = await prepareRawTxn({
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* // Sign and send transaction
|
|
752
|
+
* const rawTx = await prepareRawTxn({ from: address!, to: '0x...', value: '0.001' });
|
|
753
|
+
* const { estimateGas } = useEstimateGas(publicClient);
|
|
754
|
+
* const { getGasPrice } = useGetGasPrice(publicClient);
|
|
755
|
+
* const { gasLimit } = await estimateGas({ account: address!, to: rawTx.to, data: rawTx.data, value: rawTx.value });
|
|
756
|
+
* const fees = await getGasPrice();
|
|
819
757
|
* const result = await signAndSendTxn({
|
|
820
758
|
* from: address!,
|
|
821
|
-
* ...rawTx,
|
|
759
|
+
* ...rawTx,
|
|
760
|
+
* gas: { gasLimit, maxFeePerGas: fees.maxFeePerGas!, maxPriorityFeePerGas: fees.maxPriorityFeePerGas! },
|
|
822
761
|
* });
|
|
823
|
-
*
|
|
824
762
|
* console.log('Transaction hash:', result.hash);
|
|
825
763
|
* ```
|
|
826
764
|
*/
|
|
765
|
+
// export function useSignAndSendTxn(provider: PublicClient) {
|
|
766
|
+
// const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
767
|
+
// const signAndSendTxn = useCallback(async ({
|
|
768
|
+
// from,
|
|
769
|
+
// to,
|
|
770
|
+
// value,
|
|
771
|
+
// data,
|
|
772
|
+
// chainId,
|
|
773
|
+
// gas,
|
|
774
|
+
// }: {
|
|
775
|
+
// from: Address;
|
|
776
|
+
// to: Address;
|
|
777
|
+
// value: string | bigint;
|
|
778
|
+
// data: `0x${string}`;
|
|
779
|
+
// chainId?: number;
|
|
780
|
+
// /** Gas: pass gasLimit + (maxFeePerGas & maxPriorityFeePerGas for EIP-1559) or (gasPrice for legacy). Use useEstimateGas and useGetGasPrice. */
|
|
781
|
+
// gas: {
|
|
782
|
+
// gasLimit: bigint;
|
|
783
|
+
// gasPrice?: bigint;
|
|
784
|
+
// maxFeePerGas?: bigint;
|
|
785
|
+
// maxPriorityFeePerGas?: bigint;
|
|
786
|
+
// };
|
|
787
|
+
// }) => {
|
|
788
|
+
// if (!isConnected || !wallet) {
|
|
789
|
+
// throw new Error('Wallet is not connected');
|
|
790
|
+
// }
|
|
791
|
+
// if (!provider) {
|
|
792
|
+
// throw new Error('Provider (publicClient) is required');
|
|
793
|
+
// }
|
|
794
|
+
// if (!from) {
|
|
795
|
+
// throw new Error('From address is required');
|
|
796
|
+
// }
|
|
797
|
+
// if (!to) {
|
|
798
|
+
// throw new Error('To address is required');
|
|
799
|
+
// }
|
|
800
|
+
// if (gas.gasLimit === undefined) {
|
|
801
|
+
// throw new Error('gas.gasLimit is required. Use useEstimateGas to get values.');
|
|
802
|
+
// }
|
|
803
|
+
// const isLegacy = gas.gasPrice !== undefined;
|
|
804
|
+
// if (!isLegacy) {
|
|
805
|
+
// if (gas.maxFeePerGas === undefined || gas.maxPriorityFeePerGas === undefined) {
|
|
806
|
+
// throw new Error('gas.maxFeePerGas and gas.maxPriorityFeePerGas are required for EIP-1559, or pass gas.gasPrice for legacy.');
|
|
807
|
+
// }
|
|
808
|
+
// }
|
|
809
|
+
// // Get chain ID from provider or use provided one
|
|
810
|
+
// const targetChainId = chainId || provider.chain?.id;
|
|
811
|
+
// if (!targetChainId) {
|
|
812
|
+
// throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
|
|
813
|
+
// }
|
|
814
|
+
// // Get nonce
|
|
815
|
+
// const nonce = await provider.getTransactionCount({ address: from });
|
|
816
|
+
// // Value in wei (pass-through from prepareRawTxn, no hex conversion)
|
|
817
|
+
// const valueInWei = typeof value === 'bigint' ? value : BigInt(value === '0x' || value === '' ? '0' : value);
|
|
818
|
+
// // Build unsigned tx from user gas (no estimation in hook)
|
|
819
|
+
// const unsignedTx = isLegacy
|
|
820
|
+
// ? {
|
|
821
|
+
// chainId: targetChainId,
|
|
822
|
+
// from,
|
|
823
|
+
// to,
|
|
824
|
+
// data,
|
|
825
|
+
// value: valueInWei,
|
|
826
|
+
// nonce,
|
|
827
|
+
// gas: gas.gasLimit,
|
|
828
|
+
// gasPrice: gas.gasPrice,
|
|
829
|
+
// }
|
|
830
|
+
// : {
|
|
831
|
+
// chainId: targetChainId,
|
|
832
|
+
// from,
|
|
833
|
+
// to,
|
|
834
|
+
// data,
|
|
835
|
+
// value: valueInWei,
|
|
836
|
+
// nonce,
|
|
837
|
+
// gas: gas.gasLimit,
|
|
838
|
+
// maxFeePerGas: gas.maxFeePerGas!,
|
|
839
|
+
// maxPriorityFeePerGas: gas.maxPriorityFeePerGas!,
|
|
840
|
+
// };
|
|
841
|
+
// // Serialize transaction
|
|
842
|
+
// const serializedTx = serializeTransaction(unsignedTx);
|
|
843
|
+
// // Sign transaction via Turnkey API
|
|
844
|
+
// const signResult = await signTransactionViaAPI(serializedTx, from);
|
|
845
|
+
// // Send the signed transaction
|
|
846
|
+
// const hash = await provider.sendRawTransaction({
|
|
847
|
+
// serializedTransaction: signResult.signedTransaction as `0x${string}`,
|
|
848
|
+
// });
|
|
849
|
+
// return {
|
|
850
|
+
// hash,
|
|
851
|
+
// unsignedTransaction: serializedTx,
|
|
852
|
+
// signedTransaction: signResult.signedTransaction,
|
|
853
|
+
// };
|
|
854
|
+
// }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
|
|
855
|
+
// return { signAndSendTxn, isConnected, address };
|
|
856
|
+
// }
|
|
827
857
|
export function useSignAndSendTxn(provider) {
|
|
828
858
|
const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
|
|
829
|
-
const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
|
|
859
|
+
const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, gas, }) => {
|
|
830
860
|
if (!isConnected || !wallet) {
|
|
831
861
|
throw new Error('Wallet is not connected');
|
|
832
862
|
}
|
|
@@ -846,32 +876,31 @@ export function useSignAndSendTxn(provider) {
|
|
|
846
876
|
}
|
|
847
877
|
// Get nonce
|
|
848
878
|
const nonce = await provider.getTransactionCount({ address: from });
|
|
849
|
-
//
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
const
|
|
853
|
-
// Estimate gas
|
|
854
|
-
const gas = await provider.estimateGas({
|
|
855
|
-
account: from,
|
|
856
|
-
to,
|
|
857
|
-
data,
|
|
858
|
-
value: valueInWei,
|
|
859
|
-
});
|
|
860
|
-
// Estimate fees
|
|
861
|
-
const fee = await provider.estimateFeesPerGas();
|
|
862
|
-
// Create unsigned transaction
|
|
863
|
-
const unsignedTx = {
|
|
879
|
+
// Value in wei (pass-through from prepareRawTxn, no hex conversion)
|
|
880
|
+
const valueInWei = typeof value === 'bigint' ? value : BigInt(value === '0x' || value === '' ? '0' : value);
|
|
881
|
+
// Build base transaction object
|
|
882
|
+
const baseTx = {
|
|
864
883
|
chainId: targetChainId,
|
|
865
884
|
from,
|
|
866
885
|
to,
|
|
867
886
|
data,
|
|
868
887
|
value: valueInWei,
|
|
869
888
|
nonce,
|
|
870
|
-
gas,
|
|
871
|
-
maxFeePerGas: fee.maxFeePerGas,
|
|
872
|
-
maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
|
|
873
|
-
type: 'eip1559',
|
|
874
889
|
};
|
|
890
|
+
// Add gas properties if they exist
|
|
891
|
+
const unsignedTx = { ...baseTx };
|
|
892
|
+
if (gas?.gasLimit !== undefined) {
|
|
893
|
+
unsignedTx.gas = gas.gasLimit;
|
|
894
|
+
}
|
|
895
|
+
if (gas?.gasPrice !== undefined) {
|
|
896
|
+
unsignedTx.gasPrice = gas.gasPrice;
|
|
897
|
+
}
|
|
898
|
+
if (gas?.maxFeePerGas !== undefined) {
|
|
899
|
+
unsignedTx.maxFeePerGas = gas.maxFeePerGas;
|
|
900
|
+
}
|
|
901
|
+
if (gas?.maxPriorityFeePerGas !== undefined) {
|
|
902
|
+
unsignedTx.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
|
|
903
|
+
}
|
|
875
904
|
// Serialize transaction
|
|
876
905
|
const serializedTx = serializeTransaction(unsignedTx);
|
|
877
906
|
// Sign transaction via Turnkey API
|
|
@@ -941,6 +970,87 @@ export function useWaitForTxnReceipt(provider) {
|
|
|
941
970
|
}, [provider]);
|
|
942
971
|
return { waitForTxnReceipt };
|
|
943
972
|
}
|
|
973
|
+
/**
|
|
974
|
+
* Hook to estimate gas for a transaction
|
|
975
|
+
* Returns gasLimit to pass as gas.gasLimit to useSignTxn / useSignAndSendTxn (use useGetGasPrice for fees; no estimation inside those hooks).
|
|
976
|
+
*
|
|
977
|
+
* @param provider - PublicClient instance (can be created using usePublicClient hook)
|
|
978
|
+
* @returns Object with estimateGas function
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```tsx
|
|
982
|
+
* const { estimateGas } = useEstimateGas(publicClient);
|
|
983
|
+
* const result = await estimateGas({
|
|
984
|
+
* account: address!,
|
|
985
|
+
* to: rawTx.to,
|
|
986
|
+
* data: rawTx.data,
|
|
987
|
+
* value: rawTx.value,
|
|
988
|
+
* });
|
|
989
|
+
* // gas: { gasLimit: result.gasLimit }
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
export function useEstimateGas(provider) {
|
|
993
|
+
const estimateGas = useCallback(async ({ account, to, data = '0x', value = 0n, }) => {
|
|
994
|
+
if (!provider) {
|
|
995
|
+
throw new Error('Provider (publicClient) is required');
|
|
996
|
+
}
|
|
997
|
+
if (!account) {
|
|
998
|
+
throw new Error('Account (from) is required');
|
|
999
|
+
}
|
|
1000
|
+
if (!to) {
|
|
1001
|
+
throw new Error('To address is required');
|
|
1002
|
+
}
|
|
1003
|
+
const valueInWei = typeof value === 'bigint' ? value : (value === undefined || value === '' || value === '0x' ? 0n : BigInt(value));
|
|
1004
|
+
const gasLimit = await provider.estimateGas({
|
|
1005
|
+
account,
|
|
1006
|
+
to,
|
|
1007
|
+
data: data ?? '0x',
|
|
1008
|
+
value: valueInWei,
|
|
1009
|
+
});
|
|
1010
|
+
return { gasLimit };
|
|
1011
|
+
}, [provider]);
|
|
1012
|
+
return { estimateGas };
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Hook to get current gas price / EIP-1559 fees from the chain.
|
|
1016
|
+
* Use with useEstimateGas when you need gas limit and gas price for the gas param in useSignTxn / useSignAndSendTxn.
|
|
1017
|
+
*
|
|
1018
|
+
* @param provider - PublicClient instance (can be created using usePublicClient hook)
|
|
1019
|
+
* @returns Object with getGasPrice function returning maxFeePerGas, maxPriorityFeePerGas, and/or gasPrice
|
|
1020
|
+
*
|
|
1021
|
+
* @example
|
|
1022
|
+
* ```tsx
|
|
1023
|
+
* const { publicClient } = usePublicClient(...);
|
|
1024
|
+
* const { getGasPrice } = useGetGasPrice(publicClient);
|
|
1025
|
+
* const fees = await getGasPrice();
|
|
1026
|
+
* // EIP-1559: gas: { gasLimit, maxFeePerGas: fees.maxFeePerGas!, maxPriorityFeePerGas: fees.maxPriorityFeePerGas! }
|
|
1027
|
+
* // Legacy: gas: { gasLimit, gasPrice: fees.gasPrice! }
|
|
1028
|
+
* ```
|
|
1029
|
+
*/
|
|
1030
|
+
export function useGetGasPrice(provider) {
|
|
1031
|
+
const getGasPrice = useCallback(async () => {
|
|
1032
|
+
if (!provider) {
|
|
1033
|
+
throw new Error('Provider (publicClient) is required');
|
|
1034
|
+
}
|
|
1035
|
+
const result = {};
|
|
1036
|
+
try {
|
|
1037
|
+
const fees = await provider.estimateFeesPerGas();
|
|
1038
|
+
result.maxFeePerGas = fees.maxFeePerGas;
|
|
1039
|
+
}
|
|
1040
|
+
catch {
|
|
1041
|
+
// EIP-1559 not supported, will try legacy below
|
|
1042
|
+
}
|
|
1043
|
+
try {
|
|
1044
|
+
result.gasPrice = await provider.getGasPrice();
|
|
1045
|
+
result.maxPriorityFeePerGas = await provider.estimateMaxPriorityFeePerGas();
|
|
1046
|
+
}
|
|
1047
|
+
catch {
|
|
1048
|
+
// Optional: gasPrice not available
|
|
1049
|
+
}
|
|
1050
|
+
return result;
|
|
1051
|
+
}, [provider]);
|
|
1052
|
+
return { getGasPrice };
|
|
1053
|
+
}
|
|
944
1054
|
/**
|
|
945
1055
|
* Hook to read from contract
|
|
946
1056
|
* Reads contract data using publicClient.readContract
|
|
@@ -1010,7 +1120,7 @@ export function useReadContract(provider) {
|
|
|
1010
1120
|
*
|
|
1011
1121
|
* @example
|
|
1012
1122
|
* ```tsx
|
|
1013
|
-
* import { useExternalWalletClient } from 'signer-
|
|
1123
|
+
* import { useExternalWalletClient } from '@abstraxn/signer-react';
|
|
1014
1124
|
*
|
|
1015
1125
|
* function MyComponent() {
|
|
1016
1126
|
* const { walletClient, isConnected } = useExternalWalletClient();
|
|
@@ -1058,7 +1168,7 @@ export function useExternalWalletClient() {
|
|
|
1058
1168
|
*
|
|
1059
1169
|
* @example
|
|
1060
1170
|
* ```tsx
|
|
1061
|
-
* import { useWriteContract } from 'signer-
|
|
1171
|
+
* import { useWriteContract } from '@abstraxn/signer-react';
|
|
1062
1172
|
* import erc20Abi from './erc20Abi.json';
|
|
1063
1173
|
*
|
|
1064
1174
|
* function MyComponent() {
|
|
@@ -1100,36 +1210,55 @@ export function useWriteContract() {
|
|
|
1100
1210
|
const { walletClient: hookClient, isPending, isError, error, isConnected } = useExternalWalletClient();
|
|
1101
1211
|
const config = useConfig();
|
|
1102
1212
|
const currentChainId = useWagmiChainId();
|
|
1103
|
-
const writeContract = useCallback(({ address, abi, functionName, args, value, account, gas, gasPrice, maxFeePerGas, maxPriorityFeePerGas, nonce, }) => {
|
|
1213
|
+
const writeContract = useCallback(({ address, abi, functionName, args, value, account, gas, gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas, nonce, }) => {
|
|
1104
1214
|
return (async () => {
|
|
1105
1215
|
let activeClient = hookClient;
|
|
1106
1216
|
// If hook client is missing or in error, try to recover
|
|
1107
1217
|
if (!activeClient || isError) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1218
|
+
// Check if the initial error was a chain mismatch
|
|
1219
|
+
const isMismatch = isError && (error?.name === 'ConnectorChainMismatchError' || error?.message?.includes('ChainMismatch'));
|
|
1220
|
+
if (isMismatch) {
|
|
1221
|
+
try {
|
|
1222
|
+
if (!currentChainId)
|
|
1223
|
+
throw new Error('Current chain ID is not available');
|
|
1224
|
+
await switchChain(config, { chainId: currentChainId });
|
|
1225
|
+
// Retry getting client after switch
|
|
1226
|
+
activeClient = await getWalletClient(config);
|
|
1227
|
+
}
|
|
1228
|
+
catch (switchErr) {
|
|
1229
|
+
console.error("Failed to auto-switch chain:", switchErr);
|
|
1230
|
+
// Fall through to try getWalletClient directly or throw
|
|
1231
|
+
}
|
|
1111
1232
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
throw new Error('Current chain ID is not available');
|
|
1118
|
-
await switchChain(config, { chainId: currentChainId });
|
|
1119
|
-
// Retry getting client
|
|
1120
|
-
activeClient = await getWalletClient(config);
|
|
1121
|
-
}
|
|
1122
|
-
catch (switchErr) {
|
|
1123
|
-
throw new Error(`Failed to switch chain: ${switchErr instanceof Error ? switchErr.message : 'Unknown error'}`);
|
|
1124
|
-
}
|
|
1233
|
+
// If we still don't have a client (or switch failed), try to get it directly
|
|
1234
|
+
if (!activeClient) {
|
|
1235
|
+
try {
|
|
1236
|
+
// Try to get client directly
|
|
1237
|
+
activeClient = await getWalletClient(config);
|
|
1125
1238
|
}
|
|
1126
|
-
|
|
1127
|
-
//
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1239
|
+
catch (err) {
|
|
1240
|
+
// If mismatch error (and we haven't tried switching yet or it failed differently), try to switch chain
|
|
1241
|
+
// This catches cases where getWalletClient throws mismatch but the hook error wasn't mismatch (unlikely but possible)
|
|
1242
|
+
if (err?.name === 'ConnectorChainMismatchError' || err?.message?.includes('ChainMismatch')) {
|
|
1243
|
+
try {
|
|
1244
|
+
if (!currentChainId)
|
|
1245
|
+
throw new Error('Current chain ID is not available');
|
|
1246
|
+
await switchChain(config, { chainId: currentChainId });
|
|
1247
|
+
// Retry getting client
|
|
1248
|
+
activeClient = await getWalletClient(config);
|
|
1249
|
+
}
|
|
1250
|
+
catch (switchErr) {
|
|
1251
|
+
throw new Error(`Failed to switch chain: ${switchErr instanceof Error ? switchErr.message : 'Unknown error'}`);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
// Re-throw if it's not a mismatch we can handle
|
|
1256
|
+
if (isError && error) {
|
|
1257
|
+
console.error("WalletClient Initialization Error:", error);
|
|
1258
|
+
throw new Error(`Failed to initialize WalletClient: ${error.message}. Please try reconnecting your wallet.`);
|
|
1259
|
+
}
|
|
1260
|
+
throw err;
|
|
1131
1261
|
}
|
|
1132
|
-
throw err;
|
|
1133
1262
|
}
|
|
1134
1263
|
}
|
|
1135
1264
|
}
|
|
@@ -1169,7 +1298,7 @@ export function useWriteContract() {
|
|
|
1169
1298
|
'Make sure the function exists in the ABI and is a state-changing function.');
|
|
1170
1299
|
}
|
|
1171
1300
|
// Prepare the call options (viem contract write methods accept options as the last parameter)
|
|
1172
|
-
const hasOptions = value !== undefined || account !== undefined || gas !== undefined ||
|
|
1301
|
+
const hasOptions = value !== undefined || account !== undefined || gas !== undefined || gasLimit !== undefined ||
|
|
1173
1302
|
gasPrice !== undefined || maxFeePerGas !== undefined ||
|
|
1174
1303
|
maxPriorityFeePerGas !== undefined || nonce !== undefined;
|
|
1175
1304
|
// Viem contract write methods expect args as an array, not spread
|
|
@@ -1180,8 +1309,13 @@ export function useWriteContract() {
|
|
|
1180
1309
|
callOptions.value = value;
|
|
1181
1310
|
if (account !== undefined)
|
|
1182
1311
|
callOptions.account = account;
|
|
1183
|
-
|
|
1312
|
+
// `gas` in viem is the gas *limit*. Allow either `gas` or `gasLimit` from the hook caller.
|
|
1313
|
+
if (gas !== undefined) {
|
|
1184
1314
|
callOptions.gas = gas;
|
|
1315
|
+
}
|
|
1316
|
+
else if (gasLimit !== undefined) {
|
|
1317
|
+
callOptions.gas = gasLimit;
|
|
1318
|
+
}
|
|
1185
1319
|
if (gasPrice !== undefined)
|
|
1186
1320
|
callOptions.gasPrice = gasPrice;
|
|
1187
1321
|
if (maxFeePerGas !== undefined)
|
|
@@ -1210,7 +1344,7 @@ export function useWriteContract() {
|
|
|
1210
1344
|
*
|
|
1211
1345
|
* @example
|
|
1212
1346
|
* ```tsx
|
|
1213
|
-
* import { useConnectionType } from 'signer-
|
|
1347
|
+
* import { useConnectionType } from '@abstraxn/signer-react';
|
|
1214
1348
|
*
|
|
1215
1349
|
* function MyComponent() {
|
|
1216
1350
|
* const { connectionType, connectorMeta } = useConnectionType();
|
|
@@ -1262,4 +1396,112 @@ function normalizeConnectionType(type) {
|
|
|
1262
1396
|
// Return original if no match (for other wallet types like coinbase, phantom, etc.)
|
|
1263
1397
|
return type;
|
|
1264
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Hook to switch chain
|
|
1401
|
+
* Handles both internal (social) and external wallets
|
|
1402
|
+
*/
|
|
1403
|
+
export function useSwitchChain() {
|
|
1404
|
+
const { wallet, connectionType } = useAbstraxnWallet();
|
|
1405
|
+
const { switchChainAsync } = useWagmiSwitchChain();
|
|
1406
|
+
const { isConnected: isWagmiConnected } = useAccount();
|
|
1407
|
+
const config = useConfig();
|
|
1408
|
+
const switchChain = async (chainId) => {
|
|
1409
|
+
// Normalize connection type
|
|
1410
|
+
const type = connectionType ? connectionType.toLowerCase() : '';
|
|
1411
|
+
// Check if it's a social login
|
|
1412
|
+
const isSocial = ['google', 'email', 'otp', 'x', 'discord', 'passkey'].some(t => type.includes(t));
|
|
1413
|
+
if (isSocial) {
|
|
1414
|
+
if (!wallet)
|
|
1415
|
+
throw new Error('Wallet not initialized');
|
|
1416
|
+
// Use internal wallet switch
|
|
1417
|
+
await wallet.switchChain(chainId);
|
|
1418
|
+
}
|
|
1419
|
+
else if (isWagmiConnected) {
|
|
1420
|
+
// Use wagmi for external wallets
|
|
1421
|
+
try {
|
|
1422
|
+
await switchChainAsync({ chainId });
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
// Check if error is related to chain not being added or RPC URL missing
|
|
1426
|
+
// "UserRejectedRequestError" with "chain.rpcUrls is undefined" often means the chain isn't known to the wallet/viem
|
|
1427
|
+
// Try to add the chain if we have its details
|
|
1428
|
+
let chainParams = null;
|
|
1429
|
+
// 1. Try to get from internal SDK chains
|
|
1430
|
+
const chainData = getChainById(chainId);
|
|
1431
|
+
if (chainData && chainData.type === 'evm') {
|
|
1432
|
+
chainParams = {
|
|
1433
|
+
chainName: chainData.displayName,
|
|
1434
|
+
nativeCurrency: chainData.nativeCurrency,
|
|
1435
|
+
rpcUrls: [chainData.rpcUrl],
|
|
1436
|
+
blockExplorerUrls: chainData.blockExplorer ? [chainData.blockExplorer.url] : undefined,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
// 2. If not found, try to get from wagmi config (includes custom chains passed to provider)
|
|
1440
|
+
if (!chainParams) {
|
|
1441
|
+
const wagmiChain = config.chains.find(c => c.id === chainId);
|
|
1442
|
+
if (wagmiChain) {
|
|
1443
|
+
// Handle both viem Chain (rpcUrls) and CoreChain (rpcUrl) formats
|
|
1444
|
+
// The chain object in config might be a CoreChain if it wasn't converted correctly
|
|
1445
|
+
const rpcUrls = wagmiChain.rpcUrls?.default?.http || (wagmiChain.rpcUrl ? [wagmiChain.rpcUrl] : []);
|
|
1446
|
+
const blockExplorerUrls = wagmiChain.blockExplorers?.default?.url
|
|
1447
|
+
? [wagmiChain.blockExplorers.default.url]
|
|
1448
|
+
: (wagmiChain.explorerUrl ? [wagmiChain.explorerUrl] : undefined);
|
|
1449
|
+
chainParams = {
|
|
1450
|
+
chainName: wagmiChain.name,
|
|
1451
|
+
nativeCurrency: wagmiChain.nativeCurrency,
|
|
1452
|
+
rpcUrls: rpcUrls,
|
|
1453
|
+
blockExplorerUrls: blockExplorerUrls,
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (chainParams) {
|
|
1458
|
+
try {
|
|
1459
|
+
await switchChainAsync({
|
|
1460
|
+
chainId,
|
|
1461
|
+
addEthereumChainParameter: chainParams
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
catch (retryError) {
|
|
1465
|
+
console.error('Failed to add and switch chain:', retryError);
|
|
1466
|
+
throw retryError;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
// If we don't have chain data, rethrow the original error
|
|
1471
|
+
throw error;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
else {
|
|
1476
|
+
throw new Error('No active connection to switch chain');
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
return { switchChain };
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Hook to sign a message
|
|
1483
|
+
* Handles both internal (social) and external wallets
|
|
1484
|
+
*/
|
|
1485
|
+
export function useSignMessage() {
|
|
1486
|
+
const { wallet, connectionType } = useAbstraxnWallet();
|
|
1487
|
+
const { signMessageAsync } = useWagmiSignMessage();
|
|
1488
|
+
const { isConnected: isWagmiConnected } = useAccount();
|
|
1489
|
+
const signMessage = async (message) => {
|
|
1490
|
+
// Normalize connection type
|
|
1491
|
+
const type = connectionType ? connectionType.toLowerCase() : '';
|
|
1492
|
+
// Check if it's a social login
|
|
1493
|
+
const isSocial = ['google', 'email', 'otp', 'x', 'discord', 'passkey'].some(t => type.includes(t));
|
|
1494
|
+
if (isSocial) {
|
|
1495
|
+
throw new Error('Signing messages is not supported for embedded wallets');
|
|
1496
|
+
}
|
|
1497
|
+
else if (isWagmiConnected) {
|
|
1498
|
+
// Use wagmi for external wallets
|
|
1499
|
+
return await signMessageAsync({ message });
|
|
1500
|
+
}
|
|
1501
|
+
else {
|
|
1502
|
+
throw new Error('No active connection to sign message');
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
return { signMessage };
|
|
1506
|
+
}
|
|
1265
1507
|
//# sourceMappingURL=hooks.js.map
|