@t402/react 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,24 +11,24 @@ npm install @t402/react
11
11
  ## Quick Start
12
12
 
13
13
  ```tsx
14
- import { PaymentProvider, PaymentButton, usePaymentRequired } from "@t402/react";
14
+ import { PaymentProvider, PaymentButton, usePaymentRequired } from '@t402/react'
15
15
 
16
16
  function App() {
17
17
  return (
18
18
  <PaymentProvider>
19
19
  <ProtectedContent />
20
20
  </PaymentProvider>
21
- );
21
+ )
22
22
  }
23
23
 
24
24
  function ProtectedContent() {
25
- const { paymentRequired, requirements, pay, status } = usePaymentRequired("/api/data");
25
+ const { paymentRequired, requirements, pay, status } = usePaymentRequired('/api/data')
26
26
 
27
27
  if (paymentRequired) {
28
- return <PaymentButton requirements={requirements} onPay={pay} status={status} />;
28
+ return <PaymentButton requirements={requirements} onPay={pay} status={status} />
29
29
  }
30
30
 
31
- return <div>Premium content loaded!</div>;
31
+ return <div>Premium content loaded!</div>
32
32
  }
33
33
  ```
34
34
 
@@ -39,13 +39,8 @@ function ProtectedContent() {
39
39
  Renders a payment action button with loading and status states.
40
40
 
41
41
  ```tsx
42
- import { PaymentButton } from "@t402/react";
43
-
44
- <PaymentButton
45
- requirements={requirements}
46
- onPay={handlePay}
47
- status={status}
48
- />
42
+ import { PaymentButton } from '@t402/react'
43
+ ;<PaymentButton requirements={requirements} onPay={handlePay} status={status} />
49
44
  ```
50
45
 
51
46
  ### PaymentDetails
@@ -53,9 +48,8 @@ import { PaymentButton } from "@t402/react";
53
48
  Displays payment requirements (amount, network, recipient).
54
49
 
55
50
  ```tsx
56
- import { PaymentDetails } from "@t402/react";
57
-
58
- <PaymentDetails requirements={requirements} />
51
+ import { PaymentDetails } from '@t402/react'
52
+ ;<PaymentDetails requirements={requirements} />
59
53
  ```
60
54
 
61
55
  ### PaymentStatusDisplay
@@ -63,9 +57,8 @@ import { PaymentDetails } from "@t402/react";
63
57
  Shows the current payment status with appropriate UI feedback.
64
58
 
65
59
  ```tsx
66
- import { PaymentStatusDisplay } from "@t402/react";
67
-
68
- <PaymentStatusDisplay status={status} />
60
+ import { PaymentStatusDisplay } from '@t402/react'
61
+ ;<PaymentStatusDisplay status={status} />
69
62
  ```
70
63
 
71
64
  ### AddressDisplay
@@ -73,9 +66,8 @@ import { PaymentStatusDisplay } from "@t402/react";
73
66
  Renders a blockchain address with truncation and copy functionality.
74
67
 
75
68
  ```tsx
76
- import { AddressDisplay } from "@t402/react";
77
-
78
- <AddressDisplay address="0x1234...5678" />
69
+ import { AddressDisplay } from '@t402/react'
70
+ ;<AddressDisplay address="0x1234...5678" />
79
71
  ```
80
72
 
81
73
  ## Hooks
@@ -86,11 +78,11 @@ Detects 402 responses and extracts payment requirements.
86
78
 
87
79
  ```tsx
88
80
  const {
89
- paymentRequired, // boolean - whether payment is needed
90
- requirements, // PaymentRequirements[] from 402 response
91
- pay, // () => Promise<void> - trigger payment
92
- status, // PaymentStatus - current state
93
- } = usePaymentRequired(url, options);
81
+ paymentRequired, // boolean - whether payment is needed
82
+ requirements, // PaymentRequirements[] from 402 response
83
+ pay, // () => Promise<void> - trigger payment
84
+ status, // PaymentStatus - current state
85
+ } = usePaymentRequired(url, options)
94
86
  ```
95
87
 
96
88
  ### usePaymentStatus
@@ -98,7 +90,7 @@ const {
98
90
  Tracks the lifecycle of a payment (idle, pending, confirming, complete, error).
99
91
 
100
92
  ```tsx
101
- const { status, error, reset } = usePaymentStatus();
93
+ const { status, error, reset } = usePaymentStatus()
102
94
  ```
103
95
 
104
96
  ### useAsyncPayment
@@ -106,7 +98,7 @@ const { status, error, reset } = usePaymentStatus();
106
98
  Manages async payment flows with automatic retry and status tracking.
107
99
 
108
100
  ```tsx
109
- const { execute, status, error } = useAsyncPayment(paymentFn);
101
+ const { execute, status, error } = useAsyncPayment(paymentFn)
110
102
  ```
111
103
 
112
104
  ## Provider
@@ -116,9 +108,8 @@ const { execute, status, error } = useAsyncPayment(paymentFn);
116
108
  Wraps your app to provide payment context to all child components.
117
109
 
118
110
  ```tsx
119
- import { PaymentProvider } from "@t402/react";
120
-
121
- <PaymentProvider>
111
+ import { PaymentProvider } from '@t402/react'
112
+ ;<PaymentProvider>
122
113
  <App />
123
114
  </PaymentProvider>
124
115
  ```
@@ -136,15 +127,15 @@ import {
136
127
  truncateAddress,
137
128
  formatTokenAmount,
138
129
  choosePaymentRequirement,
139
- } from "@t402/react";
130
+ } from '@t402/react'
140
131
 
141
132
  // Network detection
142
- isEvmNetwork("eip155:8453"); // true
133
+ isEvmNetwork('eip155:8453') // true
143
134
 
144
135
  // Display helpers
145
- getNetworkDisplayName("eip155:8453"); // "Base"
146
- truncateAddress("0x1234567890abcdef"); // "0x1234...cdef"
147
- formatTokenAmount(1500000n, 6); // "1.50"
136
+ getNetworkDisplayName('eip155:8453') // "Base"
137
+ truncateAddress('0x1234567890abcdef') // "0x1234...cdef"
138
+ formatTokenAmount(1500000n, 6) // "1.50"
148
139
  ```
149
140
 
150
141
  ## Peer Dependencies
@@ -628,6 +628,262 @@ function useAsyncPayment(options) {
628
628
  reset
629
629
  };
630
630
  }
631
+ function useGaslessPayment(options) {
632
+ const { payFn, onSuccess, onError, autoWait = true } = options;
633
+ const [status, setStatus] = react.useState("idle");
634
+ const [userOpHash, setUserOpHash] = react.useState(null);
635
+ const [txHash, setTxHash] = react.useState(null);
636
+ const [sponsored, setSponsored] = react.useState(null);
637
+ const [error, setError] = react.useState(null);
638
+ const isMountedRef = react.useRef(true);
639
+ const pay = react.useCallback(
640
+ async (params) => {
641
+ setStatus("loading");
642
+ setError(null);
643
+ setUserOpHash(null);
644
+ setTxHash(null);
645
+ setSponsored(null);
646
+ try {
647
+ const result = await payFn(params);
648
+ if (isMountedRef.current) {
649
+ setUserOpHash(result.userOpHash);
650
+ setSponsored(result.sponsored);
651
+ }
652
+ if (autoWait) {
653
+ const receipt = await result.wait();
654
+ if (isMountedRef.current) {
655
+ setTxHash(receipt.txHash);
656
+ setStatus("success");
657
+ onSuccess?.(receipt);
658
+ }
659
+ } else {
660
+ if (isMountedRef.current) {
661
+ setStatus("success");
662
+ onSuccess?.({ txHash: result.userOpHash, success: true });
663
+ }
664
+ }
665
+ } catch (err) {
666
+ const errorMessage = err instanceof Error ? err.message : "Gasless payment failed";
667
+ if (isMountedRef.current) {
668
+ setError(errorMessage);
669
+ setStatus("error");
670
+ }
671
+ onError?.(err instanceof Error ? err : new Error(errorMessage));
672
+ }
673
+ },
674
+ [payFn, onSuccess, onError, autoWait]
675
+ );
676
+ const reset = react.useCallback(() => {
677
+ setStatus("idle");
678
+ setUserOpHash(null);
679
+ setTxHash(null);
680
+ setSponsored(null);
681
+ setError(null);
682
+ }, []);
683
+ return {
684
+ pay,
685
+ status,
686
+ userOpHash,
687
+ txHash,
688
+ sponsored,
689
+ error,
690
+ isLoading: status === "loading",
691
+ isSuccess: status === "success",
692
+ isError: status === "error",
693
+ reset
694
+ };
695
+ }
696
+ function useBridgePayment(options) {
697
+ const { bridgeFn, autoBridgeFn, onSuccess, onError, autoWaitForDelivery = false } = options;
698
+ const [status, setStatus] = react.useState("idle");
699
+ const [txHash, setTxHash] = react.useState(null);
700
+ const [messageGuid, setMessageGuid] = react.useState(null);
701
+ const [dstTxHash, setDstTxHash] = react.useState(null);
702
+ const [error, setError] = react.useState(null);
703
+ const isMountedRef = react.useRef(true);
704
+ const executeBridge = react.useCallback(
705
+ async (bridgeCall) => {
706
+ setStatus("bridging");
707
+ setError(null);
708
+ setTxHash(null);
709
+ setMessageGuid(null);
710
+ setDstTxHash(null);
711
+ try {
712
+ const result = await bridgeCall();
713
+ if (isMountedRef.current) {
714
+ setTxHash(result.txHash);
715
+ setMessageGuid(result.messageGuid);
716
+ }
717
+ if (autoWaitForDelivery) {
718
+ if (isMountedRef.current) setStatus("waiting");
719
+ const delivery = await result.waitForDelivery();
720
+ if (isMountedRef.current) {
721
+ if (delivery.dstTxHash) setDstTxHash(delivery.dstTxHash);
722
+ setStatus("success");
723
+ onSuccess?.({
724
+ txHash: result.txHash,
725
+ dstTxHash: delivery.dstTxHash,
726
+ fromChain: result.fromChain,
727
+ toChain: result.toChain
728
+ });
729
+ }
730
+ } else {
731
+ if (isMountedRef.current) {
732
+ setStatus("success");
733
+ onSuccess?.({
734
+ txHash: result.txHash,
735
+ fromChain: result.fromChain,
736
+ toChain: result.toChain
737
+ });
738
+ }
739
+ }
740
+ } catch (err) {
741
+ const errorMessage = err instanceof Error ? err.message : "Bridge operation failed";
742
+ if (isMountedRef.current) {
743
+ setError(errorMessage);
744
+ setStatus("error");
745
+ }
746
+ onError?.(err instanceof Error ? err : new Error(errorMessage));
747
+ }
748
+ },
749
+ [onSuccess, onError, autoWaitForDelivery]
750
+ );
751
+ const bridge = react.useCallback(
752
+ async (params) => {
753
+ await executeBridge(() => bridgeFn(params));
754
+ },
755
+ [bridgeFn, executeBridge]
756
+ );
757
+ const autoBridge = react.useCallback(
758
+ async (params) => {
759
+ const fn = autoBridgeFn ?? (() => {
760
+ throw new Error("autoBridgeFn not provided");
761
+ });
762
+ await executeBridge(() => fn(params));
763
+ },
764
+ [autoBridgeFn, executeBridge]
765
+ );
766
+ const reset = react.useCallback(() => {
767
+ setStatus("idle");
768
+ setTxHash(null);
769
+ setMessageGuid(null);
770
+ setDstTxHash(null);
771
+ setError(null);
772
+ }, []);
773
+ return {
774
+ bridge,
775
+ autoBridge,
776
+ status,
777
+ txHash,
778
+ messageGuid,
779
+ dstTxHash,
780
+ error,
781
+ isLoading: status === "bridging" || status === "quoting" || status === "waiting",
782
+ isSuccess: status === "success",
783
+ isError: status === "error",
784
+ reset
785
+ };
786
+ }
787
+ function useMultiSigPayment(options) {
788
+ const { initiateFn, submitFn, onSuccess, onError, autoWait = true } = options;
789
+ const [status, setStatus] = react.useState("idle");
790
+ const [requestId, setRequestId] = react.useState(null);
791
+ const [userOpHash, setUserOpHash] = react.useState(null);
792
+ const [txHash, setTxHash] = react.useState(null);
793
+ const [threshold, setThreshold] = react.useState(0);
794
+ const [collectedCount, setCollectedCount] = react.useState(0);
795
+ const [isReady, setIsReady] = react.useState(false);
796
+ const [error, setError] = react.useState(null);
797
+ const isMountedRef = react.useRef(true);
798
+ const initiate = react.useCallback(
799
+ async (params) => {
800
+ setStatus("initiating");
801
+ setError(null);
802
+ setRequestId(null);
803
+ setUserOpHash(null);
804
+ setTxHash(null);
805
+ try {
806
+ const result = await initiateFn(params);
807
+ if (isMountedRef.current) {
808
+ setRequestId(result.requestId);
809
+ setUserOpHash(result.userOpHash);
810
+ setThreshold(result.threshold);
811
+ setCollectedCount(result.collectedCount);
812
+ setIsReady(result.isReady);
813
+ setStatus("collecting");
814
+ }
815
+ } catch (err) {
816
+ const errorMessage = err instanceof Error ? err.message : "Failed to initiate multi-sig payment";
817
+ if (isMountedRef.current) {
818
+ setError(errorMessage);
819
+ setStatus("error");
820
+ }
821
+ onError?.(err instanceof Error ? err : new Error(errorMessage));
822
+ }
823
+ },
824
+ [initiateFn, onError]
825
+ );
826
+ const submit = react.useCallback(async () => {
827
+ if (!requestId) {
828
+ const err = new Error("No active request to submit");
829
+ setError(err.message);
830
+ setStatus("error");
831
+ onError?.(err);
832
+ return;
833
+ }
834
+ setStatus("submitting");
835
+ setError(null);
836
+ try {
837
+ const result = await submitFn(requestId);
838
+ if (autoWait) {
839
+ const receipt = await result.wait();
840
+ if (isMountedRef.current) {
841
+ setTxHash(receipt.txHash);
842
+ setStatus("success");
843
+ onSuccess?.(receipt);
844
+ }
845
+ } else {
846
+ if (isMountedRef.current) {
847
+ setStatus("success");
848
+ onSuccess?.({ txHash: result.userOpHash, success: true });
849
+ }
850
+ }
851
+ } catch (err) {
852
+ const errorMessage = err instanceof Error ? err.message : "Failed to submit multi-sig transaction";
853
+ if (isMountedRef.current) {
854
+ setError(errorMessage);
855
+ setStatus("error");
856
+ }
857
+ onError?.(err instanceof Error ? err : new Error(errorMessage));
858
+ }
859
+ }, [requestId, submitFn, onSuccess, onError, autoWait]);
860
+ const reset = react.useCallback(() => {
861
+ setStatus("idle");
862
+ setRequestId(null);
863
+ setUserOpHash(null);
864
+ setTxHash(null);
865
+ setThreshold(0);
866
+ setCollectedCount(0);
867
+ setIsReady(false);
868
+ setError(null);
869
+ }, []);
870
+ return {
871
+ initiate,
872
+ submit,
873
+ status,
874
+ requestId,
875
+ userOpHash,
876
+ txHash,
877
+ threshold,
878
+ collectedCount,
879
+ isReady,
880
+ error,
881
+ isLoading: status === "initiating" || status === "submitting",
882
+ isSuccess: status === "success",
883
+ isError: status === "error",
884
+ reset
885
+ };
886
+ }
631
887
  var initialState = {
632
888
  status: "idle",
633
889
  error: null,
@@ -738,6 +994,9 @@ exports.isTronNetwork = isTronNetwork;
738
994
  exports.normalizePaymentRequirements = normalizePaymentRequirements;
739
995
  exports.truncateAddress = truncateAddress;
740
996
  exports.useAsyncPayment = useAsyncPayment;
997
+ exports.useBridgePayment = useBridgePayment;
998
+ exports.useGaslessPayment = useGaslessPayment;
999
+ exports.useMultiSigPayment = useMultiSigPayment;
741
1000
  exports.usePaymentContext = usePaymentContext;
742
1001
  exports.usePaymentRequired = usePaymentRequired;
743
1002
  exports.usePaymentStatus = usePaymentStatus;