@faremeter/types 0.16.0 → 0.17.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/dist/src/client.d.ts +66 -1
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +52 -1
- package/dist/src/evm.d.ts +18 -0
- package/dist/src/evm.d.ts.map +1 -1
- package/dist/src/evm.js +18 -0
- package/dist/src/facilitator.d.ts +25 -4
- package/dist/src/facilitator.d.ts.map +1 -1
- package/dist/src/facilitator.js +1 -1
- package/dist/src/index.d.ts +43 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +43 -0
- package/dist/src/literal.d.ts +8 -0
- package/dist/src/literal.d.ts.map +1 -1
- package/dist/src/literal.js +8 -0
- package/dist/src/solana.d.ts +58 -0
- package/dist/src/solana.d.ts.map +1 -1
- package/dist/src/solana.js +70 -0
- package/dist/src/validation.d.ts +13 -0
- package/dist/src/validation.d.ts.map +1 -1
- package/dist/src/validation.js +13 -0
- package/dist/src/x402-adapters.d.ts +130 -0
- package/dist/src/x402-adapters.d.ts.map +1 -0
- package/dist/src/x402-adapters.js +297 -0
- package/dist/src/x402-adapters.test.d.ts +3 -0
- package/dist/src/x402-adapters.test.d.ts.map +1 -0
- package/dist/src/x402-adapters.test.js +912 -0
- package/dist/src/x402.d.ts +113 -9
- package/dist/src/x402.d.ts.map +1 -1
- package/dist/src/x402.js +121 -4
- package/dist/src/x402v2.d.ts +190 -0
- package/dist/src/x402v2.d.ts.map +1 -0
- package/dist/src/x402v2.js +81 -0
- package/dist/src/x402v2.test.d.ts +3 -0
- package/dist/src/x402v2.test.d.ts.map +1 -0
- package/dist/src/x402v2.test.js +63 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -1
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
#!/usr/bin/env pnpm tsx
|
|
2
|
+
import t from "tap";
|
|
3
|
+
import { adaptRequirementsV1ToV2, adaptRequirementsV2ToV1, extractResourceInfoV1, adaptPayloadV1ToV2, adaptPaymentRequiredResponseV1ToV2, adaptPaymentRequiredResponseV2ToV1, adaptVerifyResponseV2ToV1, adaptVerifyResponseV1ToV2, adaptSettleResponseV2ToV1, adaptSettleResponseV2ToV1Legacy, adaptSettleResponseV1ToV2, adaptSettleResponseLegacyToV2, adaptSettleResponseLenientToV2, adaptSupportedKindV2ToV1, adaptSupportedKindV1ToV2, } from "./x402-adapters.js";
|
|
4
|
+
// Identity translator for tests that don't care about network translation
|
|
5
|
+
const identity = (n) => n;
|
|
6
|
+
// Mock translator that maps test networks
|
|
7
|
+
const mockTranslator = (n) => {
|
|
8
|
+
if (n === "solana-devnet")
|
|
9
|
+
return "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
|
|
10
|
+
if (n === "base-sepolia")
|
|
11
|
+
return "eip155:84532";
|
|
12
|
+
return n;
|
|
13
|
+
};
|
|
14
|
+
const makeV1Requirements = (overrides) => ({
|
|
15
|
+
scheme: "exact",
|
|
16
|
+
network: "base-sepolia",
|
|
17
|
+
maxAmountRequired: "1000",
|
|
18
|
+
resource: "https://example.com/api",
|
|
19
|
+
description: "Test resource",
|
|
20
|
+
mimeType: "application/json",
|
|
21
|
+
payTo: "0x1234567890abcdef",
|
|
22
|
+
maxTimeoutSeconds: 60,
|
|
23
|
+
asset: "0xUSDC",
|
|
24
|
+
...overrides,
|
|
25
|
+
});
|
|
26
|
+
const makeV2Requirements = (overrides) => ({
|
|
27
|
+
scheme: "exact",
|
|
28
|
+
network: "eip155:84532",
|
|
29
|
+
amount: "1000",
|
|
30
|
+
payTo: "0x1234567890abcdef",
|
|
31
|
+
maxTimeoutSeconds: 60,
|
|
32
|
+
asset: "0xUSDC",
|
|
33
|
+
...overrides,
|
|
34
|
+
});
|
|
35
|
+
const makeResourceInfo = (overrides) => ({
|
|
36
|
+
url: "https://example.com/api",
|
|
37
|
+
...overrides,
|
|
38
|
+
});
|
|
39
|
+
await t.test("adaptRequirementsV1ToV2", async (t) => {
|
|
40
|
+
await t.test("converts maxAmountRequired to amount", (t) => {
|
|
41
|
+
const v1 = makeV1Requirements({ maxAmountRequired: "5000" });
|
|
42
|
+
const v2 = adaptRequirementsV1ToV2(v1, identity);
|
|
43
|
+
t.equal(v2.amount, "5000");
|
|
44
|
+
t.notOk("maxAmountRequired" in v2);
|
|
45
|
+
t.notOk("resource" in v2);
|
|
46
|
+
t.notOk("description" in v2);
|
|
47
|
+
t.notOk("mimeType" in v2);
|
|
48
|
+
t.end();
|
|
49
|
+
});
|
|
50
|
+
await t.test("preserves all other fields", (t) => {
|
|
51
|
+
const v1 = makeV1Requirements();
|
|
52
|
+
const v2 = adaptRequirementsV1ToV2(v1, identity);
|
|
53
|
+
t.equal(v2.scheme, v1.scheme);
|
|
54
|
+
t.equal(v2.network, v1.network);
|
|
55
|
+
t.equal(v2.asset, v1.asset);
|
|
56
|
+
t.equal(v2.payTo, v1.payTo);
|
|
57
|
+
t.equal(v2.maxTimeoutSeconds, v1.maxTimeoutSeconds);
|
|
58
|
+
t.end();
|
|
59
|
+
});
|
|
60
|
+
await t.test("preserves extra field when present", (t) => {
|
|
61
|
+
const extra = { foo: "bar", nested: { value: 42 } };
|
|
62
|
+
const v1 = makeV1Requirements({ extra });
|
|
63
|
+
const v2 = adaptRequirementsV1ToV2(v1, identity);
|
|
64
|
+
t.ok("extra" in v2);
|
|
65
|
+
t.matchOnly(v2.extra, extra);
|
|
66
|
+
t.end();
|
|
67
|
+
});
|
|
68
|
+
await t.test("omits extra field when undefined", (t) => {
|
|
69
|
+
const v1 = makeV1Requirements();
|
|
70
|
+
delete v1.extra;
|
|
71
|
+
const v2 = adaptRequirementsV1ToV2(v1, identity);
|
|
72
|
+
t.notOk("extra" in v2);
|
|
73
|
+
t.end();
|
|
74
|
+
});
|
|
75
|
+
await t.test("applies network translator", (t) => {
|
|
76
|
+
const v1 = makeV1Requirements({ network: "solana-devnet" });
|
|
77
|
+
const v2 = adaptRequirementsV1ToV2(v1, mockTranslator);
|
|
78
|
+
t.equal(v2.network, "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1");
|
|
79
|
+
t.end();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
await t.test("adaptRequirementsV2ToV1", async (t) => {
|
|
83
|
+
await t.test("converts amount to maxAmountRequired", (t) => {
|
|
84
|
+
const v2 = makeV2Requirements({ amount: "2500" });
|
|
85
|
+
const resource = makeResourceInfo({
|
|
86
|
+
description: "A resource",
|
|
87
|
+
mimeType: "text/plain",
|
|
88
|
+
});
|
|
89
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
90
|
+
t.equal(v1.maxAmountRequired, "2500");
|
|
91
|
+
t.notOk("amount" in v1);
|
|
92
|
+
t.end();
|
|
93
|
+
});
|
|
94
|
+
await t.test("populates resource fields from resource info", (t) => {
|
|
95
|
+
const v2 = makeV2Requirements();
|
|
96
|
+
const resource = makeResourceInfo({
|
|
97
|
+
url: "https://api.example.com/data",
|
|
98
|
+
description: "Data endpoint",
|
|
99
|
+
mimeType: "application/json",
|
|
100
|
+
});
|
|
101
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
102
|
+
t.equal(v1.resource, "https://api.example.com/data");
|
|
103
|
+
t.equal(v1.description, "Data endpoint");
|
|
104
|
+
t.equal(v1.mimeType, "application/json");
|
|
105
|
+
t.end();
|
|
106
|
+
});
|
|
107
|
+
await t.test("defaults description and mimeType to empty strings", (t) => {
|
|
108
|
+
const v2 = makeV2Requirements();
|
|
109
|
+
const resource = makeResourceInfo();
|
|
110
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
111
|
+
t.equal(v1.description, "");
|
|
112
|
+
t.equal(v1.mimeType, "");
|
|
113
|
+
t.end();
|
|
114
|
+
});
|
|
115
|
+
await t.test("applies network translator when provided", (t) => {
|
|
116
|
+
const v2 = makeV2Requirements({ network: "eip155:84532" });
|
|
117
|
+
const resource = makeResourceInfo();
|
|
118
|
+
const translator = (n) => n === "eip155:84532" ? "base-sepolia" : n;
|
|
119
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource, translator);
|
|
120
|
+
t.equal(v1.network, "base-sepolia");
|
|
121
|
+
t.end();
|
|
122
|
+
});
|
|
123
|
+
await t.test("uses original network when no translator", (t) => {
|
|
124
|
+
const v2 = makeV2Requirements({ network: "eip155:84532" });
|
|
125
|
+
const resource = makeResourceInfo();
|
|
126
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
127
|
+
t.equal(v1.network, "eip155:84532");
|
|
128
|
+
t.end();
|
|
129
|
+
});
|
|
130
|
+
await t.test("preserves extra field when present", (t) => {
|
|
131
|
+
const extra = { custom: "data" };
|
|
132
|
+
const v2 = makeV2Requirements({ extra });
|
|
133
|
+
const resource = makeResourceInfo();
|
|
134
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
135
|
+
t.ok("extra" in v1);
|
|
136
|
+
t.matchOnly(v1.extra, extra);
|
|
137
|
+
t.end();
|
|
138
|
+
});
|
|
139
|
+
await t.test("omits extra field when undefined", (t) => {
|
|
140
|
+
const v2 = makeV2Requirements();
|
|
141
|
+
const resource = makeResourceInfo();
|
|
142
|
+
const v1 = adaptRequirementsV2ToV1(v2, resource);
|
|
143
|
+
t.notOk("extra" in v1);
|
|
144
|
+
t.end();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
await t.test("extractResourceInfoV1", async (t) => {
|
|
148
|
+
await t.test("extracts all fields when present", (t) => {
|
|
149
|
+
const v1 = makeV1Requirements({
|
|
150
|
+
resource: "https://example.com/resource",
|
|
151
|
+
description: "Full description",
|
|
152
|
+
mimeType: "application/octet-stream",
|
|
153
|
+
});
|
|
154
|
+
const info = extractResourceInfoV1(v1);
|
|
155
|
+
t.equal(info.url, "https://example.com/resource");
|
|
156
|
+
t.equal(info.description, "Full description");
|
|
157
|
+
t.equal(info.mimeType, "application/octet-stream");
|
|
158
|
+
t.end();
|
|
159
|
+
});
|
|
160
|
+
await t.test("omits description when empty string", (t) => {
|
|
161
|
+
const v1 = makeV1Requirements({
|
|
162
|
+
description: "",
|
|
163
|
+
mimeType: "text/html",
|
|
164
|
+
});
|
|
165
|
+
const info = extractResourceInfoV1(v1);
|
|
166
|
+
t.notOk("description" in info);
|
|
167
|
+
t.equal(info.mimeType, "text/html");
|
|
168
|
+
t.end();
|
|
169
|
+
});
|
|
170
|
+
await t.test("omits mimeType when empty string", (t) => {
|
|
171
|
+
const v1 = makeV1Requirements({
|
|
172
|
+
description: "Has description",
|
|
173
|
+
mimeType: "",
|
|
174
|
+
});
|
|
175
|
+
const info = extractResourceInfoV1(v1);
|
|
176
|
+
t.equal(info.description, "Has description");
|
|
177
|
+
t.notOk("mimeType" in info);
|
|
178
|
+
t.end();
|
|
179
|
+
});
|
|
180
|
+
await t.test("returns only url when both are empty", (t) => {
|
|
181
|
+
const v1 = makeV1Requirements({
|
|
182
|
+
description: "",
|
|
183
|
+
mimeType: "",
|
|
184
|
+
});
|
|
185
|
+
const info = extractResourceInfoV1(v1);
|
|
186
|
+
t.equal(info.url, v1.resource);
|
|
187
|
+
t.notOk("description" in info);
|
|
188
|
+
t.notOk("mimeType" in info);
|
|
189
|
+
t.end();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
await t.test("adaptPayloadV1ToV2", async (t) => {
|
|
193
|
+
await t.test("sets x402Version to 2", (t) => {
|
|
194
|
+
const payload = {
|
|
195
|
+
x402Version: 1,
|
|
196
|
+
scheme: "exact",
|
|
197
|
+
network: "base-sepolia",
|
|
198
|
+
payload: { signature: "0xabc" },
|
|
199
|
+
};
|
|
200
|
+
const requirements = makeV1Requirements();
|
|
201
|
+
const v2 = adaptPayloadV1ToV2(payload, requirements, identity);
|
|
202
|
+
t.equal(v2.x402Version, 2);
|
|
203
|
+
t.end();
|
|
204
|
+
});
|
|
205
|
+
await t.test("converts requirements to accepted field", (t) => {
|
|
206
|
+
const payload = {
|
|
207
|
+
x402Version: 1,
|
|
208
|
+
scheme: "exact",
|
|
209
|
+
network: "base-sepolia",
|
|
210
|
+
payload: { signature: "0xabc" },
|
|
211
|
+
};
|
|
212
|
+
const requirements = makeV1Requirements({ maxAmountRequired: "7500" });
|
|
213
|
+
const v2 = adaptPayloadV1ToV2(payload, requirements, identity);
|
|
214
|
+
t.equal(v2.accepted.amount, "7500");
|
|
215
|
+
t.equal(v2.accepted.scheme, requirements.scheme);
|
|
216
|
+
t.end();
|
|
217
|
+
});
|
|
218
|
+
await t.test("extracts resource from requirements", (t) => {
|
|
219
|
+
const payload = {
|
|
220
|
+
x402Version: 1,
|
|
221
|
+
scheme: "exact",
|
|
222
|
+
network: "base-sepolia",
|
|
223
|
+
payload: { data: "test" },
|
|
224
|
+
};
|
|
225
|
+
const requirements = makeV1Requirements({
|
|
226
|
+
resource: "https://api.test.com/endpoint",
|
|
227
|
+
description: "Test API",
|
|
228
|
+
mimeType: "application/json",
|
|
229
|
+
});
|
|
230
|
+
const v2 = adaptPayloadV1ToV2(payload, requirements, identity);
|
|
231
|
+
t.equal(v2.resource?.url, "https://api.test.com/endpoint");
|
|
232
|
+
t.equal(v2.resource?.description, "Test API");
|
|
233
|
+
t.equal(v2.resource?.mimeType, "application/json");
|
|
234
|
+
t.end();
|
|
235
|
+
});
|
|
236
|
+
await t.test("preserves original payload object", (t) => {
|
|
237
|
+
const innerPayload = { signature: "0xdef", nonce: 123 };
|
|
238
|
+
const payload = {
|
|
239
|
+
x402Version: 1,
|
|
240
|
+
scheme: "exact",
|
|
241
|
+
network: "base-sepolia",
|
|
242
|
+
payload: innerPayload,
|
|
243
|
+
};
|
|
244
|
+
const requirements = makeV1Requirements();
|
|
245
|
+
const v2 = adaptPayloadV1ToV2(payload, requirements, identity);
|
|
246
|
+
t.matchOnly(v2.payload, innerPayload);
|
|
247
|
+
t.end();
|
|
248
|
+
});
|
|
249
|
+
await t.test("applies network translator to accepted field", (t) => {
|
|
250
|
+
const payload = {
|
|
251
|
+
x402Version: 1,
|
|
252
|
+
scheme: "exact",
|
|
253
|
+
network: "base-sepolia",
|
|
254
|
+
payload: { signature: "0xabc" },
|
|
255
|
+
};
|
|
256
|
+
const requirements = makeV1Requirements({ network: "solana-devnet" });
|
|
257
|
+
const v2 = adaptPayloadV1ToV2(payload, requirements, mockTranslator);
|
|
258
|
+
t.equal(v2.accepted.network, "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1");
|
|
259
|
+
t.end();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
await t.test("adaptPaymentRequiredResponseV1ToV2", async (t) => {
|
|
263
|
+
await t.test("sets x402Version to 2", (t) => {
|
|
264
|
+
const v1Response = {
|
|
265
|
+
x402Version: 1,
|
|
266
|
+
accepts: [makeV1Requirements()],
|
|
267
|
+
};
|
|
268
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", identity);
|
|
269
|
+
t.equal(v2.x402Version, 2);
|
|
270
|
+
t.end();
|
|
271
|
+
});
|
|
272
|
+
await t.test("converts single accept", (t) => {
|
|
273
|
+
const v1Response = {
|
|
274
|
+
x402Version: 1,
|
|
275
|
+
accepts: [makeV1Requirements({ maxAmountRequired: "3000" })],
|
|
276
|
+
};
|
|
277
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", identity);
|
|
278
|
+
t.equal(v2.accepts.length, 1);
|
|
279
|
+
t.equal(v2.accepts[0]?.amount, "3000");
|
|
280
|
+
t.end();
|
|
281
|
+
});
|
|
282
|
+
await t.test("converts multiple accepts", (t) => {
|
|
283
|
+
const v1Response = {
|
|
284
|
+
x402Version: 1,
|
|
285
|
+
accepts: [
|
|
286
|
+
makeV1Requirements({ scheme: "exact", maxAmountRequired: "1000" }),
|
|
287
|
+
makeV1Requirements({ scheme: "upto", maxAmountRequired: "2000" }),
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", identity);
|
|
291
|
+
t.equal(v2.accepts.length, 2);
|
|
292
|
+
t.equal(v2.accepts[0]?.scheme, "exact");
|
|
293
|
+
t.equal(v2.accepts[0]?.amount, "1000");
|
|
294
|
+
t.equal(v2.accepts[1]?.scheme, "upto");
|
|
295
|
+
t.equal(v2.accepts[1]?.amount, "2000");
|
|
296
|
+
t.end();
|
|
297
|
+
});
|
|
298
|
+
await t.test("extracts resource info from first accept", (t) => {
|
|
299
|
+
const v1Response = {
|
|
300
|
+
x402Version: 1,
|
|
301
|
+
accepts: [
|
|
302
|
+
makeV1Requirements({
|
|
303
|
+
description: "First description",
|
|
304
|
+
mimeType: "text/plain",
|
|
305
|
+
}),
|
|
306
|
+
makeV1Requirements({
|
|
307
|
+
description: "Second description",
|
|
308
|
+
mimeType: "application/json",
|
|
309
|
+
}),
|
|
310
|
+
],
|
|
311
|
+
};
|
|
312
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://resource.url", identity);
|
|
313
|
+
t.equal(v2.resource.url, "https://resource.url");
|
|
314
|
+
t.equal(v2.resource.description, "First description");
|
|
315
|
+
t.equal(v2.resource.mimeType, "text/plain");
|
|
316
|
+
t.end();
|
|
317
|
+
});
|
|
318
|
+
await t.test("preserves error when present", (t) => {
|
|
319
|
+
const v1Response = {
|
|
320
|
+
x402Version: 1,
|
|
321
|
+
accepts: [makeV1Requirements()],
|
|
322
|
+
error: "Payment failed",
|
|
323
|
+
};
|
|
324
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", identity);
|
|
325
|
+
t.equal(v2.error, "Payment failed");
|
|
326
|
+
t.end();
|
|
327
|
+
});
|
|
328
|
+
await t.test("omits error when not present", (t) => {
|
|
329
|
+
const v1Response = {
|
|
330
|
+
x402Version: 1,
|
|
331
|
+
accepts: [makeV1Requirements()],
|
|
332
|
+
};
|
|
333
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", identity);
|
|
334
|
+
t.notOk("error" in v2);
|
|
335
|
+
t.end();
|
|
336
|
+
});
|
|
337
|
+
await t.test("handles empty accepts array", (t) => {
|
|
338
|
+
const v1Response = {
|
|
339
|
+
x402Version: 1,
|
|
340
|
+
accepts: [],
|
|
341
|
+
};
|
|
342
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://fallback.url", identity);
|
|
343
|
+
t.equal(v2.accepts.length, 0);
|
|
344
|
+
t.equal(v2.resource.url, "https://fallback.url");
|
|
345
|
+
t.notOk("description" in v2.resource);
|
|
346
|
+
t.notOk("mimeType" in v2.resource);
|
|
347
|
+
t.end();
|
|
348
|
+
});
|
|
349
|
+
await t.test("applies network translator to all accepts", (t) => {
|
|
350
|
+
const v1Response = {
|
|
351
|
+
x402Version: 1,
|
|
352
|
+
accepts: [
|
|
353
|
+
makeV1Requirements({ network: "solana-devnet" }),
|
|
354
|
+
makeV1Requirements({ network: "base-sepolia" }),
|
|
355
|
+
],
|
|
356
|
+
};
|
|
357
|
+
const v2 = adaptPaymentRequiredResponseV1ToV2(v1Response, "https://example.com", mockTranslator);
|
|
358
|
+
t.equal(v2.accepts[0]?.network, "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1");
|
|
359
|
+
t.equal(v2.accepts[1]?.network, "eip155:84532");
|
|
360
|
+
t.end();
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
await t.test("adaptPaymentRequiredResponseV2ToV1", async (t) => {
|
|
364
|
+
await t.test("sets x402Version to 1", (t) => {
|
|
365
|
+
const v2Response = {
|
|
366
|
+
x402Version: 2,
|
|
367
|
+
resource: makeResourceInfo(),
|
|
368
|
+
accepts: [makeV2Requirements()],
|
|
369
|
+
};
|
|
370
|
+
const v1 = adaptPaymentRequiredResponseV2ToV1(v2Response);
|
|
371
|
+
t.equal(v1.x402Version, 1);
|
|
372
|
+
t.end();
|
|
373
|
+
});
|
|
374
|
+
await t.test("converts accepts with resource info", (t) => {
|
|
375
|
+
const v2Response = {
|
|
376
|
+
x402Version: 2,
|
|
377
|
+
resource: makeResourceInfo({
|
|
378
|
+
url: "https://api.com/data",
|
|
379
|
+
description: "Data API",
|
|
380
|
+
mimeType: "application/json",
|
|
381
|
+
}),
|
|
382
|
+
accepts: [makeV2Requirements({ amount: "4000" })],
|
|
383
|
+
};
|
|
384
|
+
const v1 = adaptPaymentRequiredResponseV2ToV1(v2Response);
|
|
385
|
+
t.equal(v1.accepts.length, 1);
|
|
386
|
+
t.equal(v1.accepts[0]?.maxAmountRequired, "4000");
|
|
387
|
+
t.equal(v1.accepts[0]?.resource, "https://api.com/data");
|
|
388
|
+
t.equal(v1.accepts[0]?.description, "Data API");
|
|
389
|
+
t.end();
|
|
390
|
+
});
|
|
391
|
+
await t.test("applies network translator to all accepts", (t) => {
|
|
392
|
+
const v2Response = {
|
|
393
|
+
x402Version: 2,
|
|
394
|
+
resource: makeResourceInfo(),
|
|
395
|
+
accepts: [
|
|
396
|
+
makeV2Requirements({ network: "eip155:84532" }),
|
|
397
|
+
makeV2Requirements({ network: "eip155:8453" }),
|
|
398
|
+
],
|
|
399
|
+
};
|
|
400
|
+
const translator = (n) => {
|
|
401
|
+
if (n === "eip155:84532")
|
|
402
|
+
return "base-sepolia";
|
|
403
|
+
if (n === "eip155:8453")
|
|
404
|
+
return "base";
|
|
405
|
+
return n;
|
|
406
|
+
};
|
|
407
|
+
const v1 = adaptPaymentRequiredResponseV2ToV1(v2Response, translator);
|
|
408
|
+
t.equal(v1.accepts[0]?.network, "base-sepolia");
|
|
409
|
+
t.equal(v1.accepts[1]?.network, "base");
|
|
410
|
+
t.end();
|
|
411
|
+
});
|
|
412
|
+
await t.test("preserves error when present", (t) => {
|
|
413
|
+
const v2Response = {
|
|
414
|
+
x402Version: 2,
|
|
415
|
+
resource: makeResourceInfo(),
|
|
416
|
+
accepts: [makeV2Requirements()],
|
|
417
|
+
error: "Insufficient funds",
|
|
418
|
+
};
|
|
419
|
+
const v1 = adaptPaymentRequiredResponseV2ToV1(v2Response);
|
|
420
|
+
t.equal(v1.error, "Insufficient funds");
|
|
421
|
+
t.end();
|
|
422
|
+
});
|
|
423
|
+
await t.test("uses empty string for error when not present", (t) => {
|
|
424
|
+
const v2Response = {
|
|
425
|
+
x402Version: 2,
|
|
426
|
+
resource: makeResourceInfo(),
|
|
427
|
+
accepts: [makeV2Requirements()],
|
|
428
|
+
};
|
|
429
|
+
const v1 = adaptPaymentRequiredResponseV2ToV1(v2Response);
|
|
430
|
+
t.equal(v1.error, "");
|
|
431
|
+
t.end();
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
await t.test("adaptVerifyResponseV2ToV1", async (t) => {
|
|
435
|
+
await t.test("converts valid response", (t) => {
|
|
436
|
+
const v2 = { isValid: true };
|
|
437
|
+
const v1 = adaptVerifyResponseV2ToV1(v2);
|
|
438
|
+
t.equal(v1.isValid, true);
|
|
439
|
+
t.notOk("invalidReason" in v1);
|
|
440
|
+
t.end();
|
|
441
|
+
});
|
|
442
|
+
await t.test("converts invalid response with reason", (t) => {
|
|
443
|
+
const v2 = { isValid: false, invalidReason: "Signature mismatch" };
|
|
444
|
+
const v1 = adaptVerifyResponseV2ToV1(v2);
|
|
445
|
+
t.equal(v1.isValid, false);
|
|
446
|
+
t.equal(v1.invalidReason, "Signature mismatch");
|
|
447
|
+
t.end();
|
|
448
|
+
});
|
|
449
|
+
await t.test("preserves payer field when present", (t) => {
|
|
450
|
+
const v2 = { isValid: true, payer: "0xPayerAddress" };
|
|
451
|
+
const v1 = adaptVerifyResponseV2ToV1(v2);
|
|
452
|
+
t.equal(v1.isValid, true);
|
|
453
|
+
t.equal(v1.payer, "0xPayerAddress");
|
|
454
|
+
t.end();
|
|
455
|
+
});
|
|
456
|
+
await t.test("uses empty string for payer when not present", (t) => {
|
|
457
|
+
const v2 = { isValid: true };
|
|
458
|
+
const v1 = adaptVerifyResponseV2ToV1(v2);
|
|
459
|
+
t.equal(v1.isValid, true);
|
|
460
|
+
t.equal(v1.payer, "");
|
|
461
|
+
t.end();
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
await t.test("adaptVerifyResponseV1ToV2", async (t) => {
|
|
465
|
+
await t.test("converts valid response", (t) => {
|
|
466
|
+
const v1 = { isValid: true };
|
|
467
|
+
const v2 = adaptVerifyResponseV1ToV2(v1);
|
|
468
|
+
t.equal(v2.isValid, true);
|
|
469
|
+
t.notOk("invalidReason" in v2);
|
|
470
|
+
t.end();
|
|
471
|
+
});
|
|
472
|
+
await t.test("converts invalid response with reason", (t) => {
|
|
473
|
+
const v1 = { isValid: false, invalidReason: "Expired transaction" };
|
|
474
|
+
const v2 = adaptVerifyResponseV1ToV2(v1);
|
|
475
|
+
t.equal(v2.isValid, false);
|
|
476
|
+
t.equal(v2.invalidReason, "Expired transaction");
|
|
477
|
+
t.end();
|
|
478
|
+
});
|
|
479
|
+
await t.test("preserves null invalidReason", (t) => {
|
|
480
|
+
const v1 = { isValid: false, invalidReason: null };
|
|
481
|
+
const v2 = adaptVerifyResponseV1ToV2(v1);
|
|
482
|
+
t.equal(v2.isValid, false);
|
|
483
|
+
t.notOk("invalidReason" in v2);
|
|
484
|
+
t.end();
|
|
485
|
+
});
|
|
486
|
+
await t.test("preserves payer field when present", (t) => {
|
|
487
|
+
const v1 = { isValid: true, payer: "0xPayerAddress" };
|
|
488
|
+
const v2 = adaptVerifyResponseV1ToV2(v1);
|
|
489
|
+
t.equal(v2.isValid, true);
|
|
490
|
+
t.equal(v2.payer, "0xPayerAddress");
|
|
491
|
+
t.end();
|
|
492
|
+
});
|
|
493
|
+
await t.test("omits payer field when not present", (t) => {
|
|
494
|
+
const v1 = { isValid: true };
|
|
495
|
+
const v2 = adaptVerifyResponseV1ToV2(v1);
|
|
496
|
+
t.notOk("payer" in v2);
|
|
497
|
+
t.end();
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
await t.test("adaptSettleResponseV2ToV1", async (t) => {
|
|
501
|
+
await t.test("converts success response with spec-compliant field names", (t) => {
|
|
502
|
+
const v2 = {
|
|
503
|
+
success: true,
|
|
504
|
+
transaction: "0xTransactionHash",
|
|
505
|
+
network: "eip155:84532",
|
|
506
|
+
};
|
|
507
|
+
const v1 = adaptSettleResponseV2ToV1(v2);
|
|
508
|
+
t.equal(v1.success, true);
|
|
509
|
+
t.equal(v1.transaction, "0xTransactionHash");
|
|
510
|
+
t.equal(v1.network, "eip155:84532");
|
|
511
|
+
t.end();
|
|
512
|
+
});
|
|
513
|
+
await t.test("converts failure response with errorReason", (t) => {
|
|
514
|
+
const v2 = {
|
|
515
|
+
success: false,
|
|
516
|
+
transaction: "",
|
|
517
|
+
network: "eip155:84532",
|
|
518
|
+
errorReason: "Transaction reverted",
|
|
519
|
+
};
|
|
520
|
+
const v1 = adaptSettleResponseV2ToV1(v2);
|
|
521
|
+
t.equal(v1.success, false);
|
|
522
|
+
t.equal(v1.errorReason, "Transaction reverted");
|
|
523
|
+
t.end();
|
|
524
|
+
});
|
|
525
|
+
await t.test("applies network translator", (t) => {
|
|
526
|
+
const v2 = {
|
|
527
|
+
success: true,
|
|
528
|
+
transaction: "0xHash",
|
|
529
|
+
network: "eip155:84532",
|
|
530
|
+
};
|
|
531
|
+
const translator = (n) => n === "eip155:84532" ? "base-sepolia" : n;
|
|
532
|
+
const v1 = adaptSettleResponseV2ToV1(v2, translator);
|
|
533
|
+
t.equal(v1.network, "base-sepolia");
|
|
534
|
+
t.end();
|
|
535
|
+
});
|
|
536
|
+
await t.test("transfers payer field when present", (t) => {
|
|
537
|
+
const v2 = {
|
|
538
|
+
success: true,
|
|
539
|
+
transaction: "0xHash",
|
|
540
|
+
network: "eip155:84532",
|
|
541
|
+
payer: "0xPayerAddress",
|
|
542
|
+
};
|
|
543
|
+
const v1 = adaptSettleResponseV2ToV1(v2);
|
|
544
|
+
t.equal(v1.payer, "0xPayerAddress");
|
|
545
|
+
t.end();
|
|
546
|
+
});
|
|
547
|
+
await t.test("uses empty string for payer when not present in v2", (t) => {
|
|
548
|
+
const v2 = {
|
|
549
|
+
success: true,
|
|
550
|
+
transaction: "0xHash",
|
|
551
|
+
network: "eip155:84532",
|
|
552
|
+
};
|
|
553
|
+
const v1 = adaptSettleResponseV2ToV1(v2);
|
|
554
|
+
t.equal(v1.payer, "", "payer should be empty string when not in v2");
|
|
555
|
+
t.end();
|
|
556
|
+
});
|
|
557
|
+
await t.test("omits errorReason when not present", (t) => {
|
|
558
|
+
const v2 = {
|
|
559
|
+
success: true,
|
|
560
|
+
transaction: "0xHash",
|
|
561
|
+
network: "eip155:84532",
|
|
562
|
+
};
|
|
563
|
+
const v1 = adaptSettleResponseV2ToV1(v2);
|
|
564
|
+
t.notOk("errorReason" in v1);
|
|
565
|
+
t.end();
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
await t.test("adaptSettleResponseV2ToV1Legacy", async (t) => {
|
|
569
|
+
await t.test("converts success response with legacy field names", (t) => {
|
|
570
|
+
const v2 = {
|
|
571
|
+
success: true,
|
|
572
|
+
transaction: "0xTransactionHash",
|
|
573
|
+
network: "eip155:84532",
|
|
574
|
+
};
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
576
|
+
const v1Legacy = adaptSettleResponseV2ToV1Legacy(v2);
|
|
577
|
+
t.equal(v1Legacy.success, true);
|
|
578
|
+
t.equal(v1Legacy.txHash, "0xTransactionHash");
|
|
579
|
+
t.equal(v1Legacy.networkId, "eip155:84532");
|
|
580
|
+
t.end();
|
|
581
|
+
});
|
|
582
|
+
await t.test("converts failure response with error field", (t) => {
|
|
583
|
+
const v2 = {
|
|
584
|
+
success: false,
|
|
585
|
+
transaction: "",
|
|
586
|
+
network: "eip155:84532",
|
|
587
|
+
errorReason: "Transaction reverted",
|
|
588
|
+
};
|
|
589
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
590
|
+
const v1Legacy = adaptSettleResponseV2ToV1Legacy(v2);
|
|
591
|
+
t.equal(v1Legacy.success, false);
|
|
592
|
+
t.equal(v1Legacy.error, "Transaction reverted");
|
|
593
|
+
t.end();
|
|
594
|
+
});
|
|
595
|
+
await t.test("applies network translator", (t) => {
|
|
596
|
+
const v2 = {
|
|
597
|
+
success: true,
|
|
598
|
+
transaction: "0xHash",
|
|
599
|
+
network: "eip155:84532",
|
|
600
|
+
};
|
|
601
|
+
const translator = (n) => n === "eip155:84532" ? "base-sepolia" : n;
|
|
602
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
603
|
+
const v1Legacy = adaptSettleResponseV2ToV1Legacy(v2, translator);
|
|
604
|
+
t.equal(v1Legacy.networkId, "base-sepolia");
|
|
605
|
+
t.end();
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
await t.test("adaptSettleResponseV1ToV2", async (t) => {
|
|
609
|
+
await t.test("converts success response with spec-compliant field names", (t) => {
|
|
610
|
+
const v1 = {
|
|
611
|
+
success: true,
|
|
612
|
+
transaction: "0xSuccessHash",
|
|
613
|
+
network: "base-sepolia",
|
|
614
|
+
};
|
|
615
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
616
|
+
t.equal(v2.success, true);
|
|
617
|
+
t.equal(v2.transaction, "0xSuccessHash");
|
|
618
|
+
t.equal(v2.network, "base-sepolia");
|
|
619
|
+
t.end();
|
|
620
|
+
});
|
|
621
|
+
await t.test("throws on missing network", (t) => {
|
|
622
|
+
const v1 = {
|
|
623
|
+
success: true,
|
|
624
|
+
transaction: "0xHash",
|
|
625
|
+
network: null,
|
|
626
|
+
};
|
|
627
|
+
t.throws(() => adaptSettleResponseV1ToV2(v1), /missing network/);
|
|
628
|
+
t.end();
|
|
629
|
+
});
|
|
630
|
+
await t.test("throws on success with missing transaction", (t) => {
|
|
631
|
+
const v1 = {
|
|
632
|
+
success: true,
|
|
633
|
+
transaction: null,
|
|
634
|
+
network: "base-sepolia",
|
|
635
|
+
};
|
|
636
|
+
t.throws(() => adaptSettleResponseV1ToV2(v1), /missing transaction/);
|
|
637
|
+
t.end();
|
|
638
|
+
});
|
|
639
|
+
await t.test("uses empty string for null transaction on failure", (t) => {
|
|
640
|
+
const v1 = {
|
|
641
|
+
success: false,
|
|
642
|
+
transaction: null,
|
|
643
|
+
network: "base-sepolia",
|
|
644
|
+
errorReason: "Failed",
|
|
645
|
+
};
|
|
646
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
647
|
+
t.equal(v2.success, false);
|
|
648
|
+
t.equal(v2.transaction, "");
|
|
649
|
+
t.end();
|
|
650
|
+
});
|
|
651
|
+
await t.test("passes through errorReason", (t) => {
|
|
652
|
+
const v1 = {
|
|
653
|
+
success: false,
|
|
654
|
+
transaction: null,
|
|
655
|
+
network: "base-sepolia",
|
|
656
|
+
errorReason: "Settlement failed",
|
|
657
|
+
};
|
|
658
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
659
|
+
t.equal(v2.errorReason, "Settlement failed");
|
|
660
|
+
t.end();
|
|
661
|
+
});
|
|
662
|
+
await t.test("omits errorReason when null", (t) => {
|
|
663
|
+
const v1 = {
|
|
664
|
+
success: false,
|
|
665
|
+
transaction: "",
|
|
666
|
+
network: "base-sepolia",
|
|
667
|
+
errorReason: null,
|
|
668
|
+
};
|
|
669
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
670
|
+
t.notOk("errorReason" in v2);
|
|
671
|
+
t.end();
|
|
672
|
+
});
|
|
673
|
+
await t.test("omits errorReason when undefined", (t) => {
|
|
674
|
+
const v1 = {
|
|
675
|
+
success: false,
|
|
676
|
+
transaction: "",
|
|
677
|
+
network: "base-sepolia",
|
|
678
|
+
};
|
|
679
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
680
|
+
t.notOk("errorReason" in v2);
|
|
681
|
+
t.end();
|
|
682
|
+
});
|
|
683
|
+
await t.test("preserves payer field when present", (t) => {
|
|
684
|
+
const v1 = {
|
|
685
|
+
success: true,
|
|
686
|
+
transaction: "0xHash",
|
|
687
|
+
network: "base-sepolia",
|
|
688
|
+
payer: "0xPayerAddress",
|
|
689
|
+
};
|
|
690
|
+
const v2 = adaptSettleResponseV1ToV2(v1);
|
|
691
|
+
t.equal(v2.payer, "0xPayerAddress");
|
|
692
|
+
t.end();
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
await t.test("adaptSettleResponseLegacyToV2", async (t) => {
|
|
696
|
+
await t.test("converts legacy success response", (t) => {
|
|
697
|
+
const v1Legacy = {
|
|
698
|
+
success: true,
|
|
699
|
+
txHash: "0xSuccessHash",
|
|
700
|
+
networkId: "base-sepolia",
|
|
701
|
+
};
|
|
702
|
+
const v2 = adaptSettleResponseLegacyToV2(v1Legacy);
|
|
703
|
+
t.equal(v2.success, true);
|
|
704
|
+
t.equal(v2.transaction, "0xSuccessHash");
|
|
705
|
+
t.equal(v2.network, "base-sepolia");
|
|
706
|
+
t.end();
|
|
707
|
+
});
|
|
708
|
+
await t.test("throws on missing networkId", (t) => {
|
|
709
|
+
const v1Legacy = {
|
|
710
|
+
success: true,
|
|
711
|
+
txHash: "0xHash",
|
|
712
|
+
networkId: null,
|
|
713
|
+
};
|
|
714
|
+
t.throws(() => adaptSettleResponseLegacyToV2(v1Legacy), /missing networkId/);
|
|
715
|
+
t.end();
|
|
716
|
+
});
|
|
717
|
+
await t.test("throws on success with missing txHash", (t) => {
|
|
718
|
+
const v1Legacy = {
|
|
719
|
+
success: true,
|
|
720
|
+
txHash: null,
|
|
721
|
+
networkId: "base-sepolia",
|
|
722
|
+
};
|
|
723
|
+
t.throws(() => adaptSettleResponseLegacyToV2(v1Legacy), /missing txHash/);
|
|
724
|
+
t.end();
|
|
725
|
+
});
|
|
726
|
+
await t.test("maps error to errorReason", (t) => {
|
|
727
|
+
const v1Legacy = {
|
|
728
|
+
success: false,
|
|
729
|
+
txHash: null,
|
|
730
|
+
networkId: "base-sepolia",
|
|
731
|
+
error: "Settlement failed",
|
|
732
|
+
};
|
|
733
|
+
const v2 = adaptSettleResponseLegacyToV2(v1Legacy);
|
|
734
|
+
t.equal(v2.errorReason, "Settlement failed");
|
|
735
|
+
t.end();
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
await t.test("adaptSettleResponseLenientToV2", async (t) => {
|
|
739
|
+
await t.test("converts spec-compliant input", (t) => {
|
|
740
|
+
const lenient = {
|
|
741
|
+
success: true,
|
|
742
|
+
transaction: "0xSpecHash",
|
|
743
|
+
network: "base-sepolia",
|
|
744
|
+
};
|
|
745
|
+
const v2 = adaptSettleResponseLenientToV2(lenient);
|
|
746
|
+
t.equal(v2.success, true);
|
|
747
|
+
t.equal(v2.transaction, "0xSpecHash");
|
|
748
|
+
t.equal(v2.network, "base-sepolia");
|
|
749
|
+
t.end();
|
|
750
|
+
});
|
|
751
|
+
await t.test("converts legacy input", (t) => {
|
|
752
|
+
const lenient = {
|
|
753
|
+
success: true,
|
|
754
|
+
txHash: "0xLegacyHash",
|
|
755
|
+
networkId: "base-sepolia",
|
|
756
|
+
};
|
|
757
|
+
const v2 = adaptSettleResponseLenientToV2(lenient);
|
|
758
|
+
t.equal(v2.success, true);
|
|
759
|
+
t.equal(v2.transaction, "0xLegacyHash");
|
|
760
|
+
t.equal(v2.network, "base-sepolia");
|
|
761
|
+
t.end();
|
|
762
|
+
});
|
|
763
|
+
await t.test("prefers spec-compliant fields when both present", (t) => {
|
|
764
|
+
const lenient = {
|
|
765
|
+
success: true,
|
|
766
|
+
transaction: "0xSpecHash",
|
|
767
|
+
txHash: "0xLegacyHash",
|
|
768
|
+
network: "spec-network",
|
|
769
|
+
networkId: "legacy-network",
|
|
770
|
+
};
|
|
771
|
+
const v2 = adaptSettleResponseLenientToV2(lenient);
|
|
772
|
+
t.equal(v2.transaction, "0xSpecHash");
|
|
773
|
+
t.equal(v2.network, "spec-network");
|
|
774
|
+
t.end();
|
|
775
|
+
});
|
|
776
|
+
await t.test("maps legacy error to errorReason", (t) => {
|
|
777
|
+
const lenient = {
|
|
778
|
+
success: false,
|
|
779
|
+
txHash: null,
|
|
780
|
+
networkId: "base-sepolia",
|
|
781
|
+
error: "Legacy error",
|
|
782
|
+
};
|
|
783
|
+
const v2 = adaptSettleResponseLenientToV2(lenient);
|
|
784
|
+
t.equal(v2.errorReason, "Legacy error");
|
|
785
|
+
t.end();
|
|
786
|
+
});
|
|
787
|
+
await t.test("prefers errorReason over error when both present", (t) => {
|
|
788
|
+
const lenient = {
|
|
789
|
+
success: false,
|
|
790
|
+
transaction: "",
|
|
791
|
+
network: "base-sepolia",
|
|
792
|
+
errorReason: "Spec error",
|
|
793
|
+
error: "Legacy error",
|
|
794
|
+
};
|
|
795
|
+
const v2 = adaptSettleResponseLenientToV2(lenient);
|
|
796
|
+
t.equal(v2.errorReason, "Spec error");
|
|
797
|
+
t.end();
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
await t.test("adaptSupportedKindV2ToV1", async (t) => {
|
|
801
|
+
await t.test("sets x402Version to 1", (t) => {
|
|
802
|
+
const v2 = {
|
|
803
|
+
x402Version: 2,
|
|
804
|
+
scheme: "exact",
|
|
805
|
+
network: "eip155:84532",
|
|
806
|
+
};
|
|
807
|
+
const v1 = adaptSupportedKindV2ToV1(v2);
|
|
808
|
+
t.equal(v1.x402Version, 1);
|
|
809
|
+
t.end();
|
|
810
|
+
});
|
|
811
|
+
await t.test("preserves scheme and network", (t) => {
|
|
812
|
+
const v2 = {
|
|
813
|
+
x402Version: 2,
|
|
814
|
+
scheme: "upto",
|
|
815
|
+
network: "solana:devnet",
|
|
816
|
+
};
|
|
817
|
+
const v1 = adaptSupportedKindV2ToV1(v2);
|
|
818
|
+
t.equal(v1.scheme, "upto");
|
|
819
|
+
t.equal(v1.network, "solana:devnet");
|
|
820
|
+
t.end();
|
|
821
|
+
});
|
|
822
|
+
await t.test("applies network translator", (t) => {
|
|
823
|
+
const v2 = {
|
|
824
|
+
x402Version: 2,
|
|
825
|
+
scheme: "exact",
|
|
826
|
+
network: "eip155:84532",
|
|
827
|
+
};
|
|
828
|
+
const translator = (n) => n === "eip155:84532" ? "base-sepolia" : n;
|
|
829
|
+
const v1 = adaptSupportedKindV2ToV1(v2, translator);
|
|
830
|
+
t.equal(v1.network, "base-sepolia");
|
|
831
|
+
t.end();
|
|
832
|
+
});
|
|
833
|
+
await t.test("preserves extra field when present", (t) => {
|
|
834
|
+
const extra = { capability: "streaming" };
|
|
835
|
+
const v2 = {
|
|
836
|
+
x402Version: 2,
|
|
837
|
+
scheme: "exact",
|
|
838
|
+
network: "eip155:84532",
|
|
839
|
+
extra,
|
|
840
|
+
};
|
|
841
|
+
const v1 = adaptSupportedKindV2ToV1(v2);
|
|
842
|
+
t.ok("extra" in v1);
|
|
843
|
+
t.matchOnly(v1.extra, extra);
|
|
844
|
+
t.end();
|
|
845
|
+
});
|
|
846
|
+
await t.test("omits extra field when undefined", (t) => {
|
|
847
|
+
const v2 = {
|
|
848
|
+
x402Version: 2,
|
|
849
|
+
scheme: "exact",
|
|
850
|
+
network: "eip155:84532",
|
|
851
|
+
};
|
|
852
|
+
const v1 = adaptSupportedKindV2ToV1(v2);
|
|
853
|
+
t.notOk("extra" in v1);
|
|
854
|
+
t.end();
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
await t.test("adaptSupportedKindV1ToV2", async (t) => {
|
|
858
|
+
await t.test("sets x402Version to 2", (t) => {
|
|
859
|
+
const v1 = {
|
|
860
|
+
x402Version: 1,
|
|
861
|
+
scheme: "exact",
|
|
862
|
+
network: "base-sepolia",
|
|
863
|
+
};
|
|
864
|
+
const v2 = adaptSupportedKindV1ToV2(v1, identity);
|
|
865
|
+
t.equal(v2.x402Version, 2);
|
|
866
|
+
t.end();
|
|
867
|
+
});
|
|
868
|
+
await t.test("preserves scheme and network with identity translator", (t) => {
|
|
869
|
+
const v1 = {
|
|
870
|
+
x402Version: 1,
|
|
871
|
+
scheme: "upto",
|
|
872
|
+
network: "solana:mainnet",
|
|
873
|
+
};
|
|
874
|
+
const v2 = adaptSupportedKindV1ToV2(v1, identity);
|
|
875
|
+
t.equal(v2.scheme, "upto");
|
|
876
|
+
t.equal(v2.network, "solana:mainnet");
|
|
877
|
+
t.end();
|
|
878
|
+
});
|
|
879
|
+
await t.test("preserves extra field when present", (t) => {
|
|
880
|
+
const extra = { feature: "batch" };
|
|
881
|
+
const v1 = {
|
|
882
|
+
x402Version: 1,
|
|
883
|
+
scheme: "exact",
|
|
884
|
+
network: "base-sepolia",
|
|
885
|
+
extra,
|
|
886
|
+
};
|
|
887
|
+
const v2 = adaptSupportedKindV1ToV2(v1, identity);
|
|
888
|
+
t.ok("extra" in v2);
|
|
889
|
+
t.matchOnly(v2.extra, extra);
|
|
890
|
+
t.end();
|
|
891
|
+
});
|
|
892
|
+
await t.test("omits extra field when undefined", (t) => {
|
|
893
|
+
const v1 = {
|
|
894
|
+
x402Version: 1,
|
|
895
|
+
scheme: "exact",
|
|
896
|
+
network: "base-sepolia",
|
|
897
|
+
};
|
|
898
|
+
const v2 = adaptSupportedKindV1ToV2(v1, identity);
|
|
899
|
+
t.notOk("extra" in v2);
|
|
900
|
+
t.end();
|
|
901
|
+
});
|
|
902
|
+
await t.test("applies network translator", (t) => {
|
|
903
|
+
const v1 = {
|
|
904
|
+
x402Version: 1,
|
|
905
|
+
scheme: "exact",
|
|
906
|
+
network: "solana-devnet",
|
|
907
|
+
};
|
|
908
|
+
const v2 = adaptSupportedKindV1ToV2(v1, mockTranslator);
|
|
909
|
+
t.equal(v2.network, "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1");
|
|
910
|
+
t.end();
|
|
911
|
+
});
|
|
912
|
+
});
|