@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.
- package/README.md +47 -10
- package/dist/action-providers/across/acrossActionProvider.js +2 -4
- package/dist/action-providers/across/acrossActionProvider.test.js +10 -5
- package/dist/action-providers/baseAccount/baseAccountActionProvider.js +5 -7
- package/dist/action-providers/clanker/utils.d.ts +2 -1
- package/dist/action-providers/clanker/utils.js +2 -2
- package/dist/action-providers/index.d.ts +1 -0
- package/dist/action-providers/index.js +1 -0
- package/dist/action-providers/jupiter/jupiterActionProvider.js +2 -2
- package/dist/action-providers/spl/splActionProvider.js +12 -13
- package/dist/action-providers/superfluid/graphQueries/superfluidGraphQueries.js +2 -2
- package/dist/action-providers/sushi/constants.d.ts +35 -0
- package/dist/action-providers/sushi/constants.js +7 -0
- package/dist/action-providers/sushi/index.d.ts +4 -0
- package/dist/action-providers/sushi/index.js +20 -0
- package/dist/action-providers/sushi/sushiDataActionProvider.d.ts +32 -0
- package/dist/action-providers/sushi/sushiDataActionProvider.js +113 -0
- package/dist/action-providers/sushi/sushiDataSchemas.d.ts +11 -0
- package/dist/action-providers/sushi/sushiDataSchemas.js +16 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.d.ts +40 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.js +386 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.test.d.ts +1 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.test.js +392 -0
- package/dist/action-providers/sushi/sushiRouterSchemas.d.ts +36 -0
- package/dist/action-providers/sushi/sushiRouterSchemas.js +55 -0
- package/dist/action-providers/vaultsfyi/constants.d.ts +8 -12
- package/dist/action-providers/vaultsfyi/constants.js +47 -13
- package/dist/action-providers/vaultsfyi/schemas.d.ts +120 -65
- package/dist/action-providers/vaultsfyi/schemas.js +72 -38
- package/dist/action-providers/vaultsfyi/sdk.d.ts +8 -0
- package/dist/action-providers/vaultsfyi/sdk.js +15 -0
- package/dist/action-providers/vaultsfyi/utils.d.ts +151 -55
- package/dist/action-providers/vaultsfyi/utils.js +29 -75
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.d.ts +55 -16
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.js +413 -217
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.test.js +509 -316
- package/dist/action-providers/x402/constants.d.ts +67 -0
- package/dist/action-providers/x402/constants.js +37 -0
- package/dist/action-providers/x402/schemas.d.ts +45 -5
- package/dist/action-providers/x402/schemas.js +81 -11
- package/dist/action-providers/x402/utils.d.ts +85 -10
- package/dist/action-providers/x402/utils.js +302 -35
- package/dist/action-providers/x402/x402ActionProvider.d.ts +15 -1
- package/dist/action-providers/x402/x402ActionProvider.js +230 -179
- package/dist/action-providers/x402/x402ActionProvider.test.js +222 -262
- package/dist/action-providers/zora/zoraActionProvider.js +4 -5
- package/package.json +10 -7
- package/dist/action-providers/vaultsfyi/api/actions.d.ts +0 -41
- package/dist/action-providers/vaultsfyi/api/actions.js +0 -28
- package/dist/action-providers/vaultsfyi/api/historicalData.d.ts +0 -31
- package/dist/action-providers/vaultsfyi/api/historicalData.js +0 -44
- package/dist/action-providers/vaultsfyi/api/types.d.ts +0 -34
- package/dist/action-providers/vaultsfyi/api/types.js +0 -2
- package/dist/action-providers/vaultsfyi/api/vaults.d.ts +0 -66
- 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
|
|
42
|
-
const
|
|
43
|
-
const
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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.
|
|
110
|
-
jest.
|
|
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.
|
|
113
|
-
jest.mocked(utils.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
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
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
190
|
+
mockFetch.mockResolvedValue(createMockResponse({
|
|
208
191
|
status: 200,
|
|
209
192
|
data: { message: "Success" },
|
|
210
|
-
headers: {},
|
|
211
|
-
|
|
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
|
-
|
|
205
|
+
mockGetX402Networks.mockReturnValue(["base-sepolia"]);
|
|
224
206
|
mockFormatPaymentOption.mockResolvedValue("10000 USDC on base-sepolia network");
|
|
225
|
-
|
|
207
|
+
mockFetch.mockResolvedValue(createMockResponse({
|
|
226
208
|
status: 402,
|
|
227
|
-
data: MOCK_PAYMENT_INFO_RESPONSE
|
|
228
|
-
headers: {},
|
|
229
|
-
|
|
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.
|
|
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
|
|
242
|
-
error
|
|
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).
|
|
230
|
+
expect(parsedResult.message).toBeDefined();
|
|
252
231
|
});
|
|
253
232
|
});
|
|
254
|
-
describe("
|
|
233
|
+
describe("discoverX402Services", () => {
|
|
255
234
|
it("should list services without filters", async () => {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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.
|
|
271
|
+
expect(parsed.services.length).toBe(1);
|
|
283
272
|
});
|
|
284
|
-
it("should filter services by
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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(
|
|
349
|
-
expect(parsed.
|
|
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
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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("
|
|
338
|
+
describe("retryWithX402", () => {
|
|
361
339
|
it("should successfully retry with payment", async () => {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
"
|
|
347
|
+
"content-type": "application/json",
|
|
348
|
+
"x-payment-response": encodedPaymentProof,
|
|
370
349
|
},
|
|
371
|
-
|
|
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
|
-
|
|
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
|
|
395
|
-
|
|
396
|
-
error
|
|
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
|
-
|
|
417
|
-
|
|
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
|
-
"
|
|
392
|
+
"content-type": "application/json",
|
|
393
|
+
"x-payment-response": encodedPaymentProof,
|
|
423
394
|
},
|
|
424
|
-
|
|
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(
|
|
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
|
-
|
|
442
|
-
mockRequest.mockResolvedValue({
|
|
407
|
+
mockFetchWithPayment.mockResolvedValue(createMockResponse({
|
|
443
408
|
status: 200,
|
|
444
|
-
statusText: "OK",
|
|
445
409
|
data: { message: "Free content" },
|
|
446
|
-
headers: {},
|
|
447
|
-
|
|
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
|
|
460
|
-
error
|
|
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
|
});
|