@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/LICENSE +21 -0
- package/README.md +51 -33
- package/dist/hooks.d.mts +460 -11
- package/dist/hooks.d.ts +460 -11
- package/dist/hooks.js +456 -115
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.mjs +456 -118
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +457 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +457 -123
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import React, { createContext, useContext, useMemo, useCallback, useState, useEffect } from 'react';
|
|
1
|
+
import React, { createContext, useContext, useMemo, useCallback, useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { QueryClient, QueryClientProvider, useMutation, useQueryClient, useQuery } from '@tanstack/react-query';
|
|
3
3
|
import { ChipiSDK, decryptPrivateKey, encryptPrivateKey } from '@chipi-stack/backend';
|
|
4
4
|
import { getWalletEncryptKey, createWalletPasskey } from '@chipi-stack/chipi-passkey';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { ChipiApiError } from '@chipi-stack/shared';
|
|
7
|
-
import { Chain, ChainToken } from '@chipi-stack/types';
|
|
7
|
+
import { OnChainTxStatus, Chain, ChainToken } from '@chipi-stack/types';
|
|
8
8
|
export { Chain, ChainToken } from '@chipi-stack/types';
|
|
9
|
+
import { buildX402TypedData } from '@chipi-stack/core';
|
|
9
10
|
|
|
10
11
|
// src/context/ChipiProvider.tsx
|
|
11
12
|
var ChipiContext = createContext(null);
|
|
@@ -32,11 +33,7 @@ var createDefaultQueryClient = () => new QueryClient({
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
});
|
|
35
|
-
function ChipiProvider({
|
|
36
|
-
children,
|
|
37
|
-
config,
|
|
38
|
-
passkeyAdapter
|
|
39
|
-
}) {
|
|
36
|
+
function ChipiProvider({ children, config, passkeyAdapter }) {
|
|
40
37
|
const queryClient = React.useMemo(() => createDefaultQueryClient(), []);
|
|
41
38
|
const chipiSDK = React.useMemo(() => new ChipiSDK(config), [config]);
|
|
42
39
|
const resolvedPasskeyAdapter = passkeyAdapter ?? defaultPasskeyAdapter;
|
|
@@ -80,6 +77,9 @@ function useCreateWallet() {
|
|
|
80
77
|
throw new Error("Failed to create passkey for wallet");
|
|
81
78
|
}
|
|
82
79
|
}
|
|
80
|
+
if (!encryptKey) {
|
|
81
|
+
throw new Error("encryptKey is required when usePasskey is false");
|
|
82
|
+
}
|
|
83
83
|
return chipiSDK.createWallet({
|
|
84
84
|
params: {
|
|
85
85
|
...input.params,
|
|
@@ -106,8 +106,7 @@ function useGetWallet(input) {
|
|
|
106
106
|
const query = useQuery({
|
|
107
107
|
queryKey: ["wallet", input?.params?.externalUserId],
|
|
108
108
|
queryFn: async () => {
|
|
109
|
-
if (!input || !input.params || !input.getBearerToken)
|
|
110
|
-
throw new Error("Input is required");
|
|
109
|
+
if (!input || !input.params || !input.getBearerToken) throw new Error("Input is required");
|
|
111
110
|
const bearerToken = await input.getBearerToken();
|
|
112
111
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
113
112
|
return chipiSDK.getWallet(input.params, bearerToken);
|
|
@@ -149,22 +148,16 @@ function useGetWallet(input) {
|
|
|
149
148
|
function useChipiWallet(config) {
|
|
150
149
|
const { chipiSDK } = useChipiContext();
|
|
151
150
|
const queryClient = useQueryClient();
|
|
152
|
-
const {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
defaultToken = "USDC",
|
|
156
|
-
enabled = true
|
|
157
|
-
} = config;
|
|
158
|
-
const isEnabled = Boolean(
|
|
159
|
-
enabled && externalUserId && externalUserId.trim() !== ""
|
|
160
|
-
);
|
|
151
|
+
const { externalUserId, getBearerToken, defaultToken = "USDC", enabled = true } = config;
|
|
152
|
+
const trimmedUserId = externalUserId?.trim() || null;
|
|
153
|
+
const isEnabled = Boolean(enabled && trimmedUserId);
|
|
161
154
|
const walletQuery = useQuery({
|
|
162
|
-
queryKey: ["chipi-wallet",
|
|
155
|
+
queryKey: ["chipi-wallet", trimmedUserId],
|
|
163
156
|
queryFn: async () => {
|
|
164
|
-
if (!
|
|
157
|
+
if (!trimmedUserId) return null;
|
|
165
158
|
const bearerToken = await getBearerToken();
|
|
166
159
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
167
|
-
return chipiSDK.getWallet({ externalUserId }, bearerToken);
|
|
160
|
+
return chipiSDK.getWallet({ externalUserId: trimmedUserId }, bearerToken);
|
|
168
161
|
},
|
|
169
162
|
enabled: isEnabled,
|
|
170
163
|
retry: (failureCount, error) => {
|
|
@@ -190,7 +183,7 @@ function useChipiWallet(config) {
|
|
|
190
183
|
bearerToken
|
|
191
184
|
);
|
|
192
185
|
},
|
|
193
|
-
enabled: Boolean(walletPublicKey),
|
|
186
|
+
enabled: Boolean(isEnabled && walletPublicKey),
|
|
194
187
|
retry: (failureCount, error) => {
|
|
195
188
|
if (error instanceof ChipiApiError || error?.status) {
|
|
196
189
|
return false;
|
|
@@ -200,14 +193,14 @@ function useChipiWallet(config) {
|
|
|
200
193
|
});
|
|
201
194
|
const createWalletMutation = useMutation({
|
|
202
195
|
mutationFn: async ({ encryptKey, walletType = "CHIPI" }) => {
|
|
203
|
-
if (!
|
|
196
|
+
if (!trimmedUserId) throw new Error("External user ID is required");
|
|
204
197
|
const bearerToken = await getBearerToken();
|
|
205
198
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
206
199
|
return chipiSDK.createWallet({
|
|
207
200
|
params: {
|
|
208
201
|
chain: Chain.STARKNET,
|
|
209
202
|
encryptKey,
|
|
210
|
-
externalUserId,
|
|
203
|
+
externalUserId: trimmedUserId,
|
|
211
204
|
walletType
|
|
212
205
|
},
|
|
213
206
|
bearerToken
|
|
@@ -215,7 +208,7 @@ function useChipiWallet(config) {
|
|
|
215
208
|
},
|
|
216
209
|
onSuccess: () => {
|
|
217
210
|
queryClient.invalidateQueries({
|
|
218
|
-
queryKey: ["chipi-wallet",
|
|
211
|
+
queryKey: ["chipi-wallet", trimmedUserId]
|
|
219
212
|
});
|
|
220
213
|
}
|
|
221
214
|
});
|
|
@@ -290,14 +283,10 @@ function useChipiSession(config) {
|
|
|
290
283
|
defaultMaxCalls = 1e3,
|
|
291
284
|
autoCheckStatus = true
|
|
292
285
|
} = config;
|
|
293
|
-
const [localSession, setLocalSession] = useState(
|
|
294
|
-
storedSession ?? null
|
|
295
|
-
);
|
|
286
|
+
const [localSession, setLocalSession] = useState(storedSession ?? null);
|
|
296
287
|
const [error, setError] = useState(null);
|
|
297
288
|
useEffect(() => {
|
|
298
|
-
|
|
299
|
-
setLocalSession(storedSession);
|
|
300
|
-
}
|
|
289
|
+
setLocalSession(storedSession ?? null);
|
|
301
290
|
}, [storedSession]);
|
|
302
291
|
const supportsSession = useMemo(() => {
|
|
303
292
|
if (!wallet) return false;
|
|
@@ -333,27 +322,32 @@ function useChipiSession(config) {
|
|
|
333
322
|
const hasActiveSession = sessionState === "active";
|
|
334
323
|
const isSessionExpired = sessionState === "expired";
|
|
335
324
|
const remainingCalls = sessionStatusQuery.data?.remainingCalls;
|
|
336
|
-
const createSessionMutation = useMutation(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
325
|
+
const createSessionMutation = useMutation(
|
|
326
|
+
{
|
|
327
|
+
mutationFn: async (params = {}) => {
|
|
328
|
+
if (!encryptKey) throw new Error("Encryption key (PIN) is required");
|
|
329
|
+
if (!supportsSession)
|
|
330
|
+
throw new Error(
|
|
331
|
+
"Wallet does not support sessions. Only CHIPI wallets support session keys."
|
|
332
|
+
);
|
|
333
|
+
const sessionData = chipiSDK.sessions.createSessionKey({
|
|
334
|
+
encryptKey,
|
|
335
|
+
durationSeconds: params.durationSeconds ?? defaultDurationSeconds
|
|
336
|
+
});
|
|
337
|
+
return sessionData;
|
|
338
|
+
},
|
|
339
|
+
onSuccess: async (sessionData) => {
|
|
340
|
+
setLocalSession(sessionData);
|
|
341
|
+
setError(null);
|
|
342
|
+
if (onSessionCreated) {
|
|
343
|
+
await onSessionCreated(sessionData);
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
onError: (err) => {
|
|
347
|
+
setError(err);
|
|
351
348
|
}
|
|
352
|
-
},
|
|
353
|
-
onError: (err) => {
|
|
354
|
-
setError(err);
|
|
355
349
|
}
|
|
356
|
-
|
|
350
|
+
);
|
|
357
351
|
const registerSessionMutation = useMutation({
|
|
358
352
|
mutationFn: async (sessionConfig = {}) => {
|
|
359
353
|
if (!wallet) throw new Error("Wallet is required");
|
|
@@ -514,22 +508,19 @@ function useMigrateWalletToPasskey() {
|
|
|
514
508
|
mutationFn: async (input) => {
|
|
515
509
|
const { wallet, oldEncryptKey, externalUserId, bearerToken } = input;
|
|
516
510
|
try {
|
|
517
|
-
const passkeyResult = await createWalletPasskey(
|
|
518
|
-
externalUserId,
|
|
519
|
-
externalUserId
|
|
520
|
-
// Using externalUserId as userName
|
|
521
|
-
);
|
|
522
511
|
let decryptedPrivateKey;
|
|
523
512
|
try {
|
|
524
|
-
decryptedPrivateKey = decryptPrivateKey(
|
|
525
|
-
|
|
526
|
-
oldEncryptKey
|
|
527
|
-
);
|
|
528
|
-
} catch (error) {
|
|
513
|
+
decryptedPrivateKey = decryptPrivateKey(wallet.encryptedPrivateKey, oldEncryptKey);
|
|
514
|
+
} catch {
|
|
529
515
|
throw new Error(
|
|
530
516
|
"Failed to decrypt wallet with provided encryptKey. Please verify your PIN/password is correct."
|
|
531
517
|
);
|
|
532
518
|
}
|
|
519
|
+
const passkeyResult = await createWalletPasskey(
|
|
520
|
+
externalUserId,
|
|
521
|
+
externalUserId
|
|
522
|
+
// Using externalUserId as userName
|
|
523
|
+
);
|
|
533
524
|
const newEncryptedPrivateKey = encryptPrivateKey(
|
|
534
525
|
decryptedPrivateKey,
|
|
535
526
|
passkeyResult.encryptKey
|
|
@@ -589,44 +580,49 @@ function useUpdateWalletEncryption() {
|
|
|
589
580
|
}
|
|
590
581
|
function useTransfer() {
|
|
591
582
|
const { chipiSDK, passkeyAdapter } = useChipiContext();
|
|
592
|
-
const mutation = useMutation(
|
|
593
|
-
{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (input.params.
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
583
|
+
const mutation = useMutation({
|
|
584
|
+
mutationFn: async (input) => {
|
|
585
|
+
let encryptKey = input.params.encryptKey;
|
|
586
|
+
if (input.params.usePasskey) {
|
|
587
|
+
if (!input.params.externalUserId) {
|
|
588
|
+
throw new Error("externalUserId is required when usePasskey is true");
|
|
589
|
+
}
|
|
590
|
+
try {
|
|
591
|
+
const key = await passkeyAdapter.getWalletEncryptKey({
|
|
592
|
+
externalUserId: input.params.externalUserId
|
|
593
|
+
});
|
|
594
|
+
if (!key) {
|
|
595
|
+
throw new Error("Passkey authentication was cancelled");
|
|
596
|
+
}
|
|
597
|
+
encryptKey = key;
|
|
598
|
+
} catch (error) {
|
|
599
|
+
if (error instanceof DOMException && error.name === "NotAllowedError") {
|
|
600
|
+
throw new Error("Passkey authentication was cancelled by the user");
|
|
601
601
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
externalUserId: input.params.externalUserId
|
|
605
|
-
});
|
|
606
|
-
if (!key) {
|
|
607
|
-
throw new Error("Passkey authentication was cancelled");
|
|
608
|
-
}
|
|
609
|
-
encryptKey = key;
|
|
610
|
-
} catch (error) {
|
|
611
|
-
if (error instanceof Error) {
|
|
612
|
-
throw new Error(
|
|
613
|
-
`Passkey authentication failed: ${error.message}`
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
|
-
throw new Error("Failed to authenticate with passkey");
|
|
602
|
+
if (error instanceof Error) {
|
|
603
|
+
throw error;
|
|
617
604
|
}
|
|
605
|
+
throw new Error("Failed to authenticate with passkey");
|
|
618
606
|
}
|
|
619
|
-
return chipiSDK.transfer({
|
|
620
|
-
params: {
|
|
621
|
-
...input.params,
|
|
622
|
-
amount: String(input.params.amount),
|
|
623
|
-
encryptKey
|
|
624
|
-
},
|
|
625
|
-
bearerToken: input.bearerToken
|
|
626
|
-
});
|
|
627
607
|
}
|
|
608
|
+
if (!encryptKey) {
|
|
609
|
+
throw new Error("encryptKey is required when usePasskey is false");
|
|
610
|
+
}
|
|
611
|
+
const {
|
|
612
|
+
usePasskey: _usePasskey,
|
|
613
|
+
externalUserId: _externalUserId,
|
|
614
|
+
...transferParams
|
|
615
|
+
} = input.params;
|
|
616
|
+
return chipiSDK.transfer({
|
|
617
|
+
params: {
|
|
618
|
+
...transferParams,
|
|
619
|
+
amount: String(transferParams.amount),
|
|
620
|
+
encryptKey
|
|
621
|
+
},
|
|
622
|
+
bearerToken: input.bearerToken
|
|
623
|
+
});
|
|
628
624
|
}
|
|
629
|
-
);
|
|
625
|
+
});
|
|
630
626
|
return {
|
|
631
627
|
transfer: mutation.mutate,
|
|
632
628
|
transferAsync: mutation.mutateAsync,
|
|
@@ -662,10 +658,20 @@ function useApprove() {
|
|
|
662
658
|
throw new Error("Failed to authenticate with passkey");
|
|
663
659
|
}
|
|
664
660
|
}
|
|
661
|
+
if (!encryptKey) {
|
|
662
|
+
throw new Error(
|
|
663
|
+
"encryptKey is required when usePasskey is false. Provide a PIN or enable usePasskey."
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
const {
|
|
667
|
+
usePasskey: _usePasskey,
|
|
668
|
+
externalUserId: _externalUserId,
|
|
669
|
+
...approveParams
|
|
670
|
+
} = params.params;
|
|
665
671
|
return chipiSDK.approve({
|
|
666
672
|
params: {
|
|
667
|
-
...
|
|
668
|
-
amount: String(
|
|
673
|
+
...approveParams,
|
|
674
|
+
amount: String(approveParams.amount),
|
|
669
675
|
encryptKey
|
|
670
676
|
},
|
|
671
677
|
bearerToken: params.bearerToken
|
|
@@ -715,9 +721,8 @@ function useGetTransactionList(input) {
|
|
|
715
721
|
});
|
|
716
722
|
const fetchTransactionList = async (newInput) => {
|
|
717
723
|
return queryClient.fetchQuery({
|
|
718
|
-
queryKey: buildQueryKey(newInput
|
|
724
|
+
queryKey: buildQueryKey(newInput.query),
|
|
719
725
|
queryFn: async () => {
|
|
720
|
-
if (!newInput?.getBearerToken) throw new Error("getBearerToken is required");
|
|
721
726
|
const bearerToken = await newInput.getBearerToken();
|
|
722
727
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
723
728
|
return chipiSDK.getTransactionList(newInput.query, bearerToken);
|
|
@@ -735,6 +740,104 @@ function useGetTransactionList(input) {
|
|
|
735
740
|
fetchTransactionList
|
|
736
741
|
};
|
|
737
742
|
}
|
|
743
|
+
function useGetTransaction(input) {
|
|
744
|
+
const { chipiSDK } = useChipiContext();
|
|
745
|
+
const queryClient = useQueryClient();
|
|
746
|
+
const buildQueryKey = (hashOrId) => ["transaction", hashOrId ?? null];
|
|
747
|
+
const query = useQuery({
|
|
748
|
+
queryKey: buildQueryKey(input?.hashOrId),
|
|
749
|
+
queryFn: async () => {
|
|
750
|
+
if (!input?.hashOrId) throw new Error("hashOrId is required");
|
|
751
|
+
if (!input?.getBearerToken) throw new Error("getBearerToken is required");
|
|
752
|
+
const bearerToken = await input.getBearerToken();
|
|
753
|
+
if (!bearerToken) throw new Error("Bearer token is required");
|
|
754
|
+
return chipiSDK.getTransaction(input.hashOrId, bearerToken);
|
|
755
|
+
},
|
|
756
|
+
enabled: Boolean(input?.hashOrId && input?.getBearerToken),
|
|
757
|
+
retry: (failureCount, error) => {
|
|
758
|
+
if (error instanceof ChipiApiError || error?.status) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
return failureCount < 3;
|
|
762
|
+
},
|
|
763
|
+
...input?.queryOptions
|
|
764
|
+
});
|
|
765
|
+
const fetchTransaction = async (newInput) => {
|
|
766
|
+
return queryClient.fetchQuery({
|
|
767
|
+
queryKey: buildQueryKey(newInput.hashOrId),
|
|
768
|
+
queryFn: async () => {
|
|
769
|
+
const bearerToken = await newInput.getBearerToken();
|
|
770
|
+
if (!bearerToken) throw new Error("Bearer token is required");
|
|
771
|
+
return chipiSDK.getTransaction(newInput.hashOrId, bearerToken);
|
|
772
|
+
},
|
|
773
|
+
retry: (failureCount, error) => {
|
|
774
|
+
if (error instanceof ChipiApiError || error?.status) {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
return failureCount < 3;
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
};
|
|
781
|
+
return {
|
|
782
|
+
...query,
|
|
783
|
+
fetchTransaction
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
var TERMINAL_STATUSES = [
|
|
787
|
+
OnChainTxStatus.ACCEPTED_ON_L1,
|
|
788
|
+
OnChainTxStatus.REJECTED,
|
|
789
|
+
OnChainTxStatus.REVERTED
|
|
790
|
+
];
|
|
791
|
+
function useGetTransactionStatus(input) {
|
|
792
|
+
const { chipiSDK } = useChipiContext();
|
|
793
|
+
const queryClient = useQueryClient();
|
|
794
|
+
const buildQueryKey = (hash) => ["transaction-status", hash ?? null];
|
|
795
|
+
const query = useQuery({
|
|
796
|
+
queryKey: buildQueryKey(input?.hash),
|
|
797
|
+
queryFn: async () => {
|
|
798
|
+
if (!input?.hash) throw new Error("hash is required");
|
|
799
|
+
if (!input?.getBearerToken) throw new Error("getBearerToken is required");
|
|
800
|
+
const bearerToken = await input.getBearerToken();
|
|
801
|
+
if (!bearerToken) throw new Error("Bearer token is required");
|
|
802
|
+
return chipiSDK.getTransactionStatus(input.hash, bearerToken);
|
|
803
|
+
},
|
|
804
|
+
enabled: Boolean(input?.hash && input?.getBearerToken),
|
|
805
|
+
refetchInterval: (query2) => {
|
|
806
|
+
const status = query2.state.data?.status;
|
|
807
|
+
if (status && TERMINAL_STATUSES.includes(status)) {
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
return input?.refetchInterval ?? 3e3;
|
|
811
|
+
},
|
|
812
|
+
retry: (failureCount, error) => {
|
|
813
|
+
if (error instanceof ChipiApiError || error?.status) {
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
816
|
+
return failureCount < 3;
|
|
817
|
+
},
|
|
818
|
+
...input?.queryOptions
|
|
819
|
+
});
|
|
820
|
+
const fetchTransactionStatus = async (newInput) => {
|
|
821
|
+
return queryClient.fetchQuery({
|
|
822
|
+
queryKey: buildQueryKey(newInput.hash),
|
|
823
|
+
queryFn: async () => {
|
|
824
|
+
const bearerToken = await newInput.getBearerToken();
|
|
825
|
+
if (!bearerToken) throw new Error("Bearer token is required");
|
|
826
|
+
return chipiSDK.getTransactionStatus(newInput.hash, bearerToken);
|
|
827
|
+
},
|
|
828
|
+
retry: (failureCount, error) => {
|
|
829
|
+
if (error instanceof ChipiApiError || error?.status) {
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
return failureCount < 3;
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
};
|
|
836
|
+
return {
|
|
837
|
+
...query,
|
|
838
|
+
fetchTransactionStatus
|
|
839
|
+
};
|
|
840
|
+
}
|
|
738
841
|
function usePurchaseSku() {
|
|
739
842
|
const { chipiSDK } = useChipiContext();
|
|
740
843
|
const mutation = useMutation({
|
|
@@ -774,8 +877,7 @@ function useGetSkuPurchase(input) {
|
|
|
774
877
|
queryKey: ["sku-purchase", newInput?.id],
|
|
775
878
|
queryFn: async () => {
|
|
776
879
|
if (!newInput?.id) throw new Error("id is required");
|
|
777
|
-
if (!newInput?.getBearerToken)
|
|
778
|
-
throw new Error("getBearerToken is required");
|
|
880
|
+
if (!newInput?.getBearerToken) throw new Error("getBearerToken is required");
|
|
779
881
|
const bearerToken = await newInput.getBearerToken();
|
|
780
882
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
781
883
|
return chipiSDK.getSkuPurchase(newInput.id, bearerToken);
|
|
@@ -849,9 +951,7 @@ function useGetSku(input) {
|
|
|
849
951
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
850
952
|
return chipiSDK.getSku(input.id, bearerToken);
|
|
851
953
|
},
|
|
852
|
-
enabled: Boolean(
|
|
853
|
-
input?.id && input?.getBearerToken && input.id.trim() !== ""
|
|
854
|
-
),
|
|
954
|
+
enabled: Boolean(input?.id && input?.getBearerToken && input.id.trim() !== ""),
|
|
855
955
|
retry: (failureCount, error) => {
|
|
856
956
|
if (error instanceof ChipiApiError || error?.status) {
|
|
857
957
|
return false;
|
|
@@ -864,14 +964,13 @@ function useGetSku(input) {
|
|
|
864
964
|
return queryClient.fetchQuery({
|
|
865
965
|
queryKey: ["sku", input2?.id],
|
|
866
966
|
queryFn: async () => {
|
|
867
|
-
if (!input2?.getBearerToken)
|
|
868
|
-
throw new Error("getBearerToken is required");
|
|
967
|
+
if (!input2?.getBearerToken) throw new Error("getBearerToken is required");
|
|
869
968
|
const bearerToken = await input2.getBearerToken();
|
|
870
969
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
871
970
|
return chipiSDK.getSku(input2.id, bearerToken);
|
|
872
971
|
},
|
|
873
972
|
retry: (failureCount, error) => {
|
|
874
|
-
if (error instanceof ChipiApiError || error?.
|
|
973
|
+
if (error instanceof ChipiApiError || error?.status) {
|
|
875
974
|
return false;
|
|
876
975
|
}
|
|
877
976
|
return failureCount < 3;
|
|
@@ -890,9 +989,7 @@ function useCallAnyContract() {
|
|
|
890
989
|
let encryptKey = params.params.encryptKey;
|
|
891
990
|
if (params.params.usePasskey) {
|
|
892
991
|
if (!params.params.externalUserId) {
|
|
893
|
-
throw new Error(
|
|
894
|
-
"externalUserId is required when usePasskey is true"
|
|
895
|
-
);
|
|
992
|
+
throw new Error("externalUserId is required when usePasskey is true");
|
|
896
993
|
}
|
|
897
994
|
try {
|
|
898
995
|
const key = await passkeyAdapter.getWalletEncryptKey({
|
|
@@ -903,17 +1000,26 @@ function useCallAnyContract() {
|
|
|
903
1000
|
}
|
|
904
1001
|
encryptKey = key;
|
|
905
1002
|
} catch (error) {
|
|
1003
|
+
if (error instanceof DOMException && error.name === "NotAllowedError") {
|
|
1004
|
+
throw new Error("Passkey authentication was cancelled by the user");
|
|
1005
|
+
}
|
|
906
1006
|
if (error instanceof Error) {
|
|
907
|
-
throw
|
|
908
|
-
`Passkey authentication failed: ${error.message}`
|
|
909
|
-
);
|
|
1007
|
+
throw error;
|
|
910
1008
|
}
|
|
911
1009
|
throw new Error("Failed to authenticate with passkey");
|
|
912
1010
|
}
|
|
913
1011
|
}
|
|
1012
|
+
if (!encryptKey) {
|
|
1013
|
+
throw new Error("encryptKey is required when usePasskey is false");
|
|
1014
|
+
}
|
|
1015
|
+
const {
|
|
1016
|
+
usePasskey: _usePasskey,
|
|
1017
|
+
externalUserId: _externalUserId,
|
|
1018
|
+
...contractParams
|
|
1019
|
+
} = params.params;
|
|
914
1020
|
return chipiSDK.callAnyContract({
|
|
915
1021
|
params: {
|
|
916
|
-
...
|
|
1022
|
+
...contractParams,
|
|
917
1023
|
encryptKey
|
|
918
1024
|
},
|
|
919
1025
|
bearerToken: params.bearerToken
|
|
@@ -981,7 +1087,8 @@ function useGetTokenBalance(input) {
|
|
|
981
1087
|
return queryClient.fetchQuery({
|
|
982
1088
|
queryKey: buildQueryKey(newInput?.params),
|
|
983
1089
|
queryFn: async () => {
|
|
984
|
-
if (!newInput || !newInput.getBearerToken || !newInput.params)
|
|
1090
|
+
if (!newInput || !newInput.getBearerToken || !newInput.params)
|
|
1091
|
+
throw new Error("Input is required");
|
|
985
1092
|
const bearerToken = await newInput.getBearerToken();
|
|
986
1093
|
if (!bearerToken) throw new Error("Bearer token is required");
|
|
987
1094
|
return chipiSDK.getTokenBalance(newInput.params, bearerToken);
|
|
@@ -1018,10 +1125,7 @@ function useCreateSessionKey() {
|
|
|
1018
1125
|
function useAddSessionKeyToContract() {
|
|
1019
1126
|
const { chipiSDK } = useChipiContext();
|
|
1020
1127
|
const mutation = useMutation({
|
|
1021
|
-
mutationFn: (input) => chipiSDK.sessions.addSessionKeyToContract(
|
|
1022
|
-
input.params,
|
|
1023
|
-
input.bearerToken
|
|
1024
|
-
)
|
|
1128
|
+
mutationFn: (input) => chipiSDK.sessions.addSessionKeyToContract(input.params, input.bearerToken)
|
|
1025
1129
|
});
|
|
1026
1130
|
return {
|
|
1027
1131
|
addSessionKeyToContract: mutation.mutate,
|
|
@@ -1053,11 +1157,7 @@ function useRevokeSessionKey() {
|
|
|
1053
1157
|
function useGetSessionData(params, options) {
|
|
1054
1158
|
const { chipiSDK } = useChipiContext();
|
|
1055
1159
|
const query = useQuery({
|
|
1056
|
-
queryKey: [
|
|
1057
|
-
"sessionData",
|
|
1058
|
-
params?.walletAddress,
|
|
1059
|
-
params?.sessionPublicKey
|
|
1060
|
-
],
|
|
1160
|
+
queryKey: ["sessionData", params?.walletAddress, params?.sessionPublicKey],
|
|
1061
1161
|
queryFn: () => {
|
|
1062
1162
|
if (!params) {
|
|
1063
1163
|
throw new Error("Session data params are required");
|
|
@@ -1094,7 +1194,241 @@ function useExecuteWithSession() {
|
|
|
1094
1194
|
reset: mutation.reset
|
|
1095
1195
|
};
|
|
1096
1196
|
}
|
|
1197
|
+
function useX402Payment(config) {
|
|
1198
|
+
const { chipiSDK } = useChipiContext();
|
|
1199
|
+
const {
|
|
1200
|
+
wallet,
|
|
1201
|
+
encryptKey,
|
|
1202
|
+
getBearerToken: getBearerTokenFn,
|
|
1203
|
+
bearerToken: staticBearerToken,
|
|
1204
|
+
maxPaymentAmount: maxPaymentAmountProp,
|
|
1205
|
+
maxAmount,
|
|
1206
|
+
allowedRecipients,
|
|
1207
|
+
session,
|
|
1208
|
+
onPaymentComplete
|
|
1209
|
+
} = config;
|
|
1210
|
+
const maxPaymentAmount = maxPaymentAmountProp ?? maxAmount;
|
|
1211
|
+
const getBearerToken = useMemo(
|
|
1212
|
+
() => getBearerTokenFn ?? (staticBearerToken ? () => Promise.resolve(staticBearerToken) : () => Promise.resolve(void 0)),
|
|
1213
|
+
[getBearerTokenFn, staticBearerToken]
|
|
1214
|
+
);
|
|
1215
|
+
const [lastTxHash, setLastTxHash] = useState(null);
|
|
1216
|
+
const [lastAmount, setLastAmount] = useState(null);
|
|
1217
|
+
const [lastPayment, setLastPayment] = useState(null);
|
|
1218
|
+
const [error, setError] = useState(null);
|
|
1219
|
+
const paymentCountRef = useRef(0);
|
|
1220
|
+
const [paymentCount, setPaymentCount] = useState(0);
|
|
1221
|
+
const totalSpentRef = useRef(0n);
|
|
1222
|
+
const [totalSpent, setTotalSpent] = useState("0.000000");
|
|
1223
|
+
const parsePaymentRequired = useCallback((response) => {
|
|
1224
|
+
const headerValue = response.headers.get("payment-required");
|
|
1225
|
+
if (!headerValue) {
|
|
1226
|
+
throw new Error(
|
|
1227
|
+
"Server returned 402 but no PAYMENT-REQUIRED header. The server may not support x402 or is misconfigured."
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
try {
|
|
1231
|
+
return JSON.parse(headerValue);
|
|
1232
|
+
} catch {
|
|
1233
|
+
throw new Error("Malformed PAYMENT-REQUIRED header: invalid JSON");
|
|
1234
|
+
}
|
|
1235
|
+
}, []);
|
|
1236
|
+
const validateRequirement = useCallback(
|
|
1237
|
+
(requirement) => {
|
|
1238
|
+
if (requirement.scheme !== "exact") {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`Unsupported payment scheme: "${requirement.scheme}". Only "exact" is supported.`
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
if (requirement.network !== "starknet-mainnet") {
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
`Unsupported network: "${requirement.network}". Only "starknet-mainnet" is supported.`
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
const USDC_MAINNET = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
|
|
1249
|
+
const normalizeAddr = (a) => a.toLowerCase().replace(/^0x0+/, "0x");
|
|
1250
|
+
if (normalizeAddr(requirement.asset) !== normalizeAddr(USDC_MAINNET)) {
|
|
1251
|
+
throw new Error(`Unsupported asset: ${requirement.asset}. Only USDC is supported.`);
|
|
1252
|
+
}
|
|
1253
|
+
if (!requirement.payTo) {
|
|
1254
|
+
throw new Error("Invalid payment requirement: missing payTo address");
|
|
1255
|
+
}
|
|
1256
|
+
const timeoutSeconds = Number(requirement.maxTimeoutSeconds);
|
|
1257
|
+
if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
1258
|
+
throw new Error("Invalid payment requirement: maxTimeoutSeconds must be a positive number");
|
|
1259
|
+
}
|
|
1260
|
+
let requiredAmount;
|
|
1261
|
+
try {
|
|
1262
|
+
requiredAmount = BigInt(requirement.maxAmountRequired);
|
|
1263
|
+
} catch {
|
|
1264
|
+
throw new Error("Invalid payment requirement: maxAmountRequired must be an integer string");
|
|
1265
|
+
}
|
|
1266
|
+
if (requiredAmount <= 0n) {
|
|
1267
|
+
throw new Error("Invalid payment requirement: maxAmountRequired must be > 0");
|
|
1268
|
+
}
|
|
1269
|
+
if (maxPaymentAmount) {
|
|
1270
|
+
const trimmed = maxPaymentAmount.trim();
|
|
1271
|
+
if (!/^\d+(\.\d{1,6})?$/.test(trimmed)) {
|
|
1272
|
+
throw new Error(`Invalid maxPaymentAmount format: "${maxPaymentAmount}"`);
|
|
1273
|
+
}
|
|
1274
|
+
const [whole, frac = ""] = trimmed.split(".");
|
|
1275
|
+
const fracPadded = (frac + "000000").slice(0, 6);
|
|
1276
|
+
const maxBaseUnits = BigInt((whole || "0") + fracPadded);
|
|
1277
|
+
if (requiredAmount > maxBaseUnits) {
|
|
1278
|
+
const requiredHuman = (Number(requiredAmount) / 1e6).toFixed(6);
|
|
1279
|
+
throw new Error(
|
|
1280
|
+
`Payment amount ${requiredHuman} USDC exceeds maximum allowed ${maxPaymentAmount} USDC`
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (allowedRecipients && allowedRecipients.length > 0) {
|
|
1285
|
+
const normalize = (a) => a.toLowerCase().replace(/^0x0+/, "0x");
|
|
1286
|
+
const normalized = normalize(requirement.payTo);
|
|
1287
|
+
const isAllowed = allowedRecipients.some((addr) => normalize(addr) === normalized);
|
|
1288
|
+
if (!isAllowed) {
|
|
1289
|
+
throw new Error(`Recipient ${requirement.payTo} is not in the allowed recipients list`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
[maxPaymentAmount, allowedRecipients]
|
|
1294
|
+
);
|
|
1295
|
+
const signPayment = useCallback(
|
|
1296
|
+
async (requirement) => {
|
|
1297
|
+
if (!wallet) {
|
|
1298
|
+
throw new Error("Wallet is required for x402 payments");
|
|
1299
|
+
}
|
|
1300
|
+
if (!encryptKey) {
|
|
1301
|
+
throw new Error("Encryption key (PIN) is required for x402 payments");
|
|
1302
|
+
}
|
|
1303
|
+
validateRequirement(requirement);
|
|
1304
|
+
const nonce = crypto.randomUUID();
|
|
1305
|
+
const validUntil = Math.floor(Date.now() / 1e3) + requirement.maxTimeoutSeconds;
|
|
1306
|
+
if (session) {
|
|
1307
|
+
const bearerToken = await getBearerToken();
|
|
1308
|
+
if (!bearerToken) throw new Error("Bearer token is required");
|
|
1309
|
+
const txHash = await chipiSDK.executeTransactionWithSession({
|
|
1310
|
+
params: {
|
|
1311
|
+
encryptKey,
|
|
1312
|
+
wallet,
|
|
1313
|
+
session,
|
|
1314
|
+
calls: [
|
|
1315
|
+
{
|
|
1316
|
+
contractAddress: requirement.asset,
|
|
1317
|
+
entrypoint: "transfer",
|
|
1318
|
+
calldata: [requirement.payTo, requirement.maxAmountRequired, "0x0"]
|
|
1319
|
+
}
|
|
1320
|
+
]
|
|
1321
|
+
},
|
|
1322
|
+
bearerToken
|
|
1323
|
+
});
|
|
1324
|
+
return {
|
|
1325
|
+
x402Version: 1,
|
|
1326
|
+
scheme: "exact",
|
|
1327
|
+
network: "starknet-mainnet",
|
|
1328
|
+
payload: {
|
|
1329
|
+
signature: { r: txHash, s: "session" },
|
|
1330
|
+
fromAddress: wallet.publicKey,
|
|
1331
|
+
toAddress: requirement.payTo,
|
|
1332
|
+
amount: requirement.maxAmountRequired,
|
|
1333
|
+
asset: requirement.asset,
|
|
1334
|
+
validUntil,
|
|
1335
|
+
nonce
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
const typedData = buildX402TypedData({
|
|
1340
|
+
from_address: wallet.publicKey,
|
|
1341
|
+
to_address: requirement.payTo,
|
|
1342
|
+
amount: requirement.maxAmountRequired,
|
|
1343
|
+
asset: requirement.asset,
|
|
1344
|
+
valid_until: String(validUntil),
|
|
1345
|
+
nonce
|
|
1346
|
+
});
|
|
1347
|
+
const signature = await chipiSDK.signTypedData({
|
|
1348
|
+
params: {
|
|
1349
|
+
encryptKey,
|
|
1350
|
+
wallet,
|
|
1351
|
+
typedData
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
return {
|
|
1355
|
+
x402Version: 1,
|
|
1356
|
+
scheme: "exact",
|
|
1357
|
+
network: "starknet-mainnet",
|
|
1358
|
+
payload: {
|
|
1359
|
+
signature,
|
|
1360
|
+
fromAddress: wallet.publicKey,
|
|
1361
|
+
toAddress: requirement.payTo,
|
|
1362
|
+
amount: requirement.maxAmountRequired,
|
|
1363
|
+
asset: requirement.asset,
|
|
1364
|
+
validUntil,
|
|
1365
|
+
nonce
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
},
|
|
1369
|
+
[wallet, encryptKey, session, getBearerToken, chipiSDK, validateRequirement]
|
|
1370
|
+
);
|
|
1371
|
+
const paymentMutation = useMutation(
|
|
1372
|
+
{
|
|
1373
|
+
mutationFn: async ({ url, init }) => {
|
|
1374
|
+
const response = await globalThis.fetch(url, init);
|
|
1375
|
+
if (response.status !== 402) {
|
|
1376
|
+
return response;
|
|
1377
|
+
}
|
|
1378
|
+
const requirement = parsePaymentRequired(response);
|
|
1379
|
+
const payment = await signPayment(requirement);
|
|
1380
|
+
const humanAmount = (Number(BigInt(requirement.maxAmountRequired)) / 1e6).toFixed(6);
|
|
1381
|
+
const isSessionPayment = payment.payload.signature.s === "session";
|
|
1382
|
+
const txHash = isSessionPayment ? payment.payload.signature.r : null;
|
|
1383
|
+
setLastAmount(humanAmount);
|
|
1384
|
+
setLastTxHash(txHash);
|
|
1385
|
+
setLastPayment({ txHash, amount: humanAmount, timestamp: Date.now() });
|
|
1386
|
+
totalSpentRef.current += BigInt(requirement.maxAmountRequired);
|
|
1387
|
+
setTotalSpent((Number(totalSpentRef.current) / 1e6).toFixed(6));
|
|
1388
|
+
paymentCountRef.current += 1;
|
|
1389
|
+
setPaymentCount(paymentCountRef.current);
|
|
1390
|
+
const headers = new Headers(init?.headers);
|
|
1391
|
+
headers.set("X-PAYMENT", JSON.stringify(payment));
|
|
1392
|
+
const paidResponse = await globalThis.fetch(url, { ...init, headers });
|
|
1393
|
+
if (paidResponse.status === 402) {
|
|
1394
|
+
throw new Error(
|
|
1395
|
+
"Server returned 402 after payment was submitted. Payment may have failed verification."
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
if (onPaymentComplete) {
|
|
1399
|
+
void Promise.resolve(onPaymentComplete(txHash ?? "", humanAmount)).catch(() => {
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
return paidResponse;
|
|
1403
|
+
},
|
|
1404
|
+
onError: (err) => {
|
|
1405
|
+
setError(err);
|
|
1406
|
+
},
|
|
1407
|
+
onSuccess: () => {
|
|
1408
|
+
setError(null);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
const x402Fetch = useCallback(
|
|
1413
|
+
async (url, init) => {
|
|
1414
|
+
return paymentMutation.mutateAsync({ url, init });
|
|
1415
|
+
},
|
|
1416
|
+
[paymentMutation]
|
|
1417
|
+
);
|
|
1418
|
+
return {
|
|
1419
|
+
x402Fetch,
|
|
1420
|
+
payFetch: x402Fetch,
|
|
1421
|
+
signPayment,
|
|
1422
|
+
isPaying: paymentMutation.isPending,
|
|
1423
|
+
lastTxHash,
|
|
1424
|
+
lastAmount,
|
|
1425
|
+
lastPayment,
|
|
1426
|
+
totalSpent,
|
|
1427
|
+
error: error || paymentMutation.error || null,
|
|
1428
|
+
paymentCount
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1097
1431
|
|
|
1098
|
-
export { ChipiProvider, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransactionList, useGetWallet, useMigrateWalletToPasskey, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useTransfer, useUpdateWalletEncryption };
|
|
1432
|
+
export { ChipiProvider, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useMigrateWalletToPasskey, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useTransfer, useUpdateWalletEncryption, useX402Payment };
|
|
1099
1433
|
//# sourceMappingURL=index.mjs.map
|
|
1100
1434
|
//# sourceMappingURL=index.mjs.map
|