@chipi-stack/chipi-react 13.13.0 → 14.1.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/dist/index.js CHANGED
@@ -7,6 +7,7 @@ var chipiPasskey = require('@chipi-stack/chipi-passkey');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var shared = require('@chipi-stack/shared');
9
9
  var types = require('@chipi-stack/types');
10
+ var core = require('@chipi-stack/core');
10
11
 
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
13
 
@@ -37,11 +38,7 @@ var createDefaultQueryClient = () => new reactQuery.QueryClient({
37
38
  }
38
39
  }
39
40
  });
40
- function ChipiProvider({
41
- children,
42
- config,
43
- passkeyAdapter
44
- }) {
41
+ function ChipiProvider({ children, config, passkeyAdapter }) {
45
42
  const queryClient = React__default.default.useMemo(() => createDefaultQueryClient(), []);
46
43
  const chipiSDK = React__default.default.useMemo(() => new backend.ChipiSDK(config), [config]);
47
44
  const resolvedPasskeyAdapter = passkeyAdapter ?? defaultPasskeyAdapter;
@@ -85,6 +82,9 @@ function useCreateWallet() {
85
82
  throw new Error("Failed to create passkey for wallet");
86
83
  }
87
84
  }
85
+ if (!encryptKey) {
86
+ throw new Error("encryptKey is required when usePasskey is false");
87
+ }
88
88
  return chipiSDK.createWallet({
89
89
  params: {
90
90
  ...input.params,
@@ -111,8 +111,7 @@ function useGetWallet(input) {
111
111
  const query = reactQuery.useQuery({
112
112
  queryKey: ["wallet", input?.params?.externalUserId],
113
113
  queryFn: async () => {
114
- if (!input || !input.params || !input.getBearerToken)
115
- throw new Error("Input is required");
114
+ if (!input || !input.params || !input.getBearerToken) throw new Error("Input is required");
116
115
  const bearerToken = await input.getBearerToken();
117
116
  if (!bearerToken) throw new Error("Bearer token is required");
118
117
  return chipiSDK.getWallet(input.params, bearerToken);
@@ -154,22 +153,16 @@ function useGetWallet(input) {
154
153
  function useChipiWallet(config) {
155
154
  const { chipiSDK } = useChipiContext();
156
155
  const queryClient = reactQuery.useQueryClient();
157
- const {
158
- externalUserId,
159
- getBearerToken,
160
- defaultToken = "USDC",
161
- enabled = true
162
- } = config;
163
- const isEnabled = Boolean(
164
- enabled && externalUserId && externalUserId.trim() !== ""
165
- );
156
+ const { externalUserId, getBearerToken, defaultToken = "USDC", enabled = true } = config;
157
+ const trimmedUserId = externalUserId?.trim() || null;
158
+ const isEnabled = Boolean(enabled && trimmedUserId);
166
159
  const walletQuery = reactQuery.useQuery({
167
- queryKey: ["chipi-wallet", externalUserId],
160
+ queryKey: ["chipi-wallet", trimmedUserId],
168
161
  queryFn: async () => {
169
- if (!externalUserId) return null;
162
+ if (!trimmedUserId) return null;
170
163
  const bearerToken = await getBearerToken();
171
164
  if (!bearerToken) throw new Error("Bearer token is required");
172
- return chipiSDK.getWallet({ externalUserId }, bearerToken);
165
+ return chipiSDK.getWallet({ externalUserId: trimmedUserId }, bearerToken);
173
166
  },
174
167
  enabled: isEnabled,
175
168
  retry: (failureCount, error) => {
@@ -195,7 +188,7 @@ function useChipiWallet(config) {
195
188
  bearerToken
196
189
  );
197
190
  },
198
- enabled: Boolean(walletPublicKey),
191
+ enabled: Boolean(isEnabled && walletPublicKey),
199
192
  retry: (failureCount, error) => {
200
193
  if (error instanceof shared.ChipiApiError || error?.status) {
201
194
  return false;
@@ -205,14 +198,14 @@ function useChipiWallet(config) {
205
198
  });
206
199
  const createWalletMutation = reactQuery.useMutation({
207
200
  mutationFn: async ({ encryptKey, walletType = "CHIPI" }) => {
208
- if (!externalUserId) throw new Error("External user ID is required");
201
+ if (!trimmedUserId) throw new Error("External user ID is required");
209
202
  const bearerToken = await getBearerToken();
210
203
  if (!bearerToken) throw new Error("Bearer token is required");
211
204
  return chipiSDK.createWallet({
212
205
  params: {
213
206
  chain: types.Chain.STARKNET,
214
207
  encryptKey,
215
- externalUserId,
208
+ externalUserId: trimmedUserId,
216
209
  walletType
217
210
  },
218
211
  bearerToken
@@ -220,7 +213,7 @@ function useChipiWallet(config) {
220
213
  },
221
214
  onSuccess: () => {
222
215
  queryClient.invalidateQueries({
223
- queryKey: ["chipi-wallet", externalUserId]
216
+ queryKey: ["chipi-wallet", trimmedUserId]
224
217
  });
225
218
  }
226
219
  });
@@ -295,14 +288,10 @@ function useChipiSession(config) {
295
288
  defaultMaxCalls = 1e3,
296
289
  autoCheckStatus = true
297
290
  } = config;
298
- const [localSession, setLocalSession] = React.useState(
299
- storedSession ?? null
300
- );
291
+ const [localSession, setLocalSession] = React.useState(storedSession ?? null);
301
292
  const [error, setError] = React.useState(null);
302
293
  React.useEffect(() => {
303
- if (storedSession) {
304
- setLocalSession(storedSession);
305
- }
294
+ setLocalSession(storedSession ?? null);
306
295
  }, [storedSession]);
307
296
  const supportsSession = React.useMemo(() => {
308
297
  if (!wallet) return false;
@@ -338,27 +327,32 @@ function useChipiSession(config) {
338
327
  const hasActiveSession = sessionState === "active";
339
328
  const isSessionExpired = sessionState === "expired";
340
329
  const remainingCalls = sessionStatusQuery.data?.remainingCalls;
341
- const createSessionMutation = reactQuery.useMutation({
342
- mutationFn: async (params = {}) => {
343
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
344
- if (!supportsSession) throw new Error("Wallet does not support sessions. Only CHIPI wallets support session keys.");
345
- const sessionData = chipiSDK.sessions.createSessionKey({
346
- encryptKey,
347
- durationSeconds: params.durationSeconds ?? defaultDurationSeconds
348
- });
349
- return sessionData;
350
- },
351
- onSuccess: async (sessionData) => {
352
- setLocalSession(sessionData);
353
- setError(null);
354
- if (onSessionCreated) {
355
- await onSessionCreated(sessionData);
330
+ const createSessionMutation = reactQuery.useMutation(
331
+ {
332
+ mutationFn: async (params = {}) => {
333
+ if (!encryptKey) throw new Error("Encryption key (PIN) is required");
334
+ if (!supportsSession)
335
+ throw new Error(
336
+ "Wallet does not support sessions. Only CHIPI wallets support session keys."
337
+ );
338
+ const sessionData = chipiSDK.sessions.createSessionKey({
339
+ encryptKey,
340
+ durationSeconds: params.durationSeconds ?? defaultDurationSeconds
341
+ });
342
+ return sessionData;
343
+ },
344
+ onSuccess: async (sessionData) => {
345
+ setLocalSession(sessionData);
346
+ setError(null);
347
+ if (onSessionCreated) {
348
+ await onSessionCreated(sessionData);
349
+ }
350
+ },
351
+ onError: (err) => {
352
+ setError(err);
356
353
  }
357
- },
358
- onError: (err) => {
359
- setError(err);
360
354
  }
361
- });
355
+ );
362
356
  const registerSessionMutation = reactQuery.useMutation({
363
357
  mutationFn: async (sessionConfig = {}) => {
364
358
  if (!wallet) throw new Error("Wallet is required");
@@ -519,22 +513,19 @@ function useMigrateWalletToPasskey() {
519
513
  mutationFn: async (input) => {
520
514
  const { wallet, oldEncryptKey, externalUserId, bearerToken } = input;
521
515
  try {
522
- const passkeyResult = await chipiPasskey.createWalletPasskey(
523
- externalUserId,
524
- externalUserId
525
- // Using externalUserId as userName
526
- );
527
516
  let decryptedPrivateKey;
528
517
  try {
529
- decryptedPrivateKey = backend.decryptPrivateKey(
530
- wallet.encryptedPrivateKey,
531
- oldEncryptKey
532
- );
533
- } catch (error) {
518
+ decryptedPrivateKey = backend.decryptPrivateKey(wallet.encryptedPrivateKey, oldEncryptKey);
519
+ } catch {
534
520
  throw new Error(
535
521
  "Failed to decrypt wallet with provided encryptKey. Please verify your PIN/password is correct."
536
522
  );
537
523
  }
524
+ const passkeyResult = await chipiPasskey.createWalletPasskey(
525
+ externalUserId,
526
+ externalUserId
527
+ // Using externalUserId as userName
528
+ );
538
529
  const newEncryptedPrivateKey = backend.encryptPrivateKey(
539
530
  decryptedPrivateKey,
540
531
  passkeyResult.encryptKey
@@ -594,44 +585,49 @@ function useUpdateWalletEncryption() {
594
585
  }
595
586
  function useTransfer() {
596
587
  const { chipiSDK, passkeyAdapter } = useChipiContext();
597
- const mutation = reactQuery.useMutation(
598
- {
599
- mutationFn: async (input) => {
600
- let encryptKey = input.params.encryptKey;
601
- if (input.params.usePasskey) {
602
- if (!input.params.externalUserId) {
603
- throw new Error(
604
- "externalUserId is required when usePasskey is true"
605
- );
588
+ const mutation = reactQuery.useMutation({
589
+ mutationFn: async (input) => {
590
+ let encryptKey = input.params.encryptKey;
591
+ if (input.params.usePasskey) {
592
+ if (!input.params.externalUserId) {
593
+ throw new Error("externalUserId is required when usePasskey is true");
594
+ }
595
+ try {
596
+ const key = await passkeyAdapter.getWalletEncryptKey({
597
+ externalUserId: input.params.externalUserId
598
+ });
599
+ if (!key) {
600
+ throw new Error("Passkey authentication was cancelled");
601
+ }
602
+ encryptKey = key;
603
+ } catch (error) {
604
+ if (error instanceof DOMException && error.name === "NotAllowedError") {
605
+ throw new Error("Passkey authentication was cancelled by the user");
606
606
  }
607
- try {
608
- const key = await passkeyAdapter.getWalletEncryptKey({
609
- externalUserId: input.params.externalUserId
610
- });
611
- if (!key) {
612
- throw new Error("Passkey authentication was cancelled");
613
- }
614
- encryptKey = key;
615
- } catch (error) {
616
- if (error instanceof Error) {
617
- throw new Error(
618
- `Passkey authentication failed: ${error.message}`
619
- );
620
- }
621
- throw new Error("Failed to authenticate with passkey");
607
+ if (error instanceof Error) {
608
+ throw error;
622
609
  }
610
+ throw new Error("Failed to authenticate with passkey");
623
611
  }
624
- return chipiSDK.transfer({
625
- params: {
626
- ...input.params,
627
- amount: String(input.params.amount),
628
- encryptKey
629
- },
630
- bearerToken: input.bearerToken
631
- });
632
612
  }
613
+ if (!encryptKey) {
614
+ throw new Error("encryptKey is required when usePasskey is false");
615
+ }
616
+ const {
617
+ usePasskey: _usePasskey,
618
+ externalUserId: _externalUserId,
619
+ ...transferParams
620
+ } = input.params;
621
+ return chipiSDK.transfer({
622
+ params: {
623
+ ...transferParams,
624
+ amount: String(transferParams.amount),
625
+ encryptKey
626
+ },
627
+ bearerToken: input.bearerToken
628
+ });
633
629
  }
634
- );
630
+ });
635
631
  return {
636
632
  transfer: mutation.mutate,
637
633
  transferAsync: mutation.mutateAsync,
@@ -667,10 +663,20 @@ function useApprove() {
667
663
  throw new Error("Failed to authenticate with passkey");
668
664
  }
669
665
  }
666
+ if (!encryptKey) {
667
+ throw new Error(
668
+ "encryptKey is required when usePasskey is false. Provide a PIN or enable usePasskey."
669
+ );
670
+ }
671
+ const {
672
+ usePasskey: _usePasskey,
673
+ externalUserId: _externalUserId,
674
+ ...approveParams
675
+ } = params.params;
670
676
  return chipiSDK.approve({
671
677
  params: {
672
- ...params.params,
673
- amount: String(params.params.amount),
678
+ ...approveParams,
679
+ amount: String(approveParams.amount),
674
680
  encryptKey
675
681
  },
676
682
  bearerToken: params.bearerToken
@@ -720,9 +726,8 @@ function useGetTransactionList(input) {
720
726
  });
721
727
  const fetchTransactionList = async (newInput) => {
722
728
  return queryClient.fetchQuery({
723
- queryKey: buildQueryKey(newInput?.query),
729
+ queryKey: buildQueryKey(newInput.query),
724
730
  queryFn: async () => {
725
- if (!newInput?.getBearerToken) throw new Error("getBearerToken is required");
726
731
  const bearerToken = await newInput.getBearerToken();
727
732
  if (!bearerToken) throw new Error("Bearer token is required");
728
733
  return chipiSDK.getTransactionList(newInput.query, bearerToken);
@@ -740,6 +745,104 @@ function useGetTransactionList(input) {
740
745
  fetchTransactionList
741
746
  };
742
747
  }
748
+ function useGetTransaction(input) {
749
+ const { chipiSDK } = useChipiContext();
750
+ const queryClient = reactQuery.useQueryClient();
751
+ const buildQueryKey = (hashOrId) => ["transaction", hashOrId ?? null];
752
+ const query = reactQuery.useQuery({
753
+ queryKey: buildQueryKey(input?.hashOrId),
754
+ queryFn: async () => {
755
+ if (!input?.hashOrId) throw new Error("hashOrId is required");
756
+ if (!input?.getBearerToken) throw new Error("getBearerToken is required");
757
+ const bearerToken = await input.getBearerToken();
758
+ if (!bearerToken) throw new Error("Bearer token is required");
759
+ return chipiSDK.getTransaction(input.hashOrId, bearerToken);
760
+ },
761
+ enabled: Boolean(input?.hashOrId && input?.getBearerToken),
762
+ retry: (failureCount, error) => {
763
+ if (error instanceof shared.ChipiApiError || error?.status) {
764
+ return false;
765
+ }
766
+ return failureCount < 3;
767
+ },
768
+ ...input?.queryOptions
769
+ });
770
+ const fetchTransaction = async (newInput) => {
771
+ return queryClient.fetchQuery({
772
+ queryKey: buildQueryKey(newInput.hashOrId),
773
+ queryFn: async () => {
774
+ const bearerToken = await newInput.getBearerToken();
775
+ if (!bearerToken) throw new Error("Bearer token is required");
776
+ return chipiSDK.getTransaction(newInput.hashOrId, bearerToken);
777
+ },
778
+ retry: (failureCount, error) => {
779
+ if (error instanceof shared.ChipiApiError || error?.status) {
780
+ return false;
781
+ }
782
+ return failureCount < 3;
783
+ }
784
+ });
785
+ };
786
+ return {
787
+ ...query,
788
+ fetchTransaction
789
+ };
790
+ }
791
+ var TERMINAL_STATUSES = [
792
+ types.OnChainTxStatus.ACCEPTED_ON_L1,
793
+ types.OnChainTxStatus.REJECTED,
794
+ types.OnChainTxStatus.REVERTED
795
+ ];
796
+ function useGetTransactionStatus(input) {
797
+ const { chipiSDK } = useChipiContext();
798
+ const queryClient = reactQuery.useQueryClient();
799
+ const buildQueryKey = (hash) => ["transaction-status", hash ?? null];
800
+ const query = reactQuery.useQuery({
801
+ queryKey: buildQueryKey(input?.hash),
802
+ queryFn: async () => {
803
+ if (!input?.hash) throw new Error("hash is required");
804
+ if (!input?.getBearerToken) throw new Error("getBearerToken is required");
805
+ const bearerToken = await input.getBearerToken();
806
+ if (!bearerToken) throw new Error("Bearer token is required");
807
+ return chipiSDK.getTransactionStatus(input.hash, bearerToken);
808
+ },
809
+ enabled: Boolean(input?.hash && input?.getBearerToken),
810
+ refetchInterval: (query2) => {
811
+ const status = query2.state.data?.status;
812
+ if (status && TERMINAL_STATUSES.includes(status)) {
813
+ return false;
814
+ }
815
+ return input?.refetchInterval ?? 3e3;
816
+ },
817
+ retry: (failureCount, error) => {
818
+ if (error instanceof shared.ChipiApiError || error?.status) {
819
+ return false;
820
+ }
821
+ return failureCount < 3;
822
+ },
823
+ ...input?.queryOptions
824
+ });
825
+ const fetchTransactionStatus = async (newInput) => {
826
+ return queryClient.fetchQuery({
827
+ queryKey: buildQueryKey(newInput.hash),
828
+ queryFn: async () => {
829
+ const bearerToken = await newInput.getBearerToken();
830
+ if (!bearerToken) throw new Error("Bearer token is required");
831
+ return chipiSDK.getTransactionStatus(newInput.hash, bearerToken);
832
+ },
833
+ retry: (failureCount, error) => {
834
+ if (error instanceof shared.ChipiApiError || error?.status) {
835
+ return false;
836
+ }
837
+ return failureCount < 3;
838
+ }
839
+ });
840
+ };
841
+ return {
842
+ ...query,
843
+ fetchTransactionStatus
844
+ };
845
+ }
743
846
  function usePurchaseSku() {
744
847
  const { chipiSDK } = useChipiContext();
745
848
  const mutation = reactQuery.useMutation({
@@ -779,8 +882,7 @@ function useGetSkuPurchase(input) {
779
882
  queryKey: ["sku-purchase", newInput?.id],
780
883
  queryFn: async () => {
781
884
  if (!newInput?.id) throw new Error("id is required");
782
- if (!newInput?.getBearerToken)
783
- throw new Error("getBearerToken is required");
885
+ if (!newInput?.getBearerToken) throw new Error("getBearerToken is required");
784
886
  const bearerToken = await newInput.getBearerToken();
785
887
  if (!bearerToken) throw new Error("Bearer token is required");
786
888
  return chipiSDK.getSkuPurchase(newInput.id, bearerToken);
@@ -854,9 +956,7 @@ function useGetSku(input) {
854
956
  if (!bearerToken) throw new Error("Bearer token is required");
855
957
  return chipiSDK.getSku(input.id, bearerToken);
856
958
  },
857
- enabled: Boolean(
858
- input?.id && input?.getBearerToken && input.id.trim() !== ""
859
- ),
959
+ enabled: Boolean(input?.id && input?.getBearerToken && input.id.trim() !== ""),
860
960
  retry: (failureCount, error) => {
861
961
  if (error instanceof shared.ChipiApiError || error?.status) {
862
962
  return false;
@@ -869,14 +969,13 @@ function useGetSku(input) {
869
969
  return queryClient.fetchQuery({
870
970
  queryKey: ["sku", input2?.id],
871
971
  queryFn: async () => {
872
- if (!input2?.getBearerToken)
873
- throw new Error("getBearerToken is required");
972
+ if (!input2?.getBearerToken) throw new Error("getBearerToken is required");
874
973
  const bearerToken = await input2.getBearerToken();
875
974
  if (!bearerToken) throw new Error("Bearer token is required");
876
975
  return chipiSDK.getSku(input2.id, bearerToken);
877
976
  },
878
977
  retry: (failureCount, error) => {
879
- if (error instanceof shared.ChipiApiError || error?.code) {
978
+ if (error instanceof shared.ChipiApiError || error?.status) {
880
979
  return false;
881
980
  }
882
981
  return failureCount < 3;
@@ -895,9 +994,7 @@ function useCallAnyContract() {
895
994
  let encryptKey = params.params.encryptKey;
896
995
  if (params.params.usePasskey) {
897
996
  if (!params.params.externalUserId) {
898
- throw new Error(
899
- "externalUserId is required when usePasskey is true"
900
- );
997
+ throw new Error("externalUserId is required when usePasskey is true");
901
998
  }
902
999
  try {
903
1000
  const key = await passkeyAdapter.getWalletEncryptKey({
@@ -908,17 +1005,26 @@ function useCallAnyContract() {
908
1005
  }
909
1006
  encryptKey = key;
910
1007
  } catch (error) {
1008
+ if (error instanceof DOMException && error.name === "NotAllowedError") {
1009
+ throw new Error("Passkey authentication was cancelled by the user");
1010
+ }
911
1011
  if (error instanceof Error) {
912
- throw new Error(
913
- `Passkey authentication failed: ${error.message}`
914
- );
1012
+ throw error;
915
1013
  }
916
1014
  throw new Error("Failed to authenticate with passkey");
917
1015
  }
918
1016
  }
1017
+ if (!encryptKey) {
1018
+ throw new Error("encryptKey is required when usePasskey is false");
1019
+ }
1020
+ const {
1021
+ usePasskey: _usePasskey,
1022
+ externalUserId: _externalUserId,
1023
+ ...contractParams
1024
+ } = params.params;
919
1025
  return chipiSDK.callAnyContract({
920
1026
  params: {
921
- ...params.params,
1027
+ ...contractParams,
922
1028
  encryptKey
923
1029
  },
924
1030
  bearerToken: params.bearerToken
@@ -986,7 +1092,8 @@ function useGetTokenBalance(input) {
986
1092
  return queryClient.fetchQuery({
987
1093
  queryKey: buildQueryKey(newInput?.params),
988
1094
  queryFn: async () => {
989
- if (!newInput || !newInput.getBearerToken || !newInput.params) throw new Error("Input is required");
1095
+ if (!newInput || !newInput.getBearerToken || !newInput.params)
1096
+ throw new Error("Input is required");
990
1097
  const bearerToken = await newInput.getBearerToken();
991
1098
  if (!bearerToken) throw new Error("Bearer token is required");
992
1099
  return chipiSDK.getTokenBalance(newInput.params, bearerToken);
@@ -1023,10 +1130,7 @@ function useCreateSessionKey() {
1023
1130
  function useAddSessionKeyToContract() {
1024
1131
  const { chipiSDK } = useChipiContext();
1025
1132
  const mutation = reactQuery.useMutation({
1026
- mutationFn: (input) => chipiSDK.sessions.addSessionKeyToContract(
1027
- input.params,
1028
- input.bearerToken
1029
- )
1133
+ mutationFn: (input) => chipiSDK.sessions.addSessionKeyToContract(input.params, input.bearerToken)
1030
1134
  });
1031
1135
  return {
1032
1136
  addSessionKeyToContract: mutation.mutate,
@@ -1058,11 +1162,7 @@ function useRevokeSessionKey() {
1058
1162
  function useGetSessionData(params, options) {
1059
1163
  const { chipiSDK } = useChipiContext();
1060
1164
  const query = reactQuery.useQuery({
1061
- queryKey: [
1062
- "sessionData",
1063
- params?.walletAddress,
1064
- params?.sessionPublicKey
1065
- ],
1165
+ queryKey: ["sessionData", params?.walletAddress, params?.sessionPublicKey],
1066
1166
  queryFn: () => {
1067
1167
  if (!params) {
1068
1168
  throw new Error("Session data params are required");
@@ -1099,6 +1199,240 @@ function useExecuteWithSession() {
1099
1199
  reset: mutation.reset
1100
1200
  };
1101
1201
  }
1202
+ function useX402Payment(config) {
1203
+ const { chipiSDK } = useChipiContext();
1204
+ const {
1205
+ wallet,
1206
+ encryptKey,
1207
+ getBearerToken: getBearerTokenFn,
1208
+ bearerToken: staticBearerToken,
1209
+ maxPaymentAmount: maxPaymentAmountProp,
1210
+ maxAmount,
1211
+ allowedRecipients,
1212
+ session,
1213
+ onPaymentComplete
1214
+ } = config;
1215
+ const maxPaymentAmount = maxPaymentAmountProp ?? maxAmount;
1216
+ const getBearerToken = React.useMemo(
1217
+ () => getBearerTokenFn ?? (staticBearerToken ? () => Promise.resolve(staticBearerToken) : () => Promise.resolve(void 0)),
1218
+ [getBearerTokenFn, staticBearerToken]
1219
+ );
1220
+ const [lastTxHash, setLastTxHash] = React.useState(null);
1221
+ const [lastAmount, setLastAmount] = React.useState(null);
1222
+ const [lastPayment, setLastPayment] = React.useState(null);
1223
+ const [error, setError] = React.useState(null);
1224
+ const paymentCountRef = React.useRef(0);
1225
+ const [paymentCount, setPaymentCount] = React.useState(0);
1226
+ const totalSpentRef = React.useRef(0n);
1227
+ const [totalSpent, setTotalSpent] = React.useState("0.000000");
1228
+ const parsePaymentRequired = React.useCallback((response) => {
1229
+ const headerValue = response.headers.get("payment-required");
1230
+ if (!headerValue) {
1231
+ throw new Error(
1232
+ "Server returned 402 but no PAYMENT-REQUIRED header. The server may not support x402 or is misconfigured."
1233
+ );
1234
+ }
1235
+ try {
1236
+ return JSON.parse(headerValue);
1237
+ } catch {
1238
+ throw new Error("Malformed PAYMENT-REQUIRED header: invalid JSON");
1239
+ }
1240
+ }, []);
1241
+ const validateRequirement = React.useCallback(
1242
+ (requirement) => {
1243
+ if (requirement.scheme !== "exact") {
1244
+ throw new Error(
1245
+ `Unsupported payment scheme: "${requirement.scheme}". Only "exact" is supported.`
1246
+ );
1247
+ }
1248
+ if (requirement.network !== "starknet-mainnet") {
1249
+ throw new Error(
1250
+ `Unsupported network: "${requirement.network}". Only "starknet-mainnet" is supported.`
1251
+ );
1252
+ }
1253
+ const USDC_MAINNET = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
1254
+ const normalizeAddr = (a) => a.toLowerCase().replace(/^0x0+/, "0x");
1255
+ if (normalizeAddr(requirement.asset) !== normalizeAddr(USDC_MAINNET)) {
1256
+ throw new Error(`Unsupported asset: ${requirement.asset}. Only USDC is supported.`);
1257
+ }
1258
+ if (!requirement.payTo) {
1259
+ throw new Error("Invalid payment requirement: missing payTo address");
1260
+ }
1261
+ const timeoutSeconds = Number(requirement.maxTimeoutSeconds);
1262
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
1263
+ throw new Error("Invalid payment requirement: maxTimeoutSeconds must be a positive number");
1264
+ }
1265
+ let requiredAmount;
1266
+ try {
1267
+ requiredAmount = BigInt(requirement.maxAmountRequired);
1268
+ } catch {
1269
+ throw new Error("Invalid payment requirement: maxAmountRequired must be an integer string");
1270
+ }
1271
+ if (requiredAmount <= 0n) {
1272
+ throw new Error("Invalid payment requirement: maxAmountRequired must be > 0");
1273
+ }
1274
+ if (maxPaymentAmount) {
1275
+ const trimmed = maxPaymentAmount.trim();
1276
+ if (!/^\d+(\.\d{1,6})?$/.test(trimmed)) {
1277
+ throw new Error(`Invalid maxPaymentAmount format: "${maxPaymentAmount}"`);
1278
+ }
1279
+ const [whole, frac = ""] = trimmed.split(".");
1280
+ const fracPadded = (frac + "000000").slice(0, 6);
1281
+ const maxBaseUnits = BigInt((whole || "0") + fracPadded);
1282
+ if (requiredAmount > maxBaseUnits) {
1283
+ const requiredHuman = (Number(requiredAmount) / 1e6).toFixed(6);
1284
+ throw new Error(
1285
+ `Payment amount ${requiredHuman} USDC exceeds maximum allowed ${maxPaymentAmount} USDC`
1286
+ );
1287
+ }
1288
+ }
1289
+ if (allowedRecipients && allowedRecipients.length > 0) {
1290
+ const normalize = (a) => a.toLowerCase().replace(/^0x0+/, "0x");
1291
+ const normalized = normalize(requirement.payTo);
1292
+ const isAllowed = allowedRecipients.some((addr) => normalize(addr) === normalized);
1293
+ if (!isAllowed) {
1294
+ throw new Error(`Recipient ${requirement.payTo} is not in the allowed recipients list`);
1295
+ }
1296
+ }
1297
+ },
1298
+ [maxPaymentAmount, allowedRecipients]
1299
+ );
1300
+ const signPayment = React.useCallback(
1301
+ async (requirement) => {
1302
+ if (!wallet) {
1303
+ throw new Error("Wallet is required for x402 payments");
1304
+ }
1305
+ if (!encryptKey) {
1306
+ throw new Error("Encryption key (PIN) is required for x402 payments");
1307
+ }
1308
+ validateRequirement(requirement);
1309
+ const nonce = crypto.randomUUID();
1310
+ const validUntil = Math.floor(Date.now() / 1e3) + requirement.maxTimeoutSeconds;
1311
+ if (session) {
1312
+ const bearerToken = await getBearerToken();
1313
+ if (!bearerToken) throw new Error("Bearer token is required");
1314
+ const txHash = await chipiSDK.executeTransactionWithSession({
1315
+ params: {
1316
+ encryptKey,
1317
+ wallet,
1318
+ session,
1319
+ calls: [
1320
+ {
1321
+ contractAddress: requirement.asset,
1322
+ entrypoint: "transfer",
1323
+ calldata: [requirement.payTo, requirement.maxAmountRequired, "0x0"]
1324
+ }
1325
+ ]
1326
+ },
1327
+ bearerToken
1328
+ });
1329
+ return {
1330
+ x402Version: 1,
1331
+ scheme: "exact",
1332
+ network: "starknet-mainnet",
1333
+ payload: {
1334
+ signature: { r: txHash, s: "session" },
1335
+ fromAddress: wallet.publicKey,
1336
+ toAddress: requirement.payTo,
1337
+ amount: requirement.maxAmountRequired,
1338
+ asset: requirement.asset,
1339
+ validUntil,
1340
+ nonce
1341
+ }
1342
+ };
1343
+ }
1344
+ const typedData = core.buildX402TypedData({
1345
+ from_address: wallet.publicKey,
1346
+ to_address: requirement.payTo,
1347
+ amount: requirement.maxAmountRequired,
1348
+ asset: requirement.asset,
1349
+ valid_until: String(validUntil),
1350
+ nonce
1351
+ });
1352
+ const signature = await chipiSDK.signTypedData({
1353
+ params: {
1354
+ encryptKey,
1355
+ wallet,
1356
+ typedData
1357
+ }
1358
+ });
1359
+ return {
1360
+ x402Version: 1,
1361
+ scheme: "exact",
1362
+ network: "starknet-mainnet",
1363
+ payload: {
1364
+ signature,
1365
+ fromAddress: wallet.publicKey,
1366
+ toAddress: requirement.payTo,
1367
+ amount: requirement.maxAmountRequired,
1368
+ asset: requirement.asset,
1369
+ validUntil,
1370
+ nonce
1371
+ }
1372
+ };
1373
+ },
1374
+ [wallet, encryptKey, session, getBearerToken, chipiSDK, validateRequirement]
1375
+ );
1376
+ const paymentMutation = reactQuery.useMutation(
1377
+ {
1378
+ mutationFn: async ({ url, init }) => {
1379
+ const response = await globalThis.fetch(url, init);
1380
+ if (response.status !== 402) {
1381
+ return response;
1382
+ }
1383
+ const requirement = parsePaymentRequired(response);
1384
+ const payment = await signPayment(requirement);
1385
+ const humanAmount = (Number(BigInt(requirement.maxAmountRequired)) / 1e6).toFixed(6);
1386
+ const isSessionPayment = payment.payload.signature.s === "session";
1387
+ const txHash = isSessionPayment ? payment.payload.signature.r : null;
1388
+ setLastAmount(humanAmount);
1389
+ setLastTxHash(txHash);
1390
+ setLastPayment({ txHash, amount: humanAmount, timestamp: Date.now() });
1391
+ totalSpentRef.current += BigInt(requirement.maxAmountRequired);
1392
+ setTotalSpent((Number(totalSpentRef.current) / 1e6).toFixed(6));
1393
+ paymentCountRef.current += 1;
1394
+ setPaymentCount(paymentCountRef.current);
1395
+ const headers = new Headers(init?.headers);
1396
+ headers.set("X-PAYMENT", JSON.stringify(payment));
1397
+ const paidResponse = await globalThis.fetch(url, { ...init, headers });
1398
+ if (paidResponse.status === 402) {
1399
+ throw new Error(
1400
+ "Server returned 402 after payment was submitted. Payment may have failed verification."
1401
+ );
1402
+ }
1403
+ if (onPaymentComplete) {
1404
+ void Promise.resolve(onPaymentComplete(txHash ?? "", humanAmount)).catch(() => {
1405
+ });
1406
+ }
1407
+ return paidResponse;
1408
+ },
1409
+ onError: (err) => {
1410
+ setError(err);
1411
+ },
1412
+ onSuccess: () => {
1413
+ setError(null);
1414
+ }
1415
+ }
1416
+ );
1417
+ const x402Fetch = React.useCallback(
1418
+ async (url, init) => {
1419
+ return paymentMutation.mutateAsync({ url, init });
1420
+ },
1421
+ [paymentMutation]
1422
+ );
1423
+ return {
1424
+ x402Fetch,
1425
+ payFetch: x402Fetch,
1426
+ signPayment,
1427
+ isPaying: paymentMutation.isPending,
1428
+ lastTxHash,
1429
+ lastAmount,
1430
+ lastPayment,
1431
+ totalSpent,
1432
+ error: error || paymentMutation.error || null,
1433
+ paymentCount
1434
+ };
1435
+ }
1102
1436
 
1103
1437
  Object.defineProperty(exports, "Chain", {
1104
1438
  enumerable: true,
@@ -1123,7 +1457,9 @@ exports.useGetSku = useGetSku;
1123
1457
  exports.useGetSkuList = useGetSkuList;
1124
1458
  exports.useGetSkuPurchase = useGetSkuPurchase;
1125
1459
  exports.useGetTokenBalance = useGetTokenBalance;
1460
+ exports.useGetTransaction = useGetTransaction;
1126
1461
  exports.useGetTransactionList = useGetTransactionList;
1462
+ exports.useGetTransactionStatus = useGetTransactionStatus;
1127
1463
  exports.useGetWallet = useGetWallet;
1128
1464
  exports.useMigrateWalletToPasskey = useMigrateWalletToPasskey;
1129
1465
  exports.usePurchaseSku = usePurchaseSku;
@@ -1131,5 +1467,6 @@ exports.useRecordSendTransaction = useRecordSendTransaction;
1131
1467
  exports.useRevokeSessionKey = useRevokeSessionKey;
1132
1468
  exports.useTransfer = useTransfer;
1133
1469
  exports.useUpdateWalletEncryption = useUpdateWalletEncryption;
1470
+ exports.useX402Payment = useX402Payment;
1134
1471
  //# sourceMappingURL=index.js.map
1135
1472
  //# sourceMappingURL=index.js.map