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,261 @@
1
+ "use client";
2
+ import { useState } from "react";
3
+ import { getCachedChain } from "../../../../chains/utils.js";
4
+ import type { ThirdwebClient } from "../../../../client/client.js";
5
+ import {
6
+ extractEvmChainId,
7
+ networkToCaip2ChainId,
8
+ type RequestedPaymentRequirements,
9
+ } from "../../../../x402/schemas.js";
10
+ import type { PaymentRequiredResult } from "../../../../x402/types.js";
11
+ import { CustomThemeProvider } from "../../../core/design-system/CustomThemeProvider.js";
12
+ import type { Theme } from "../../../core/design-system/index.js";
13
+ import { spacing } from "../../../core/design-system/index.js";
14
+ import { useActiveWallet } from "../../../core/hooks/wallets/useActiveWallet.js";
15
+ import { BuyWidget, type BuyWidgetProps } from "../Bridge/BuyWidget.js";
16
+ import {
17
+ Container,
18
+ ModalHeader,
19
+ ScreenBottomContainer,
20
+ } from "../components/basic.js";
21
+ import { Button } from "../components/buttons.js";
22
+ import { Modal } from "../components/Modal.js";
23
+ import { Text } from "../components/text.js";
24
+
25
+ type PaymentErrorModalProps = {
26
+ client: ThirdwebClient;
27
+ errorData: PaymentRequiredResult["responseBody"];
28
+ onRetry: () => void;
29
+ onCancel: () => void;
30
+ theme: Theme | "light" | "dark";
31
+ fundWalletOptions?: Partial<
32
+ Omit<
33
+ BuyWidgetProps,
34
+ | "client"
35
+ | "chain"
36
+ | "tokenAddress"
37
+ | "amount"
38
+ | "onSuccess"
39
+ | "onCancel"
40
+ | "theme"
41
+ >
42
+ >;
43
+ paymentRequirementsSelector?: (
44
+ paymentRequirements: RequestedPaymentRequirements[],
45
+ ) => RequestedPaymentRequirements | undefined;
46
+ };
47
+
48
+ type Screen = "error" | "buy-widget";
49
+
50
+ /**
51
+ * @internal
52
+ */
53
+ export function PaymentErrorModal(props: PaymentErrorModalProps) {
54
+ const {
55
+ client,
56
+ errorData,
57
+ onRetry,
58
+ onCancel,
59
+ theme,
60
+ fundWalletOptions,
61
+ paymentRequirementsSelector,
62
+ } = props;
63
+ const [screen, setScreen] = useState<Screen>("error");
64
+ const isInsufficientFunds = errorData.error === "insufficient_funds";
65
+ const wallet = useActiveWallet();
66
+
67
+ // Extract chain and token info from errorData for BuyWidget
68
+ const getBuyWidgetConfig = () => {
69
+ if (!errorData.accepts || errorData.accepts.length === 0) {
70
+ return null;
71
+ }
72
+
73
+ // Get payment requirements from errorData
74
+ const parsedPaymentRequirements = errorData.accepts;
75
+
76
+ // Get the current chain from wallet
77
+ const currentChain = wallet?.getChain();
78
+ const currentChainId = currentChain?.id;
79
+
80
+ // Select payment requirement using the same logic as wrapFetchWithPayment
81
+ const selectedRequirement = paymentRequirementsSelector
82
+ ? paymentRequirementsSelector(parsedPaymentRequirements)
83
+ : defaultPaymentRequirementsSelector(
84
+ parsedPaymentRequirements,
85
+ currentChainId,
86
+ "exact",
87
+ errorData.error,
88
+ );
89
+
90
+ if (!selectedRequirement) return null;
91
+
92
+ const caip2ChainId = networkToCaip2ChainId(selectedRequirement.network);
93
+ const chainId = extractEvmChainId(caip2ChainId);
94
+
95
+ if (!chainId) return null;
96
+
97
+ const chain = getCachedChain(chainId);
98
+ const tokenAddress = selectedRequirement.asset as `0x${string}`;
99
+
100
+ return {
101
+ chain,
102
+ tokenAddress,
103
+ amount: undefined,
104
+ };
105
+ };
106
+
107
+ const buyWidgetConfig = isInsufficientFunds ? getBuyWidgetConfig() : null;
108
+
109
+ if (screen === "buy-widget" && buyWidgetConfig) {
110
+ return (
111
+ <CustomThemeProvider theme={theme}>
112
+ <Modal
113
+ className="tw-payment-error-modal"
114
+ hideCloseIcon={false}
115
+ open={true}
116
+ setOpen={(open) => {
117
+ if (!open) {
118
+ onCancel();
119
+ }
120
+ }}
121
+ size="compact"
122
+ title="Top up your wallet"
123
+ crossContainerStyles={{
124
+ position: "absolute",
125
+ right: spacing.lg,
126
+ top: spacing.lg,
127
+ zIndex: 1,
128
+ }}
129
+ >
130
+ {/* BuyWidget without padding */}
131
+ <BuyWidget
132
+ client={client}
133
+ theme={theme}
134
+ chain={buyWidgetConfig.chain}
135
+ tokenAddress={buyWidgetConfig.tokenAddress}
136
+ amount={buyWidgetConfig.amount}
137
+ style={{
138
+ border: "none",
139
+ width: "100%",
140
+ }}
141
+ buttonLabel="Continue"
142
+ title="Get funds"
143
+ description="Top up your wallet to complete your payment."
144
+ {...fundWalletOptions}
145
+ onSuccess={() => {
146
+ // Close modal and retry the payment
147
+ onRetry();
148
+ }}
149
+ onCancel={() => {
150
+ // Go back to error screen
151
+ setScreen("error");
152
+ }}
153
+ />
154
+ </Modal>
155
+ </CustomThemeProvider>
156
+ );
157
+ }
158
+
159
+ // Error screen (default)
160
+ return (
161
+ <CustomThemeProvider theme={theme}>
162
+ <Modal
163
+ className="tw-payment-error-modal"
164
+ hideCloseIcon={true}
165
+ open={true}
166
+ setOpen={(open) => {
167
+ if (!open) {
168
+ onCancel();
169
+ }
170
+ }}
171
+ size="compact"
172
+ title="Payment Failed"
173
+ >
174
+ <Container p="lg">
175
+ <ModalHeader title="Payment failed" />
176
+
177
+ <Container
178
+ flex="column"
179
+ gap="lg"
180
+ style={{
181
+ paddingTop: spacing.lg,
182
+ }}
183
+ >
184
+ {/* Error Message */}
185
+ <Text
186
+ size="sm"
187
+ style={{
188
+ color: "inherit",
189
+ lineHeight: 1.5,
190
+ }}
191
+ >
192
+ {isInsufficientFunds
193
+ ? "Your wallet doesn't have enough funds to complete this payment. Please top up your wallet and try again."
194
+ : errorData.errorMessage ||
195
+ "An error occurred while processing your payment."}
196
+ </Text>
197
+ </Container>
198
+ </Container>
199
+
200
+ {/* Action Buttons */}
201
+ <ScreenBottomContainer>
202
+ {isInsufficientFunds && buyWidgetConfig ? (
203
+ <>
204
+ <Button
205
+ fullWidth
206
+ gap="xs"
207
+ onClick={() => setScreen("buy-widget")}
208
+ variant="accent"
209
+ >
210
+ Top up your wallet
211
+ </Button>
212
+ <Button fullWidth gap="xs" onClick={onCancel} variant="secondary">
213
+ Cancel
214
+ </Button>
215
+ </>
216
+ ) : (
217
+ <>
218
+ <Button fullWidth gap="xs" onClick={onRetry} variant="accent">
219
+ Try Again
220
+ </Button>
221
+ <Button fullWidth gap="xs" onClick={onCancel} variant="secondary">
222
+ Cancel
223
+ </Button>
224
+ </>
225
+ )}
226
+ </ScreenBottomContainer>
227
+ </Modal>
228
+ </CustomThemeProvider>
229
+ );
230
+ }
231
+
232
+ // Default payment requirement selector - same logic as in fetchWithPayment.ts
233
+ function defaultPaymentRequirementsSelector(
234
+ paymentRequirements: RequestedPaymentRequirements[],
235
+ chainId: number | undefined,
236
+ scheme: "exact",
237
+ _error?: string,
238
+ ): RequestedPaymentRequirements | undefined {
239
+ if (!paymentRequirements.length) {
240
+ return undefined;
241
+ }
242
+
243
+ // If we have a chainId, find matching payment requirements
244
+ if (chainId !== undefined) {
245
+ const matchingPaymentRequirements = paymentRequirements.find(
246
+ (x) =>
247
+ extractEvmChainId(networkToCaip2ChainId(x.network)) === chainId &&
248
+ x.scheme === scheme,
249
+ );
250
+
251
+ if (matchingPaymentRequirements) {
252
+ return matchingPaymentRequirements;
253
+ }
254
+ }
255
+
256
+ // If no matching payment requirements, use the first payment requirement
257
+ const firstPaymentRequirement = paymentRequirements.find(
258
+ (x) => x.scheme === scheme,
259
+ );
260
+ return firstPaymentRequirement;
261
+ }
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { CustomThemeProvider } from "../../../core/design-system/CustomThemeProvider.js";
3
+ import type { Theme } from "../../../core/design-system/index.js";
4
+ import { spacing } from "../../../core/design-system/index.js";
5
+ import {
6
+ Container,
7
+ ModalHeader,
8
+ ScreenBottomContainer,
9
+ } from "../components/basic.js";
10
+ import { Button } from "../components/buttons.js";
11
+ import { Modal } from "../components/Modal.js";
12
+ import { Text } from "../components/text.js";
13
+
14
+ type SignInRequiredModalProps = {
15
+ theme: Theme | "light" | "dark";
16
+ onSignIn: () => void;
17
+ onCancel: () => void;
18
+ };
19
+
20
+ /**
21
+ * @internal
22
+ */
23
+ export function SignInRequiredModal(props: SignInRequiredModalProps) {
24
+ const { theme, onSignIn, onCancel } = props;
25
+
26
+ return (
27
+ <CustomThemeProvider theme={theme}>
28
+ <Modal
29
+ className="tw-signin-required-modal"
30
+ hideCloseIcon={true}
31
+ open={true}
32
+ setOpen={(open) => {
33
+ if (!open) {
34
+ onCancel();
35
+ }
36
+ }}
37
+ size="compact"
38
+ title="Sign in required"
39
+ >
40
+ <Container p="lg">
41
+ <ModalHeader title="Sign in required" />
42
+
43
+ <Container
44
+ flex="column"
45
+ gap="lg"
46
+ style={{
47
+ paddingTop: spacing.lg,
48
+ }}
49
+ >
50
+ {/* Description */}
51
+ <Text
52
+ size="sm"
53
+ style={{
54
+ color: "inherit",
55
+ lineHeight: 1.5,
56
+ }}
57
+ >
58
+ Account required to complete payment, please sign in to continue.
59
+ </Text>
60
+ </Container>
61
+ </Container>
62
+
63
+ {/* Action Buttons */}
64
+ <ScreenBottomContainer>
65
+ <Button fullWidth gap="xs" onClick={onSignIn} variant="accent">
66
+ Sign in
67
+ </Button>
68
+ <Button fullWidth gap="xs" onClick={onCancel} variant="secondary">
69
+ Cancel
70
+ </Button>
71
+ </ScreenBottomContainer>
72
+ </Modal>
73
+ </CustomThemeProvider>
74
+ );
75
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.112.4";
1
+ export const version = "5.113.0";
@@ -474,6 +474,16 @@ async function switchChainCoinbaseWalletSDK(
474
474
  provider: ProviderInterface,
475
475
  chain: Chain,
476
476
  ) {
477
+ // check if chain is already connected
478
+ const connectedChainId = (await provider.request({
479
+ method: "eth_chainId",
480
+ })) as string | number;
481
+ const connectedChain = getCachedChain(normalizeChainId(connectedChainId));
482
+ if (connectedChain?.id === chain.id) {
483
+ // chain is already connected, no need to switch
484
+ return;
485
+ }
486
+
477
487
  const chainIdHex = numberToHex(chain.id);
478
488
 
479
489
  try {
@@ -107,7 +107,7 @@ const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
107
107
 
108
108
  * ```
109
109
  *
110
- * @bridge x402
110
+ * @x402
111
111
  */
112
112
  export function facilitator(
113
113
  config: ThirdwebX402FacilitatorConfig,
@@ -23,7 +23,7 @@ import { createPaymentHeader } from "./sign.js";
23
23
  * @param fetch - The fetch function to wrap (typically globalThis.fetch)
24
24
  * @param client - The thirdweb client used to access RPC infrastructure
25
25
  * @param wallet - The wallet used to sign payment messages
26
- * @param maxValue - The maximum allowed payment amount in base units (defaults to 1 USDC)
26
+ * @param maxValue - The maximum allowed payment amount in base units
27
27
  * @returns A wrapped fetch function that handles 402 responses automatically
28
28
  *
29
29
  * @example
@@ -46,13 +46,18 @@ import { createPaymentHeader } from "./sign.js";
46
46
  * @throws {Error} If a payment has already been attempted for this request
47
47
  * @throws {Error} If there's an error creating the payment header
48
48
  *
49
- * @bridge x402
49
+ * @x402
50
50
  */
51
51
  export function wrapFetchWithPayment(
52
52
  fetch: typeof globalThis.fetch,
53
53
  client: ThirdwebClient,
54
54
  wallet: Wallet,
55
- maxValue?: bigint,
55
+ options?: {
56
+ maxValue?: bigint;
57
+ paymentRequirementsSelector?: (
58
+ paymentRequirements: RequestedPaymentRequirements[],
59
+ ) => RequestedPaymentRequirements | undefined;
60
+ },
56
61
  ) {
57
62
  return async (input: RequestInfo, init?: RequestInit) => {
58
63
  const response = await fetch(input, init);
@@ -78,12 +83,14 @@ export function wrapFetchWithPayment(
78
83
  "Wallet not connected. Please connect your wallet to continue.",
79
84
  );
80
85
  }
81
- const selectedPaymentRequirements = defaultPaymentRequirementsSelector(
82
- parsedPaymentRequirements,
83
- chain.id,
84
- "exact",
85
- error,
86
- );
86
+ const selectedPaymentRequirements = options?.paymentRequirementsSelector
87
+ ? options.paymentRequirementsSelector(parsedPaymentRequirements)
88
+ : defaultPaymentRequirementsSelector(
89
+ parsedPaymentRequirements,
90
+ chain.id,
91
+ "exact",
92
+ error,
93
+ );
87
94
 
88
95
  if (!selectedPaymentRequirements) {
89
96
  throw new Error(
@@ -92,11 +99,11 @@ export function wrapFetchWithPayment(
92
99
  }
93
100
 
94
101
  if (
95
- maxValue &&
96
- BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue
102
+ options?.maxValue &&
103
+ BigInt(selectedPaymentRequirements.maxAmountRequired) > options.maxValue
97
104
  ) {
98
105
  throw new Error(
99
- `Payment amount exceeds maximum allowed (currently set to ${maxValue} in base units)`,
106
+ `Payment amount exceeds maximum allowed (currently set to ${options.maxValue} in base units)`,
100
107
  );
101
108
  }
102
109
 
@@ -122,7 +122,7 @@ import {
122
122
  *
123
123
  * @public
124
124
  * @beta
125
- * @bridge x402
125
+ * @x402
126
126
  */
127
127
  export async function settlePayment(
128
128
  args: SettlePaymentArgs,
@@ -70,7 +70,7 @@ import {
70
70
  *
71
71
  * @public
72
72
  * @beta
73
- * @bridge x402
73
+ * @x402
74
74
  */
75
75
  export async function verifyPayment(
76
76
  args: PaymentArgs,