@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.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
|
-
|
|
159
|
-
|
|
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",
|
|
160
|
+
queryKey: ["chipi-wallet", trimmedUserId],
|
|
168
161
|
queryFn: async () => {
|
|
169
|
-
if (!
|
|
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 (!
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
if (input.params.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
608
|
-
|
|
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
|
-
...
|
|
673
|
-
amount: String(
|
|
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
|
|
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?.
|
|
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
|
|
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
|
-
...
|
|
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)
|
|
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
|