thirdweb 5.112.4 → 5.113.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.
Files changed (83) hide show
  1. package/dist/cjs/exports/react.js +4 -1
  2. package/dist/cjs/exports/react.js.map +1 -1
  3. package/dist/cjs/exports/react.native.js +4 -1
  4. package/dist/cjs/exports/react.native.js.map +1 -1
  5. package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js +110 -0
  6. package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -0
  7. package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js +89 -0
  8. package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js.map +1 -0
  9. package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js +168 -0
  10. package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js.map +1 -0
  11. package/dist/cjs/react/web/ui/components/basic.js +1 -1
  12. package/dist/cjs/react/web/ui/x402/PaymentErrorModal.js +107 -0
  13. package/dist/cjs/react/web/ui/x402/PaymentErrorModal.js.map +1 -0
  14. package/dist/cjs/react/web/ui/x402/SignInRequiredModal.js +28 -0
  15. package/dist/cjs/react/web/ui/x402/SignInRequiredModal.js.map +1 -0
  16. package/dist/cjs/version.js +1 -1
  17. package/dist/cjs/wallets/coinbase/coinbase-web.js +9 -0
  18. package/dist/cjs/wallets/coinbase/coinbase-web.js.map +1 -1
  19. package/dist/cjs/x402/facilitator.js +1 -1
  20. package/dist/cjs/x402/fetchWithPayment.js +9 -7
  21. package/dist/cjs/x402/fetchWithPayment.js.map +1 -1
  22. package/dist/cjs/x402/settle-payment.js +1 -1
  23. package/dist/cjs/x402/verify-payment.js +1 -1
  24. package/dist/esm/exports/react.js +2 -0
  25. package/dist/esm/exports/react.js.map +1 -1
  26. package/dist/esm/exports/react.native.js +2 -0
  27. package/dist/esm/exports/react.native.js.map +1 -1
  28. package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js +107 -0
  29. package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -0
  30. package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js +86 -0
  31. package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js.map +1 -0
  32. package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js +165 -0
  33. package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js.map +1 -0
  34. package/dist/esm/react/web/ui/components/basic.js +1 -1
  35. package/dist/esm/react/web/ui/x402/PaymentErrorModal.js +104 -0
  36. package/dist/esm/react/web/ui/x402/PaymentErrorModal.js.map +1 -0
  37. package/dist/esm/react/web/ui/x402/SignInRequiredModal.js +25 -0
  38. package/dist/esm/react/web/ui/x402/SignInRequiredModal.js.map +1 -0
  39. package/dist/esm/version.js +1 -1
  40. package/dist/esm/wallets/coinbase/coinbase-web.js +9 -0
  41. package/dist/esm/wallets/coinbase/coinbase-web.js.map +1 -1
  42. package/dist/esm/x402/facilitator.js +1 -1
  43. package/dist/esm/x402/fetchWithPayment.js +9 -7
  44. package/dist/esm/x402/fetchWithPayment.js.map +1 -1
  45. package/dist/esm/x402/settle-payment.js +1 -1
  46. package/dist/esm/x402/verify-payment.js +1 -1
  47. package/dist/scripts/bridge-widget.js +50 -50
  48. package/dist/types/exports/react.d.ts +1 -0
  49. package/dist/types/exports/react.d.ts.map +1 -1
  50. package/dist/types/exports/react.native.d.ts +1 -0
  51. package/dist/types/exports/react.native.d.ts.map +1 -1
  52. package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts +131 -0
  53. package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts.map +1 -0
  54. package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts +189 -0
  55. package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts.map +1 -0
  56. package/dist/types/react/web/hooks/x402/useFetchWithPayment.d.ts +248 -0
  57. package/dist/types/react/web/hooks/x402/useFetchWithPayment.d.ts.map +1 -0
  58. package/dist/types/react/web/ui/x402/PaymentErrorModal.d.ts +20 -0
  59. package/dist/types/react/web/ui/x402/PaymentErrorModal.d.ts.map +1 -0
  60. package/dist/types/react/web/ui/x402/SignInRequiredModal.d.ts +12 -0
  61. package/dist/types/react/web/ui/x402/SignInRequiredModal.d.ts.map +1 -0
  62. package/dist/types/version.d.ts +1 -1
  63. package/dist/types/x402/facilitator.d.ts +1 -1
  64. package/dist/types/x402/fetchWithPayment.d.ts +7 -3
  65. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -1
  66. package/dist/types/x402/schemas.d.ts +2 -2
  67. package/dist/types/x402/settle-payment.d.ts +1 -1
  68. package/dist/types/x402/verify-payment.d.ts +1 -1
  69. package/package.json +1 -1
  70. package/src/exports/react.native.ts +5 -0
  71. package/src/exports/react.ts +5 -0
  72. package/src/react/core/hooks/x402/useFetchWithPaymentCore.ts +160 -0
  73. package/src/react/native/hooks/x402/useFetchWithPayment.ts +96 -0
  74. package/src/react/web/hooks/x402/useFetchWithPayment.tsx +238 -0
  75. package/src/react/web/ui/components/basic.tsx +1 -1
  76. package/src/react/web/ui/x402/PaymentErrorModal.tsx +261 -0
  77. package/src/react/web/ui/x402/SignInRequiredModal.tsx +75 -0
  78. package/src/version.ts +1 -1
  79. package/src/wallets/coinbase/coinbase-web.ts +10 -0
  80. package/src/x402/facilitator.ts +1 -1
  81. package/src/x402/fetchWithPayment.ts +19 -12
  82. package/src/x402/settle-payment.ts +1 -1
  83. package/src/x402/verify-payment.ts +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaymentErrorModal.d.ts","sourceRoot":"","sources":["../../../../../../src/react/web/ui/x402/PaymentErrorModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAGlE,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAUxE,KAAK,sBAAsB,GAAG;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC;IACjD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CACzB,IAAI,CACF,cAAc,EACZ,QAAQ,GACR,OAAO,GACP,cAAc,GACd,QAAQ,GACR,WAAW,GACX,UAAU,GACV,OAAO,CACV,CACF,CAAC;IACF,2BAA2B,CAAC,EAAE,CAC5B,mBAAmB,EAAE,4BAA4B,EAAE,KAChD,4BAA4B,GAAG,SAAS,CAAC;CAC/C,CAAC;AAIF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,2CAiL9D"}
@@ -0,0 +1,12 @@
1
+ import type { Theme } from "../../../core/design-system/index.js";
2
+ type SignInRequiredModalProps = {
3
+ theme: Theme | "light" | "dark";
4
+ onSignIn: () => void;
5
+ onCancel: () => void;
6
+ };
7
+ /**
8
+ * @internal
9
+ */
10
+ export declare function SignInRequiredModal(props: SignInRequiredModalProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=SignInRequiredModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignInRequiredModal.d.ts","sourceRoot":"","sources":["../../../../../../src/react/web/ui/x402/SignInRequiredModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAWlE,KAAK,wBAAwB,GAAG;IAC9B,KAAK,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAoDlE"}
@@ -1,2 +1,2 @@
1
- export declare const version = "5.112.4";
1
+ export declare const version = "5.113.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -82,7 +82,7 @@ export type ThirdwebX402Facilitator = {
82
82
 
83
83
  * ```
84
84
  *
85
- * @bridge x402
85
+ * @x402
86
86
  */
87
87
  export declare function facilitator(config: ThirdwebX402FacilitatorConfig): ThirdwebX402Facilitator;
88
88
  //# sourceMappingURL=facilitator.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import type { ThirdwebClient } from "../client/client.js";
2
2
  import type { Wallet } from "../wallets/interfaces/wallet.js";
3
+ import { type RequestedPaymentRequirements } from "./schemas.js";
3
4
  /**
4
5
  * Enables the payment of APIs using the x402 payment protocol.
5
6
  *
@@ -14,7 +15,7 @@ import type { Wallet } from "../wallets/interfaces/wallet.js";
14
15
  * @param fetch - The fetch function to wrap (typically globalThis.fetch)
15
16
  * @param client - The thirdweb client used to access RPC infrastructure
16
17
  * @param wallet - The wallet used to sign payment messages
17
- * @param maxValue - The maximum allowed payment amount in base units (defaults to 1 USDC)
18
+ * @param maxValue - The maximum allowed payment amount in base units
18
19
  * @returns A wrapped fetch function that handles 402 responses automatically
19
20
  *
20
21
  * @example
@@ -37,7 +38,10 @@ import type { Wallet } from "../wallets/interfaces/wallet.js";
37
38
  * @throws {Error} If a payment has already been attempted for this request
38
39
  * @throws {Error} If there's an error creating the payment header
39
40
  *
40
- * @bridge x402
41
+ * @x402
41
42
  */
42
- export declare function wrapFetchWithPayment(fetch: typeof globalThis.fetch, client: ThirdwebClient, wallet: Wallet, maxValue?: bigint): (input: RequestInfo, init?: RequestInit) => Promise<Response>;
43
+ export declare function wrapFetchWithPayment(fetch: typeof globalThis.fetch, client: ThirdwebClient, wallet: Wallet, options?: {
44
+ maxValue?: bigint;
45
+ paymentRequirementsSelector?: (paymentRequirements: RequestedPaymentRequirements[]) => RequestedPaymentRequirements | undefined;
46
+ }): (input: RequestInfo, init?: RequestInit) => Promise<Response>;
43
47
  //# sourceMappingURL=fetchWithPayment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetchWithPayment.d.ts","sourceRoot":"","sources":["../../../src/x402/fetchWithPayment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAS9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,EAC9B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,IAEH,OAAO,WAAW,EAAE,OAAO,WAAW,uBA4FrD"}
1
+ {"version":3,"file":"fetchWithPayment.d.ts","sourceRoot":"","sources":["../../../src/x402/fetchWithPayment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAGL,KAAK,4BAA4B,EAElC,MAAM,cAAc,CAAC;AAGtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,EAC9B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2BAA2B,CAAC,EAAE,CAC5B,mBAAmB,EAAE,4BAA4B,EAAE,KAChD,4BAA4B,GAAG,SAAS,CAAC;CAC/C,IAEa,OAAO,WAAW,EAAE,OAAO,WAAW,uBA8FrD"}
@@ -118,11 +118,11 @@ export declare const RequestedPaymentRequirementsSchema: z.ZodObject<{
118
118
  scheme: "exact";
119
119
  resource: string;
120
120
  mimeType: string;
121
- asset: string;
122
121
  network: string;
123
122
  maxAmountRequired: string;
124
123
  payTo: string;
125
124
  maxTimeoutSeconds: number;
125
+ asset: string;
126
126
  outputSchema?: Record<string, any> | undefined;
127
127
  extra?: Record<string, any> | undefined;
128
128
  }, {
@@ -130,11 +130,11 @@ export declare const RequestedPaymentRequirementsSchema: z.ZodObject<{
130
130
  scheme: "exact";
131
131
  resource: string;
132
132
  mimeType: string;
133
- asset: string;
134
133
  network: string;
135
134
  maxAmountRequired: string;
136
135
  payTo: string;
137
136
  maxTimeoutSeconds: number;
137
+ asset: string;
138
138
  outputSchema?: Record<string, any> | undefined;
139
139
  extra?: Record<string, any> | undefined;
140
140
  }>;
@@ -114,7 +114,7 @@ import { type SettlePaymentArgs, type SettlePaymentResult } from "./types.js";
114
114
  *
115
115
  * @public
116
116
  * @beta
117
- * @bridge x402
117
+ * @x402
118
118
  */
119
119
  export declare function settlePayment(args: SettlePaymentArgs): Promise<SettlePaymentResult>;
120
120
  //# sourceMappingURL=settle-payment.d.ts.map
@@ -64,7 +64,7 @@ import { type PaymentArgs, type VerifyPaymentResult } from "./types.js";
64
64
  *
65
65
  * @public
66
66
  * @beta
67
- * @bridge x402
67
+ * @x402
68
68
  */
69
69
  export declare function verifyPayment(args: PaymentArgs): Promise<VerifyPaymentResult>;
70
70
  //# sourceMappingURL=verify-payment.d.ts.map
package/package.json CHANGED
@@ -399,7 +399,7 @@
399
399
  }
400
400
  },
401
401
  "typings": "./dist/types/exports/thirdweb.d.ts",
402
- "version": "5.112.4",
402
+ "version": "5.113.0",
403
403
  "scripts": {
404
404
  "bench": "vitest -c ./test/vitest.config.ts bench",
405
405
  "bench:compare": "bun run ./benchmarks/run.ts",
@@ -114,6 +114,11 @@ export { useAutoConnect } from "../react/native/hooks/wallets/useAutoConnect.js"
114
114
  export { useLinkProfile } from "../react/native/hooks/wallets/useLinkProfile.js";
115
115
  export { useProfiles } from "../react/native/hooks/wallets/useProfiles.js";
116
116
  export { useUnlinkProfile } from "../react/native/hooks/wallets/useUnlinkProfile.js";
117
+ // x402
118
+ export {
119
+ type UseFetchWithPaymentOptions,
120
+ useFetchWithPayment,
121
+ } from "../react/native/hooks/x402/useFetchWithPayment.js";
117
122
  export { ThirdwebProvider } from "../react/native/providers/thirdweb-provider.js";
118
123
  // Components
119
124
  export { AutoConnect } from "../react/native/ui/AutoConnect/AutoConnect.js";
@@ -131,6 +131,11 @@ export { useAutoConnect } from "../react/web/hooks/wallets/useAutoConnect.js";
131
131
  export { useLinkProfile } from "../react/web/hooks/wallets/useLinkProfile.js";
132
132
  export { useProfiles } from "../react/web/hooks/wallets/useProfiles.js";
133
133
  export { useUnlinkProfile } from "../react/web/hooks/wallets/useUnlinkProfile.js";
134
+ // x402
135
+ export {
136
+ type UseFetchWithPaymentOptions,
137
+ useFetchWithPayment,
138
+ } from "../react/web/hooks/x402/useFetchWithPayment.js";
134
139
  export { ThirdwebProvider } from "../react/web/providers/thirdweb-provider.js";
135
140
  export { AutoConnect } from "../react/web/ui/AutoConnect/AutoConnect.js";
136
141
  export type { BuyOrOnrampPrepareResult } from "../react/web/ui/Bridge/BuyWidget.js";
@@ -0,0 +1,160 @@
1
+ "use client";
2
+
3
+ import { useMutation } from "@tanstack/react-query";
4
+ import type { ThirdwebClient } from "../../../../client/client.js";
5
+ import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
6
+ import { wrapFetchWithPayment } from "../../../../x402/fetchWithPayment.js";
7
+ import type { RequestedPaymentRequirements } from "../../../../x402/schemas.js";
8
+ import type { PaymentRequiredResult } from "../../../../x402/types.js";
9
+ import { useActiveWallet } from "../wallets/useActiveWallet.js";
10
+
11
+ export type UseFetchWithPaymentOptions = {
12
+ maxValue?: bigint;
13
+ paymentRequirementsSelector?: (
14
+ paymentRequirements: RequestedPaymentRequirements[],
15
+ ) => RequestedPaymentRequirements | undefined;
16
+ parseAs?: "json" | "text" | "raw";
17
+ };
18
+
19
+ type ShowErrorModalCallback = (data: {
20
+ errorData: PaymentRequiredResult["responseBody"];
21
+ onRetry: () => void;
22
+ onCancel: () => void;
23
+ }) => void;
24
+
25
+ type ShowConnectModalCallback = (data: {
26
+ onConnect: (wallet: Wallet) => void;
27
+ onCancel: () => void;
28
+ }) => void;
29
+
30
+ /**
31
+ * Core hook for fetch with payment functionality.
32
+ * This is the platform-agnostic implementation used by both web and native versions.
33
+ * @internal
34
+ */
35
+ export function useFetchWithPaymentCore(
36
+ client: ThirdwebClient,
37
+ options?: UseFetchWithPaymentOptions,
38
+ showErrorModal?: ShowErrorModalCallback,
39
+ showConnectModal?: ShowConnectModalCallback,
40
+ ) {
41
+ const wallet = useActiveWallet();
42
+
43
+ const mutation = useMutation({
44
+ mutationFn: async ({
45
+ input,
46
+ init,
47
+ }: {
48
+ input: RequestInfo;
49
+ init?: RequestInit;
50
+ }) => {
51
+ // Recursive function that handles fetch + 402 error + retry
52
+ const executeFetch = async (currentWallet = wallet): Promise<unknown> => {
53
+ if (!currentWallet) {
54
+ // If a connect modal handler is provided, show the connect modal
55
+ if (showConnectModal) {
56
+ return new Promise<unknown>((resolve, reject) => {
57
+ showConnectModal({
58
+ onConnect: async (newWallet) => {
59
+ // After connection, retry the fetch with the newly connected wallet
60
+ try {
61
+ const result = await executeFetch(newWallet);
62
+ resolve(result);
63
+ } catch (error) {
64
+ reject(error);
65
+ }
66
+ },
67
+ onCancel: () => {
68
+ reject(new Error("Wallet connection cancelled by user"));
69
+ },
70
+ });
71
+ });
72
+ }
73
+
74
+ // If no connect modal handler, throw an error
75
+ throw new Error(
76
+ "No wallet connected. Please connect your wallet to make paid API calls.",
77
+ );
78
+ }
79
+
80
+ const wrappedFetch = wrapFetchWithPayment(
81
+ globalThis.fetch,
82
+ client,
83
+ currentWallet,
84
+ options,
85
+ );
86
+
87
+ const response = await wrappedFetch(input, init);
88
+
89
+ // Check if we got a 402 response (payment error)
90
+ if (response.status === 402) {
91
+ try {
92
+ const errorBody =
93
+ (await response.json()) as PaymentRequiredResult["responseBody"];
94
+
95
+ // If a modal handler is provided, show the modal and handle retry/cancel
96
+ if (showErrorModal) {
97
+ return new Promise<unknown>((resolve, reject) => {
98
+ showErrorModal({
99
+ errorData: errorBody,
100
+ onRetry: async () => {
101
+ // Retry the entire fetch+error handling logic recursively
102
+ try {
103
+ const result = await executeFetch();
104
+ resolve(result);
105
+ } catch (error) {
106
+ reject(error);
107
+ }
108
+ },
109
+ onCancel: () => {
110
+ reject(new Error("Payment cancelled by user"));
111
+ },
112
+ });
113
+ });
114
+ }
115
+
116
+ // If no modal handler, throw the error with details
117
+ throw new Error(
118
+ errorBody.errorMessage || `Payment failed: ${errorBody.error}`,
119
+ );
120
+ } catch (_parseError) {
121
+ // If we can't parse the error body, throw a generic error
122
+ throw new Error("Payment failed with status 402");
123
+ }
124
+ }
125
+
126
+ if (!response.ok) {
127
+ const errorText = await response.text();
128
+ throw new Error(
129
+ `Payment failed with status ${response.status} ${response.statusText} - ${errorText || "Unknown error"}`,
130
+ );
131
+ }
132
+
133
+ const parseAs = options?.parseAs ?? "json";
134
+ return parseResponse(response, parseAs);
135
+ };
136
+
137
+ // Start the fetch process
138
+ return executeFetch();
139
+ },
140
+ });
141
+
142
+ return {
143
+ fetchWithPayment: async (input: RequestInfo, init?: RequestInit) => {
144
+ return mutation.mutateAsync({ input, init });
145
+ },
146
+ ...mutation,
147
+ };
148
+ }
149
+
150
+ function parseResponse(response: Response, parseAs: "json" | "text" | "raw") {
151
+ if (parseAs === "json") {
152
+ return response.json();
153
+ } else if (parseAs === "text") {
154
+ return response.text();
155
+ } else if (parseAs === "raw") {
156
+ return response;
157
+ } else {
158
+ throw new Error(`Invalid parseAs option: ${parseAs}`);
159
+ }
160
+ }
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import type { ThirdwebClient } from "../../../../client/client.js";
4
+ import {
5
+ type UseFetchWithPaymentOptions,
6
+ useFetchWithPaymentCore,
7
+ } from "../../../core/hooks/x402/useFetchWithPaymentCore.js";
8
+
9
+ export type { UseFetchWithPaymentOptions };
10
+
11
+ /**
12
+ * A React hook that wraps the native fetch API to automatically handle 402 Payment Required responses
13
+ * using the x402 payment protocol with the currently connected wallet.
14
+ *
15
+ * This hook enables you to make API calls that require payment without manually handling the payment flow.
16
+ * Responses are automatically parsed as JSON by default (can be customized with `parseAs` option).
17
+ *
18
+ * When a 402 response is received, it will automatically:
19
+ * 1. Parse the payment requirements
20
+ * 2. Verify the payment amount is within the allowed maximum
21
+ * 3. Create a payment header using the connected wallet
22
+ * 4. Retry the request with the payment header
23
+ *
24
+ * Note: This is the React Native version which does not include modal UI.
25
+ * Payment errors will be thrown and should be handled by your application.
26
+ *
27
+ * @param client - The thirdweb client used to access RPC infrastructure
28
+ * @param options - Optional configuration for payment handling
29
+ * @param options.maxValue - The maximum allowed payment amount in base units
30
+ * @param options.paymentRequirementsSelector - Custom function to select payment requirements from available options
31
+ * @param options.parseAs - How to parse the response: "json" (default), "text", or "raw"
32
+ * @returns An object containing:
33
+ * - `fetchWithPayment`: Function to make fetch requests with automatic payment handling (returns parsed data)
34
+ * - `isPending`: Boolean indicating if a request is in progress
35
+ * - `error`: Any error that occurred during the request
36
+ * - `data`: The parsed response data (JSON by default, or based on `parseAs` option)
37
+ * - Other mutation properties from React Query
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * import { useFetchWithPayment } from "thirdweb/react";
42
+ * import { createThirdwebClient } from "thirdweb";
43
+ *
44
+ * const client = createThirdwebClient({ clientId: "your-client-id" });
45
+ *
46
+ * function MyComponent() {
47
+ * const { fetchWithPayment, isPending, error } = useFetchWithPayment(client);
48
+ *
49
+ * const handleApiCall = async () => {
50
+ * try {
51
+ * // Response is automatically parsed as JSON
52
+ * const data = await fetchWithPayment('https://api.example.com/paid-endpoint');
53
+ * console.log(data);
54
+ * } catch (err) {
55
+ * // Handle payment errors manually in React Native
56
+ * console.error("Payment failed:", err);
57
+ * }
58
+ * };
59
+ *
60
+ * return (
61
+ * <button onClick={handleApiCall} disabled={isPending}>
62
+ * {isPending ? 'Loading...' : 'Make Paid API Call'}
63
+ * </button>
64
+ * );
65
+ * }
66
+ * ```
67
+ *
68
+ * ### Customize response parsing
69
+ * ```tsx
70
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
71
+ * parseAs: "text", // Get response as text instead of JSON
72
+ * });
73
+ *
74
+ * const textData = await fetchWithPayment('https://api.example.com/endpoint');
75
+ * ```
76
+ *
77
+ * ### Customize payment options
78
+ * ```tsx
79
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
80
+ * maxValue: 5000000n, // 5 USDC in base units
81
+ * paymentRequirementsSelector: (requirements) => {
82
+ * // Custom logic to select preferred payment method
83
+ * return requirements[0];
84
+ * }
85
+ * });
86
+ * ```
87
+ *
88
+ * @x402
89
+ */
90
+ export function useFetchWithPayment(
91
+ client: ThirdwebClient,
92
+ options?: UseFetchWithPaymentOptions,
93
+ ) {
94
+ // Native version doesn't show modal, errors bubble up naturally
95
+ return useFetchWithPaymentCore(client, options);
96
+ }
@@ -0,0 +1,238 @@
1
+ "use client";
2
+
3
+ import { useContext } from "react";
4
+ import type { ThirdwebClient } from "../../../../client/client.js";
5
+ import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
6
+ import type { Theme } from "../../../core/design-system/index.js";
7
+ import {
8
+ type UseFetchWithPaymentOptions,
9
+ useFetchWithPaymentCore,
10
+ } from "../../../core/hooks/x402/useFetchWithPaymentCore.js";
11
+ import { SetRootElementContext } from "../../../core/providers/RootElementContext.js";
12
+ import type { BuyWidgetProps } from "../../ui/Bridge/BuyWidget.js";
13
+ import {
14
+ type UseConnectModalOptions,
15
+ useConnectModal,
16
+ } from "../../ui/ConnectWallet/useConnectModal.js";
17
+ import { PaymentErrorModal } from "../../ui/x402/PaymentErrorModal.js";
18
+ import { SignInRequiredModal } from "../../ui/x402/SignInRequiredModal.js";
19
+
20
+ export type { UseFetchWithPaymentOptions };
21
+
22
+ type UseFetchWithPaymentConfig = UseFetchWithPaymentOptions & {
23
+ /**
24
+ * Whether to show the UI for connection, funding or payment retries.
25
+ * If false, no UI will be shown and errors will have to be handled manually.
26
+ * @default true
27
+ */
28
+ uiEnabled?: boolean;
29
+ /**
30
+ * Theme for the payment error modal
31
+ * @default "dark"
32
+ */
33
+ theme?: Theme | "light" | "dark";
34
+ /**
35
+ * Options to customize the BuyWidget that appears when the user needs to fund their wallet.
36
+ * These options will be merged with default values.
37
+ */
38
+ fundWalletOptions?: Partial<
39
+ Omit<
40
+ BuyWidgetProps,
41
+ "client" | "chain" | "tokenAddress" | "onSuccess" | "onCancel" | "theme"
42
+ >
43
+ >;
44
+ /**
45
+ * Options to customize the ConnectModal that appears when the user needs to sign in.
46
+ * These options will be merged with the client, theme, and chain from the hook.
47
+ */
48
+ connectOptions?: Omit<UseConnectModalOptions, "client" | "theme">;
49
+ };
50
+
51
+ /**
52
+ * A React hook that wraps the native fetch API to automatically handle 402 Payment Required responses
53
+ * using the x402 payment protocol with the currently connected wallet.
54
+ *
55
+ * This hook enables you to make API calls that require payment without manually handling the payment flow.
56
+ * Responses are automatically parsed as JSON by default (can be customized with `parseAs` option).
57
+ *
58
+ * When a 402 response is received, it will automatically:
59
+ * 1. Parse the payment requirements
60
+ * 2. Verify the payment amount is within the allowed maximum
61
+ * 3. Create a payment header using the connected wallet
62
+ * 4. Retry the request with the payment header
63
+ *
64
+ * If payment fails (e.g. insufficient funds), a modal will be shown to help the user resolve the issue.
65
+ * If no wallet is connected, a sign-in modal will be shown to connect a wallet.
66
+ *
67
+ * @param client - The thirdweb client used to access RPC infrastructure
68
+ * @param options - Optional configuration for payment handling
69
+ * @param options.maxValue - The maximum allowed payment amount in base units
70
+ * @param options.paymentRequirementsSelector - Custom function to select payment requirements from available options
71
+ * @param options.parseAs - How to parse the response: "json" (default), "text", or "raw"
72
+ * @param options.uiEnabled - Whether to show the UI for connection, funding or payment retries (defaults to true). Set to false to handle errors yourself
73
+ * @param options.theme - Theme for the payment error modal (defaults to "dark")
74
+ * @param options.fundWalletOptions - Customize the BuyWidget shown when user needs to fund their wallet
75
+ * @param options.connectOptions - Customize the ConnectModal shown when user needs to sign in
76
+ * @returns An object containing:
77
+ * - `fetchWithPayment`: Function to make fetch requests with automatic payment handling (returns parsed data)
78
+ * - `isPending`: Boolean indicating if a request is in progress
79
+ * - `error`: Any error that occurred during the request
80
+ * - `data`: The parsed response data (JSON by default, or based on `parseAs` option)
81
+ * - Other mutation properties from React Query
82
+ *
83
+ * @example
84
+ * ```tsx
85
+ * import { useFetchWithPayment } from "thirdweb/react";
86
+ * import { createThirdwebClient } from "thirdweb";
87
+ *
88
+ * const client = createThirdwebClient({ clientId: "your-client-id" });
89
+ *
90
+ * function MyComponent() {
91
+ * const { fetchWithPayment, isPending } = useFetchWithPayment(client);
92
+ *
93
+ * const handleApiCall = async () => {
94
+ * // Response is automatically parsed as JSON
95
+ * const data = await fetchWithPayment('https://api.example.com/paid-endpoint');
96
+ * console.log(data);
97
+ * };
98
+ *
99
+ * return (
100
+ * <button onClick={handleApiCall} disabled={isPending}>
101
+ * {isPending ? 'Loading...' : 'Make Paid API Call'}
102
+ * </button>
103
+ * );
104
+ * }
105
+ * ```
106
+ *
107
+ * ### Customize response parsing
108
+ * ```tsx
109
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
110
+ * parseAs: "text", // Get response as text instead of JSON
111
+ * });
112
+ *
113
+ * const textData = await fetchWithPayment('https://api.example.com/endpoint');
114
+ * ```
115
+ *
116
+ * ### Customize payment options
117
+ * ```tsx
118
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
119
+ * maxValue: 5000000n, // 5 USDC in base units
120
+ * theme: "light",
121
+ * paymentRequirementsSelector: (requirements) => {
122
+ * // Custom logic to select preferred payment method
123
+ * return requirements[0];
124
+ * }
125
+ * });
126
+ * ```
127
+ *
128
+ * ### Customize the fund wallet widget
129
+ * ```tsx
130
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
131
+ * fundWalletOptions: {
132
+ * title: "Add Funds",
133
+ * description: "You need more tokens to complete this payment",
134
+ * buttonLabel: "Get Tokens",
135
+ * }
136
+ * });
137
+ * ```
138
+ *
139
+ * ### Customize the connect modal
140
+ * ```tsx
141
+ * const { fetchWithPayment } = useFetchWithPayment(client, {
142
+ * connectOptions: {
143
+ * wallets: [inAppWallet(), createWallet("io.metamask")],
144
+ * title: "Sign in to continue",
145
+ * }
146
+ * });
147
+ * ```
148
+ *
149
+ * ### Disable the UI and handle errors yourself
150
+ * ```tsx
151
+ * const { fetchWithPayment, error } = useFetchWithPayment(client, {
152
+ * uiEnabled: false,
153
+ * });
154
+ *
155
+ * // Handle the error manually
156
+ * if (error) {
157
+ * console.error("Payment failed:", error);
158
+ * }
159
+ * ```
160
+ *
161
+ * @x402
162
+ */
163
+ export function useFetchWithPayment(
164
+ client: ThirdwebClient,
165
+ options?: UseFetchWithPaymentConfig,
166
+ ) {
167
+ const setRootEl = useContext(SetRootElementContext);
168
+ const { connect } = useConnectModal();
169
+ const theme = options?.theme || "dark";
170
+ const showModal = options?.uiEnabled !== false; // Default to true
171
+
172
+ const showErrorModal = showModal
173
+ ? (data: {
174
+ errorData: Parameters<typeof PaymentErrorModal>[0]["errorData"];
175
+ onRetry: () => void;
176
+ onCancel: () => void;
177
+ }) => {
178
+ setRootEl(
179
+ <PaymentErrorModal
180
+ client={client}
181
+ errorData={data.errorData}
182
+ onCancel={() => {
183
+ setRootEl(null);
184
+ data.onCancel();
185
+ }}
186
+ onRetry={() => {
187
+ setRootEl(null);
188
+ data.onRetry();
189
+ }}
190
+ theme={theme}
191
+ fundWalletOptions={options?.fundWalletOptions}
192
+ paymentRequirementsSelector={options?.paymentRequirementsSelector}
193
+ />,
194
+ );
195
+ }
196
+ : undefined;
197
+
198
+ const showConnectModal = showModal
199
+ ? (data: { onConnect: (wallet: Wallet) => void; onCancel: () => void }) => {
200
+ // First, show the SignInRequiredModal
201
+ setRootEl(
202
+ <SignInRequiredModal
203
+ theme={theme}
204
+ onSignIn={async () => {
205
+ // Close the SignInRequiredModal
206
+ setRootEl(null);
207
+
208
+ // Open the ConnectModal
209
+ try {
210
+ const connectedWallet = await connect({
211
+ client,
212
+ theme,
213
+ ...options?.connectOptions,
214
+ });
215
+
216
+ // On successful connection, trigger onConnect callback with the wallet
217
+ data.onConnect(connectedWallet);
218
+ } catch (_error) {
219
+ // User cancelled the connection
220
+ data.onCancel();
221
+ }
222
+ }}
223
+ onCancel={() => {
224
+ setRootEl(null);
225
+ data.onCancel();
226
+ }}
227
+ />,
228
+ );
229
+ }
230
+ : undefined;
231
+
232
+ return useFetchWithPaymentCore(
233
+ client,
234
+ options,
235
+ showErrorModal,
236
+ showConnectModal,
237
+ );
238
+ }
@@ -17,7 +17,7 @@ export const ScreenBottomContainer = /* @__PURE__ */ StyledDiv((_) => {
17
17
  borderTop: `1px solid ${theme.colors.separatorLine}`,
18
18
  display: "flex",
19
19
  flexDirection: "column",
20
- gap: spacing.lg,
20
+ gap: spacing.md,
21
21
  padding: spacing.lg,
22
22
  };
23
23
  });