signer-test-sdk-react 0.0.23 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +4 -0
  3. package/dist/src/OnboardingUI.d.ts +1 -1
  4. package/dist/src/chains.d.ts +1 -1
  5. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js.map +1 -1
  6. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +1 -1
  7. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -1
  8. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +2 -2
  9. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.d.ts +1 -1
  10. package/dist/src/components/AbstraxnProvider/useWalletInitialization.d.ts +1 -1
  11. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +1 -1
  12. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js.map +1 -1
  13. package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +1 -1
  14. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +1 -1
  15. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +1 -1
  16. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
  17. package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +1 -1
  18. package/dist/src/components/OnboardingUI/components/EmailForm.js +1 -1
  19. package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -1
  20. package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +1 -1
  21. package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +1 -1
  22. package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +1 -1
  23. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.d.ts +1 -1
  24. package/dist/src/components/OnboardingUI/hooks/useOnboarding.d.ts +1 -1
  25. package/dist/src/components/OnboardingUI/index.d.ts +1 -1
  26. package/dist/src/components/WalletModal/components/UserAvatar.d.ts +1 -1
  27. package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +1 -1
  28. package/dist/src/hooks.d.ts +104 -36
  29. package/dist/src/hooks.js +334 -121
  30. package/dist/src/hooks.js.map +1 -1
  31. package/dist/src/index.d.ts +1 -1
  32. package/dist/src/index.js +1 -1
  33. package/dist/src/index.js.map +1 -1
  34. package/dist/src/types.d.ts +6 -6
  35. package/dist/src/wagmiConfig.d.ts +1 -1
  36. package/dist/src/wagmiConfig.js +1 -1
  37. package/dist/src/wagmiConfig.js.map +1 -1
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +5 -4
package/dist/src/hooks.js CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { useState, useCallback, useEffect, useMemo } from 'react';
5
5
  import { useAbstraxnWallet } from './AbstraxnProvider';
6
- import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, parseEther, encodeFunctionData } from 'viem';
6
+ import { createPublicClient, createWalletClient, http, getContract, serializeTransaction, encodeFunctionData } from 'viem';
7
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';
@@ -307,7 +307,7 @@ export function useExternalWalletInfo() {
307
307
  *
308
308
  * @example
309
309
  * ```tsx
310
- * import { usePublicClient } from 'signer-test-sdk-react';
310
+ * import { usePublicClient } from '@abstraxn/signer-react';
311
311
  * import { polygonAmoy } from 'viem/chains';
312
312
  *
313
313
  * function MyComponent() {
@@ -350,7 +350,7 @@ export function usePublicClient(chain, rpcUrl) {
350
350
  *
351
351
  * @example
352
352
  * ```tsx
353
- * import { useWalletClient } from 'signer-test-sdk-react';
353
+ * import { useWalletClient } from '@abstraxn/signer-react';
354
354
  * import { polygonAmoy } from 'viem/chains';
355
355
  *
356
356
  * function MyComponent() {
@@ -406,7 +406,7 @@ export function useWalletClient(chain, rpcUrl) {
406
406
  *
407
407
  * @example
408
408
  * ```tsx
409
- * import { useContract, usePublicClient } from 'signer-test-sdk-react';
409
+ * import { useContract, usePublicClient } from '@abstraxn/signer-react';
410
410
  * import { polygonAmoy } from 'viem/chains';
411
411
  * import erc20Abi from './erc20Abi.json';
412
412
  *
@@ -510,21 +510,11 @@ export function usePrepareRawTxn(provider) {
510
510
  // Determine if this is a native transfer
511
511
  const isNativeTransfer = !needsEncoding && !hasPreEncodedData;
512
512
  if (isNativeTransfer) {
513
- // Native transfer: convert value to wei and set data to '0x'
514
- if (!value || value === '0' || value === 0) {
513
+ // Native transfer: pass value through as-is (no hex conversion)
514
+ if (value === undefined || value === null || value === '' || value === '0' || value === 0) {
515
515
  throw new Error('Value is required for native transfer');
516
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)}`;
517
+ finalValue = typeof value === 'bigint' ? value : (typeof value === 'string' ? value.trim() : String(value));
528
518
  finalData = '0x';
529
519
  }
530
520
  else {
@@ -537,36 +527,8 @@ export function usePrepareRawTxn(provider) {
537
527
  if (!functionName) {
538
528
  throw new Error('Function name is required for encoding function data');
539
529
  }
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
- });
530
+ // Pass args through as-is (no conversion); caller must pass amounts in wei / correct format
531
+ const processedArgs = args ?? [];
570
532
  try {
571
533
  finalData = encodeFunctionData({
572
534
  abi: abi,
@@ -588,18 +550,9 @@ export function usePrepareRawTxn(provider) {
588
550
  else {
589
551
  throw new Error('Either provide encoded data, or provide abi/functionName/args for encoding.');
590
552
  }
591
- // Handle value for contract calls
553
+ // Handle value for contract calls: pass through as-is (no hex conversion)
592
554
  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)}`;
555
+ finalValue = typeof value === 'bigint' ? value : (typeof value === 'string' ? value.trim() : String(value));
603
556
  }
604
557
  else {
605
558
  finalValue = '0x0';
@@ -626,22 +579,108 @@ export function usePrepareRawTxn(provider) {
626
579
  * const { signTxn } = useSignTxn(publicClient);
627
580
  *
628
581
  * // Prepare transaction
629
- * const rawTx = await prepareRawTxn({
630
- * from: address!,
631
- * to: '0x...',
632
- * value: '0.001',
633
- * });
634
- *
635
- * // 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();
636
587
  * const signedTx = await signTxn({
637
588
  * from: address!,
638
- * ...rawTx, // Spread to, value, data from prepareRawTxn
589
+ * ...rawTx,
590
+ * gas: { gasLimit, maxFeePerGas: fees.maxFeePerGas!, maxPriorityFeePerGas: fees.maxPriorityFeePerGas! },
639
591
  * });
640
592
  * ```
641
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
+ // }
642
681
  export function useSignTxn(provider) {
643
682
  const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
644
- const signTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
683
+ const signTxn = useCallback(async ({ from, to, value, data, chainId, gas, }) => {
645
684
  if (!isConnected || !wallet) {
646
685
  throw new Error('Wallet is not connected');
647
686
  }
@@ -661,32 +700,31 @@ export function useSignTxn(provider) {
661
700
  }
662
701
  // Get nonce
663
702
  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 = {
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 = {
679
707
  chainId: targetChainId,
680
708
  from,
681
709
  to,
682
710
  data,
683
711
  value: valueInWei,
684
712
  nonce,
685
- gas,
686
- maxFeePerGas: fee.maxFeePerGas,
687
- maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
688
- type: 'eip1559',
689
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
+ }
690
728
  // Serialize transaction
691
729
  const serializedTx = serializeTransaction(unsignedTx);
692
730
  // Sign transaction via Turnkey API
@@ -711,24 +749,114 @@ export function useSignTxn(provider) {
711
749
  * const { signAndSendTxn } = useSignAndSendTxn(publicClient);
712
750
  *
713
751
  * // Prepare transaction
714
- * const rawTx = await prepareRawTxn({
715
- * from: address!,
716
- * to: '0x...',
717
- * value: '0.001',
718
- * });
719
- *
720
- * // 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();
721
757
  * const result = await signAndSendTxn({
722
758
  * from: address!,
723
- * ...rawTx, // Spread to, value, data from prepareRawTxn
759
+ * ...rawTx,
760
+ * gas: { gasLimit, maxFeePerGas: fees.maxFeePerGas!, maxPriorityFeePerGas: fees.maxPriorityFeePerGas! },
724
761
  * });
725
- *
726
762
  * console.log('Transaction hash:', result.hash);
727
763
  * ```
728
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
+ // }
729
857
  export function useSignAndSendTxn(provider) {
730
858
  const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
731
- const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
859
+ const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, gas, }) => {
732
860
  if (!isConnected || !wallet) {
733
861
  throw new Error('Wallet is not connected');
734
862
  }
@@ -748,32 +876,31 @@ export function useSignAndSendTxn(provider) {
748
876
  }
749
877
  // Get nonce
750
878
  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 = {
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 = {
766
883
  chainId: targetChainId,
767
884
  from,
768
885
  to,
769
886
  data,
770
887
  value: valueInWei,
771
888
  nonce,
772
- gas,
773
- maxFeePerGas: fee.maxFeePerGas,
774
- maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
775
- type: 'eip1559',
776
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
+ }
777
904
  // Serialize transaction
778
905
  const serializedTx = serializeTransaction(unsignedTx);
779
906
  // Sign transaction via Turnkey API
@@ -843,6 +970,87 @@ export function useWaitForTxnReceipt(provider) {
843
970
  }, [provider]);
844
971
  return { waitForTxnReceipt };
845
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
+ }
846
1054
  /**
847
1055
  * Hook to read from contract
848
1056
  * Reads contract data using publicClient.readContract
@@ -912,7 +1120,7 @@ export function useReadContract(provider) {
912
1120
  *
913
1121
  * @example
914
1122
  * ```tsx
915
- * import { useExternalWalletClient } from 'signer-test-sdk-react';
1123
+ * import { useExternalWalletClient } from '@abstraxn/signer-react';
916
1124
  *
917
1125
  * function MyComponent() {
918
1126
  * const { walletClient, isConnected } = useExternalWalletClient();
@@ -960,7 +1168,7 @@ export function useExternalWalletClient() {
960
1168
  *
961
1169
  * @example
962
1170
  * ```tsx
963
- * import { useWriteContract } from 'signer-test-sdk-react';
1171
+ * import { useWriteContract } from '@abstraxn/signer-react';
964
1172
  * import erc20Abi from './erc20Abi.json';
965
1173
  *
966
1174
  * function MyComponent() {
@@ -1002,7 +1210,7 @@ export function useWriteContract() {
1002
1210
  const { walletClient: hookClient, isPending, isError, error, isConnected } = useExternalWalletClient();
1003
1211
  const config = useConfig();
1004
1212
  const currentChainId = useWagmiChainId();
1005
- 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, }) => {
1006
1214
  return (async () => {
1007
1215
  let activeClient = hookClient;
1008
1216
  // If hook client is missing or in error, try to recover
@@ -1090,7 +1298,7 @@ export function useWriteContract() {
1090
1298
  'Make sure the function exists in the ABI and is a state-changing function.');
1091
1299
  }
1092
1300
  // Prepare the call options (viem contract write methods accept options as the last parameter)
1093
- const hasOptions = value !== undefined || account !== undefined || gas !== undefined ||
1301
+ const hasOptions = value !== undefined || account !== undefined || gas !== undefined || gasLimit !== undefined ||
1094
1302
  gasPrice !== undefined || maxFeePerGas !== undefined ||
1095
1303
  maxPriorityFeePerGas !== undefined || nonce !== undefined;
1096
1304
  // Viem contract write methods expect args as an array, not spread
@@ -1101,8 +1309,13 @@ export function useWriteContract() {
1101
1309
  callOptions.value = value;
1102
1310
  if (account !== undefined)
1103
1311
  callOptions.account = account;
1104
- if (gas !== undefined)
1312
+ // `gas` in viem is the gas *limit*. Allow either `gas` or `gasLimit` from the hook caller.
1313
+ if (gas !== undefined) {
1105
1314
  callOptions.gas = gas;
1315
+ }
1316
+ else if (gasLimit !== undefined) {
1317
+ callOptions.gas = gasLimit;
1318
+ }
1106
1319
  if (gasPrice !== undefined)
1107
1320
  callOptions.gasPrice = gasPrice;
1108
1321
  if (maxFeePerGas !== undefined)
@@ -1131,7 +1344,7 @@ export function useWriteContract() {
1131
1344
  *
1132
1345
  * @example
1133
1346
  * ```tsx
1134
- * import { useConnectionType } from 'signer-test-sdk-react';
1347
+ * import { useConnectionType } from '@abstraxn/signer-react';
1135
1348
  *
1136
1349
  * function MyComponent() {
1137
1350
  * const { connectionType, connectorMeta } = useConnectionType();