@wtflabs/x402-fetch 0.0.1-beta.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.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # x402-fetch
2
+
3
+ A utility package that extends the native `fetch` API to automatically handle 402 Payment Required responses using the x402 payment protocol. This package enables seamless integration of payment functionality into your applications when making HTTP requests.
4
+
5
+ ## Supported Authorization Types
6
+
7
+ This package supports multiple EVM authorization standards:
8
+
9
+ - **EIP-3009** (default): `transferWithAuthorization` - gasless token transfers
10
+ - **EIP-2612 Permit**: Standard permit functionality for ERC-20 tokens
11
+ - **Permit2**: Uniswap's universal token approval system
12
+
13
+ The authorization type is automatically selected based on the server's payment requirements (`paymentType` field).
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install x402-fetch
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import { createWalletClient, http } from "viem";
25
+ import { privateKeyToAccount } from "viem/accounts";
26
+ import { wrapFetchWithPayment } from "x402-fetch";
27
+ import { baseSepolia } from "viem/chains";
28
+
29
+ // Create a wallet client
30
+ const account = privateKeyToAccount("0xYourPrivateKey");
31
+ const client = createWalletClient({
32
+ account,
33
+ transport: http(),
34
+ chain: baseSepolia,
35
+ });
36
+
37
+ // Wrap the fetch function with payment handling
38
+ const fetchWithPay = wrapFetchWithPayment(fetch, client);
39
+
40
+ // Make a request that may require payment
41
+ const response = await fetchWithPay("https://api.example.com/paid-endpoint", {
42
+ method: "GET",
43
+ });
44
+
45
+ const data = await response.json();
46
+ ```
47
+
48
+ ## 多链支持
49
+
50
+ x402-fetch 现在支持灵活的多 EVM 链配置!使用新的 `createEvmSigner` API:
51
+
52
+ ```typescript
53
+ import { createEvmSigner, wrapFetchWithPayment } from 'x402-fetch';
54
+
55
+ // 方式 1:使用链名称
56
+ const bscSigner = createEvmSigner('bsc', '0xYourPrivateKey');
57
+
58
+ // 方式 2:使用 viem chain 对象
59
+ import { polygon } from 'viem/chains';
60
+ const polygonSigner = createEvmSigner(polygon, '0xYourPrivateKey');
61
+
62
+ // 方式 3:自定义配置(包括自定义 RPC)
63
+ const customSigner = createEvmSigner({
64
+ chainId: 56,
65
+ name: 'BSC',
66
+ rpcUrl: 'https://my-custom-rpc.com',
67
+ }, '0xYourPrivateKey');
68
+
69
+ // 为每条链创建独立的 fetch wrapper
70
+ const fetchBsc = wrapFetchWithPayment(fetch, bscSigner);
71
+ const fetchPolygon = wrapFetchWithPayment(fetch, polygonSigner);
72
+ ```
73
+
74
+ **[📖 查看完整的多链使用指南](./MULTI_CHAIN_USAGE.md)**
75
+
76
+ ## API
77
+
78
+ ### `wrapFetchWithPayment(fetch, walletClient, maxValue?, paymentRequirementsSelector?)`
79
+
80
+ Wraps the native fetch API to handle 402 Payment Required responses automatically.
81
+
82
+ #### Parameters
83
+
84
+ - `fetch`: The fetch function to wrap (typically `globalThis.fetch`)
85
+ - `walletClient`: The wallet client used to sign payment messages (must implement the x402 wallet interface)
86
+ - `maxValue`: Optional maximum allowed payment amount in base units (defaults to 0.1 USDC)
87
+ - `paymentRequirementsSelector`: Optional function to select payment requirements from the response (defaults to `selectPaymentRequirements`)
88
+
89
+ #### Returns
90
+
91
+ A wrapped fetch function that automatically handles 402 responses by:
92
+ 1. Making the initial request
93
+ 2. If a 402 response is received, parsing the payment requirements
94
+ 3. Verifying the payment amount is within the allowed maximum
95
+ 4. Creating a payment header using the provided wallet client
96
+ 5. Retrying the request with the payment header
97
+
98
+ ## Examples
99
+
100
+ ### Basic Usage
101
+
102
+ ```typescript
103
+ import { config } from "dotenv";
104
+ import { createWalletClient, http } from "viem";
105
+ import { privateKeyToAccount } from "viem/accounts";
106
+ import { wrapFetchWithPayment } from "x402-fetch";
107
+ import { baseSepolia } from "viem/chains";
108
+
109
+ config();
110
+
111
+ const { PRIVATE_KEY, API_URL } = process.env;
112
+
113
+ const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`);
114
+ const client = createWalletClient({
115
+ account,
116
+ transport: http(),
117
+ chain: baseSepolia,
118
+ });
119
+
120
+ const fetchWithPay = wrapFetchWithPayment(fetch, client);
121
+
122
+ // Make a request to a paid API endpoint
123
+ fetchWithPay(API_URL, {
124
+ method: "GET",
125
+ })
126
+ .then(async response => {
127
+ const data = await response.json();
128
+ console.log(data);
129
+ })
130
+ .catch(error => {
131
+ console.error(error);
132
+ });
133
+ ```
134
+
135
+ ### Server-Side: Specifying Payment Type
136
+
137
+ The server specifies which payment type the client should use in the 402 response:
138
+
139
+ ```typescript
140
+ // EIP-2612 Permit example
141
+ app.post("/api/protected", async (c) => {
142
+ const paymentHeader = c.req.header("X-PAYMENT");
143
+
144
+ if (!paymentHeader) {
145
+ return c.json({
146
+ x402Version: 1,
147
+ accepts: [{
148
+ scheme: "exact",
149
+ network: "base-sepolia",
150
+ maxAmountRequired: "100000",
151
+ resource: "http://localhost:3000/api/protected",
152
+ description: "Access to protected resource",
153
+ mimeType: "application/json",
154
+ payTo: "0x...",
155
+ maxTimeoutSeconds: 3600,
156
+ asset: "0x...", // Token address
157
+ paymentType: "permit", // Specify permit
158
+ }]
159
+ }, 402);
160
+ }
161
+
162
+ // Verify and settle payment...
163
+ });
164
+ ```
165
+
166
+ ```typescript
167
+ // Permit2 example
168
+ app.post("/api/protected", async (c) => {
169
+ const paymentHeader = c.req.header("X-PAYMENT");
170
+
171
+ if (!paymentHeader) {
172
+ return c.json({
173
+ x402Version: 1,
174
+ accepts: [{
175
+ scheme: "exact",
176
+ network: "base-sepolia",
177
+ maxAmountRequired: "100000",
178
+ resource: "http://localhost:3000/api/protected",
179
+ description: "Access to protected resource",
180
+ mimeType: "application/json",
181
+ payTo: "0x...",
182
+ maxTimeoutSeconds: 3600,
183
+ asset: "0x...", // Token address
184
+ paymentType: "permit2", // Specify permit2
185
+ }]
186
+ }, 402);
187
+ }
188
+
189
+ // Verify and settle payment...
190
+ });
191
+ ```
192
+
193
+ ### Payment Type Selection
194
+
195
+ The client automatically detects and uses the appropriate payment type:
196
+
197
+ 1. **Server specifies `paymentType: "permit"`** → Client uses EIP-2612 Permit
198
+ 2. **Server specifies `paymentType: "permit2"`** → Client uses Permit2
199
+ 3. **Server specifies `paymentType: "eip3009"` or omits it** → Client uses EIP-3009 (default)
200
+
201
+ ### Authorization Type Comparison
202
+
203
+ | Feature | EIP-3009 | EIP-2612 Permit | Permit2 |
204
+ |---------|----------|-----------------|---------|
205
+ | Gas efficiency | High | High | High |
206
+ | Token support | Limited (tokens with EIP-3009) | Wide (most ERC-20) | Universal |
207
+ | Approval management | Per-transaction | Per-spender | Universal router |
208
+ | Nonce management | Custom | On-chain | Advanced |
209
+ | Best for | Specialized tokens | Standard ERC-20 | DeFi integrations |
210
+
@@ -0,0 +1,48 @@
1
+ import { Signer, MultiNetworkSigner, X402Config } from 'x402/types';
2
+ export { ConnectedClient, EvmChainConfig, MultiNetworkSigner, Signer, X402Config, createConnectedClient, createSigner, withChain } from 'x402/types';
3
+ import { PaymentRequirementsSelector } from 'x402/client';
4
+ export { PaymentRequirementsSelector } from 'x402/client';
5
+ export { decodeXPaymentResponse } from 'x402/shared';
6
+ export { Chain, Hex } from 'viem';
7
+
8
+ /**
9
+ * Enables the payment of APIs using the x402 payment protocol.
10
+ *
11
+ * This function wraps the native fetch API to automatically handle 402 Payment Required responses
12
+ * by creating and sending a payment header. It will:
13
+ * 1. Make the initial request
14
+ * 2. If a 402 response is received, parse the payment requirements
15
+ * 3. Verify the payment amount is within the allowed maximum
16
+ * 4. Create a payment header using the provided wallet client
17
+ * 5. Retry the request with the payment header
18
+ *
19
+ * @param fetch - The fetch function to wrap (typically globalThis.fetch)
20
+ * @param walletClient - The wallet client used to sign payment messages
21
+ * @param maxValue - The maximum allowed payment amount in base units (defaults to 0.1 USDC)
22
+ * @param paymentRequirementsSelector - A function that selects the payment requirements from the response
23
+ * @param config - Optional configuration for X402 operations (e.g., custom RPC URLs)
24
+ * @returns A wrapped fetch function that handles 402 responses automatically
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const wallet = new SignerWallet(...);
29
+ * const fetchWithPay = wrapFetchWithPayment(fetch, wallet);
30
+ *
31
+ * // With custom RPC configuration
32
+ * const fetchWithPay = wrapFetchWithPayment(fetch, wallet, undefined, undefined, {
33
+ * svmConfig: { rpcUrl: "http://localhost:8899" }
34
+ * });
35
+ *
36
+ * // Make a request that may require payment
37
+ * const response = await fetchWithPay('https://api.example.com/paid-endpoint');
38
+ * ```
39
+ *
40
+ * @throws {Error} If the payment amount exceeds the maximum allowed value
41
+ * @throws {Error} If the request configuration is missing
42
+ * @throws {Error} If a payment has already been attempted for this request
43
+ * @throws {Error} If there's an error creating the payment header
44
+ */
45
+ declare function wrapFetchWithPayment(fetch: typeof globalThis.fetch, walletClient: Signer | MultiNetworkSigner, maxValue?: bigint, // Default to 0.10 USDC
46
+ paymentRequirementsSelector?: PaymentRequirementsSelector, config?: X402Config): (input: RequestInfo, init?: RequestInit) => Promise<Response>;
47
+
48
+ export { wrapFetchWithPayment };
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createConnectedClient: () => import_types2.createConnectedClient,
24
+ createSigner: () => import_types2.createSigner,
25
+ decodeXPaymentResponse: () => import_shared.decodeXPaymentResponse,
26
+ withChain: () => import_types2.withChain,
27
+ wrapFetchWithPayment: () => wrapFetchWithPayment
28
+ });
29
+ module.exports = __toCommonJS(src_exports);
30
+ var import_types = require("x402/types");
31
+ var import_client = require("x402/client");
32
+ var import_schemes = require("x402/schemes");
33
+ var import_shared = require("x402/shared");
34
+ var import_types2 = require("x402/types");
35
+ function wrapFetchWithPayment(fetch, walletClient, maxValue = BigInt(0.1 * 10 ** 6), paymentRequirementsSelector = import_client.selectPaymentRequirements, config) {
36
+ return async (input, init) => {
37
+ var _a;
38
+ const response = await fetch(input, init);
39
+ if (response.status !== 402) {
40
+ return response;
41
+ }
42
+ const { x402Version, accepts } = await response.json();
43
+ const parsedPaymentRequirements = accepts.map((x) => import_types.PaymentRequirementsSchema.parse(x));
44
+ const network = (0, import_types.isMultiNetworkSigner)(walletClient) ? void 0 : import_types.evm.isSignerWallet(walletClient) ? import_types.ChainIdToNetwork[(_a = walletClient.chain) == null ? void 0 : _a.id] : (0, import_types.isSvmSignerWallet)(walletClient) ? ["solana", "solana-devnet"] : void 0;
45
+ const selectedPaymentRequirements = paymentRequirementsSelector(
46
+ parsedPaymentRequirements,
47
+ network,
48
+ "exact"
49
+ );
50
+ if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
51
+ throw new Error("Payment amount exceeds maximum allowed");
52
+ }
53
+ const paymentType = selectedPaymentRequirements.paymentType || "eip3009";
54
+ let paymentHeader;
55
+ const isEvmNetwork = network && !["solana", "solana-devnet"].includes(network[0]);
56
+ if (paymentType === "permit" && isEvmNetwork) {
57
+ if (!import_types.evm.isSignerWallet(walletClient)) {
58
+ throw new Error("Permit authorization requires an EVM signer wallet");
59
+ }
60
+ paymentHeader = await import_schemes.exact.evm.permit.createPaymentHeader(
61
+ walletClient,
62
+ x402Version,
63
+ selectedPaymentRequirements
64
+ );
65
+ } else if (paymentType === "permit2" && isEvmNetwork) {
66
+ if (!import_types.evm.isSignerWallet(walletClient)) {
67
+ throw new Error("Permit2 authorization requires an EVM signer wallet");
68
+ }
69
+ paymentHeader = await import_schemes.exact.evm.permit2.createPaymentHeader(
70
+ walletClient,
71
+ x402Version,
72
+ selectedPaymentRequirements
73
+ );
74
+ } else if (paymentType === "eip3009" || !paymentType) {
75
+ paymentHeader = await (0, import_client.createPaymentHeader)(
76
+ walletClient,
77
+ x402Version,
78
+ selectedPaymentRequirements,
79
+ config
80
+ );
81
+ } else {
82
+ throw new Error(`Unsupported payment type: ${paymentType}`);
83
+ }
84
+ if (!init) {
85
+ throw new Error("Missing fetch request configuration");
86
+ }
87
+ if (init.__is402Retry) {
88
+ throw new Error("Payment already attempted");
89
+ }
90
+ const newInit = {
91
+ ...init,
92
+ headers: {
93
+ ...init.headers || {},
94
+ "X-PAYMENT": paymentHeader,
95
+ "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
96
+ },
97
+ __is402Retry: true
98
+ };
99
+ const secondResponse = await fetch(input, newInit);
100
+ return secondResponse;
101
+ };
102
+ }
103
+ // Annotate the CommonJS export names for ESM import in node:
104
+ 0 && (module.exports = {
105
+ createConnectedClient,
106
+ createSigner,
107
+ decodeXPaymentResponse,
108
+ withChain,
109
+ wrapFetchWithPayment
110
+ });
111
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["import {\n ChainIdToNetwork,\n PaymentRequirementsSchema,\n Signer,\n evm,\n MultiNetworkSigner,\n isMultiNetworkSigner,\n isSvmSignerWallet,\n Network,\n X402Config,\n} from \"x402/types\";\nimport {\n createPaymentHeader,\n PaymentRequirementsSelector,\n selectPaymentRequirements,\n} from \"x402/client\";\nimport { exact } from \"x402/schemes\";\n\n/**\n * Enables the payment of APIs using the x402 payment protocol.\n *\n * This function wraps the native fetch API to automatically handle 402 Payment Required responses\n * by creating and sending a payment header. It will:\n * 1. Make the initial request\n * 2. If a 402 response is received, parse the payment requirements\n * 3. Verify the payment amount is within the allowed maximum\n * 4. Create a payment header using the provided wallet client\n * 5. Retry the request with the payment header\n *\n * @param fetch - The fetch function to wrap (typically globalThis.fetch)\n * @param walletClient - The wallet client used to sign payment messages\n * @param maxValue - The maximum allowed payment amount in base units (defaults to 0.1 USDC)\n * @param paymentRequirementsSelector - A function that selects the payment requirements from the response\n * @param config - Optional configuration for X402 operations (e.g., custom RPC URLs)\n * @returns A wrapped fetch function that handles 402 responses automatically\n *\n * @example\n * ```typescript\n * const wallet = new SignerWallet(...);\n * const fetchWithPay = wrapFetchWithPayment(fetch, wallet);\n *\n * // With custom RPC configuration\n * const fetchWithPay = wrapFetchWithPayment(fetch, wallet, undefined, undefined, {\n * svmConfig: { rpcUrl: \"http://localhost:8899\" }\n * });\n *\n * // Make a request that may require payment\n * const response = await fetchWithPay('https://api.example.com/paid-endpoint');\n * ```\n *\n * @throws {Error} If the payment amount exceeds the maximum allowed value\n * @throws {Error} If the request configuration is missing\n * @throws {Error} If a payment has already been attempted for this request\n * @throws {Error} If there's an error creating the payment header\n */\nexport function wrapFetchWithPayment(\n fetch: typeof globalThis.fetch,\n walletClient: Signer | MultiNetworkSigner,\n maxValue: bigint = BigInt(0.1 * 10 ** 6), // Default to 0.10 USDC\n paymentRequirementsSelector: PaymentRequirementsSelector = selectPaymentRequirements,\n config?: X402Config,\n) {\n return async (input: RequestInfo, init?: RequestInit) => {\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n const { x402Version, accepts } = (await response.json()) as {\n x402Version: number;\n accepts: unknown[];\n };\n const parsedPaymentRequirements = accepts.map(x => PaymentRequirementsSchema.parse(x));\n\n const network = isMultiNetworkSigner(walletClient)\n ? undefined\n : evm.isSignerWallet(walletClient as typeof evm.EvmSigner)\n ? ChainIdToNetwork[(walletClient as typeof evm.EvmSigner).chain?.id]\n : isSvmSignerWallet(walletClient)\n ? ([\"solana\", \"solana-devnet\"] as Network[])\n : undefined;\n\n const selectedPaymentRequirements = paymentRequirementsSelector(\n parsedPaymentRequirements,\n network,\n \"exact\",\n );\n\n if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {\n throw new Error(\"Payment amount exceeds maximum allowed\");\n }\n\n // 获取支付类型,默认为 eip3009\n const paymentType = selectedPaymentRequirements.paymentType || \"eip3009\";\n\n // 根据支付类型创建支付头\n let paymentHeader: string;\n\n // 仅对 EVM 网络支持 permit 和 permit2\n const isEvmNetwork = network && ![\"solana\", \"solana-devnet\"].includes(network[0]);\n\n if (paymentType === \"permit\" && isEvmNetwork) {\n // 使用 EIP-2612 Permit\n if (!evm.isSignerWallet(walletClient as typeof evm.EvmSigner)) {\n throw new Error(\"Permit authorization requires an EVM signer wallet\");\n }\n paymentHeader = await exact.evm.permit.createPaymentHeader(\n walletClient as typeof evm.EvmSigner,\n x402Version,\n selectedPaymentRequirements,\n );\n } else if (paymentType === \"permit2\" && isEvmNetwork) {\n // 使用 Permit2\n if (!evm.isSignerWallet(walletClient as typeof evm.EvmSigner)) {\n throw new Error(\"Permit2 authorization requires an EVM signer wallet\");\n }\n paymentHeader = await exact.evm.permit2.createPaymentHeader(\n walletClient as typeof evm.EvmSigner,\n x402Version,\n selectedPaymentRequirements,\n );\n } else if (paymentType === \"eip3009\" || !paymentType) {\n // 默认使用 EIP-3009(统一的 createPaymentHeader)\n paymentHeader = await createPaymentHeader(\n walletClient,\n x402Version,\n selectedPaymentRequirements,\n config,\n );\n } else {\n throw new Error(`Unsupported payment type: ${paymentType}`);\n }\n\n if (!init) {\n throw new Error(\"Missing fetch request configuration\");\n }\n\n if ((init as { __is402Retry?: boolean }).__is402Retry) {\n throw new Error(\"Payment already attempted\");\n }\n\n const newInit = {\n ...init,\n headers: {\n ...(init.headers || {}),\n \"X-PAYMENT\": paymentHeader,\n \"Access-Control-Expose-Headers\": \"X-PAYMENT-RESPONSE\",\n },\n __is402Retry: true,\n };\n\n const secondResponse = await fetch(input, newInit);\n return secondResponse;\n };\n}\n\nexport { decodeXPaymentResponse } from \"x402/shared\";\nexport {\n createSigner,\n createConnectedClient,\n withChain,\n type Signer,\n type ConnectedClient,\n type MultiNetworkSigner,\n type X402Config,\n type EvmChainConfig\n} from \"x402/types\";\nexport { type PaymentRequirementsSelector } from \"x402/client\";\nexport type { Hex, Chain } from \"viem\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAUO;AACP,oBAIO;AACP,qBAAsB;AA6ItB,oBAAuC;AACvC,IAAAA,gBASO;AAhHA,SAAS,qBACd,OACA,cACA,WAAmB,OAAO,MAAM,MAAM,CAAC,GACvC,8BAA2D,yCAC3D,QACA;AACA,SAAO,OAAO,OAAoB,SAAuB;AA9D3D;AA+DI,UAAM,WAAW,MAAM,MAAM,OAAO,IAAI;AAExC,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,EAAE,aAAa,QAAQ,IAAK,MAAM,SAAS,KAAK;AAItD,UAAM,4BAA4B,QAAQ,IAAI,OAAK,uCAA0B,MAAM,CAAC,CAAC;AAErF,UAAM,cAAU,mCAAqB,YAAY,IAC7C,SACA,iBAAI,eAAe,YAAoC,IACrD,+BAAkB,kBAAsC,UAAtC,mBAA6C,EAAE,QACjE,gCAAkB,YAAY,IAC3B,CAAC,UAAU,eAAe,IAC3B;AAER,UAAM,8BAA8B;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,OAAO,4BAA4B,iBAAiB,IAAI,UAAU;AACpE,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAGA,UAAM,cAAc,4BAA4B,eAAe;AAG/D,QAAI;AAGJ,UAAM,eAAe,WAAW,CAAC,CAAC,UAAU,eAAe,EAAE,SAAS,QAAQ,CAAC,CAAC;AAEhF,QAAI,gBAAgB,YAAY,cAAc;AAE5C,UAAI,CAAC,iBAAI,eAAe,YAAoC,GAAG;AAC7D,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AACA,sBAAgB,MAAM,qBAAM,IAAI,OAAO;AAAA,QACrC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,gBAAgB,aAAa,cAAc;AAEpD,UAAI,CAAC,iBAAI,eAAe,YAAoC,GAAG;AAC7D,cAAM,IAAI,MAAM,qDAAqD;AAAA,MACvE;AACA,sBAAgB,MAAM,qBAAM,IAAI,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,gBAAgB,aAAa,CAAC,aAAa;AAEpD,sBAAgB,UAAM;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,IAAI,MAAM,6BAA6B,WAAW,EAAE;AAAA,IAC5D;AAEA,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAK,KAAoC,cAAc;AACrD,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAI,KAAK,WAAW,CAAC;AAAA,QACrB,aAAa;AAAA,QACb,iCAAiC;AAAA,MACnC;AAAA,MACA,cAAc;AAAA,IAChB;AAEA,UAAM,iBAAiB,MAAM,MAAM,OAAO,OAAO;AACjD,WAAO;AAAA,EACT;AACF;","names":["import_types"]}
@@ -0,0 +1,48 @@
1
+ import { Signer, MultiNetworkSigner, X402Config } from 'x402/types';
2
+ export { ConnectedClient, EvmChainConfig, MultiNetworkSigner, Signer, X402Config, createConnectedClient, createSigner, withChain } from 'x402/types';
3
+ import { PaymentRequirementsSelector } from 'x402/client';
4
+ export { PaymentRequirementsSelector } from 'x402/client';
5
+ export { decodeXPaymentResponse } from 'x402/shared';
6
+ export { Chain, Hex } from 'viem';
7
+
8
+ /**
9
+ * Enables the payment of APIs using the x402 payment protocol.
10
+ *
11
+ * This function wraps the native fetch API to automatically handle 402 Payment Required responses
12
+ * by creating and sending a payment header. It will:
13
+ * 1. Make the initial request
14
+ * 2. If a 402 response is received, parse the payment requirements
15
+ * 3. Verify the payment amount is within the allowed maximum
16
+ * 4. Create a payment header using the provided wallet client
17
+ * 5. Retry the request with the payment header
18
+ *
19
+ * @param fetch - The fetch function to wrap (typically globalThis.fetch)
20
+ * @param walletClient - The wallet client used to sign payment messages
21
+ * @param maxValue - The maximum allowed payment amount in base units (defaults to 0.1 USDC)
22
+ * @param paymentRequirementsSelector - A function that selects the payment requirements from the response
23
+ * @param config - Optional configuration for X402 operations (e.g., custom RPC URLs)
24
+ * @returns A wrapped fetch function that handles 402 responses automatically
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const wallet = new SignerWallet(...);
29
+ * const fetchWithPay = wrapFetchWithPayment(fetch, wallet);
30
+ *
31
+ * // With custom RPC configuration
32
+ * const fetchWithPay = wrapFetchWithPayment(fetch, wallet, undefined, undefined, {
33
+ * svmConfig: { rpcUrl: "http://localhost:8899" }
34
+ * });
35
+ *
36
+ * // Make a request that may require payment
37
+ * const response = await fetchWithPay('https://api.example.com/paid-endpoint');
38
+ * ```
39
+ *
40
+ * @throws {Error} If the payment amount exceeds the maximum allowed value
41
+ * @throws {Error} If the request configuration is missing
42
+ * @throws {Error} If a payment has already been attempted for this request
43
+ * @throws {Error} If there's an error creating the payment header
44
+ */
45
+ declare function wrapFetchWithPayment(fetch: typeof globalThis.fetch, walletClient: Signer | MultiNetworkSigner, maxValue?: bigint, // Default to 0.10 USDC
46
+ paymentRequirementsSelector?: PaymentRequirementsSelector, config?: X402Config): (input: RequestInfo, init?: RequestInit) => Promise<Response>;
47
+
48
+ export { wrapFetchWithPayment };
@@ -0,0 +1,95 @@
1
+ // src/index.ts
2
+ import {
3
+ ChainIdToNetwork,
4
+ PaymentRequirementsSchema,
5
+ evm,
6
+ isMultiNetworkSigner,
7
+ isSvmSignerWallet
8
+ } from "x402/types";
9
+ import {
10
+ createPaymentHeader,
11
+ selectPaymentRequirements
12
+ } from "x402/client";
13
+ import { exact } from "x402/schemes";
14
+ import { decodeXPaymentResponse } from "x402/shared";
15
+ import {
16
+ createSigner,
17
+ createConnectedClient,
18
+ withChain
19
+ } from "x402/types";
20
+ function wrapFetchWithPayment(fetch, walletClient, maxValue = BigInt(0.1 * 10 ** 6), paymentRequirementsSelector = selectPaymentRequirements, config) {
21
+ return async (input, init) => {
22
+ var _a;
23
+ const response = await fetch(input, init);
24
+ if (response.status !== 402) {
25
+ return response;
26
+ }
27
+ const { x402Version, accepts } = await response.json();
28
+ const parsedPaymentRequirements = accepts.map((x) => PaymentRequirementsSchema.parse(x));
29
+ const network = isMultiNetworkSigner(walletClient) ? void 0 : evm.isSignerWallet(walletClient) ? ChainIdToNetwork[(_a = walletClient.chain) == null ? void 0 : _a.id] : isSvmSignerWallet(walletClient) ? ["solana", "solana-devnet"] : void 0;
30
+ const selectedPaymentRequirements = paymentRequirementsSelector(
31
+ parsedPaymentRequirements,
32
+ network,
33
+ "exact"
34
+ );
35
+ if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
36
+ throw new Error("Payment amount exceeds maximum allowed");
37
+ }
38
+ const paymentType = selectedPaymentRequirements.paymentType || "eip3009";
39
+ let paymentHeader;
40
+ const isEvmNetwork = network && !["solana", "solana-devnet"].includes(network[0]);
41
+ if (paymentType === "permit" && isEvmNetwork) {
42
+ if (!evm.isSignerWallet(walletClient)) {
43
+ throw new Error("Permit authorization requires an EVM signer wallet");
44
+ }
45
+ paymentHeader = await exact.evm.permit.createPaymentHeader(
46
+ walletClient,
47
+ x402Version,
48
+ selectedPaymentRequirements
49
+ );
50
+ } else if (paymentType === "permit2" && isEvmNetwork) {
51
+ if (!evm.isSignerWallet(walletClient)) {
52
+ throw new Error("Permit2 authorization requires an EVM signer wallet");
53
+ }
54
+ paymentHeader = await exact.evm.permit2.createPaymentHeader(
55
+ walletClient,
56
+ x402Version,
57
+ selectedPaymentRequirements
58
+ );
59
+ } else if (paymentType === "eip3009" || !paymentType) {
60
+ paymentHeader = await createPaymentHeader(
61
+ walletClient,
62
+ x402Version,
63
+ selectedPaymentRequirements,
64
+ config
65
+ );
66
+ } else {
67
+ throw new Error(`Unsupported payment type: ${paymentType}`);
68
+ }
69
+ if (!init) {
70
+ throw new Error("Missing fetch request configuration");
71
+ }
72
+ if (init.__is402Retry) {
73
+ throw new Error("Payment already attempted");
74
+ }
75
+ const newInit = {
76
+ ...init,
77
+ headers: {
78
+ ...init.headers || {},
79
+ "X-PAYMENT": paymentHeader,
80
+ "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
81
+ },
82
+ __is402Retry: true
83
+ };
84
+ const secondResponse = await fetch(input, newInit);
85
+ return secondResponse;
86
+ };
87
+ }
88
+ export {
89
+ createConnectedClient,
90
+ createSigner,
91
+ decodeXPaymentResponse,
92
+ withChain,
93
+ wrapFetchWithPayment
94
+ };
95
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["import {\n ChainIdToNetwork,\n PaymentRequirementsSchema,\n Signer,\n evm,\n MultiNetworkSigner,\n isMultiNetworkSigner,\n isSvmSignerWallet,\n Network,\n X402Config,\n} from \"x402/types\";\nimport {\n createPaymentHeader,\n PaymentRequirementsSelector,\n selectPaymentRequirements,\n} from \"x402/client\";\nimport { exact } from \"x402/schemes\";\n\n/**\n * Enables the payment of APIs using the x402 payment protocol.\n *\n * This function wraps the native fetch API to automatically handle 402 Payment Required responses\n * by creating and sending a payment header. It will:\n * 1. Make the initial request\n * 2. If a 402 response is received, parse the payment requirements\n * 3. Verify the payment amount is within the allowed maximum\n * 4. Create a payment header using the provided wallet client\n * 5. Retry the request with the payment header\n *\n * @param fetch - The fetch function to wrap (typically globalThis.fetch)\n * @param walletClient - The wallet client used to sign payment messages\n * @param maxValue - The maximum allowed payment amount in base units (defaults to 0.1 USDC)\n * @param paymentRequirementsSelector - A function that selects the payment requirements from the response\n * @param config - Optional configuration for X402 operations (e.g., custom RPC URLs)\n * @returns A wrapped fetch function that handles 402 responses automatically\n *\n * @example\n * ```typescript\n * const wallet = new SignerWallet(...);\n * const fetchWithPay = wrapFetchWithPayment(fetch, wallet);\n *\n * // With custom RPC configuration\n * const fetchWithPay = wrapFetchWithPayment(fetch, wallet, undefined, undefined, {\n * svmConfig: { rpcUrl: \"http://localhost:8899\" }\n * });\n *\n * // Make a request that may require payment\n * const response = await fetchWithPay('https://api.example.com/paid-endpoint');\n * ```\n *\n * @throws {Error} If the payment amount exceeds the maximum allowed value\n * @throws {Error} If the request configuration is missing\n * @throws {Error} If a payment has already been attempted for this request\n * @throws {Error} If there's an error creating the payment header\n */\nexport function wrapFetchWithPayment(\n fetch: typeof globalThis.fetch,\n walletClient: Signer | MultiNetworkSigner,\n maxValue: bigint = BigInt(0.1 * 10 ** 6), // Default to 0.10 USDC\n paymentRequirementsSelector: PaymentRequirementsSelector = selectPaymentRequirements,\n config?: X402Config,\n) {\n return async (input: RequestInfo, init?: RequestInit) => {\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n const { x402Version, accepts } = (await response.json()) as {\n x402Version: number;\n accepts: unknown[];\n };\n const parsedPaymentRequirements = accepts.map(x => PaymentRequirementsSchema.parse(x));\n\n const network = isMultiNetworkSigner(walletClient)\n ? undefined\n : evm.isSignerWallet(walletClient as typeof evm.EvmSigner)\n ? ChainIdToNetwork[(walletClient as typeof evm.EvmSigner).chain?.id]\n : isSvmSignerWallet(walletClient)\n ? ([\"solana\", \"solana-devnet\"] as Network[])\n : undefined;\n\n const selectedPaymentRequirements = paymentRequirementsSelector(\n parsedPaymentRequirements,\n network,\n \"exact\",\n );\n\n if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {\n throw new Error(\"Payment amount exceeds maximum allowed\");\n }\n\n // 获取支付类型,默认为 eip3009\n const paymentType = selectedPaymentRequirements.paymentType || \"eip3009\";\n\n // 根据支付类型创建支付头\n let paymentHeader: string;\n\n // 仅对 EVM 网络支持 permit 和 permit2\n const isEvmNetwork = network && ![\"solana\", \"solana-devnet\"].includes(network[0]);\n\n if (paymentType === \"permit\" && isEvmNetwork) {\n // 使用 EIP-2612 Permit\n if (!evm.isSignerWallet(walletClient as typeof evm.EvmSigner)) {\n throw new Error(\"Permit authorization requires an EVM signer wallet\");\n }\n paymentHeader = await exact.evm.permit.createPaymentHeader(\n walletClient as typeof evm.EvmSigner,\n x402Version,\n selectedPaymentRequirements,\n );\n } else if (paymentType === \"permit2\" && isEvmNetwork) {\n // 使用 Permit2\n if (!evm.isSignerWallet(walletClient as typeof evm.EvmSigner)) {\n throw new Error(\"Permit2 authorization requires an EVM signer wallet\");\n }\n paymentHeader = await exact.evm.permit2.createPaymentHeader(\n walletClient as typeof evm.EvmSigner,\n x402Version,\n selectedPaymentRequirements,\n );\n } else if (paymentType === \"eip3009\" || !paymentType) {\n // 默认使用 EIP-3009(统一的 createPaymentHeader)\n paymentHeader = await createPaymentHeader(\n walletClient,\n x402Version,\n selectedPaymentRequirements,\n config,\n );\n } else {\n throw new Error(`Unsupported payment type: ${paymentType}`);\n }\n\n if (!init) {\n throw new Error(\"Missing fetch request configuration\");\n }\n\n if ((init as { __is402Retry?: boolean }).__is402Retry) {\n throw new Error(\"Payment already attempted\");\n }\n\n const newInit = {\n ...init,\n headers: {\n ...(init.headers || {}),\n \"X-PAYMENT\": paymentHeader,\n \"Access-Control-Expose-Headers\": \"X-PAYMENT-RESPONSE\",\n },\n __is402Retry: true,\n };\n\n const secondResponse = await fetch(input, newInit);\n return secondResponse;\n };\n}\n\nexport { decodeXPaymentResponse } from \"x402/shared\";\nexport {\n createSigner,\n createConnectedClient,\n withChain,\n type Signer,\n type ConnectedClient,\n type MultiNetworkSigner,\n type X402Config,\n type EvmChainConfig\n} from \"x402/types\";\nexport { type PaymentRequirementsSelector } from \"x402/client\";\nexport type { Hex, Chain } from \"viem\";\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EAEA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,OACK;AACP,SAAS,aAAa;AA6ItB,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAhHA,SAAS,qBACd,OACA,cACA,WAAmB,OAAO,MAAM,MAAM,CAAC,GACvC,8BAA2D,2BAC3D,QACA;AACA,SAAO,OAAO,OAAoB,SAAuB;AA9D3D;AA+DI,UAAM,WAAW,MAAM,MAAM,OAAO,IAAI;AAExC,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,EAAE,aAAa,QAAQ,IAAK,MAAM,SAAS,KAAK;AAItD,UAAM,4BAA4B,QAAQ,IAAI,OAAK,0BAA0B,MAAM,CAAC,CAAC;AAErF,UAAM,UAAU,qBAAqB,YAAY,IAC7C,SACA,IAAI,eAAe,YAAoC,IACrD,kBAAkB,kBAAsC,UAAtC,mBAA6C,EAAE,IACjE,kBAAkB,YAAY,IAC3B,CAAC,UAAU,eAAe,IAC3B;AAER,UAAM,8BAA8B;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,OAAO,4BAA4B,iBAAiB,IAAI,UAAU;AACpE,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAGA,UAAM,cAAc,4BAA4B,eAAe;AAG/D,QAAI;AAGJ,UAAM,eAAe,WAAW,CAAC,CAAC,UAAU,eAAe,EAAE,SAAS,QAAQ,CAAC,CAAC;AAEhF,QAAI,gBAAgB,YAAY,cAAc;AAE5C,UAAI,CAAC,IAAI,eAAe,YAAoC,GAAG;AAC7D,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AACA,sBAAgB,MAAM,MAAM,IAAI,OAAO;AAAA,QACrC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,gBAAgB,aAAa,cAAc;AAEpD,UAAI,CAAC,IAAI,eAAe,YAAoC,GAAG;AAC7D,cAAM,IAAI,MAAM,qDAAqD;AAAA,MACvE;AACA,sBAAgB,MAAM,MAAM,IAAI,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,gBAAgB,aAAa,CAAC,aAAa;AAEpD,sBAAgB,MAAM;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,IAAI,MAAM,6BAA6B,WAAW,EAAE;AAAA,IAC5D;AAEA,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAK,KAAoC,cAAc;AACrD,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAI,KAAK,WAAW,CAAC;AAAA,QACrB,aAAa;AAAA,QACb,iCAAiC;AAAA,MACnC;AAAA,MACA,cAAc;AAAA,IAChB;AAEA,UAAM,iBAAiB,MAAM,MAAM,OAAO,OAAO;AACjD,WAAO;AAAA,EACT;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@wtflabs/x402-fetch",
3
+ "version": "0.0.1-beta.0",
4
+ "main": "./dist/cjs/index.js",
5
+ "module": "./dist/esm/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "start": "tsx --env-file=.env index.ts",
9
+ "test": "vitest run",
10
+ "test:watch": "vitest",
11
+ "build": "tsup",
12
+ "watch": "tsc --watch",
13
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
14
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
15
+ "lint": "eslint . --ext .ts --fix",
16
+ "lint:check": "eslint . --ext .ts"
17
+ },
18
+ "keywords": [],
19
+ "license": "Apache-2.0",
20
+ "author": "Coinbase Inc.",
21
+ "repository": "https://github.com/coinbase/x402",
22
+ "description": "x402 Payment Protocol",
23
+ "devDependencies": {
24
+ "@types/node": "^22.13.4",
25
+ "@eslint/js": "^9.24.0",
26
+ "eslint": "^9.24.0",
27
+ "eslint-plugin-jsdoc": "^50.6.9",
28
+ "eslint-plugin-prettier": "^5.2.6",
29
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
30
+ "@typescript-eslint/parser": "^8.29.1",
31
+ "eslint-plugin-import": "^2.31.0",
32
+ "prettier": "3.5.2",
33
+ "tsup": "^8.4.0",
34
+ "tsx": "^4.19.2",
35
+ "typescript": "^5.7.3",
36
+ "vite-tsconfig-paths": "^5.1.4",
37
+ "vitest": "^3.0.5",
38
+ "vite": "^6.2.6"
39
+ },
40
+ "dependencies": {
41
+ "viem": "^2.21.26",
42
+ "zod": "^3.24.2",
43
+ "x402": "workspace:^"
44
+ },
45
+ "exports": {
46
+ ".": {
47
+ "import": {
48
+ "types": "./dist/esm/index.d.mts",
49
+ "default": "./dist/esm/index.mjs"
50
+ },
51
+ "require": {
52
+ "types": "./dist/cjs/index.d.ts",
53
+ "default": "./dist/cjs/index.js"
54
+ }
55
+ }
56
+ },
57
+ "files": [
58
+ "dist"
59
+ ]
60
+ }