thirdweb 5.106.0 → 5.107.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 (51) hide show
  1. package/dist/cjs/exports/x402.js +8 -0
  2. package/dist/cjs/exports/x402.js.map +1 -0
  3. package/dist/cjs/react/web/ui/Bridge/BuyWidget.js +1 -1
  4. package/dist/cjs/react/web/ui/Bridge/CheckoutWidget.js +1 -1
  5. package/dist/cjs/react/web/ui/Bridge/TransactionWidget.js +1 -1
  6. package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js +10 -3
  7. package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
  8. package/dist/cjs/version.js +1 -1
  9. package/dist/cjs/wallets/smart/lib/bundler.js +3 -0
  10. package/dist/cjs/wallets/smart/lib/bundler.js.map +1 -1
  11. package/dist/cjs/x402/facilitator.js +76 -0
  12. package/dist/cjs/x402/facilitator.js.map +1 -0
  13. package/dist/cjs/x402/fetchWithPayment.js +121 -0
  14. package/dist/cjs/x402/fetchWithPayment.js.map +1 -0
  15. package/dist/esm/exports/x402.js +3 -0
  16. package/dist/esm/exports/x402.js.map +1 -0
  17. package/dist/esm/react/web/ui/Bridge/BuyWidget.js +1 -1
  18. package/dist/esm/react/web/ui/Bridge/CheckoutWidget.js +1 -1
  19. package/dist/esm/react/web/ui/Bridge/TransactionWidget.js +1 -1
  20. package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js +10 -3
  21. package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
  22. package/dist/esm/version.js +1 -1
  23. package/dist/esm/wallets/smart/lib/bundler.js +3 -0
  24. package/dist/esm/wallets/smart/lib/bundler.js.map +1 -1
  25. package/dist/esm/x402/facilitator.js +73 -0
  26. package/dist/esm/x402/facilitator.js.map +1 -0
  27. package/dist/esm/x402/fetchWithPayment.js +118 -0
  28. package/dist/esm/x402/fetchWithPayment.js.map +1 -0
  29. package/dist/types/exports/x402.d.ts +3 -0
  30. package/dist/types/exports/x402.d.ts.map +1 -0
  31. package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts +1 -1
  32. package/dist/types/react/web/ui/Bridge/CheckoutWidget.d.ts +1 -1
  33. package/dist/types/react/web/ui/Bridge/TransactionWidget.d.ts +1 -1
  34. package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts +8 -0
  35. package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts.map +1 -1
  36. package/dist/types/version.d.ts +1 -1
  37. package/dist/types/wallets/smart/lib/bundler.d.ts.map +1 -1
  38. package/dist/types/x402/facilitator.d.ts +48 -0
  39. package/dist/types/x402/facilitator.d.ts.map +1 -0
  40. package/dist/types/x402/fetchWithPayment.d.ts +43 -0
  41. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -0
  42. package/package.json +12 -3
  43. package/src/exports/x402.ts +5 -0
  44. package/src/react/web/ui/Bridge/BuyWidget.tsx +1 -1
  45. package/src/react/web/ui/Bridge/CheckoutWidget.tsx +1 -1
  46. package/src/react/web/ui/Bridge/TransactionWidget.tsx +1 -1
  47. package/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx +24 -3
  48. package/src/version.ts +1 -1
  49. package/src/wallets/smart/lib/bundler.ts +4 -0
  50. package/src/x402/facilitator.ts +87 -0
  51. package/src/x402/fetchWithPayment.ts +173 -0
@@ -0,0 +1,173 @@
1
+ import { createPaymentHeader } from "x402/client";
2
+ import {
3
+ ChainIdToNetwork,
4
+ EvmNetworkToChainId,
5
+ type PaymentRequirements,
6
+ PaymentRequirementsSchema,
7
+ type Signer,
8
+ } from "x402/types";
9
+ import { viemAdapter } from "../adapters/viem.js";
10
+ import { getCachedChain } from "../chains/utils.js";
11
+ import type { ThirdwebClient } from "../client/client.js";
12
+ import type { Wallet } from "../wallets/interfaces/wallet.js";
13
+
14
+ /**
15
+ * Enables the payment of APIs using the x402 payment protocol.
16
+ *
17
+ * This function wraps the native fetch API to automatically handle 402 Payment Required responses
18
+ * by creating and sending a payment header. It will:
19
+ * 1. Make the initial request
20
+ * 2. If a 402 response is received, parse the payment requirements
21
+ * 3. Verify the payment amount is within the allowed maximum
22
+ * 4. Create a payment header using the provided wallet client
23
+ * 5. Retry the request with the payment header
24
+ *
25
+ * @param fetch - The fetch function to wrap (typically globalThis.fetch)
26
+ * @param client - The thirdweb client used to access RPC infrastructure
27
+ * @param wallet - The wallet used to sign payment messages
28
+ * @param maxValue - The maximum allowed payment amount in base units (defaults to 1 USDC)
29
+ * @returns A wrapped fetch function that handles 402 responses automatically
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { wrapFetchWithPayment } from "thirdweb/x402";
34
+ * import { createThirdwebClient } from "thirdweb";
35
+ * import { createWallet } from "thirdweb/wallets";
36
+ *
37
+ * const client = createThirdwebClient({ clientId: "your-client-id" });
38
+ * const wallet = createWallet("io.metamask");
39
+ * await wallet.connect({ client })
40
+ *
41
+ * const fetchWithPay = wrapFetchWithPayment(fetch, client, wallet);
42
+ *
43
+ * // Make a request that may require payment
44
+ * const response = await fetchWithPay('https://api.example.com/paid-endpoint');
45
+ * ```
46
+ *
47
+ * @throws {Error} If the payment amount exceeds the maximum allowed value
48
+ * @throws {Error} If a payment has already been attempted for this request
49
+ * @throws {Error} If there's an error creating the payment header
50
+ *
51
+ * @bridge x402
52
+ */
53
+ export function wrapFetchWithPayment(
54
+ fetch: typeof globalThis.fetch,
55
+ client: ThirdwebClient,
56
+ wallet: Wallet,
57
+ maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
58
+ ) {
59
+ return async (input: RequestInfo, init?: RequestInit) => {
60
+ const response = await fetch(input, init);
61
+
62
+ if (response.status !== 402) {
63
+ return response;
64
+ }
65
+
66
+ const { x402Version, accepts } = (await response.json()) as {
67
+ x402Version: number;
68
+ accepts: unknown[];
69
+ };
70
+ const parsedPaymentRequirements = accepts
71
+ .map((x) => PaymentRequirementsSchema.parse(x))
72
+ .filter((x) => x.scheme === "exact"); // TODO (402): accept other schemes
73
+
74
+ const account = wallet.getAccount();
75
+ let chain = wallet.getChain();
76
+
77
+ if (!account || !chain) {
78
+ throw new Error(
79
+ "Wallet not connected. Please connect your wallet to continue.",
80
+ );
81
+ }
82
+ const selectedPaymentRequirements = defaultPaymentRequirementsSelector(
83
+ parsedPaymentRequirements,
84
+ chain.id,
85
+ "exact",
86
+ );
87
+
88
+ if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
89
+ throw new Error("Payment amount exceeds maximum allowed");
90
+ }
91
+
92
+ const paymentChainId = EvmNetworkToChainId.get(
93
+ selectedPaymentRequirements.network,
94
+ );
95
+
96
+ if (!paymentChainId) {
97
+ throw new Error(
98
+ `No chain found for the selected payment requirement: ${selectedPaymentRequirements.network}`,
99
+ );
100
+ }
101
+
102
+ // switch to the payment chain if it's not the current chain
103
+ if (paymentChainId !== chain.id) {
104
+ await wallet.switchChain(getCachedChain(paymentChainId));
105
+ chain = wallet.getChain();
106
+ if (!chain) {
107
+ throw new Error(`Failed to switch chain (${paymentChainId})`);
108
+ }
109
+ }
110
+
111
+ const walletClient = viemAdapter.wallet.toViem({
112
+ wallet: wallet,
113
+ chain,
114
+ client,
115
+ }) as Signer;
116
+
117
+ const paymentHeader = await createPaymentHeader(
118
+ walletClient,
119
+ x402Version,
120
+ selectedPaymentRequirements,
121
+ );
122
+
123
+ const initParams = init || {};
124
+
125
+ if ((initParams as { __is402Retry?: boolean }).__is402Retry) {
126
+ throw new Error("Payment already attempted");
127
+ }
128
+
129
+ const newInit = {
130
+ ...initParams,
131
+ headers: {
132
+ ...(initParams.headers || {}),
133
+ "X-PAYMENT": paymentHeader,
134
+ "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE",
135
+ },
136
+ __is402Retry: true,
137
+ };
138
+
139
+ const secondResponse = await fetch(input, newInit);
140
+ return secondResponse;
141
+ };
142
+ }
143
+
144
+ function defaultPaymentRequirementsSelector(
145
+ paymentRequirements: PaymentRequirements[],
146
+ chainId: number,
147
+ scheme: "exact",
148
+ ) {
149
+ if (!paymentRequirements.length) {
150
+ throw new Error(
151
+ "No valid payment requirements found in server 402 response",
152
+ );
153
+ }
154
+ const currentWalletNetwork = ChainIdToNetwork[chainId];
155
+ // find the payment requirements matching the connected wallet chain
156
+ const matchingPaymentRequirements = paymentRequirements.find(
157
+ (x) => x.network === currentWalletNetwork && x.scheme === scheme,
158
+ );
159
+
160
+ if (matchingPaymentRequirements) {
161
+ return matchingPaymentRequirements;
162
+ } else {
163
+ // if no matching payment requirements, use the first payment requirement
164
+ // and switch the wallet to that chain
165
+ const firstPaymentRequirement = paymentRequirements.find(
166
+ (x) => x.scheme === scheme,
167
+ );
168
+ if (!firstPaymentRequirement) {
169
+ throw new Error("No suitable payment requirements found");
170
+ }
171
+ return firstPaymentRequirement;
172
+ }
173
+ }