thirdweb 5.107.1-nightly-51177fbe17153b711d80ec87b43aadc8ca12fb9e-20250922000346 → 5.108.0-nightly-a94f22928a662a5aff7a203fc2d383d9fa0907ec-20250923000340

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 (81) hide show
  1. package/dist/cjs/exports/x402.js +6 -1
  2. package/dist/cjs/exports/x402.js.map +1 -1
  3. package/dist/cjs/gas/fee-data.js +1 -0
  4. package/dist/cjs/gas/fee-data.js.map +1 -1
  5. package/dist/cjs/react/core/utils/defaultTokens.js +2 -8
  6. package/dist/cjs/react/core/utils/defaultTokens.js.map +1 -1
  7. package/dist/cjs/react/web/ui/Bridge/BridgeOrchestrator.js +18 -14
  8. package/dist/cjs/react/web/ui/Bridge/BridgeOrchestrator.js.map +1 -1
  9. package/dist/cjs/react/web/ui/Bridge/BuyWidget.js +15 -6
  10. package/dist/cjs/react/web/ui/Bridge/BuyWidget.js.map +1 -1
  11. package/dist/cjs/version.js +1 -1
  12. package/dist/cjs/x402/encode.js +71 -0
  13. package/dist/cjs/x402/encode.js.map +1 -0
  14. package/dist/cjs/x402/facilitator.js +83 -2
  15. package/dist/cjs/x402/facilitator.js.map +1 -1
  16. package/dist/cjs/x402/fetchWithPayment.js +7 -15
  17. package/dist/cjs/x402/fetchWithPayment.js.map +1 -1
  18. package/dist/cjs/x402/schemas.js +50 -0
  19. package/dist/cjs/x402/schemas.js.map +1 -0
  20. package/dist/cjs/x402/sign.js +148 -0
  21. package/dist/cjs/x402/sign.js.map +1 -0
  22. package/dist/cjs/x402/verify-payment.js +341 -0
  23. package/dist/cjs/x402/verify-payment.js.map +1 -0
  24. package/dist/esm/exports/x402.js +2 -0
  25. package/dist/esm/exports/x402.js.map +1 -1
  26. package/dist/esm/gas/fee-data.js +1 -0
  27. package/dist/esm/gas/fee-data.js.map +1 -1
  28. package/dist/esm/react/core/utils/defaultTokens.js +2 -8
  29. package/dist/esm/react/core/utils/defaultTokens.js.map +1 -1
  30. package/dist/esm/react/web/ui/Bridge/BridgeOrchestrator.js +18 -14
  31. package/dist/esm/react/web/ui/Bridge/BridgeOrchestrator.js.map +1 -1
  32. package/dist/esm/react/web/ui/Bridge/BuyWidget.js +15 -6
  33. package/dist/esm/react/web/ui/Bridge/BuyWidget.js.map +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/dist/esm/x402/encode.js +66 -0
  36. package/dist/esm/x402/encode.js.map +1 -0
  37. package/dist/esm/x402/facilitator.js +83 -2
  38. package/dist/esm/x402/facilitator.js.map +1 -1
  39. package/dist/esm/x402/fetchWithPayment.js +8 -16
  40. package/dist/esm/x402/fetchWithPayment.js.map +1 -1
  41. package/dist/esm/x402/schemas.js +46 -0
  42. package/dist/esm/x402/schemas.js.map +1 -0
  43. package/dist/esm/x402/sign.js +145 -0
  44. package/dist/esm/x402/sign.js.map +1 -0
  45. package/dist/esm/x402/verify-payment.js +338 -0
  46. package/dist/esm/x402/verify-payment.js.map +1 -0
  47. package/dist/types/exports/x402.d.ts +2 -0
  48. package/dist/types/exports/x402.d.ts.map +1 -1
  49. package/dist/types/gas/fee-data.d.ts.map +1 -1
  50. package/dist/types/react/core/utils/defaultTokens.d.ts +2 -7
  51. package/dist/types/react/core/utils/defaultTokens.d.ts.map +1 -1
  52. package/dist/types/react/web/ui/Bridge/BridgeOrchestrator.d.ts +4 -3
  53. package/dist/types/react/web/ui/Bridge/BridgeOrchestrator.d.ts.map +1 -1
  54. package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts +7 -3
  55. package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts.map +1 -1
  56. package/dist/types/version.d.ts +1 -1
  57. package/dist/types/x402/encode.d.ts +23 -0
  58. package/dist/types/x402/encode.d.ts.map +1 -0
  59. package/dist/types/x402/facilitator.d.ts +44 -3
  60. package/dist/types/x402/facilitator.d.ts.map +1 -1
  61. package/dist/types/x402/fetchWithPayment.d.ts +1 -1
  62. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -1
  63. package/dist/types/x402/schemas.d.ts +164 -0
  64. package/dist/types/x402/schemas.d.ts.map +1 -0
  65. package/dist/types/x402/sign.d.ts +12 -0
  66. package/dist/types/x402/sign.d.ts.map +1 -0
  67. package/dist/types/x402/verify-payment.d.ts +158 -0
  68. package/dist/types/x402/verify-payment.d.ts.map +1 -0
  69. package/package.json +3 -3
  70. package/src/exports/x402.ts +6 -0
  71. package/src/gas/fee-data.ts +1 -0
  72. package/src/react/core/utils/defaultTokens.ts +2 -8
  73. package/src/react/web/ui/Bridge/BridgeOrchestrator.tsx +42 -31
  74. package/src/react/web/ui/Bridge/BuyWidget.tsx +24 -9
  75. package/src/version.ts +1 -1
  76. package/src/x402/encode.ts +81 -0
  77. package/src/x402/facilitator.ts +114 -6
  78. package/src/x402/fetchWithPayment.ts +13 -27
  79. package/src/x402/schemas.ts +76 -0
  80. package/src/x402/sign.ts +206 -0
  81. package/src/x402/verify-payment.ts +469 -0
@@ -82,17 +82,17 @@ export interface BridgeOrchestratorProps {
82
82
  /**
83
83
  * Called when the flow is completed successfully
84
84
  */
85
- onComplete: () => void;
85
+ onComplete: (quote: BridgePrepareResult) => void;
86
86
 
87
87
  /**
88
88
  * Called when the flow encounters an error
89
89
  */
90
- onError: (error: Error) => void;
90
+ onError: (error: Error, quote: BridgePrepareResult | undefined) => void;
91
91
 
92
92
  /**
93
93
  * Called when the user cancels the flow
94
94
  */
95
- onCancel: () => void;
95
+ onCancel: (quote: BridgePrepareResult | undefined) => void;
96
96
 
97
97
  /**
98
98
  * Connect options for wallet connection
@@ -189,19 +189,22 @@ export function BridgeOrchestrator({
189
189
  }, [send, uiOptions.mode]);
190
190
 
191
191
  // Handle post-buy transaction completion
192
- const handlePostBuyTransactionComplete = useCallback(() => {
193
- onComplete?.();
194
- send({ type: "RESET" });
195
- }, [onComplete, send]);
192
+ const handlePostBuyTransactionComplete = useCallback(
193
+ (quote: BridgePrepareResult) => {
194
+ onComplete?.(quote);
195
+ send({ type: "RESET" });
196
+ },
197
+ [onComplete, send],
198
+ );
196
199
 
197
200
  // Handle errors
198
201
  const handleError = useCallback(
199
202
  (error: Error) => {
200
203
  console.error(error);
201
- onError?.(error);
204
+ onError?.(error, state.context.quote);
202
205
  send({ error, type: "ERROR_OCCURRED" });
203
206
  },
204
- [onError, send],
207
+ [onError, send, state.context.quote],
205
208
  );
206
209
 
207
210
  // Handle payment method selection
@@ -227,10 +230,13 @@ export function BridgeOrchestrator({
227
230
 
228
231
  // Handle execution complete
229
232
  const handleExecutionComplete = useCallback(
230
- (completedStatuses: CompletedStatusResult[]) => {
233
+ (
234
+ completedStatuses: CompletedStatusResult[],
235
+ quote: BridgePrepareResult,
236
+ ) => {
231
237
  send({ completedStatuses, type: "EXECUTION_COMPLETE" });
232
238
  if (uiOptions.mode !== "transaction") {
233
- onComplete?.();
239
+ onComplete?.(quote);
234
240
  }
235
241
  },
236
242
  [send, onComplete, uiOptions.mode],
@@ -241,6 +247,8 @@ export function BridgeOrchestrator({
241
247
  send({ type: "RETRY" });
242
248
  }, [send]);
243
249
 
250
+ const quote = state.context.quote;
251
+
244
252
  // Handle requirements resolved from FundWallet and DirectPayment
245
253
  const handleRequirementsResolved = useCallback(
246
254
  (amount: string, token: TokenWithPrices, receiverAddress: Address) => {
@@ -263,7 +271,7 @@ export function BridgeOrchestrator({
263
271
  error={state.context.currentError}
264
272
  onCancel={() => {
265
273
  send({ type: "RESET" });
266
- onCancel?.();
274
+ onCancel?.(quote);
267
275
  }}
268
276
  onRetry={handleRetry}
269
277
  />
@@ -369,31 +377,33 @@ export function BridgeOrchestrator({
369
377
  />
370
378
  )}
371
379
 
372
- {state.value === "execute" &&
373
- state.context.quote &&
374
- state.context.request && (
375
- <StepRunner
376
- autoStart={true}
377
- client={client}
378
- onBack={() => {
379
- send({ type: "BACK" });
380
- }}
381
- onCancel={onCancel}
382
- onComplete={handleExecutionComplete}
383
- request={state.context.request}
384
- wallet={state.context.selectedPaymentMethod?.payerWallet}
385
- windowAdapter={webWindowAdapter}
386
- />
387
- )}
380
+ {state.value === "execute" && quote && state.context.request && (
381
+ <StepRunner
382
+ autoStart={true}
383
+ client={client}
384
+ onBack={() => {
385
+ send({ type: "BACK" });
386
+ }}
387
+ onCancel={() => {
388
+ onCancel(quote);
389
+ }}
390
+ onComplete={(completedStatuses) => {
391
+ handleExecutionComplete(completedStatuses, quote);
392
+ }}
393
+ request={state.context.request}
394
+ wallet={state.context.selectedPaymentMethod?.payerWallet}
395
+ windowAdapter={webWindowAdapter}
396
+ />
397
+ )}
388
398
 
389
399
  {state.value === "success" &&
390
- state.context.quote &&
400
+ quote &&
391
401
  state.context.completedStatuses && (
392
402
  <SuccessScreen
393
403
  client={client}
394
404
  completedStatuses={state.context.completedStatuses}
395
405
  onDone={handleDoneOrContinueClick}
396
- preparedQuote={state.context.quote}
406
+ preparedQuote={quote}
397
407
  uiOptions={uiOptions}
398
408
  windowAdapter={webWindowAdapter}
399
409
  hasPaymentId={!!paymentLinkId}
@@ -402,9 +412,10 @@ export function BridgeOrchestrator({
402
412
 
403
413
  {state.value === "post-buy-transaction" &&
404
414
  uiOptions.mode === "transaction" &&
415
+ quote &&
405
416
  uiOptions.transaction && (
406
417
  <ExecutingTxScreen
407
- closeModal={handlePostBuyTransactionComplete}
418
+ closeModal={() => handlePostBuyTransactionComplete(quote)}
408
419
  onTxSent={() => {
409
420
  // Do nothing
410
421
  }}
@@ -23,6 +23,7 @@ import { CustomThemeProvider } from "../../../core/design-system/CustomThemeProv
23
23
  import type { Theme } from "../../../core/design-system/index.js";
24
24
  import type { SiweAuthOptions } from "../../../core/hooks/auth/useSiweAuth.js";
25
25
  import type { ConnectButton_connectModalOptions } from "../../../core/hooks/connection/ConnectButtonProps.js";
26
+ import type { BridgePrepareResult } from "../../../core/hooks/useBridgePrepare.js";
26
27
  import type { SupportedTokens } from "../../../core/utils/defaultTokens.js";
27
28
  import { useConnectLocale } from "../ConnectWallet/locale/getConnectLocale.js";
28
29
  import { EmbedContainer } from "../ConnectWallet/Modal/ConnectEmbed.js";
@@ -32,6 +33,11 @@ import type { LocaleId } from "../types.js";
32
33
  import { BridgeOrchestrator, type UIOptions } from "./BridgeOrchestrator.js";
33
34
  import { UnsupportedTokenScreen } from "./UnsupportedTokenScreen.js";
34
35
 
36
+ type BuyOrOnrampPrepareResult = Extract<
37
+ BridgePrepareResult,
38
+ { type: "buy" | "onramp" }
39
+ >;
40
+
35
41
  export type BuyWidgetProps = {
36
42
  /**
37
43
  * Customize the supported tokens that users can pay with.
@@ -155,17 +161,17 @@ export type BuyWidgetProps = {
155
161
  /**
156
162
  * Callback triggered when the purchase is successful.
157
163
  */
158
- onSuccess?: () => void;
164
+ onSuccess?: (quote: BuyOrOnrampPrepareResult) => void;
159
165
 
160
166
  /**
161
167
  * Callback triggered when the purchase encounters an error.
162
168
  */
163
- onError?: (error: Error) => void;
169
+ onError?: (error: Error, quote: BuyOrOnrampPrepareResult | undefined) => void;
164
170
 
165
171
  /**
166
172
  * Callback triggered when the user cancels the purchase.
167
173
  */
168
- onCancel?: () => void;
174
+ onCancel?: (quote: BuyOrOnrampPrepareResult | undefined) => void;
169
175
 
170
176
  /**
171
177
  * @hidden
@@ -447,14 +453,23 @@ export function BuyWidget(props: BuyWidgetProps) {
447
453
  client={props.client}
448
454
  connectLocale={localeQuery.data}
449
455
  connectOptions={props.connectOptions}
450
- onCancel={() => {
451
- props.onCancel?.();
456
+ onCancel={(quote) => {
457
+ // type guard
458
+ if (quote?.type === "buy" || quote?.type === "onramp") {
459
+ props.onCancel?.(quote);
460
+ }
452
461
  }}
453
- onComplete={() => {
454
- props.onSuccess?.();
462
+ onComplete={(quote) => {
463
+ // type guard
464
+ if (quote?.type === "buy" || quote?.type === "onramp") {
465
+ props.onSuccess?.(quote);
466
+ }
455
467
  }}
456
- onError={(err: Error) => {
457
- props.onError?.(err);
468
+ onError={(err: Error, quote) => {
469
+ // type guard
470
+ if (quote?.type === "buy" || quote?.type === "onramp") {
471
+ props.onError?.(err, quote);
472
+ }
458
473
  }}
459
474
  paymentLinkId={props.paymentLinkId}
460
475
  paymentMethods={props.paymentMethods}
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.107.1-nightly-51177fbe17153b711d80ec87b43aadc8ca12fb9e-20250922000346";
1
+ export const version = "5.108.0-nightly-a94f22928a662a5aff7a203fc2d383d9fa0907ec-20250923000340";
@@ -0,0 +1,81 @@
1
+ import type { ExactEvmPayload } from "x402/types";
2
+ import {
3
+ type RequestedPaymentPayload,
4
+ RequestedPaymentPayloadSchema,
5
+ } from "./schemas.js";
6
+
7
+ /**
8
+ * Encodes a payment payload into a base64 string, ensuring bigint values are properly stringified
9
+ *
10
+ * @param payment - The payment payload to encode
11
+ * @returns A base64 encoded string representation of the payment payload
12
+ */
13
+ export function encodePayment(payment: RequestedPaymentPayload): string {
14
+ let safe: RequestedPaymentPayload;
15
+
16
+ // evm
17
+ const evmPayload = payment.payload as ExactEvmPayload;
18
+ safe = {
19
+ ...payment,
20
+ payload: {
21
+ ...evmPayload,
22
+ authorization: Object.fromEntries(
23
+ Object.entries(evmPayload.authorization).map(([key, value]) => [
24
+ key,
25
+ typeof value === "bigint" ? (value as bigint).toString() : value,
26
+ ]),
27
+ ) as ExactEvmPayload["authorization"],
28
+ },
29
+ };
30
+ return safeBase64Encode(JSON.stringify(safe));
31
+ }
32
+
33
+ /**
34
+ * Decodes a base64 encoded payment string back into a PaymentPayload object
35
+ *
36
+ * @param payment - The base64 encoded payment string to decode
37
+ * @returns The decoded and validated PaymentPayload object
38
+ */
39
+ export function decodePayment(payment: string): RequestedPaymentPayload {
40
+ const decoded = safeBase64Decode(payment);
41
+ const parsed = JSON.parse(decoded);
42
+
43
+ const obj: RequestedPaymentPayload = {
44
+ ...parsed,
45
+ payload: parsed.payload as ExactEvmPayload,
46
+ };
47
+ const validated = RequestedPaymentPayloadSchema.parse(obj);
48
+ return validated;
49
+ }
50
+
51
+ /**
52
+ * Encodes a string to base64 format
53
+ *
54
+ * @param data - The string to be encoded to base64
55
+ * @returns The base64 encoded string
56
+ */
57
+ export function safeBase64Encode(data: string): string {
58
+ if (
59
+ typeof globalThis !== "undefined" &&
60
+ typeof globalThis.btoa === "function"
61
+ ) {
62
+ return globalThis.btoa(data);
63
+ }
64
+ return Buffer.from(data).toString("base64");
65
+ }
66
+
67
+ /**
68
+ * Decodes a base64 string back to its original format
69
+ *
70
+ * @param data - The base64 encoded string to be decoded
71
+ * @returns The decoded string in UTF-8 format
72
+ */
73
+ function safeBase64Decode(data: string): string {
74
+ if (
75
+ typeof globalThis !== "undefined" &&
76
+ typeof globalThis.atob === "function"
77
+ ) {
78
+ return globalThis.atob(data);
79
+ }
80
+ return Buffer.from(data, "base64").toString("utf-8");
81
+ }
@@ -1,5 +1,12 @@
1
- import type { FacilitatorConfig } from "x402/types";
1
+ import type { SupportedPaymentKindsResponse, VerifyResponse } from "x402/types";
2
2
  import type { ThirdwebClient } from "../client/client.js";
3
+ import { stringify } from "../utils/json.js";
4
+ import { withCache } from "../utils/promise/withCache.js";
5
+ import type {
6
+ FacilitatorSettleResponse,
7
+ RequestedPaymentPayload,
8
+ RequestedPaymentRequirements,
9
+ } from "./schemas.js";
3
10
 
4
11
  export type ThirdwebX402FacilitatorConfig = {
5
12
  client: ThirdwebClient;
@@ -12,7 +19,7 @@ const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
12
19
 
13
20
  /**
14
21
  * Creates a facilitator for the x402 payment protocol.
15
- * Use this with any x402 middleware to enable settling transactions with your thirdweb server wallet.
22
+ * You can use this with `verifyPayment` or with any x402 middleware to enable settling transactions with your thirdweb server wallet.
16
23
  *
17
24
  * @param config - The configuration for the facilitator
18
25
  * @returns a x402 compatible FacilitatorConfig
@@ -48,9 +55,7 @@ const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
48
55
  *
49
56
  * @bridge x402
50
57
  */
51
- export function facilitator(
52
- config: ThirdwebX402FacilitatorConfig,
53
- ): FacilitatorConfig {
58
+ export function facilitator(config: ThirdwebX402FacilitatorConfig) {
54
59
  const secretKey = config.client.secretKey;
55
60
  if (!secretKey) {
56
61
  throw new Error("Client secret key is required for the x402 facilitator");
@@ -61,7 +66,7 @@ export function facilitator(
61
66
  "Server wallet address is required for the x402 facilitator",
62
67
  );
63
68
  }
64
- return {
69
+ const facilitator = {
65
70
  url: (config.baseUrl ?? DEFAULT_BASE_URL) as `${string}://${string}`,
66
71
  createAuthHeaders: async () => {
67
72
  return {
@@ -83,5 +88,108 @@ export function facilitator(
83
88
  },
84
89
  };
85
90
  },
91
+ /**
92
+ * Verifies a payment payload with the facilitator service
93
+ *
94
+ * @param payload - The payment payload to verify
95
+ * @param paymentRequirements - The payment requirements to verify against
96
+ * @returns A promise that resolves to the verification response
97
+ */
98
+ async verify(
99
+ payload: RequestedPaymentPayload,
100
+ paymentRequirements: RequestedPaymentRequirements,
101
+ ): Promise<VerifyResponse> {
102
+ const url = config.baseUrl ?? DEFAULT_BASE_URL;
103
+
104
+ let headers = { "Content-Type": "application/json" };
105
+ const authHeaders = await facilitator.createAuthHeaders();
106
+ headers = { ...headers, ...authHeaders.verify };
107
+
108
+ const res = await fetch(`${url}/verify`, {
109
+ method: "POST",
110
+ headers,
111
+ body: stringify({
112
+ x402Version: payload.x402Version,
113
+ paymentPayload: payload,
114
+ paymentRequirements: paymentRequirements,
115
+ }),
116
+ });
117
+
118
+ if (res.status !== 200) {
119
+ const text = `${res.statusText} ${await res.text()}`;
120
+ throw new Error(`Failed to verify payment: ${res.status} ${text}`);
121
+ }
122
+
123
+ const data = await res.json();
124
+ return data as VerifyResponse;
125
+ },
126
+
127
+ /**
128
+ * Settles a payment with the facilitator service
129
+ *
130
+ * @param payload - The payment payload to settle
131
+ * @param paymentRequirements - The payment requirements for the settlement
132
+ * @returns A promise that resolves to the settlement response
133
+ */
134
+ async settle(
135
+ payload: RequestedPaymentPayload,
136
+ paymentRequirements: RequestedPaymentRequirements,
137
+ ): Promise<FacilitatorSettleResponse> {
138
+ const url = config.baseUrl ?? DEFAULT_BASE_URL;
139
+
140
+ let headers = { "Content-Type": "application/json" };
141
+ const authHeaders = await facilitator.createAuthHeaders();
142
+ headers = { ...headers, ...authHeaders.settle };
143
+
144
+ const res = await fetch(`${url}/settle`, {
145
+ method: "POST",
146
+ headers,
147
+ body: JSON.stringify({
148
+ x402Version: payload.x402Version,
149
+ paymentPayload: payload,
150
+ paymentRequirements: paymentRequirements,
151
+ }),
152
+ });
153
+
154
+ if (res.status !== 200) {
155
+ const text = `${res.statusText} ${await res.text()}`;
156
+ throw new Error(`Failed to settle payment: ${res.status} ${text}`);
157
+ }
158
+
159
+ const data = await res.json();
160
+ return data as FacilitatorSettleResponse;
161
+ },
162
+
163
+ /**
164
+ * Gets the supported payment kinds from the facilitator service.
165
+ *
166
+ * @returns A promise that resolves to the supported payment kinds
167
+ */
168
+ async supported(): Promise<SupportedPaymentKindsResponse> {
169
+ const url = config.baseUrl ?? DEFAULT_BASE_URL;
170
+ return withCache(
171
+ async () => {
172
+ let headers = { "Content-Type": "application/json" };
173
+ const authHeaders = await facilitator.createAuthHeaders();
174
+ headers = { ...headers, ...authHeaders.supported };
175
+ const res = await fetch(`${url}/supported`, { headers });
176
+
177
+ if (res.status !== 200) {
178
+ throw new Error(
179
+ `Failed to get supported payment kinds: ${res.statusText}`,
180
+ );
181
+ }
182
+
183
+ const data = await res.json();
184
+ return data as SupportedPaymentKindsResponse;
185
+ },
186
+ {
187
+ cacheKey: `supported-payment-kinds-${url}`,
188
+ cacheTime: 1000 * 60 * 60 * 24, // 24 hours
189
+ },
190
+ );
191
+ },
86
192
  };
193
+
194
+ return facilitator;
87
195
  }
@@ -1,15 +1,13 @@
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";
1
+ import { ChainIdToNetwork } from "x402/types";
10
2
  import { getCachedChain } from "../chains/utils.js";
11
3
  import type { ThirdwebClient } from "../client/client.js";
12
4
  import type { Wallet } from "../wallets/interfaces/wallet.js";
5
+ import {
6
+ networkToChainId,
7
+ type RequestedPaymentRequirements,
8
+ RequestedPaymentRequirementsSchema,
9
+ } from "./schemas.js";
10
+ import { createPaymentHeader } from "./sign.js";
13
11
 
14
12
  /**
15
13
  * Enables the payment of APIs using the x402 payment protocol.
@@ -52,7 +50,7 @@ import type { Wallet } from "../wallets/interfaces/wallet.js";
52
50
  */
53
51
  export function wrapFetchWithPayment(
54
52
  fetch: typeof globalThis.fetch,
55
- client: ThirdwebClient,
53
+ _client: ThirdwebClient,
56
54
  wallet: Wallet,
57
55
  maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
58
56
  ) {
@@ -68,7 +66,7 @@ export function wrapFetchWithPayment(
68
66
  accepts: unknown[];
69
67
  };
70
68
  const parsedPaymentRequirements = accepts
71
- .map((x) => PaymentRequirementsSchema.parse(x))
69
+ .map((x) => RequestedPaymentRequirementsSchema.parse(x))
72
70
  .filter((x) => x.scheme === "exact"); // TODO (402): accept other schemes
73
71
 
74
72
  const account = wallet.getAccount();
@@ -89,16 +87,10 @@ export function wrapFetchWithPayment(
89
87
  throw new Error("Payment amount exceeds maximum allowed");
90
88
  }
91
89
 
92
- const paymentChainId = EvmNetworkToChainId.get(
90
+ const paymentChainId = networkToChainId(
93
91
  selectedPaymentRequirements.network,
94
92
  );
95
93
 
96
- if (!paymentChainId) {
97
- throw new Error(
98
- `No chain found for the selected payment requirement: ${selectedPaymentRequirements.network}`,
99
- );
100
- }
101
-
102
94
  // switch to the payment chain if it's not the current chain
103
95
  if (paymentChainId !== chain.id) {
104
96
  await wallet.switchChain(getCachedChain(paymentChainId));
@@ -108,14 +100,8 @@ export function wrapFetchWithPayment(
108
100
  }
109
101
  }
110
102
 
111
- const walletClient = viemAdapter.wallet.toViem({
112
- wallet: wallet,
113
- chain,
114
- client,
115
- }) as Signer;
116
-
117
103
  const paymentHeader = await createPaymentHeader(
118
- walletClient,
104
+ account,
119
105
  x402Version,
120
106
  selectedPaymentRequirements,
121
107
  );
@@ -142,7 +128,7 @@ export function wrapFetchWithPayment(
142
128
  }
143
129
 
144
130
  function defaultPaymentRequirementsSelector(
145
- paymentRequirements: PaymentRequirements[],
131
+ paymentRequirements: RequestedPaymentRequirements[],
146
132
  chainId: number,
147
133
  scheme: "exact",
148
134
  ) {
@@ -151,7 +137,7 @@ function defaultPaymentRequirementsSelector(
151
137
  "No valid payment requirements found in server 402 response",
152
138
  );
153
139
  }
154
- const currentWalletNetwork = ChainIdToNetwork[chainId];
140
+ const currentWalletNetwork = ChainIdToNetwork[chainId] || `eip155:${chainId}`;
155
141
  // find the payment requirements matching the connected wallet chain
156
142
  const matchingPaymentRequirements = paymentRequirements.find(
157
143
  (x) => x.network === currentWalletNetwork && x.scheme === scheme,
@@ -0,0 +1,76 @@
1
+ import {
2
+ EvmNetworkToChainId,
3
+ type ExactEvmPayload,
4
+ type Network,
5
+ PaymentPayloadSchema,
6
+ PaymentRequirementsSchema,
7
+ SettleResponseSchema,
8
+ } from "x402/types";
9
+ import { z } from "zod";
10
+
11
+ const FacilitatorNetworkSchema = z.union([
12
+ z.literal("base-sepolia"),
13
+ z.literal("base"),
14
+ z.literal("avalanche-fuji"),
15
+ z.literal("avalanche"),
16
+ z.literal("iotex"),
17
+ z.literal("solana-devnet"),
18
+ z.literal("solana"),
19
+ z.literal("sei"),
20
+ z.literal("sei-testnet"),
21
+ z.string().refine((value) => value.startsWith("eip155:"), {
22
+ message: "Invalid network",
23
+ }),
24
+ ]);
25
+
26
+ export type FacilitatorNetwork = z.infer<typeof FacilitatorNetworkSchema>;
27
+
28
+ export const RequestedPaymentPayloadSchema = PaymentPayloadSchema.extend({
29
+ network: FacilitatorNetworkSchema,
30
+ });
31
+
32
+ export type RequestedPaymentPayload = z.infer<
33
+ typeof RequestedPaymentPayloadSchema
34
+ >;
35
+ export type UnsignedPaymentPayload = Omit<
36
+ RequestedPaymentPayload,
37
+ "payload"
38
+ > & {
39
+ payload: Omit<ExactEvmPayload, "signature"> & { signature: undefined };
40
+ };
41
+
42
+ export const RequestedPaymentRequirementsSchema =
43
+ PaymentRequirementsSchema.extend({
44
+ network: FacilitatorNetworkSchema,
45
+ });
46
+
47
+ export type RequestedPaymentRequirements = z.infer<
48
+ typeof RequestedPaymentRequirementsSchema
49
+ >;
50
+
51
+ const FacilitatorSettleResponseSchema = SettleResponseSchema.extend({
52
+ network: FacilitatorNetworkSchema,
53
+ });
54
+ export type FacilitatorSettleResponse = z.infer<
55
+ typeof FacilitatorSettleResponseSchema
56
+ >;
57
+
58
+ export function networkToChainId(network: string): number {
59
+ if (network.startsWith("eip155:")) {
60
+ const chainId = parseInt(network.split(":")[1] ?? "0");
61
+ if (!Number.isNaN(chainId) && chainId > 0) {
62
+ return chainId;
63
+ } else {
64
+ throw new Error(`Invalid network: ${network}`);
65
+ }
66
+ }
67
+ const mappedChainId = EvmNetworkToChainId.get(network as Network);
68
+ if (!mappedChainId) {
69
+ throw new Error(`Invalid network: ${network}`);
70
+ }
71
+ // TODO (402): support solana networks
72
+ if (mappedChainId === 101 || mappedChainId === 103) {
73
+ throw new Error("Solana networks not supported yet.");
74
+ }
75
+ return mappedChainId;
76
+ }