signer-test-sdk-react 0.0.13 → 0.0.15

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 (34) hide show
  1. package/dist/src/AbstraxnProvider.js +78 -15
  2. package/dist/src/AbstraxnProvider.js.map +1 -1
  3. package/dist/src/ExternalWalletButtons.js +37 -10
  4. package/dist/src/ExternalWalletButtons.js.map +1 -1
  5. package/dist/src/OnboardingUI.d.ts +1 -1
  6. package/dist/src/OnboardingUI.js +1 -1
  7. package/dist/src/components/OnboardingUI/OnboardingUI.css +227 -75
  8. package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +3 -3
  9. package/dist/src/components/OnboardingUI/OnboardingUIReact.js +109 -55
  10. package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -1
  11. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +1 -2
  12. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +953 -861
  13. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
  14. package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +3 -3
  15. package/dist/src/components/OnboardingUI/components/EmailForm.js +13 -5
  16. package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -1
  17. package/dist/src/components/OnboardingUI/components/Modal.d.ts +0 -1
  18. package/dist/src/components/OnboardingUI/components/Modal.js +4 -11
  19. package/dist/src/components/OnboardingUI/components/Modal.js.map +1 -1
  20. package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +4 -3
  21. package/dist/src/components/OnboardingUI/components/OtpForm.js +19 -22
  22. package/dist/src/components/OnboardingUI/components/OtpForm.js.map +1 -1
  23. package/dist/src/hooks.d.ts +8937 -0
  24. package/dist/src/hooks.js +776 -1
  25. package/dist/src/hooks.js.map +1 -1
  26. package/dist/src/index.d.ts +1 -1
  27. package/dist/src/index.js +1 -1
  28. package/dist/src/index.js.map +1 -1
  29. package/dist/src/types.d.ts +11 -16
  30. package/dist/src/wagmiConfig.d.ts +2 -1
  31. package/dist/src/wagmiConfig.js +5 -1
  32. package/dist/src/wagmiConfig.js.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +4 -2
package/dist/src/hooks.js CHANGED
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * React Hooks for Abstraxn Wallet SDK
3
3
  */
4
- import { useState, useCallback, useEffect } from 'react';
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';
7
+ import { useConnectorClient, useAccount } from 'wagmi';
6
8
  /**
7
9
  * Hook to check if wallet is connected
8
10
  */
@@ -391,4 +393,777 @@ export function useExternalWalletInfo() {
391
393
  walletClient,
392
394
  };
393
395
  }
396
+ /**
397
+ * Hook to create a publicClient using viem
398
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
399
+ *
400
+ * @param chain - Viem chain object (can use from 'viem/chains' or create custom)
401
+ * @param rpcUrl - RPC URL for the chain
402
+ * @returns Object with publicClient instance from viem
403
+ *
404
+ * @example
405
+ * ```tsx
406
+ * import { usePublicClient } from 'signer-test-sdk-react';
407
+ * import { polygonAmoy } from 'viem/chains';
408
+ *
409
+ * function MyComponent() {
410
+ * const { publicClient } = usePublicClient(
411
+ * polygonAmoy,
412
+ * 'https://rpc-amoy.polygon.technology'
413
+ * );
414
+ *
415
+ * // Read operations
416
+ * const balance = await publicClient.getBalance({ address: '0x...' });
417
+ * const tokenBalance = await publicClient.readContract({
418
+ * address: '0x...',
419
+ * abi: erc20Abi,
420
+ * functionName: 'balanceOf',
421
+ * args: ['0x...'],
422
+ * });
423
+ * }
424
+ * ```
425
+ */
426
+ export function usePublicClient(chain, rpcUrl) {
427
+ const publicClient = useMemo(() => {
428
+ if (!chain || !rpcUrl) {
429
+ throw new Error('Chain and RPC URL are required');
430
+ }
431
+ return createPublicClient({
432
+ chain,
433
+ transport: http(rpcUrl),
434
+ });
435
+ }, [chain, rpcUrl]);
436
+ return { publicClient };
437
+ }
438
+ /**
439
+ * Hook to create a walletClient using viem
440
+ * Used ONLY to broadcast signed transactions. No private key involved.
441
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
442
+ *
443
+ * @param chain - Viem chain object (can use from 'viem/chains' or create custom)
444
+ * @param rpcUrl - RPC URL for the chain (optional, will use chain's default RPC if not provided)
445
+ * @returns Object with walletClient instance from viem
446
+ *
447
+ * @example
448
+ * ```tsx
449
+ * import { useWalletClient } from 'signer-test-sdk-react';
450
+ * import { polygonAmoy } from 'viem/chains';
451
+ *
452
+ * function MyComponent() {
453
+ * const { walletClient } = useWalletClient(
454
+ * polygonAmoy,
455
+ * 'https://rpc-amoy.polygon.technology'
456
+ * );
457
+ *
458
+ * // Broadcast signed transaction
459
+ * const hash = await walletClient.sendRawTransaction({
460
+ * serializedTransaction: signedTx,
461
+ * });
462
+ * }
463
+ * ```
464
+ */
465
+ export function useWalletClient(chain, rpcUrl) {
466
+ const walletClient = useMemo(() => {
467
+ if (!chain) {
468
+ throw new Error('Chain is required');
469
+ }
470
+ // Extract RPC URL from chain if not provided
471
+ let finalRpcUrl = rpcUrl;
472
+ if (!finalRpcUrl) {
473
+ if (chain.rpcUrls?.default?.http) {
474
+ finalRpcUrl = Array.isArray(chain.rpcUrls.default.http)
475
+ ? chain.rpcUrls.default.http[0]
476
+ : chain.rpcUrls.default.http;
477
+ }
478
+ else if (chain.rpcUrls?.public?.http) {
479
+ finalRpcUrl = Array.isArray(chain.rpcUrls.public.http)
480
+ ? chain.rpcUrls.public.http[0]
481
+ : chain.rpcUrls.public.http;
482
+ }
483
+ }
484
+ if (!finalRpcUrl) {
485
+ throw new Error('RPC URL is required. Either provide it as a parameter or ensure the chain has an RPC URL configured.');
486
+ }
487
+ return createWalletClient({
488
+ chain: chain,
489
+ transport: http(finalRpcUrl),
490
+ });
491
+ }, [chain, rpcUrl]);
492
+ return { walletClient };
493
+ }
494
+ /**
495
+ * Hook to create a contract instance using viem
496
+ * Works for all wallet types: external wallet, Google, email OTP, Discord, X, passkey
497
+ *
498
+ * @param address - Contract address
499
+ * @param abi - Contract ABI
500
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
501
+ * @returns Object with contract instance from viem
502
+ *
503
+ * @example
504
+ * ```tsx
505
+ * import { useContract, usePublicClient } from 'signer-test-sdk-react';
506
+ * import { polygonAmoy } from 'viem/chains';
507
+ * import erc20Abi from './erc20Abi.json';
508
+ *
509
+ * function MyComponent() {
510
+ * const { publicClient } = usePublicClient(
511
+ * polygonAmoy,
512
+ * 'https://rpc-amoy.polygon.technology'
513
+ * );
514
+ *
515
+ * const { contract } = useContract({
516
+ * address: '0x...',
517
+ * abi: erc20Abi,
518
+ * provider: publicClient,
519
+ * });
520
+ *
521
+ * // Read from contract
522
+ * const balance = await contract.read.balanceOf(['0x...']);
523
+ *
524
+ * // Write to contract (requires walletClient)
525
+ * const hash = await contract.write.transfer(['0x...', '1000000000000000000']);
526
+ * }
527
+ * ```
528
+ */
529
+ export function useContract({ address, abi, provider, }) {
530
+ const contract = useMemo(() => {
531
+ if (!address) {
532
+ throw new Error('Contract address is required');
533
+ }
534
+ if (!abi) {
535
+ throw new Error('Contract ABI is required');
536
+ }
537
+ if (!provider) {
538
+ throw new Error('Provider (publicClient) is required');
539
+ }
540
+ return getContract({
541
+ address,
542
+ abi,
543
+ client: provider,
544
+ });
545
+ }, [address, abi, provider]);
546
+ return { contract };
547
+ }
548
+ /**
549
+ * Hook to prepare raw transaction data with encoding support
550
+ * Returns { to, value, data } for both native transfers and contract calls
551
+ * Can encode function data internally when ABI, functionName, and args are provided
552
+ *
553
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
554
+ * @returns Object with prepareRawTxn function
555
+ *
556
+ * @example
557
+ * ```tsx
558
+ * // Native transfer
559
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
560
+ * const nativeTx = await prepareRawTxn({
561
+ * from: address!,
562
+ * to: '0x...',
563
+ * value: '0.001', // ETH amount
564
+ * });
565
+ * // Returns: { to: '0x...', value: '0x...', data: '0x' }
566
+ *
567
+ * // Contract call with encoding (recommended)
568
+ * const contractTx = await prepareRawTxn({
569
+ * from: address!,
570
+ * to: '0x...', // Contract address
571
+ * abi: erc20Abi,
572
+ * functionName: 'transfer',
573
+ * args: ['0x...', '0.001'], // Automatically converts ETH to wei
574
+ * value: '0', // Optional ETH value
575
+ * });
576
+ * // Returns: { to: '0x...', value: '0x', data: '0xa9059cbb...' }
577
+ *
578
+ * // Contract call with pre-encoded data
579
+ * const contractTx2 = await prepareRawTxn({
580
+ * from: address!,
581
+ * to: '0x...',
582
+ * data: '0xa9059cbb...', // Already encoded
583
+ * });
584
+ * // Returns: { to: '0x...', value: '0x', data: '0xa9059cbb...' }
585
+ * ```
586
+ */
587
+ export function usePrepareRawTxn(provider) {
588
+ const prepareRawTxn = useCallback(async ({ from, to, value, data, abi, functionName, args, }) => {
589
+ if (!provider) {
590
+ throw new Error('Provider (publicClient) is required');
591
+ }
592
+ if (!from) {
593
+ throw new Error('From address is required');
594
+ }
595
+ if (!to) {
596
+ throw new Error('To address is required');
597
+ }
598
+ let finalValue;
599
+ let finalData;
600
+ // Check if we need to encode function data
601
+ const needsEncoding = abi && functionName;
602
+ const hasPreEncodedData = data && data !== '0x';
603
+ if (needsEncoding && hasPreEncodedData) {
604
+ throw new Error('Cannot provide both encoded data and encoding parameters (abi/functionName). Use one or the other.');
605
+ }
606
+ // Determine if this is a native transfer
607
+ const isNativeTransfer = !needsEncoding && !hasPreEncodedData;
608
+ if (isNativeTransfer) {
609
+ // Native transfer: convert value to wei and set data to '0x'
610
+ if (!value || value === '0' || value === 0) {
611
+ throw new Error('Value is required for native transfer');
612
+ }
613
+ // Convert value to wei
614
+ const valueStr = typeof value === 'string' ? value.trim() : String(value);
615
+ let valueInWei;
616
+ try {
617
+ valueInWei = parseEther(valueStr);
618
+ }
619
+ catch (error) {
620
+ throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
621
+ }
622
+ // Convert to hex string
623
+ finalValue = `0x${valueInWei.toString(16)}`;
624
+ finalData = '0x';
625
+ }
626
+ else {
627
+ // Contract call: encode data if needed, or use provided data
628
+ if (needsEncoding) {
629
+ // Encode function data
630
+ if (!abi) {
631
+ throw new Error('ABI is required for encoding function data');
632
+ }
633
+ if (!functionName) {
634
+ throw new Error('Function name is required for encoding function data');
635
+ }
636
+ // Process args to convert ETH amounts to wei for transfer functions
637
+ const processedArgs = (args || []).map((arg, index) => {
638
+ // Check if this is likely an amount parameter for transfer/transferFrom functions
639
+ const isAmountParam = (functionName === 'transfer' || functionName === 'transferFrom') && index === 1;
640
+ if (isAmountParam) {
641
+ // If arg is a string or number that looks like ETH (has decimal point)
642
+ if (typeof arg === 'string' && arg.includes('.')) {
643
+ try {
644
+ // Convert ETH to wei
645
+ const weiAmount = parseEther(arg);
646
+ return weiAmount.toString();
647
+ }
648
+ catch (error) {
649
+ throw new Error(`Failed to convert amount "${arg}" to wei. Make sure it's a valid number string (e.g., "0.001").`);
650
+ }
651
+ }
652
+ else if (typeof arg === 'number' && arg % 1 !== 0) {
653
+ // Number with decimal places - convert to wei
654
+ try {
655
+ const weiAmount = parseEther(arg.toString());
656
+ return weiAmount.toString();
657
+ }
658
+ catch (error) {
659
+ throw new Error(`Failed to convert amount ${arg} to wei. Make sure it's a valid number.`);
660
+ }
661
+ }
662
+ }
663
+ // For other args, just return as-is
664
+ return arg;
665
+ });
666
+ try {
667
+ finalData = encodeFunctionData({
668
+ abi: abi,
669
+ functionName: functionName,
670
+ args: processedArgs,
671
+ });
672
+ }
673
+ catch (error) {
674
+ if (error instanceof Error && error.message.includes('BigInt')) {
675
+ 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).`);
676
+ }
677
+ throw error;
678
+ }
679
+ }
680
+ else if (hasPreEncodedData) {
681
+ // Use provided encoded data
682
+ finalData = data;
683
+ }
684
+ else {
685
+ throw new Error('Either provide encoded data, or provide abi/functionName/args for encoding.');
686
+ }
687
+ // Handle value for contract calls
688
+ if (value && value !== '0' && value !== 0) {
689
+ // Convert value to wei if provided
690
+ const valueStr = typeof value === 'string' ? value.trim() : String(value);
691
+ let valueInWei;
692
+ try {
693
+ valueInWei = parseEther(valueStr);
694
+ }
695
+ catch (error) {
696
+ throw new Error(`Invalid value format: "${valueStr}". Value must be a valid number string (e.g., "0.001").`);
697
+ }
698
+ finalValue = `0x${valueInWei.toString(16)}`;
699
+ }
700
+ else {
701
+ finalValue = '0x0';
702
+ }
703
+ }
704
+ return {
705
+ to,
706
+ value: finalValue,
707
+ data: finalData,
708
+ };
709
+ }, [provider]);
710
+ return { prepareRawTxn };
711
+ }
712
+ /**
713
+ * Hook to sign transaction using Turnkey API
714
+ * Accepts raw transaction data from usePrepareRawTxn, serializes it, and signs via API
715
+ *
716
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
717
+ * @returns Object with signTxn function
718
+ *
719
+ * @example
720
+ * ```tsx
721
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
722
+ * const { signTxn } = useSignTxn(publicClient);
723
+ *
724
+ * // Prepare transaction
725
+ * const rawTx = await prepareRawTxn({
726
+ * from: address!,
727
+ * to: '0x...',
728
+ * value: '0.001',
729
+ * });
730
+ *
731
+ * // Sign transaction
732
+ * const signedTx = await signTxn({
733
+ * from: address!,
734
+ * ...rawTx, // Spread to, value, data from prepareRawTxn
735
+ * });
736
+ * ```
737
+ */
738
+ export function useSignTxn(provider) {
739
+ const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
740
+ const signTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
741
+ if (!isConnected || !wallet) {
742
+ throw new Error('Wallet is not connected');
743
+ }
744
+ if (!provider) {
745
+ throw new Error('Provider (publicClient) is required');
746
+ }
747
+ if (!from) {
748
+ throw new Error('From address is required');
749
+ }
750
+ if (!to) {
751
+ throw new Error('To address is required');
752
+ }
753
+ // Get chain ID from provider or use provided one
754
+ const targetChainId = chainId || provider.chain?.id;
755
+ if (!targetChainId) {
756
+ throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
757
+ }
758
+ // Get nonce
759
+ const nonce = await provider.getTransactionCount({ address: from });
760
+ // Convert hex value to bigint
761
+ // Handle empty hex string "0x" by converting to "0x0"
762
+ const normalizedValue = value === '0x' ? '0x0' : value;
763
+ const valueInWei = BigInt(normalizedValue);
764
+ // Estimate gas
765
+ const gas = await provider.estimateGas({
766
+ account: from,
767
+ to,
768
+ data,
769
+ value: valueInWei,
770
+ });
771
+ // Estimate fees
772
+ const fee = await provider.estimateFeesPerGas();
773
+ // Create unsigned transaction
774
+ const unsignedTx = {
775
+ chainId: targetChainId,
776
+ from,
777
+ to,
778
+ data,
779
+ value: valueInWei,
780
+ nonce,
781
+ gas,
782
+ maxFeePerGas: fee.maxFeePerGas,
783
+ maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
784
+ type: 'eip1559',
785
+ };
786
+ // Serialize transaction
787
+ const serializedTx = serializeTransaction(unsignedTx);
788
+ // Sign transaction via Turnkey API
789
+ const signResult = await signTransactionViaAPI(serializedTx, from);
790
+ return {
791
+ unsignedTransaction: serializedTx,
792
+ signedTransaction: signResult.signedTransaction,
793
+ };
794
+ }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
795
+ return { signTxn, isConnected, address };
796
+ }
797
+ /**
798
+ * Hook to sign and send transaction using Turnkey API
799
+ * Same as useSignTxn but also sends the transaction on blockchain using publicClient
800
+ *
801
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
802
+ * @returns Object with signAndSendTxn function
803
+ *
804
+ * @example
805
+ * ```tsx
806
+ * const { prepareRawTxn } = usePrepareRawTxn(publicClient);
807
+ * const { signAndSendTxn } = useSignAndSendTxn(publicClient);
808
+ *
809
+ * // Prepare transaction
810
+ * const rawTx = await prepareRawTxn({
811
+ * from: address!,
812
+ * to: '0x...',
813
+ * value: '0.001',
814
+ * });
815
+ *
816
+ * // Sign and send transaction
817
+ * const result = await signAndSendTxn({
818
+ * from: address!,
819
+ * ...rawTx, // Spread to, value, data from prepareRawTxn
820
+ * });
821
+ *
822
+ * console.log('Transaction hash:', result.hash);
823
+ * ```
824
+ */
825
+ export function useSignAndSendTxn(provider) {
826
+ const { wallet, isConnected, address, signTransactionViaAPI } = useAbstraxnWallet();
827
+ const signAndSendTxn = useCallback(async ({ from, to, value, data, chainId, }) => {
828
+ if (!isConnected || !wallet) {
829
+ throw new Error('Wallet is not connected');
830
+ }
831
+ if (!provider) {
832
+ throw new Error('Provider (publicClient) is required');
833
+ }
834
+ if (!from) {
835
+ throw new Error('From address is required');
836
+ }
837
+ if (!to) {
838
+ throw new Error('To address is required');
839
+ }
840
+ // Get chain ID from provider or use provided one
841
+ const targetChainId = chainId || provider.chain?.id;
842
+ if (!targetChainId) {
843
+ throw new Error('Chain ID is required. Either provide it or ensure provider has a chain configured.');
844
+ }
845
+ // Get nonce
846
+ const nonce = await provider.getTransactionCount({ address: from });
847
+ // Convert hex value to bigint
848
+ // Handle empty hex string "0x" by converting to "0x0"
849
+ const normalizedValue = value === '0x' ? '0x0' : value;
850
+ const valueInWei = BigInt(normalizedValue);
851
+ // Estimate gas
852
+ const gas = await provider.estimateGas({
853
+ account: from,
854
+ to,
855
+ data,
856
+ value: valueInWei,
857
+ });
858
+ // Estimate fees
859
+ const fee = await provider.estimateFeesPerGas();
860
+ // Create unsigned transaction
861
+ const unsignedTx = {
862
+ chainId: targetChainId,
863
+ from,
864
+ to,
865
+ data,
866
+ value: valueInWei,
867
+ nonce,
868
+ gas,
869
+ maxFeePerGas: fee.maxFeePerGas,
870
+ maxPriorityFeePerGas: fee.maxPriorityFeePerGas,
871
+ type: 'eip1559',
872
+ };
873
+ // Serialize transaction
874
+ const serializedTx = serializeTransaction(unsignedTx);
875
+ // Sign transaction via Turnkey API
876
+ const signResult = await signTransactionViaAPI(serializedTx, from);
877
+ // Send the signed transaction
878
+ const hash = await provider.sendRawTransaction({
879
+ serializedTransaction: signResult.signedTransaction,
880
+ });
881
+ return {
882
+ hash,
883
+ unsignedTransaction: serializedTx,
884
+ signedTransaction: signResult.signedTransaction,
885
+ };
886
+ }, [provider, isConnected, wallet, address, signTransactionViaAPI]);
887
+ return { signAndSendTxn, isConnected, address };
888
+ }
889
+ /**
890
+ * Hook to wait for transaction receipt
891
+ * Waits for a transaction to be mined and returns the receipt
892
+ *
893
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
894
+ * @returns Object with waitForTxnReceipt function
895
+ *
896
+ * @example
897
+ * ```tsx
898
+ * const { publicClient } = usePublicClient(
899
+ * polygonAmoy,
900
+ * 'https://rpc-amoy.polygon.technology'
901
+ * );
902
+ * const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
903
+ *
904
+ * // After sending a transaction
905
+ * const hash = '0x...'; // Transaction hash
906
+ *
907
+ * // Wait for receipt
908
+ * const receipt = await waitForTxnReceipt({
909
+ * hash,
910
+ * confirmations: 1, // Optional: number of confirmations to wait for
911
+ * });
912
+ *
913
+ * console.log('Transaction confirmed:', receipt);
914
+ * ```
915
+ */
916
+ export function useWaitForTxnReceipt(provider) {
917
+ const waitForTxnReceipt = useCallback(async ({ hash, confirmations, timeout, }) => {
918
+ if (!provider) {
919
+ throw new Error('Provider (publicClient) is required');
920
+ }
921
+ if (!hash) {
922
+ throw new Error('Transaction hash is required');
923
+ }
924
+ try {
925
+ // waitForTransactionReceipt is a method on PublicClient
926
+ const receipt = await provider.waitForTransactionReceipt({
927
+ hash,
928
+ confirmations,
929
+ timeout,
930
+ });
931
+ return receipt;
932
+ }
933
+ catch (error) {
934
+ if (error instanceof Error) {
935
+ throw new Error(`Failed to wait for transaction receipt: ${error.message}`);
936
+ }
937
+ throw error;
938
+ }
939
+ }, [provider]);
940
+ return { waitForTxnReceipt };
941
+ }
942
+ /**
943
+ * Hook to read from contract
944
+ * Reads contract data using publicClient.readContract
945
+ *
946
+ * @param provider - PublicClient instance (can be created using usePublicClient hook)
947
+ * @returns Object with readContract function
948
+ *
949
+ * @example
950
+ * ```tsx
951
+ * const { publicClient } = usePublicClient(
952
+ * polygonAmoy,
953
+ * 'https://rpc-amoy.polygon.technology'
954
+ * );
955
+ * const { readContract } = useReadContract(publicClient);
956
+ *
957
+ * // Read contract data
958
+ * const balance = await readContract({
959
+ * address: '0x...',
960
+ * abi: erc20Abi,
961
+ * functionName: 'balanceOf',
962
+ * args: ['0x...'],
963
+ * });
964
+ *
965
+ * const name = await readContract({
966
+ * address: '0x...',
967
+ * abi: erc20Abi,
968
+ * functionName: 'name',
969
+ * });
970
+ * ```
971
+ */
972
+ export function useReadContract(provider) {
973
+ const readContract = useCallback(({ address, abi, functionName, args, }) => {
974
+ if (!provider) {
975
+ throw new Error('Provider (publicClient) is required');
976
+ }
977
+ if (!address) {
978
+ throw new Error('Contract address is required');
979
+ }
980
+ if (!abi) {
981
+ throw new Error('Contract ABI is required');
982
+ }
983
+ if (!functionName) {
984
+ throw new Error('Function name is required');
985
+ }
986
+ try {
987
+ return provider.readContract({
988
+ address,
989
+ abi: abi,
990
+ functionName: functionName,
991
+ args: args,
992
+ });
993
+ }
994
+ catch (error) {
995
+ if (error instanceof Error) {
996
+ throw new Error(`Failed to read contract: ${error.message}`);
997
+ }
998
+ throw error;
999
+ }
1000
+ }, [provider]);
1001
+ return { readContract };
1002
+ }
1003
+ /**
1004
+ * Hook to get walletClient for external wallets (MetaMask, WalletConnect, etc.)
1005
+ * Uses wagmi's useConnectorClient to get the walletClient
1006
+ *
1007
+ * @returns Object with walletClient instance (null if not connected)
1008
+ *
1009
+ * @example
1010
+ * ```tsx
1011
+ * import { useExternalWalletClient } from 'signer-test-sdk-react';
1012
+ *
1013
+ * function MyComponent() {
1014
+ * const { walletClient, isConnected } = useExternalWalletClient();
1015
+ *
1016
+ * if (!walletClient || !isConnected) {
1017
+ * return <div>Please connect your wallet</div>;
1018
+ * }
1019
+ *
1020
+ * // Use walletClient for transactions
1021
+ * }
1022
+ * ```
1023
+ */
1024
+ export function useExternalWalletClient() {
1025
+ const { isExternalWalletConnected } = useExternalWallet();
1026
+ const { data: connectorClient, isPending, isError } = useConnectorClient();
1027
+ const { isConnected: wagmiIsConnected } = useAccount();
1028
+ // Check both our internal state and wagmi's state
1029
+ // useConnectorClient might return undefined even when connected, so we check both
1030
+ const isActuallyConnected = isExternalWalletConnected && wagmiIsConnected;
1031
+ const walletClient = (isActuallyConnected && connectorClient) ? connectorClient : null;
1032
+ return {
1033
+ walletClient,
1034
+ isConnected: isActuallyConnected && !!connectorClient,
1035
+ isPending,
1036
+ isError,
1037
+ };
1038
+ }
1039
+ /**
1040
+ * Hook to write to contract (for external wallets like MetaMask, WalletConnect, etc.)
1041
+ * Uses walletClient from useExternalWalletClient to send transactions
1042
+ *
1043
+ * @returns Object with writeContract function
1044
+ *
1045
+ * @example
1046
+ * ```tsx
1047
+ * import { useWriteContract } from 'signer-test-sdk-react';
1048
+ * import erc20Abi from './erc20Abi.json';
1049
+ *
1050
+ * function MyComponent() {
1051
+ * const { writeContract } = useWriteContract();
1052
+ *
1053
+ * const handleTransfer = async () => {
1054
+ * try {
1055
+ * // Write to contract
1056
+ * const hash = await writeContract({
1057
+ * address: '0x...', // Contract address
1058
+ * abi: erc20Abi,
1059
+ * functionName: 'transfer',
1060
+ * args: ['0x...', '1000000000000000000'], // to, amount in wei
1061
+ * });
1062
+ *
1063
+ * console.log('Transaction hash:', hash);
1064
+ * } catch (error) {
1065
+ * console.error('Transaction failed:', error);
1066
+ * }
1067
+ * };
1068
+ *
1069
+ * // With ETH value
1070
+ * const handleDeposit = async () => {
1071
+ * const hash = await writeContract({
1072
+ * address: '0x...',
1073
+ * abi: contractAbi,
1074
+ * functionName: 'deposit',
1075
+ * value: parseEther('0.1'), // Send 0.1 ETH with the transaction
1076
+ * });
1077
+ * };
1078
+ *
1079
+ * return <button onClick={handleTransfer}>Transfer Tokens</button>;
1080
+ * }
1081
+ * ```
1082
+ */
1083
+ export function useWriteContract() {
1084
+ const { walletClient } = useExternalWalletClient();
1085
+ const writeContract = useCallback(({ address, abi, functionName, args, value, account, gas, gasPrice, maxFeePerGas, maxPriorityFeePerGas, nonce, }) => {
1086
+ if (!walletClient) {
1087
+ throw new Error('WalletClient is not available. Please connect an external wallet (MetaMask, WalletConnect, etc.).');
1088
+ }
1089
+ if (!address) {
1090
+ throw new Error('Contract address is required');
1091
+ }
1092
+ if (!abi) {
1093
+ throw new Error('Contract ABI is required');
1094
+ }
1095
+ if (!functionName) {
1096
+ throw new Error('Function name is required');
1097
+ }
1098
+ try {
1099
+ // Cast to any to avoid type issues with different viem versions
1100
+ const walletClientAny = walletClient;
1101
+ // Try using writeContract method directly if available
1102
+ if (walletClientAny && typeof walletClientAny.writeContract === 'function') {
1103
+ return walletClientAny.writeContract({
1104
+ address,
1105
+ abi: abi,
1106
+ functionName: functionName,
1107
+ args: args,
1108
+ value,
1109
+ account,
1110
+ gas,
1111
+ gasPrice,
1112
+ maxFeePerGas,
1113
+ maxPriorityFeePerGas,
1114
+ nonce,
1115
+ });
1116
+ }
1117
+ // If writeContract is not available, use getContract to create a contract instance
1118
+ // This works with wagmi's connectorClient
1119
+ const contract = getContract({
1120
+ address,
1121
+ abi: abi,
1122
+ client: walletClientAny,
1123
+ });
1124
+ // Get the write function from the contract
1125
+ const writeFunction = contract.write?.[functionName];
1126
+ if (!writeFunction || typeof writeFunction !== 'function') {
1127
+ throw new Error(`Function "${functionName}" not found in contract ABI or is not a write function. ` +
1128
+ 'Make sure the function exists in the ABI and is a state-changing function.');
1129
+ }
1130
+ // Prepare the call options (viem contract write methods accept options as the last parameter)
1131
+ const hasOptions = value !== undefined || account !== undefined || gas !== undefined ||
1132
+ gasPrice !== undefined || maxFeePerGas !== undefined ||
1133
+ maxPriorityFeePerGas !== undefined || nonce !== undefined;
1134
+ // Viem contract write methods expect args as an array, not spread
1135
+ const functionArgs = args || [];
1136
+ if (hasOptions) {
1137
+ const callOptions = {};
1138
+ if (value !== undefined)
1139
+ callOptions.value = value;
1140
+ if (account !== undefined)
1141
+ callOptions.account = account;
1142
+ if (gas !== undefined)
1143
+ callOptions.gas = gas;
1144
+ if (gasPrice !== undefined)
1145
+ callOptions.gasPrice = gasPrice;
1146
+ if (maxFeePerGas !== undefined)
1147
+ callOptions.maxFeePerGas = maxFeePerGas;
1148
+ if (maxPriorityFeePerGas !== undefined)
1149
+ callOptions.maxPriorityFeePerGas = maxPriorityFeePerGas;
1150
+ if (nonce !== undefined)
1151
+ callOptions.nonce = nonce;
1152
+ // Call the write function with args array and options object
1153
+ return writeFunction(functionArgs, callOptions);
1154
+ }
1155
+ else {
1156
+ // Call the write function with args array only
1157
+ return writeFunction(functionArgs);
1158
+ }
1159
+ }
1160
+ catch (error) {
1161
+ if (error instanceof Error) {
1162
+ throw new Error(`Failed to write contract: ${error.message}`);
1163
+ }
1164
+ throw error;
1165
+ }
1166
+ }, [walletClient]);
1167
+ return { writeContract };
1168
+ }
394
1169
  //# sourceMappingURL=hooks.js.map