@elizaos/plugin-x402 2.0.0-alpha.6 → 2.0.0-beta.1

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 (39) hide show
  1. package/dist/index.d.ts +57 -2
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +30919 -1913
  4. package/dist/index.js.map +114 -21
  5. package/dist/payment-config.d.ts +256 -0
  6. package/dist/payment-config.d.ts.map +1 -0
  7. package/dist/payment-wrapper.d.ts +42 -0
  8. package/dist/payment-wrapper.d.ts.map +1 -0
  9. package/dist/startup-validator.d.ts +28 -0
  10. package/dist/startup-validator.d.ts.map +1 -0
  11. package/dist/types.d.ts +158 -0
  12. package/dist/types.d.ts.map +1 -0
  13. package/dist/x402-facilitator-binding.d.ts +9 -0
  14. package/dist/x402-facilitator-binding.d.ts.map +1 -0
  15. package/dist/x402-replay-durable.d.ts +30 -0
  16. package/dist/x402-replay-durable.d.ts.map +1 -0
  17. package/dist/x402-replay-guard.d.ts +28 -0
  18. package/dist/x402-replay-guard.d.ts.map +1 -0
  19. package/dist/x402-replay-keys.d.ts +21 -0
  20. package/dist/x402-replay-keys.d.ts.map +1 -0
  21. package/dist/x402-resolve.d.ts +6 -0
  22. package/dist/x402-resolve.d.ts.map +1 -0
  23. package/dist/x402-standard-payment.d.ts +130 -0
  24. package/dist/x402-standard-payment.d.ts.map +1 -0
  25. package/dist/x402-types.d.ts +130 -0
  26. package/dist/x402-types.d.ts.map +1 -0
  27. package/package.json +43 -94
  28. package/src/index.ts +113 -0
  29. package/src/payment-config.ts +737 -0
  30. package/src/payment-wrapper.ts +1991 -0
  31. package/src/startup-validator.ts +349 -0
  32. package/src/types.ts +177 -0
  33. package/src/x402-facilitator-binding.ts +104 -0
  34. package/src/x402-replay-durable.ts +320 -0
  35. package/src/x402-replay-guard.ts +165 -0
  36. package/src/x402-replay-keys.ts +151 -0
  37. package/src/x402-resolve.ts +43 -0
  38. package/src/x402-standard-payment.ts +519 -0
  39. package/src/x402-types.ts +376 -0
@@ -0,0 +1,376 @@
1
+ /**
2
+ * x402scan Validation Schema Types
3
+ * Stricter schema required for listing on x402scan
4
+ * Allows UI-based resource invocation
5
+ */
6
+
7
+ /**
8
+ * Field definition for input/output schema
9
+ */
10
+ export type FieldDef = {
11
+ type?: string;
12
+ required?: boolean | string[];
13
+ description?: string;
14
+ enum?: string[];
15
+ properties?: Record<string, FieldDef>; // for nested objects
16
+ };
17
+
18
+ /**
19
+ * JSON Schema type for API output
20
+ */
21
+ export type OutputSchemaType = {
22
+ type?: "object" | "array" | "string" | "number" | "boolean" | "null";
23
+ description?: string;
24
+ properties?: Record<string, FieldDef>;
25
+ items?: FieldDef;
26
+ };
27
+
28
+ /**
29
+ * Output schema describing input and output expectations for the paid endpoint
30
+ */
31
+ export type OutputSchema = {
32
+ input: {
33
+ type: "http";
34
+ method: "GET" | "POST";
35
+ bodyType?: "json" | "form-data" | "multipart-form-data" | "text" | "binary";
36
+ pathParams?: Record<string, FieldDef>;
37
+ queryParams?: Record<string, FieldDef>;
38
+ bodyFields?: Record<string, FieldDef>;
39
+ headerFields?: Record<string, FieldDef>;
40
+ };
41
+ output?: OutputSchemaType;
42
+ };
43
+
44
+ /**
45
+ * Valid x402scan network types (as per their API specification)
46
+ */
47
+ export type X402ScanNetwork =
48
+ | "base-sepolia"
49
+ | "base"
50
+ | "avalanche-fuji"
51
+ | "avalanche"
52
+ | "iotex"
53
+ | "solana-devnet"
54
+ | "solana"
55
+ | "sei"
56
+ | "sei-testnet"
57
+ | "polygon"
58
+ | "polygon-amoy"
59
+ | "bsc"
60
+ | "bsc-testnet"
61
+ | "peaq";
62
+
63
+ /**
64
+ * EIP-712 domain information for EVM chains
65
+ */
66
+ export type EIP712DomainInfo = {
67
+ name: string;
68
+ version: string;
69
+ chainId: number;
70
+ verifyingContract: string;
71
+ };
72
+
73
+ /**
74
+ * Extra metadata for payment configuration
75
+ */
76
+ export type PaymentExtraMetadata = {
77
+ priceInCents: number;
78
+ priceUSD: string;
79
+ symbol: string;
80
+ paymentConfig: string;
81
+ expiresIn: number;
82
+ name?: string;
83
+ version?: string;
84
+ eip712Domain?: EIP712DomainInfo;
85
+ [key: string]: string | number | EIP712DomainInfo | undefined;
86
+ };
87
+
88
+ /**
89
+ * Accepts object defining payment terms for a resource
90
+ */
91
+ export type Accepts = {
92
+ scheme: "exact";
93
+ network: X402ScanNetwork;
94
+ maxAmountRequired: string;
95
+ resource: string; // Must be a full URL (https://...)
96
+ description: string;
97
+ mimeType: string;
98
+ payTo: string; // Wallet address - must be valid for the network
99
+ maxTimeoutSeconds: number;
100
+ asset: string;
101
+
102
+ // Optional schema describing the input and output expectations
103
+ outputSchema?: OutputSchema;
104
+
105
+ // Optional additional custom data
106
+ extra?: PaymentExtraMetadata;
107
+ };
108
+
109
+ /**
110
+ * X402 Response structure
111
+ */
112
+ export type X402Response = {
113
+ x402Version: number;
114
+ error?: string;
115
+ accepts?: Array<Accepts>;
116
+ payer?: string;
117
+ };
118
+
119
+ /**
120
+ * Validation result type
121
+ */
122
+ export type ValidationResult = {
123
+ valid: boolean;
124
+ errors: string[];
125
+ };
126
+
127
+ /**
128
+ * Valid x402scan networks
129
+ */
130
+ const VALID_NETWORKS: X402ScanNetwork[] = [
131
+ "base-sepolia",
132
+ "base",
133
+ "avalanche-fuji",
134
+ "avalanche",
135
+ "iotex",
136
+ "solana-devnet",
137
+ "solana",
138
+ "sei",
139
+ "sei-testnet",
140
+ "polygon",
141
+ "polygon-amoy",
142
+ "bsc",
143
+ "bsc-testnet",
144
+ "peaq",
145
+ ];
146
+
147
+ /**
148
+ * Validate URL format
149
+ */
150
+ function isValidUrl(url: string): boolean {
151
+ try {
152
+ const parsed = new URL(url);
153
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Validate wallet address format based on network
161
+ */
162
+ function isValidWalletAddress(
163
+ address: string,
164
+ network: X402ScanNetwork,
165
+ ): boolean {
166
+ if (!address || typeof address !== "string") return false;
167
+
168
+ // Solana addresses are base58 encoded, typically 32-44 characters
169
+ if (network.includes("solana")) {
170
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
171
+ }
172
+
173
+ // EVM-compatible chains use 0x addresses
174
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
175
+ }
176
+
177
+ /**
178
+ * Validate that an Accepts object conforms to the x402scan schema
179
+ */
180
+ export function validateAccepts(accepts: Partial<Accepts>): ValidationResult {
181
+ const errors: string[] = [];
182
+
183
+ // Required fields
184
+ if (accepts.scheme !== "exact") {
185
+ errors.push('scheme must be "exact"');
186
+ }
187
+
188
+ if (
189
+ !accepts.network ||
190
+ !VALID_NETWORKS.includes(accepts.network as X402ScanNetwork)
191
+ ) {
192
+ errors.push(`network must be one of: ${VALID_NETWORKS.join(", ")}`);
193
+ }
194
+
195
+ if (
196
+ !accepts.maxAmountRequired ||
197
+ typeof accepts.maxAmountRequired !== "string"
198
+ ) {
199
+ errors.push("maxAmountRequired is required and must be a string");
200
+ }
201
+
202
+ if (!accepts.resource || typeof accepts.resource !== "string") {
203
+ errors.push("resource is required and must be a string (full URL)");
204
+ } else if (!isValidUrl(accepts.resource)) {
205
+ errors.push(
206
+ "resource must be a valid URL (must start with http:// or https://)",
207
+ );
208
+ }
209
+
210
+ if (!accepts.description || typeof accepts.description !== "string") {
211
+ errors.push("description is required and must be a string");
212
+ }
213
+
214
+ if (!accepts.mimeType || typeof accepts.mimeType !== "string") {
215
+ errors.push(
216
+ 'mimeType is required and must be a string (e.g., "application/json")',
217
+ );
218
+ }
219
+
220
+ if (!accepts.payTo || typeof accepts.payTo !== "string") {
221
+ errors.push("payTo is required and must be a string (wallet address)");
222
+ } else if (
223
+ accepts.network &&
224
+ !isValidWalletAddress(accepts.payTo, accepts.network as X402ScanNetwork)
225
+ ) {
226
+ errors.push(
227
+ `payTo must be a valid wallet address for network ${accepts.network}`,
228
+ );
229
+ }
230
+
231
+ if (
232
+ !accepts.maxTimeoutSeconds ||
233
+ typeof accepts.maxTimeoutSeconds !== "number"
234
+ ) {
235
+ errors.push("maxTimeoutSeconds is required and must be a number");
236
+ }
237
+
238
+ if (!accepts.asset || typeof accepts.asset !== "string") {
239
+ errors.push('asset is required and must be a string (e.g., "USDC", "ETH")');
240
+ }
241
+
242
+ // Validate outputSchema if present
243
+ if (accepts.outputSchema) {
244
+ const schema = accepts.outputSchema;
245
+
246
+ if (schema.input.type !== "http") {
247
+ errors.push('outputSchema.input.type must be "http"');
248
+ }
249
+
250
+ if (
251
+ !schema.input.method ||
252
+ !["GET", "POST"].includes(schema.input.method)
253
+ ) {
254
+ errors.push('outputSchema.input.method must be "GET" or "POST"');
255
+ }
256
+
257
+ if (schema.input.bodyType) {
258
+ const validBodyTypes = [
259
+ "json",
260
+ "form-data",
261
+ "multipart-form-data",
262
+ "text",
263
+ "binary",
264
+ ];
265
+ if (!validBodyTypes.includes(schema.input.bodyType)) {
266
+ errors.push(
267
+ `outputSchema.input.bodyType must be one of: ${validBodyTypes.join(", ")}`,
268
+ );
269
+ }
270
+ }
271
+ }
272
+
273
+ return {
274
+ valid: errors.length === 0,
275
+ errors,
276
+ };
277
+ }
278
+
279
+ /**
280
+ * Validate that an X402Response conforms to the x402scan schema
281
+ */
282
+ export function validateX402Response(
283
+ response: Partial<X402Response>,
284
+ ): ValidationResult {
285
+ const errors: string[] = [];
286
+
287
+ // x402Version is required
288
+ if (typeof response.x402Version !== "number") {
289
+ errors.push("x402Version is required and must be a number");
290
+ }
291
+
292
+ // If accepts is provided, validate each entry
293
+ if (response.accepts) {
294
+ if (!Array.isArray(response.accepts)) {
295
+ errors.push("accepts must be an array");
296
+ } else {
297
+ response.accepts.forEach((accepts, index) => {
298
+ const validation = validateAccepts(accepts);
299
+ if (!validation.valid) {
300
+ errors.push(`accepts[${index}]: ${validation.errors.join(", ")}`);
301
+ }
302
+ });
303
+ }
304
+ }
305
+
306
+ return {
307
+ valid: errors.length === 0,
308
+ errors,
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Create a validated Accepts object with sensible defaults
314
+ */
315
+ export function createAccepts(params: {
316
+ network: X402ScanNetwork;
317
+ maxAmountRequired: string;
318
+ resource: string;
319
+ description: string;
320
+ payTo: string;
321
+ asset: string;
322
+ mimeType?: string;
323
+ maxTimeoutSeconds?: number;
324
+ outputSchema?: OutputSchema;
325
+ extra?: PaymentExtraMetadata;
326
+ }): Accepts {
327
+ const accepts: Accepts = {
328
+ scheme: "exact",
329
+ network: params.network,
330
+ maxAmountRequired: params.maxAmountRequired,
331
+ resource: params.resource,
332
+ description: params.description,
333
+ mimeType: params.mimeType || "application/json",
334
+ payTo: params.payTo,
335
+ maxTimeoutSeconds: params.maxTimeoutSeconds || 300, // 5 minutes default
336
+ asset: params.asset,
337
+ };
338
+
339
+ if (params.outputSchema) {
340
+ accepts.outputSchema = params.outputSchema;
341
+ }
342
+
343
+ if (params.extra) {
344
+ accepts.extra = params.extra;
345
+ }
346
+
347
+ // Validate before returning
348
+ const validation = validateAccepts(accepts);
349
+ if (!validation.valid) {
350
+ throw new Error(`Invalid Accepts object: ${validation.errors.join(", ")}`);
351
+ }
352
+
353
+ return accepts;
354
+ }
355
+
356
+ /**
357
+ * Create a validated X402Response
358
+ */
359
+ export function createX402Response(params: {
360
+ accepts?: Accepts[];
361
+ error?: string;
362
+ payer?: string;
363
+ }): X402Response {
364
+ const response: X402Response = {
365
+ x402Version: 1,
366
+ ...params,
367
+ };
368
+
369
+ // Validate before returning
370
+ const validation = validateX402Response(response);
371
+ if (!validation.valid) {
372
+ throw new Error(`Invalid X402Response: ${validation.errors.join(", ")}`);
373
+ }
374
+
375
+ return response;
376
+ }