@coinbase/agentkit 0.10.3 → 0.10.4

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 (55) hide show
  1. package/README.md +47 -10
  2. package/dist/action-providers/across/acrossActionProvider.js +2 -4
  3. package/dist/action-providers/across/acrossActionProvider.test.js +10 -5
  4. package/dist/action-providers/baseAccount/baseAccountActionProvider.js +5 -7
  5. package/dist/action-providers/clanker/utils.d.ts +2 -1
  6. package/dist/action-providers/clanker/utils.js +2 -2
  7. package/dist/action-providers/index.d.ts +1 -0
  8. package/dist/action-providers/index.js +1 -0
  9. package/dist/action-providers/jupiter/jupiterActionProvider.js +2 -2
  10. package/dist/action-providers/spl/splActionProvider.js +12 -13
  11. package/dist/action-providers/superfluid/graphQueries/superfluidGraphQueries.js +2 -2
  12. package/dist/action-providers/sushi/constants.d.ts +35 -0
  13. package/dist/action-providers/sushi/constants.js +7 -0
  14. package/dist/action-providers/sushi/index.d.ts +4 -0
  15. package/dist/action-providers/sushi/index.js +20 -0
  16. package/dist/action-providers/sushi/sushiDataActionProvider.d.ts +32 -0
  17. package/dist/action-providers/sushi/sushiDataActionProvider.js +113 -0
  18. package/dist/action-providers/sushi/sushiDataSchemas.d.ts +11 -0
  19. package/dist/action-providers/sushi/sushiDataSchemas.js +16 -0
  20. package/dist/action-providers/sushi/sushiRouterActionProvider.d.ts +40 -0
  21. package/dist/action-providers/sushi/sushiRouterActionProvider.js +386 -0
  22. package/dist/action-providers/sushi/sushiRouterActionProvider.test.d.ts +1 -0
  23. package/dist/action-providers/sushi/sushiRouterActionProvider.test.js +392 -0
  24. package/dist/action-providers/sushi/sushiRouterSchemas.d.ts +36 -0
  25. package/dist/action-providers/sushi/sushiRouterSchemas.js +55 -0
  26. package/dist/action-providers/vaultsfyi/constants.d.ts +8 -12
  27. package/dist/action-providers/vaultsfyi/constants.js +47 -13
  28. package/dist/action-providers/vaultsfyi/schemas.d.ts +120 -65
  29. package/dist/action-providers/vaultsfyi/schemas.js +72 -38
  30. package/dist/action-providers/vaultsfyi/sdk.d.ts +8 -0
  31. package/dist/action-providers/vaultsfyi/sdk.js +15 -0
  32. package/dist/action-providers/vaultsfyi/utils.d.ts +151 -55
  33. package/dist/action-providers/vaultsfyi/utils.js +29 -75
  34. package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.d.ts +55 -16
  35. package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.js +413 -217
  36. package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.test.js +509 -316
  37. package/dist/action-providers/x402/constants.d.ts +67 -0
  38. package/dist/action-providers/x402/constants.js +37 -0
  39. package/dist/action-providers/x402/schemas.d.ts +45 -5
  40. package/dist/action-providers/x402/schemas.js +81 -11
  41. package/dist/action-providers/x402/utils.d.ts +85 -10
  42. package/dist/action-providers/x402/utils.js +302 -35
  43. package/dist/action-providers/x402/x402ActionProvider.d.ts +15 -1
  44. package/dist/action-providers/x402/x402ActionProvider.js +230 -179
  45. package/dist/action-providers/x402/x402ActionProvider.test.js +222 -262
  46. package/dist/action-providers/zora/zoraActionProvider.js +4 -5
  47. package/package.json +10 -7
  48. package/dist/action-providers/vaultsfyi/api/actions.d.ts +0 -41
  49. package/dist/action-providers/vaultsfyi/api/actions.js +0 -28
  50. package/dist/action-providers/vaultsfyi/api/historicalData.d.ts +0 -31
  51. package/dist/action-providers/vaultsfyi/api/historicalData.js +0 -44
  52. package/dist/action-providers/vaultsfyi/api/types.d.ts +0 -34
  53. package/dist/action-providers/vaultsfyi/api/types.js +0 -2
  54. package/dist/action-providers/vaultsfyi/api/vaults.d.ts +0 -66
  55. package/dist/action-providers/vaultsfyi/api/vaults.js +0 -57
@@ -32,91 +32,68 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  const x402ActionProvider_1 = require("./x402ActionProvider");
40
37
  const wallet_providers_1 = require("../../wallet-providers");
41
- const axios_1 = __importDefault(require("axios"));
42
- const x402axios = __importStar(require("x402-axios"));
43
- const x402Verify = __importStar(require("x402/verify"));
38
+ const fetch_1 = require("@x402/fetch");
39
+ const client_1 = require("@x402/evm/exact/client");
40
+ const client_2 = require("@x402/svm/exact/client");
44
41
  const utils = __importStar(require("./utils"));
45
- // Mock external facilitator dependency
46
- jest.mock("@coinbase/x402", () => ({
47
- facilitator: {},
48
- }));
49
- // Mock modules
50
- jest.mock("axios");
51
- jest.mock("x402-axios");
52
- jest.mock("x402/verify");
42
+ const schemas_1 = require("./schemas");
43
+ // Mock external modules
44
+ jest.mock("@x402/fetch");
45
+ jest.mock("@x402/evm/exact/client");
46
+ jest.mock("@x402/svm/exact/client");
53
47
  jest.mock("./utils");
48
+ jest.mock("./schemas", () => ({
49
+ ...jest.requireActual("./schemas"),
50
+ resolveFacilitatorUrl: jest.fn(),
51
+ }));
54
52
  // Create mock functions
55
- const mockRequest = jest.fn();
56
- // Create a complete mock axios instance
57
- const mockAxiosInstance = {
58
- request: mockRequest,
59
- get: jest.fn(),
60
- delete: jest.fn(),
61
- head: jest.fn(),
62
- options: jest.fn(),
63
- post: jest.fn(),
64
- put: jest.fn(),
65
- patch: jest.fn(),
66
- getUri: jest.fn(),
67
- defaults: {},
68
- interceptors: {
69
- request: { use: jest.fn(), eject: jest.fn(), clear: jest.fn() },
70
- response: { use: jest.fn(), eject: jest.fn(), clear: jest.fn() },
71
- },
72
- };
73
- // Create a complete mock axios static
74
- const mockAxios = {
75
- create: jest.fn().mockReturnValue(mockAxiosInstance),
76
- request: mockRequest,
77
- get: jest.fn(),
78
- delete: jest.fn(),
79
- head: jest.fn(),
80
- options: jest.fn(),
81
- post: jest.fn(),
82
- put: jest.fn(),
83
- patch: jest.fn(),
84
- all: jest.fn(),
85
- spread: jest.fn(),
86
- isAxiosError: jest.fn(),
87
- isCancel: jest.fn(),
88
- CancelToken: {
89
- source: jest.fn(),
90
- },
91
- VERSION: "1.x",
53
+ const mockFetch = jest.fn();
54
+ const mockFetchWithPayment = jest.fn();
55
+ // Mock x402 client
56
+ const mockX402Client = {
57
+ registerScheme: jest.fn(),
92
58
  };
93
- const mockWithPaymentInterceptor = jest.fn().mockReturnValue(mockAxiosInstance);
94
- const mockDecodeXPaymentResponse = jest.fn();
95
- const mockUseFacilitator = jest.fn();
96
- const mockIsUsdcAsset = jest.fn();
97
- const mockConvertWholeUnitsToAtomic = jest.fn();
98
- const mockFormatPaymentOption = jest.fn();
99
- const mockGetX402Network = jest.fn();
100
- // Override the mocked modules
101
- axios_1.default.create = mockAxios.create;
102
- axios_1.default.request = mockRequest;
103
- axios_1.default.isAxiosError = mockAxios.isAxiosError;
104
- // Mock x402-axios functions
105
- jest.mocked(x402axios.withPaymentInterceptor).mockImplementation(mockWithPaymentInterceptor);
106
- jest.mocked(x402axios.decodeXPaymentResponse).mockImplementation(mockDecodeXPaymentResponse);
107
- jest.mocked(x402Verify.useFacilitator).mockImplementation(mockUseFacilitator);
108
59
  // Mock utils functions
109
- jest.mocked(utils.isUsdcAsset).mockImplementation(mockIsUsdcAsset);
110
- jest.mocked(utils.convertWholeUnitsToAtomic).mockImplementation(mockConvertWholeUnitsToAtomic);
60
+ const mockGetX402Networks = jest.fn();
61
+ const mockHandleHttpError = jest.fn();
62
+ const mockFormatPaymentOption = jest.fn();
63
+ const mockFetchAllDiscoveryResources = jest.fn();
64
+ const mockFilterByNetwork = jest.fn();
65
+ const mockFilterByDescription = jest.fn();
66
+ const mockFilterByX402Version = jest.fn();
67
+ const mockFilterByKeyword = jest.fn();
68
+ const mockFilterByMaxPrice = jest.fn();
69
+ const mockFormatSimplifiedResources = jest.fn();
70
+ const mockBuildUrlWithParams = jest.fn();
71
+ const mockResolveFacilitatorUrl = jest.fn();
72
+ // Setup mocks
73
+ jest
74
+ .mocked(fetch_1.x402Client)
75
+ .mockImplementation(() => mockX402Client);
76
+ jest.mocked(fetch_1.wrapFetchWithPayment).mockReturnValue(mockFetchWithPayment);
77
+ jest
78
+ .mocked(client_1.registerExactEvmScheme)
79
+ .mockImplementation(() => mockX402Client);
80
+ jest
81
+ .mocked(client_2.registerExactSvmScheme)
82
+ .mockImplementation(() => mockX402Client);
83
+ jest.mocked(utils.getX402Networks).mockImplementation(mockGetX402Networks);
84
+ jest.mocked(utils.handleHttpError).mockImplementation(mockHandleHttpError);
111
85
  jest.mocked(utils.formatPaymentOption).mockImplementation(mockFormatPaymentOption);
112
- jest.mocked(utils.getX402Network).mockImplementation(mockGetX402Network);
113
- jest.mocked(utils.handleHttpError).mockImplementation((error, url) => {
114
- return JSON.stringify({
115
- error: true,
116
- message: error instanceof Error ? error.message : "Network error",
117
- url: url,
118
- });
119
- });
86
+ jest.mocked(utils.fetchAllDiscoveryResources).mockImplementation(mockFetchAllDiscoveryResources);
87
+ jest.mocked(utils.filterByNetwork).mockImplementation(mockFilterByNetwork);
88
+ jest.mocked(utils.filterByDescription).mockImplementation(mockFilterByDescription);
89
+ jest.mocked(utils.filterByX402Version).mockImplementation(mockFilterByX402Version);
90
+ jest.mocked(utils.filterByKeyword).mockImplementation(mockFilterByKeyword);
91
+ jest.mocked(utils.filterByMaxPrice).mockImplementation(mockFilterByMaxPrice);
92
+ jest.mocked(utils.formatSimplifiedResources).mockImplementation(mockFormatSimplifiedResources);
93
+ jest.mocked(utils.buildUrlWithParams).mockImplementation(mockBuildUrlWithParams);
94
+ jest.mocked(schemas_1.resolveFacilitatorUrl).mockImplementation(mockResolveFacilitatorUrl);
95
+ // Mock global fetch
96
+ global.fetch = mockFetch;
120
97
  // Mock wallet provider
121
98
  const makeMockWalletProvider = (networkId) => {
122
99
  const mockProvider = Object.create(wallet_providers_1.EvmWalletProvider.prototype);
@@ -126,56 +103,62 @@ const makeMockWalletProvider = (networkId) => {
126
103
  };
127
104
  // Sample responses based on real examples
128
105
  const MOCK_PAYMENT_INFO_RESPONSE = {
129
- paymentRequired: true,
130
- url: "https://www.x402.org/protected",
131
- status: 402,
132
- data: {
133
- x402Version: 1,
134
- error: "X-PAYMENT header is required",
135
- accepts: [
136
- {
137
- scheme: "exact",
138
- network: "base-sepolia",
139
- maxAmountRequired: "10000",
140
- resource: "https://www.x402.org/protected",
141
- description: "Access to protected content",
142
- mimeType: "application/json",
143
- payTo: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
144
- maxTimeoutSeconds: 300,
145
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
146
- extra: {
147
- name: "USDC",
148
- version: "2",
149
- },
106
+ x402Version: 1,
107
+ error: "X-PAYMENT header is required",
108
+ accepts: [
109
+ {
110
+ scheme: "exact",
111
+ network: "base-sepolia",
112
+ maxAmountRequired: "10000",
113
+ resource: "https://www.x402.org/protected",
114
+ description: "Access to protected content",
115
+ mimeType: "application/json",
116
+ payTo: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
117
+ maxTimeoutSeconds: 300,
118
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
119
+ extra: {
120
+ name: "USDC",
121
+ version: "2",
150
122
  },
151
- ],
152
- },
123
+ },
124
+ ],
153
125
  };
154
- const MOCK_PAYMENT_RESPONSE = {
155
- success: true,
126
+ const MOCK_PAYMENT_PROOF = {
156
127
  transaction: "0xcbc385789d3744b52af5106c32809534f64adcbe097e050ec03d6b53fed5d305",
157
128
  network: "base-sepolia",
158
129
  payer: "0xa8c1a5D3C372C65c04f91f87a43F549619A9483f",
159
130
  };
131
+ // Helper to create mock Response
132
+ const createMockResponse = (options) => {
133
+ const headersMap = new Map(Object.entries(options.headers ?? {}));
134
+ return {
135
+ status: options.status,
136
+ statusText: options.statusText ?? "OK",
137
+ ok: options.status >= 200 && options.status < 300,
138
+ headers: {
139
+ get: (name) => headersMap.get(name.toLowerCase()) ?? null,
140
+ },
141
+ json: jest.fn().mockResolvedValue(options.data),
142
+ text: jest.fn().mockResolvedValue(JSON.stringify(options.data)),
143
+ };
144
+ };
160
145
  describe("X402ActionProvider", () => {
161
146
  let provider;
162
147
  beforeEach(() => {
163
148
  provider = new x402ActionProvider_1.X402ActionProvider();
164
149
  jest.clearAllMocks();
165
- // Setup mocks
166
- mockAxios.create.mockReturnValue(mockAxiosInstance);
167
- mockWithPaymentInterceptor.mockReturnValue(mockAxiosInstance);
168
- // Setup axios.isAxiosError mock
169
- jest
170
- .mocked(axios_1.default.isAxiosError)
171
- .mockImplementation((error) => Boolean(error &&
172
- typeof error === "object" &&
173
- ("isAxiosError" in error || "response" in error || "request" in error)));
174
- // Reset all utility mocks to default behavior
175
- mockGetX402Network.mockImplementation(network => network.networkId);
176
- mockIsUsdcAsset.mockReturnValue(false);
177
- mockConvertWholeUnitsToAtomic.mockResolvedValue("100000");
150
+ // Setup default mock behaviors
151
+ mockGetX402Networks.mockImplementation(network => [network.networkId]);
152
+ mockBuildUrlWithParams.mockImplementation(url => url);
153
+ mockHandleHttpError.mockImplementation((error, url) => {
154
+ return JSON.stringify({
155
+ error: true,
156
+ message: error instanceof Error ? error.message : "Network error",
157
+ url: url,
158
+ });
159
+ });
178
160
  mockFormatPaymentOption.mockResolvedValue("mocked payment option");
161
+ mockResolveFacilitatorUrl.mockReturnValue("https://api.cdp.coinbase.com/platform/v2/x402");
179
162
  });
180
163
  afterEach(() => {
181
164
  jest.clearAllMocks();
@@ -204,12 +187,11 @@ describe("X402ActionProvider", () => {
204
187
  });
205
188
  describe("makeHttpRequest", () => {
206
189
  it("should handle successful non-payment requests", async () => {
207
- mockRequest.mockResolvedValue({
190
+ mockFetch.mockResolvedValue(createMockResponse({
208
191
  status: 200,
209
192
  data: { message: "Success" },
210
- headers: {},
211
- config: {},
212
- });
193
+ headers: { "content-type": "application/json" },
194
+ }));
213
195
  const result = await provider.makeHttpRequest(makeMockWalletProvider("base-sepolia"), {
214
196
  url: "https://api.example.com/free",
215
197
  method: "GET",
@@ -220,156 +202,152 @@ describe("X402ActionProvider", () => {
220
202
  expect(parsedResult.data).toEqual({ message: "Success" });
221
203
  });
222
204
  it("should handle 402 responses with payment options", async () => {
223
- mockGetX402Network.mockReturnValue("base-sepolia");
205
+ mockGetX402Networks.mockReturnValue(["base-sepolia"]);
224
206
  mockFormatPaymentOption.mockResolvedValue("10000 USDC on base-sepolia network");
225
- mockRequest.mockResolvedValue({
207
+ mockFetch.mockResolvedValue(createMockResponse({
226
208
  status: 402,
227
- data: MOCK_PAYMENT_INFO_RESPONSE.data,
228
- headers: {},
229
- config: {},
230
- });
209
+ data: MOCK_PAYMENT_INFO_RESPONSE,
210
+ headers: { "content-type": "application/json" },
211
+ }));
231
212
  const result = await provider.makeHttpRequest(makeMockWalletProvider("base-sepolia"), {
232
213
  url: "https://www.x402.org/protected",
233
214
  method: "GET",
234
215
  });
235
216
  const parsedResult = JSON.parse(result);
236
217
  expect(parsedResult.status).toBe("error_402_payment_required");
237
- expect(parsedResult.acceptablePaymentOptions).toEqual(MOCK_PAYMENT_INFO_RESPONSE.data.accepts);
218
+ expect(parsedResult.acceptablePaymentOptions).toEqual(MOCK_PAYMENT_INFO_RESPONSE.accepts);
238
219
  expect(parsedResult.nextSteps).toBeDefined();
239
220
  });
240
221
  it("should handle network errors", async () => {
241
- const error = new Error("Network error");
242
- error.isAxiosError = true;
243
- error.request = {};
244
- mockRequest.mockRejectedValue(error);
222
+ const error = new TypeError("fetch failed");
223
+ mockFetch.mockRejectedValue(error);
245
224
  const result = await provider.makeHttpRequest(makeMockWalletProvider("base-sepolia"), {
246
225
  url: "https://api.example.com/endpoint",
247
226
  method: "GET",
248
227
  });
249
228
  const parsedResult = JSON.parse(result);
250
229
  expect(parsedResult.error).toBe(true);
251
- expect(parsedResult.message).toContain("Network error");
230
+ expect(parsedResult.message).toBeDefined();
252
231
  });
253
232
  });
254
- describe("listX402Services", () => {
233
+ describe("discoverX402Services", () => {
255
234
  it("should list services without filters", async () => {
256
- const mockList = jest.fn().mockResolvedValue({
257
- items: [
258
- {
259
- resource: "https://example.com/service1",
260
- metadata: { category: "test" },
261
- accepts: [
262
- {
263
- asset: "0xUSDC",
264
- maxAmountRequired: "90000",
265
- network: "base-sepolia",
266
- scheme: "exact",
267
- description: "Test service 1",
268
- outputSchema: { type: "object" },
269
- extra: { name: "USDC" },
270
- },
271
- ],
272
- },
273
- ],
235
+ const mockResources = [
236
+ {
237
+ resource: "https://example.com/service1",
238
+ x402Version: 1,
239
+ accepts: [
240
+ {
241
+ asset: "0xUSDC",
242
+ maxAmountRequired: "90000",
243
+ network: "base-sepolia",
244
+ scheme: "exact",
245
+ description: "Test service 1",
246
+ },
247
+ ],
248
+ },
249
+ ];
250
+ const mockSimplified = [
251
+ {
252
+ url: "https://example.com/service1",
253
+ price: "0.09 USDC on base-sepolia",
254
+ description: "Test service 1",
255
+ },
256
+ ];
257
+ mockFetchAllDiscoveryResources.mockResolvedValue(mockResources);
258
+ mockFilterByNetwork.mockReturnValue(mockResources);
259
+ mockFilterByDescription.mockReturnValue(mockResources);
260
+ mockFilterByX402Version.mockReturnValue(mockResources);
261
+ mockFormatSimplifiedResources.mockResolvedValue(mockSimplified);
262
+ mockGetX402Networks.mockReturnValue(["base-sepolia"]);
263
+ const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {
264
+ facilitator: "cdp",
265
+ x402Versions: [1, 2],
274
266
  });
275
- mockUseFacilitator.mockReturnValue({ list: mockList });
276
- mockGetX402Network.mockReturnValue("base-sepolia");
277
- const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {});
278
267
  const parsed = JSON.parse(result);
279
268
  expect(parsed.success).toBe(true);
280
269
  expect(parsed.total).toBe(1);
281
270
  expect(parsed.returned).toBe(1);
282
- expect(parsed.items.length).toBe(1);
271
+ expect(parsed.services.length).toBe(1);
283
272
  });
284
- it("should filter services by asset and maxPrice", async () => {
285
- const mockList = jest.fn().mockResolvedValue({
286
- items: [
287
- {
288
- resource: "https://example.com/service1",
289
- metadata: { category: "test" },
290
- accepts: [
291
- {
292
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Real USDC address for base-sepolia
293
- maxAmountRequired: "90000", // 0.09 USDC (should pass 0.1 filter)
294
- network: "base-sepolia",
295
- scheme: "exact",
296
- description: "Test service 1",
297
- outputSchema: { type: "object" },
298
- extra: { name: "USDC" },
299
- },
300
- ],
301
- },
302
- {
303
- resource: "https://example.com/service2",
304
- metadata: { category: "test" },
305
- accepts: [
306
- {
307
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Real USDC address for base-sepolia
308
- maxAmountRequired: "150000", // 0.15 USDC (should fail 0.1 filter)
309
- network: "base-sepolia",
310
- scheme: "exact",
311
- description: "Test service 2",
312
- outputSchema: { type: "object" },
313
- extra: { name: "USDC" },
314
- },
315
- ],
316
- },
317
- {
318
- resource: "https://example.com/service3",
319
- metadata: { category: "test" },
320
- accepts: [
321
- {
322
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Real USDC address for base-sepolia
323
- maxAmountRequired: "50000", // 0.05 USDC (should pass 0.1 filter)
324
- network: "base-sepolia",
325
- scheme: "exact",
326
- description: "Test service 3",
327
- outputSchema: { type: "object" },
328
- extra: { name: "USDC" },
329
- },
330
- ],
331
- },
332
- ],
333
- });
334
- mockUseFacilitator.mockReturnValue({ list: mockList });
335
- // Mock the utility functions for this test
336
- mockGetX402Network.mockReturnValue("base-sepolia");
337
- mockIsUsdcAsset.mockReturnValue(true); // All assets are USDC
338
- mockConvertWholeUnitsToAtomic
339
- .mockResolvedValueOnce("100000") // 0.1 USDC in atomic units
340
- .mockResolvedValueOnce("100000")
341
- .mockResolvedValueOnce("100000");
342
- mockFormatPaymentOption.mockResolvedValue("formatted payment option");
273
+ it("should filter services by maxPrice", async () => {
274
+ const mockResources = [
275
+ {
276
+ resource: "https://example.com/service1",
277
+ x402Version: 1,
278
+ accepts: [
279
+ {
280
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
281
+ maxAmountRequired: "90000",
282
+ network: "base-sepolia",
283
+ scheme: "exact",
284
+ description: "Test service 1",
285
+ },
286
+ ],
287
+ },
288
+ {
289
+ resource: "https://example.com/service2",
290
+ x402Version: 1,
291
+ accepts: [
292
+ {
293
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
294
+ maxAmountRequired: "150000",
295
+ network: "base-sepolia",
296
+ scheme: "exact",
297
+ description: "Test service 2",
298
+ },
299
+ ],
300
+ },
301
+ ];
302
+ const filteredResources = [mockResources[0]];
303
+ const mockSimplified = [
304
+ {
305
+ url: "https://example.com/service1",
306
+ price: "0.09 USDC on base-sepolia",
307
+ description: "Test service 1",
308
+ },
309
+ ];
310
+ mockFetchAllDiscoveryResources.mockResolvedValue(mockResources);
311
+ mockFilterByNetwork.mockReturnValue(mockResources);
312
+ mockFilterByDescription.mockReturnValue(mockResources);
313
+ mockFilterByX402Version.mockReturnValue(mockResources);
314
+ mockFilterByMaxPrice.mockResolvedValue(filteredResources);
315
+ mockFormatSimplifiedResources.mockResolvedValue(mockSimplified);
316
+ mockGetX402Networks.mockReturnValue(["base-sepolia"]);
343
317
  const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {
318
+ facilitator: "cdp",
344
319
  maxUsdcPrice: 0.1,
320
+ x402Versions: [1, 2],
345
321
  });
346
322
  const parsed = JSON.parse(result);
347
323
  expect(parsed.success).toBe(true);
348
- expect(parsed.returned).toBe(2);
349
- expect(parsed.items.map(item => item.resource)).toEqual(expect.arrayContaining(["https://example.com/service1", "https://example.com/service3"]));
324
+ expect(parsed.returned).toBe(1);
325
+ expect(parsed.services[0].url).toBe("https://example.com/service1");
350
326
  });
351
- it("should handle errors from facilitator", async () => {
352
- const mockList = jest.fn().mockRejectedValue(new Error("boom"));
353
- mockUseFacilitator.mockReturnValue({ list: mockList });
354
- const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {});
327
+ it("should handle errors from discovery", async () => {
328
+ mockFetchAllDiscoveryResources.mockRejectedValue(new Error("boom"));
329
+ const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {
330
+ facilitator: "cdp",
331
+ x402Versions: [1, 2],
332
+ });
355
333
  const parsed = JSON.parse(result);
356
334
  expect(parsed.error).toBe(true);
357
335
  expect(parsed.message).toContain("Failed to list x402 services");
358
336
  });
359
337
  });
360
- describe("retryHttpRequestWithX402", () => {
338
+ describe("retryWithX402", () => {
361
339
  it("should successfully retry with payment", async () => {
362
- mockDecodeXPaymentResponse.mockReturnValue(MOCK_PAYMENT_RESPONSE);
363
- mockGetX402Network.mockReturnValue("base-sepolia");
364
- mockRequest.mockResolvedValue({
340
+ mockGetX402Networks.mockReturnValue(["base-sepolia"]);
341
+ // Encode the payment proof as base64
342
+ const encodedPaymentProof = btoa(JSON.stringify(MOCK_PAYMENT_PROOF));
343
+ mockFetchWithPayment.mockResolvedValue(createMockResponse({
365
344
  status: 200,
366
- statusText: "OK",
367
345
  data: { message: "Paid content" },
368
346
  headers: {
369
- "x-payment-response": "encoded-payment-data",
347
+ "content-type": "application/json",
348
+ "x-payment-response": encodedPaymentProof,
370
349
  },
371
- config: {},
372
- });
350
+ }));
373
351
  const result = await provider.retryWithX402(makeMockWalletProvider("base-sepolia"), {
374
352
  url: "https://www.x402.org/protected",
375
353
  method: "GET",
@@ -380,22 +358,15 @@ describe("X402ActionProvider", () => {
380
358
  asset: "0x456",
381
359
  },
382
360
  });
383
- // Update expectation to accept the payment selector function
384
- expect(mockWithPaymentInterceptor).toHaveBeenCalledWith(mockAxiosInstance, "mock-signer", expect.any(Function));
361
+ expect(fetch_1.wrapFetchWithPayment).toHaveBeenCalledWith(fetch, mockX402Client);
385
362
  const parsedResult = JSON.parse(result);
386
363
  expect(parsedResult.status).toBe("success");
387
- expect(parsedResult.details.paymentProof).toEqual({
388
- transaction: MOCK_PAYMENT_RESPONSE.transaction,
389
- network: MOCK_PAYMENT_RESPONSE.network,
390
- payer: MOCK_PAYMENT_RESPONSE.payer,
391
- });
364
+ expect(parsedResult.details.paymentProof).toEqual(MOCK_PAYMENT_PROOF);
392
365
  });
393
366
  it("should handle network errors during payment", async () => {
394
- const error = new Error("Network error");
395
- error.isAxiosError = true;
396
- error.request = {};
397
- mockRequest.mockRejectedValue(error);
398
- mockGetX402Network.mockReturnValue("base-sepolia");
367
+ const error = new TypeError("fetch failed");
368
+ mockGetX402Networks.mockReturnValue(["base-sepolia"]);
369
+ mockFetchWithPayment.mockRejectedValue(error);
399
370
  const result = await provider.retryWithX402(makeMockWalletProvider("base-sepolia"), {
400
371
  url: "https://www.x402.org/protected",
401
372
  method: "GET",
@@ -408,44 +379,36 @@ describe("X402ActionProvider", () => {
408
379
  });
409
380
  const parsedResult = JSON.parse(result);
410
381
  expect(parsedResult.error).toBe(true);
411
- expect(parsedResult.message).toContain("Network error");
412
382
  });
413
383
  });
414
384
  describe("makeHttpRequestWithX402", () => {
415
385
  it("should handle successful direct payment requests", async () => {
416
- mockDecodeXPaymentResponse.mockReturnValue(MOCK_PAYMENT_RESPONSE);
417
- mockRequest.mockResolvedValue({
386
+ // Encode the payment proof as base64
387
+ const encodedPaymentProof = btoa(JSON.stringify(MOCK_PAYMENT_PROOF));
388
+ mockFetchWithPayment.mockResolvedValue(createMockResponse({
418
389
  status: 200,
419
- statusText: "OK",
420
390
  data: { message: "Paid content" },
421
391
  headers: {
422
- "x-payment-response": "encoded-payment-data",
392
+ "content-type": "application/json",
393
+ "x-payment-response": encodedPaymentProof,
423
394
  },
424
- config: {},
425
- });
395
+ }));
426
396
  const result = await provider.makeHttpRequestWithX402(makeMockWalletProvider("base-sepolia"), {
427
397
  url: "https://www.x402.org/protected",
428
398
  method: "GET",
429
399
  });
430
- expect(mockWithPaymentInterceptor).toHaveBeenCalledWith(mockAxiosInstance, "mock-signer");
400
+ expect(fetch_1.wrapFetchWithPayment).toHaveBeenCalledWith(fetch, mockX402Client);
431
401
  const parsedResult = JSON.parse(result);
432
402
  expect(parsedResult.success).toBe(true);
433
403
  expect(parsedResult.data).toEqual({ message: "Paid content" });
434
- expect(parsedResult.paymentProof).toEqual({
435
- transaction: MOCK_PAYMENT_RESPONSE.transaction,
436
- network: MOCK_PAYMENT_RESPONSE.network,
437
- payer: MOCK_PAYMENT_RESPONSE.payer,
438
- });
404
+ expect(parsedResult.paymentProof).toEqual(MOCK_PAYMENT_PROOF);
439
405
  });
440
406
  it("should handle successful non-payment requests", async () => {
441
- mockDecodeXPaymentResponse.mockReturnValue(null); // No payment made
442
- mockRequest.mockResolvedValue({
407
+ mockFetchWithPayment.mockResolvedValue(createMockResponse({
443
408
  status: 200,
444
- statusText: "OK",
445
409
  data: { message: "Free content" },
446
- headers: {},
447
- config: {},
448
- });
410
+ headers: { "content-type": "application/json" },
411
+ }));
449
412
  const result = await provider.makeHttpRequestWithX402(makeMockWalletProvider("base-sepolia"), {
450
413
  url: "https://api.example.com/free",
451
414
  method: "GET",
@@ -456,17 +419,14 @@ describe("X402ActionProvider", () => {
456
419
  expect(parsedResult.paymentProof).toBeNull();
457
420
  });
458
421
  it("should handle network errors", async () => {
459
- const error = new Error("Network error");
460
- error.isAxiosError = true;
461
- error.request = {};
462
- mockRequest.mockRejectedValue(error);
422
+ const error = new TypeError("fetch failed");
423
+ mockFetchWithPayment.mockRejectedValue(error);
463
424
  const result = await provider.makeHttpRequestWithX402(makeMockWalletProvider("base-sepolia"), {
464
425
  url: "https://api.example.com/endpoint",
465
426
  method: "GET",
466
427
  });
467
428
  const parsedResult = JSON.parse(result);
468
429
  expect(parsedResult.error).toBe(true);
469
- expect(parsedResult.message).toContain("Network error");
470
430
  });
471
431
  });
472
432
  });