thirdweb 5.108.4 → 5.108.5

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 (61) hide show
  1. package/dist/cjs/exports/x402.js.map +1 -1
  2. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js +9 -5
  3. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
  4. package/dist/cjs/react/web/ui/Bridge/swap-widget/use-tokens.js +4 -2
  5. package/dist/cjs/react/web/ui/Bridge/swap-widget/use-tokens.js.map +1 -1
  6. package/dist/cjs/version.js +1 -1
  7. package/dist/cjs/x402/common.js +60 -4
  8. package/dist/cjs/x402/common.js.map +1 -1
  9. package/dist/cjs/x402/facilitator.js +29 -5
  10. package/dist/cjs/x402/facilitator.js.map +1 -1
  11. package/dist/cjs/x402/fetchWithPayment.js +4 -4
  12. package/dist/cjs/x402/fetchWithPayment.js.map +1 -1
  13. package/dist/cjs/x402/schemas.js +29 -1
  14. package/dist/cjs/x402/schemas.js.map +1 -1
  15. package/dist/cjs/x402/settle-payment.js +2 -1
  16. package/dist/cjs/x402/settle-payment.js.map +1 -1
  17. package/dist/cjs/x402/types.js.map +1 -1
  18. package/dist/esm/exports/x402.js.map +1 -1
  19. package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js +9 -5
  20. package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
  21. package/dist/esm/react/web/ui/Bridge/swap-widget/use-tokens.js +4 -2
  22. package/dist/esm/react/web/ui/Bridge/swap-widget/use-tokens.js.map +1 -1
  23. package/dist/esm/version.js +1 -1
  24. package/dist/esm/x402/common.js +60 -4
  25. package/dist/esm/x402/common.js.map +1 -1
  26. package/dist/esm/x402/facilitator.js +29 -5
  27. package/dist/esm/x402/facilitator.js.map +1 -1
  28. package/dist/esm/x402/fetchWithPayment.js +4 -4
  29. package/dist/esm/x402/fetchWithPayment.js.map +1 -1
  30. package/dist/esm/x402/schemas.js +29 -1
  31. package/dist/esm/x402/schemas.js.map +1 -1
  32. package/dist/esm/x402/settle-payment.js +2 -1
  33. package/dist/esm/x402/settle-payment.js.map +1 -1
  34. package/dist/esm/x402/types.js.map +1 -1
  35. package/dist/scripts/bridge-widget.js +84 -84
  36. package/dist/types/exports/x402.d.ts +2 -2
  37. package/dist/types/exports/x402.d.ts.map +1 -1
  38. package/dist/types/react/web/ui/Bridge/swap-widget/select-token-ui.d.ts.map +1 -1
  39. package/dist/types/react/web/ui/Bridge/swap-widget/use-tokens.d.ts.map +1 -1
  40. package/dist/types/version.d.ts +1 -1
  41. package/dist/types/x402/common.d.ts.map +1 -1
  42. package/dist/types/x402/facilitator.d.ts +24 -4
  43. package/dist/types/x402/facilitator.d.ts.map +1 -1
  44. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -1
  45. package/dist/types/x402/schemas.d.ts +248 -0
  46. package/dist/types/x402/schemas.d.ts.map +1 -1
  47. package/dist/types/x402/settle-payment.d.ts +3 -2
  48. package/dist/types/x402/settle-payment.d.ts.map +1 -1
  49. package/dist/types/x402/types.d.ts +10 -5
  50. package/dist/types/x402/types.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/exports/x402.ts +5 -0
  53. package/src/react/web/ui/Bridge/swap-widget/select-token-ui.tsx +9 -5
  54. package/src/react/web/ui/Bridge/swap-widget/use-tokens.ts +5 -2
  55. package/src/version.ts +1 -1
  56. package/src/x402/common.ts +87 -8
  57. package/src/x402/facilitator.ts +46 -7
  58. package/src/x402/fetchWithPayment.ts +8 -4
  59. package/src/x402/schemas.ts +39 -0
  60. package/src/x402/settle-payment.ts +4 -2
  61. package/src/x402/types.ts +15 -4
@@ -8,6 +8,7 @@ import { getContract } from "../contract/contract.js";
8
8
  import { isPermitSupported } from "../extensions/erc20/__generated__/IERC20Permit/write/permit.js";
9
9
  import { isTransferWithAuthorizationSupported } from "../extensions/erc20/__generated__/USDC/write/transferWithAuthorization.js";
10
10
  import { getAddress } from "../utils/address.js";
11
+ import { toUnits } from "../utils/units.js";
11
12
  import { decodePayment } from "./encode.js";
12
13
  import type { ThirdwebX402Facilitator } from "./facilitator.js";
13
14
  import {
@@ -16,6 +17,7 @@ import {
16
17
  type RequestedPaymentRequirements,
17
18
  } from "./schemas.js";
18
19
  import {
20
+ type DefaultAsset,
19
21
  type ERC20TokenAmount,
20
22
  type PaymentArgs,
21
23
  type PaymentRequiredResult,
@@ -199,12 +201,11 @@ async function processPriceToAtomicAmount(
199
201
  chainId: number,
200
202
  facilitator: ThirdwebX402Facilitator,
201
203
  ): Promise<
202
- | { maxAmountRequired: string; asset: ERC20TokenAmount["asset"] }
203
- | { error: string }
204
+ { maxAmountRequired: string; asset: DefaultAsset } | { error: string }
204
205
  > {
205
206
  // Handle USDC amount (string) or token amount (ERC20TokenAmount)
206
207
  let maxAmountRequired: string;
207
- let asset: ERC20TokenAmount["asset"];
208
+ let asset: DefaultAsset;
208
209
 
209
210
  if (typeof price === "string" || typeof price === "number") {
210
211
  // USDC amount in dollars
@@ -222,11 +223,32 @@ async function processPriceToAtomicAmount(
222
223
  };
223
224
  }
224
225
  asset = defaultAsset;
225
- maxAmountRequired = (parsedUsdAmount * 10 ** asset.decimals).toString();
226
+ maxAmountRequired = toUnits(
227
+ parsedUsdAmount.toString(),
228
+ defaultAsset.decimals,
229
+ ).toString();
226
230
  } else {
227
231
  // Token amount in atomic units
228
232
  maxAmountRequired = price.amount;
229
- asset = price.asset;
233
+ const tokenExtras = await getOrDetectTokenExtras({
234
+ facilitator,
235
+ partialAsset: price.asset,
236
+ chainId,
237
+ });
238
+ if (!tokenExtras) {
239
+ return {
240
+ error: `Unable to find token information for ${price.asset.address} on chain ${chainId}. Please specify the asset decimals and eip712 information in the asset options.`,
241
+ };
242
+ }
243
+ asset = {
244
+ address: price.asset.address,
245
+ decimals: tokenExtras.decimals,
246
+ eip712: {
247
+ name: tokenExtras.name,
248
+ version: tokenExtras.version,
249
+ primaryType: tokenExtras.primaryType,
250
+ },
251
+ };
230
252
  }
231
253
 
232
254
  return {
@@ -238,13 +260,12 @@ async function processPriceToAtomicAmount(
238
260
  async function getDefaultAsset(
239
261
  chainId: number,
240
262
  facilitator: ThirdwebX402Facilitator,
241
- ): Promise<ERC20TokenAmount["asset"] | undefined> {
263
+ ): Promise<DefaultAsset | undefined> {
242
264
  const supportedAssets = await facilitator.supported();
243
265
  const matchingAsset = supportedAssets.kinds.find(
244
266
  (supported) => supported.network === `eip155:${chainId}`,
245
267
  );
246
- const assetConfig = matchingAsset?.extra
247
- ?.defaultAsset as ERC20TokenAmount["asset"];
268
+ const assetConfig = matchingAsset?.extra?.defaultAsset as DefaultAsset;
248
269
  return assetConfig;
249
270
  }
250
271
 
@@ -287,3 +308,61 @@ export async function getSupportedSignatureType(args: {
287
308
  }
288
309
  return undefined;
289
310
  }
311
+
312
+ async function getOrDetectTokenExtras(args: {
313
+ facilitator: ThirdwebX402Facilitator;
314
+ partialAsset: ERC20TokenAmount["asset"];
315
+ chainId: number;
316
+ }): Promise<
317
+ | {
318
+ name: string;
319
+ version: string;
320
+ decimals: number;
321
+ primaryType: SupportedSignatureType;
322
+ }
323
+ | undefined
324
+ > {
325
+ const { facilitator, partialAsset, chainId } = args;
326
+ if (
327
+ partialAsset.eip712?.name &&
328
+ partialAsset.eip712?.version &&
329
+ partialAsset.decimals !== undefined
330
+ ) {
331
+ return {
332
+ name: partialAsset.eip712.name,
333
+ version: partialAsset.eip712.version,
334
+ decimals: partialAsset.decimals,
335
+ primaryType: partialAsset.eip712.primaryType,
336
+ };
337
+ }
338
+ // read from facilitator
339
+ const response = await facilitator
340
+ .supported({
341
+ chainId,
342
+ tokenAddress: partialAsset.address,
343
+ })
344
+ .catch(() => {
345
+ return {
346
+ kinds: [],
347
+ };
348
+ });
349
+
350
+ const exactScheme = response.kinds?.find((kind) => kind.scheme === "exact");
351
+ if (!exactScheme) {
352
+ return undefined;
353
+ }
354
+ const supportedAsset = exactScheme.extra?.supportedAssets?.find(
355
+ (asset) =>
356
+ asset.address.toLowerCase() === partialAsset.address.toLowerCase(),
357
+ );
358
+ if (!supportedAsset) {
359
+ return undefined;
360
+ }
361
+
362
+ return {
363
+ name: supportedAsset.eip712.name,
364
+ version: supportedAsset.eip712.version,
365
+ decimals: supportedAsset.decimals,
366
+ primaryType: supportedAsset.eip712.primaryType as SupportedSignatureType,
367
+ };
368
+ }
@@ -1,16 +1,20 @@
1
- import type { SupportedPaymentKindsResponse, VerifyResponse } from "x402/types";
1
+ import type { VerifyResponse } from "x402/types";
2
2
  import type { ThirdwebClient } from "../client/client.js";
3
3
  import { stringify } from "../utils/json.js";
4
4
  import { withCache } from "../utils/promise/withCache.js";
5
5
  import type {
6
6
  FacilitatorSettleResponse,
7
+ FacilitatorSupportedResponse,
7
8
  RequestedPaymentPayload,
8
9
  RequestedPaymentRequirements,
9
10
  } from "./schemas.js";
10
11
 
12
+ export type WaitUntil = "simulated" | "submitted" | "confirmed";
13
+
11
14
  export type ThirdwebX402FacilitatorConfig = {
12
15
  client: ThirdwebClient;
13
16
  serverWalletAddress: string;
17
+ waitUtil?: WaitUntil;
14
18
  vaultAccessToken?: string;
15
19
  baseUrl?: string;
16
20
  };
@@ -35,8 +39,12 @@ export type ThirdwebX402Facilitator = {
35
39
  settle: (
36
40
  payload: RequestedPaymentPayload,
37
41
  paymentRequirements: RequestedPaymentRequirements,
42
+ waitUtil?: WaitUntil,
38
43
  ) => Promise<FacilitatorSettleResponse>;
39
- supported: () => Promise<SupportedPaymentKindsResponse>;
44
+ supported: (filters?: {
45
+ chainId: number;
46
+ tokenAddress?: string;
47
+ }) => Promise<FacilitatorSupportedResponse>;
40
48
  };
41
49
 
42
50
  const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
@@ -76,6 +84,21 @@ const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
76
84
  * },
77
85
  * thirdwebX402Facilitator,
78
86
  * );
87
+ * ```
88
+ *
89
+ * #### Configuration Options
90
+ *
91
+ * ```ts
92
+ * const thirdwebX402Facilitator = facilitator({
93
+ * client: client,
94
+ * serverWalletAddress: "0x1234567890123456789012345678901234567890",
95
+ * // Optional: Wait behavior for settlements
96
+ * // - "simulated": Only simulate the transaction (fastest)
97
+ * // - "submitted": Wait until transaction is submitted
98
+ * // - "confirmed": Wait for full on-chain confirmation (slowest, default)
99
+ * waitUntil: "confirmed",
100
+ * });
101
+
79
102
  * ```
80
103
  *
81
104
  * @bridge x402
@@ -162,12 +185,14 @@ export function facilitator(
162
185
  async settle(
163
186
  payload: RequestedPaymentPayload,
164
187
  paymentRequirements: RequestedPaymentRequirements,
188
+ waitUtil?: WaitUntil,
165
189
  ): Promise<FacilitatorSettleResponse> {
166
190
  const url = config.baseUrl ?? DEFAULT_BASE_URL;
167
191
 
168
192
  let headers = { "Content-Type": "application/json" };
169
193
  const authHeaders = await facilitator.createAuthHeaders();
170
194
  headers = { ...headers, ...authHeaders.settle };
195
+ const waitUtilParam = waitUtil || config.waitUtil;
171
196
 
172
197
  const res = await fetch(`${url}/settle`, {
173
198
  method: "POST",
@@ -176,6 +201,7 @@ export function facilitator(
176
201
  x402Version: payload.x402Version,
177
202
  paymentPayload: payload,
178
203
  paymentRequirements: paymentRequirements,
204
+ ...(waitUtilParam ? { waitUtil: waitUtilParam } : {}),
179
205
  }),
180
206
  });
181
207
 
@@ -193,14 +219,27 @@ export function facilitator(
193
219
  *
194
220
  * @returns A promise that resolves to the supported payment kinds
195
221
  */
196
- async supported(): Promise<SupportedPaymentKindsResponse> {
222
+ async supported(filters?: {
223
+ chainId: number;
224
+ tokenAddress?: string;
225
+ }): Promise<FacilitatorSupportedResponse> {
197
226
  const url = config.baseUrl ?? DEFAULT_BASE_URL;
198
227
  return withCache(
199
228
  async () => {
200
229
  let headers = { "Content-Type": "application/json" };
201
230
  const authHeaders = await facilitator.createAuthHeaders();
202
231
  headers = { ...headers, ...authHeaders.supported };
203
- const res = await fetch(`${url}/supported`, { headers });
232
+ const supportedUrl = new URL(`${url}/supported`);
233
+ if (filters?.chainId) {
234
+ supportedUrl.searchParams.set(
235
+ "chainId",
236
+ filters.chainId.toString(),
237
+ );
238
+ }
239
+ if (filters?.tokenAddress) {
240
+ supportedUrl.searchParams.set("tokenAddress", filters.tokenAddress);
241
+ }
242
+ const res = await fetch(supportedUrl.toString(), { headers });
204
243
 
205
244
  if (res.status !== 200) {
206
245
  throw new Error(
@@ -209,11 +248,11 @@ export function facilitator(
209
248
  }
210
249
 
211
250
  const data = await res.json();
212
- return data as SupportedPaymentKindsResponse;
251
+ return data as FacilitatorSupportedResponse;
213
252
  },
214
253
  {
215
- cacheKey: `supported-payment-kinds-${url}`,
216
- cacheTime: 1000 * 60 * 60 * 24, // 24 hours
254
+ cacheKey: `supported-payment-kinds-${url}-${filters?.chainId}-${filters?.tokenAddress}2`,
255
+ cacheTime: 1000 * 60 * 60 * 1, // 1 hour
217
256
  },
218
257
  );
219
258
  },
@@ -61,9 +61,10 @@ export function wrapFetchWithPayment(
61
61
  return response;
62
62
  }
63
63
 
64
- const { x402Version, accepts } = (await response.json()) as {
64
+ const { x402Version, accepts, error } = (await response.json()) as {
65
65
  x402Version: number;
66
66
  accepts: unknown[];
67
+ error?: string;
67
68
  };
68
69
  const parsedPaymentRequirements = accepts
69
70
  .map((x) => RequestedPaymentRequirementsSchema.parse(x))
@@ -83,6 +84,12 @@ export function wrapFetchWithPayment(
83
84
  "exact",
84
85
  );
85
86
 
87
+ if (!selectedPaymentRequirements) {
88
+ throw new Error(
89
+ `No suitable payment requirements found for chain ${chain.id}. ${error}`,
90
+ );
91
+ }
92
+
86
93
  if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
87
94
  throw new Error(
88
95
  `Payment amount exceeds maximum allowed (currently set to ${maxValue} in base units)`,
@@ -154,9 +161,6 @@ function defaultPaymentRequirementsSelector(
154
161
  const firstPaymentRequirement = paymentRequirements.find(
155
162
  (x) => x.scheme === scheme,
156
163
  );
157
- if (!firstPaymentRequirement) {
158
- throw new Error("No suitable payment requirements found");
159
- }
160
164
  return firstPaymentRequirement;
161
165
  }
162
166
  }
@@ -5,6 +5,7 @@ import {
5
5
  PaymentPayloadSchema,
6
6
  PaymentRequirementsSchema,
7
7
  SettleResponseSchema,
8
+ SupportedPaymentKindsResponseSchema,
8
9
  } from "x402/types";
9
10
  import { z } from "zod";
10
11
  import type { Chain } from "../chains/types.js";
@@ -56,6 +57,44 @@ export type FacilitatorSettleResponse = z.infer<
56
57
  typeof FacilitatorSettleResponseSchema
57
58
  >;
58
59
 
60
+ export const SupportedSignatureTypeSchema = z.enum([
61
+ "TransferWithAuthorization",
62
+ "Permit",
63
+ ]);
64
+
65
+ export const FacilitatorSupportedAssetSchema = z.object({
66
+ address: z.string(),
67
+ decimals: z.number(),
68
+ eip712: z.object({
69
+ name: z.string(),
70
+ version: z.string(),
71
+ primaryType: SupportedSignatureTypeSchema,
72
+ }),
73
+ });
74
+
75
+ const FacilitatorSupportedResponseSchema =
76
+ SupportedPaymentKindsResponseSchema.extend({
77
+ kinds: z.array(
78
+ z.object({
79
+ x402Version: z.literal(1),
80
+ scheme: z.literal("exact"),
81
+ network: FacilitatorNetworkSchema,
82
+ extra: z
83
+ .object({
84
+ defaultAsset: FacilitatorSupportedAssetSchema.optional(),
85
+ supportedAssets: z
86
+ .array(FacilitatorSupportedAssetSchema)
87
+ .optional(),
88
+ })
89
+ .optional(),
90
+ }),
91
+ ),
92
+ }).describe("Supported payment kinds for this facilitator");
93
+
94
+ export type FacilitatorSupportedResponse = z.infer<
95
+ typeof FacilitatorSupportedResponseSchema
96
+ >;
97
+
59
98
  export function networkToChainId(network: string | Chain): number {
60
99
  if (typeof network === "object") {
61
100
  return network.id;
@@ -2,7 +2,7 @@ import { stringify } from "../utils/json.js";
2
2
  import { decodePaymentRequest } from "./common.js";
3
3
  import { safeBase64Encode } from "./encode.js";
4
4
  import {
5
- type PaymentArgs,
5
+ type SettlePaymentArgs,
6
6
  type SettlePaymentResult,
7
7
  x402Version,
8
8
  } from "./types.js";
@@ -97,6 +97,7 @@ import {
97
97
  * payTo: "0x1234567890123456789012345678901234567890",
98
98
  * network: arbitrumSepolia, // or any other chain
99
99
  * price: "$0.05",
100
+ * waitUntil: "submitted",
100
101
  * facilitator: thirdwebFacilitator,
101
102
  * });
102
103
  *
@@ -124,7 +125,7 @@ import {
124
125
  * @bridge x402
125
126
  */
126
127
  export async function settlePayment(
127
- args: PaymentArgs,
128
+ args: SettlePaymentArgs,
128
129
  ): Promise<SettlePaymentResult> {
129
130
  const { routeConfig = {}, facilitator } = args;
130
131
  const { errorMessages } = routeConfig;
@@ -142,6 +143,7 @@ export async function settlePayment(
142
143
  const settlement = await facilitator.settle(
143
144
  decodedPayment,
144
145
  selectedPaymentRequirements,
146
+ args.waitUntil,
145
147
  );
146
148
 
147
149
  if (settlement.success) {
package/src/x402/types.ts CHANGED
@@ -1,13 +1,16 @@
1
1
  import type { Money, PaymentMiddlewareConfig } from "x402/types";
2
+ import type z from "zod";
2
3
  import type { Chain } from "../chains/types.js";
3
4
  import type { Address } from "../utils/address.js";
4
5
  import type { Prettify } from "../utils/type-utils.js";
5
- import type { ThirdwebX402Facilitator } from "./facilitator.js";
6
+ import type { ThirdwebX402Facilitator, WaitUntil } from "./facilitator.js";
6
7
  import type {
7
8
  FacilitatorNetwork,
8
9
  FacilitatorSettleResponse,
10
+ FacilitatorSupportedAssetSchema,
9
11
  RequestedPaymentPayload,
10
12
  RequestedPaymentRequirements,
13
+ SupportedSignatureTypeSchema,
11
14
  } from "./schemas.js";
12
15
 
13
16
  export const x402Version = 1;
@@ -36,6 +39,10 @@ export type PaymentArgs = {
36
39
  routeConfig?: PaymentMiddlewareConfig;
37
40
  };
38
41
 
42
+ export type SettlePaymentArgs = PaymentArgs & {
43
+ waitUntil?: WaitUntil;
44
+ };
45
+
39
46
  export type PaymentRequiredResult = {
40
47
  /** HTTP 402 - Payment Required, verification or processing failed or payment missing */
41
48
  status: 402;
@@ -86,17 +93,21 @@ export type VerifyPaymentResult = Prettify<
86
93
  | PaymentRequiredResult
87
94
  >;
88
95
 
89
- export type SupportedSignatureType = "TransferWithAuthorization" | "Permit";
96
+ export type SupportedSignatureType = z.infer<
97
+ typeof SupportedSignatureTypeSchema
98
+ >;
90
99
 
91
100
  export type ERC20TokenAmount = {
92
101
  amount: string;
93
102
  asset: {
94
103
  address: `0x${string}`;
95
- decimals: number;
96
- eip712: {
104
+ decimals?: number;
105
+ eip712?: {
97
106
  name: string;
98
107
  version: string;
99
108
  primaryType: SupportedSignatureType;
100
109
  };
101
110
  };
102
111
  };
112
+
113
+ export type DefaultAsset = z.infer<typeof FacilitatorSupportedAssetSchema>;