@agirails/sdk 2.5.3 → 2.5.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 (166) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +45 -11
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/config/networks.d.ts +2 -2
  20. package/dist/config/networks.d.ts.map +1 -1
  21. package/dist/config/networks.js +27 -22
  22. package/dist/config/networks.js.map +1 -1
  23. package/dist/level0/request.d.ts.map +1 -1
  24. package/dist/level0/request.js +2 -1
  25. package/dist/level0/request.js.map +1 -1
  26. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  27. package/dist/runtime/BlockchainRuntime.js +11 -5
  28. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  29. package/dist/utils/IPFSClient.d.ts +3 -1
  30. package/dist/utils/IPFSClient.d.ts.map +1 -1
  31. package/dist/utils/IPFSClient.js +27 -7
  32. package/dist/utils/IPFSClient.js.map +1 -1
  33. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  34. package/dist/wallet/AutoWalletProvider.js +52 -18
  35. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  36. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  37. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  38. package/dist/wallet/SmartWalletRouter.js +212 -0
  39. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  40. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  41. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  42. package/dist/wallet/aa/DualNonceManager.js +100 -5
  43. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  44. package/package.json +3 -6
  45. package/src/ACTPClient.ts +0 -1579
  46. package/src/abi/ACTPKernel.json +0 -1356
  47. package/src/abi/AgentRegistry.json +0 -915
  48. package/src/abi/ERC20.json +0 -40
  49. package/src/abi/EscrowVault.json +0 -134
  50. package/src/abi/IdentityRegistry.json +0 -316
  51. package/src/adapters/AdapterRegistry.ts +0 -173
  52. package/src/adapters/AdapterRouter.ts +0 -416
  53. package/src/adapters/BaseAdapter.ts +0 -498
  54. package/src/adapters/BasicAdapter.ts +0 -514
  55. package/src/adapters/IAdapter.ts +0 -292
  56. package/src/adapters/StandardAdapter.ts +0 -555
  57. package/src/adapters/X402Adapter.ts +0 -731
  58. package/src/adapters/index.ts +0 -60
  59. package/src/builders/DeliveryProofBuilder.ts +0 -327
  60. package/src/builders/QuoteBuilder.ts +0 -483
  61. package/src/builders/index.ts +0 -17
  62. package/src/cli/commands/balance.ts +0 -110
  63. package/src/cli/commands/batch.ts +0 -487
  64. package/src/cli/commands/config.ts +0 -231
  65. package/src/cli/commands/deploy-check.ts +0 -364
  66. package/src/cli/commands/deploy-env.ts +0 -120
  67. package/src/cli/commands/diff.ts +0 -141
  68. package/src/cli/commands/init.ts +0 -469
  69. package/src/cli/commands/mint.ts +0 -116
  70. package/src/cli/commands/pay.ts +0 -113
  71. package/src/cli/commands/publish.ts +0 -475
  72. package/src/cli/commands/pull.ts +0 -124
  73. package/src/cli/commands/register.ts +0 -247
  74. package/src/cli/commands/simulate.ts +0 -345
  75. package/src/cli/commands/time.ts +0 -302
  76. package/src/cli/commands/tx.ts +0 -448
  77. package/src/cli/commands/watch.ts +0 -211
  78. package/src/cli/index.ts +0 -134
  79. package/src/cli/utils/client.ts +0 -252
  80. package/src/cli/utils/config.ts +0 -389
  81. package/src/cli/utils/output.ts +0 -465
  82. package/src/cli/utils/wallet.ts +0 -109
  83. package/src/config/agirailsmd.ts +0 -262
  84. package/src/config/networks.ts +0 -275
  85. package/src/config/pendingPublish.ts +0 -237
  86. package/src/config/publishPipeline.ts +0 -359
  87. package/src/config/syncOperations.ts +0 -279
  88. package/src/erc8004/ERC8004Bridge.ts +0 -462
  89. package/src/erc8004/ReputationReporter.ts +0 -468
  90. package/src/erc8004/index.ts +0 -61
  91. package/src/errors/index.ts +0 -427
  92. package/src/index.ts +0 -364
  93. package/src/level0/Provider.ts +0 -117
  94. package/src/level0/ServiceDirectory.ts +0 -131
  95. package/src/level0/index.ts +0 -10
  96. package/src/level0/provide.ts +0 -132
  97. package/src/level0/request.ts +0 -432
  98. package/src/level1/Agent.ts +0 -1426
  99. package/src/level1/index.ts +0 -10
  100. package/src/level1/pricing/PriceCalculator.ts +0 -255
  101. package/src/level1/pricing/PricingStrategy.ts +0 -198
  102. package/src/level1/types/Job.ts +0 -179
  103. package/src/level1/types/Options.ts +0 -291
  104. package/src/level1/types/index.ts +0 -8
  105. package/src/protocol/ACTPKernel.ts +0 -808
  106. package/src/protocol/AgentRegistry.ts +0 -559
  107. package/src/protocol/DIDManager.ts +0 -629
  108. package/src/protocol/DIDResolver.ts +0 -554
  109. package/src/protocol/EASHelper.ts +0 -378
  110. package/src/protocol/EscrowVault.ts +0 -255
  111. package/src/protocol/EventMonitor.ts +0 -204
  112. package/src/protocol/MessageSigner.ts +0 -510
  113. package/src/protocol/ProofGenerator.ts +0 -339
  114. package/src/protocol/QuoteBuilder.ts +0 -15
  115. package/src/registry/AgentRegistryClient.ts +0 -202
  116. package/src/runtime/BlockchainRuntime.ts +0 -1015
  117. package/src/runtime/IACTPRuntime.ts +0 -306
  118. package/src/runtime/MockRuntime.ts +0 -1298
  119. package/src/runtime/MockStateManager.ts +0 -577
  120. package/src/runtime/index.ts +0 -25
  121. package/src/runtime/types/MockState.ts +0 -237
  122. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  123. package/src/storage/ArweaveClient.ts +0 -946
  124. package/src/storage/FilebaseClient.ts +0 -790
  125. package/src/storage/index.ts +0 -96
  126. package/src/storage/types.ts +0 -348
  127. package/src/types/adapter.ts +0 -310
  128. package/src/types/agent.ts +0 -79
  129. package/src/types/did.ts +0 -223
  130. package/src/types/eip712.ts +0 -175
  131. package/src/types/erc8004.ts +0 -293
  132. package/src/types/escrow.ts +0 -27
  133. package/src/types/index.ts +0 -17
  134. package/src/types/message.ts +0 -145
  135. package/src/types/state.ts +0 -87
  136. package/src/types/transaction.ts +0 -69
  137. package/src/types/x402.ts +0 -251
  138. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  139. package/src/utils/Helpers.ts +0 -688
  140. package/src/utils/IPFSClient.ts +0 -368
  141. package/src/utils/Logger.ts +0 -484
  142. package/src/utils/NonceManager.ts +0 -591
  143. package/src/utils/RateLimiter.ts +0 -534
  144. package/src/utils/ReceivedNonceTracker.ts +0 -567
  145. package/src/utils/SDKLifecycle.ts +0 -416
  146. package/src/utils/SecureNonce.ts +0 -78
  147. package/src/utils/Semaphore.ts +0 -276
  148. package/src/utils/UsedAttestationTracker.ts +0 -385
  149. package/src/utils/canonicalJson.ts +0 -38
  150. package/src/utils/circuitBreaker.ts +0 -324
  151. package/src/utils/computeTypeHash.ts +0 -48
  152. package/src/utils/fsSafe.ts +0 -80
  153. package/src/utils/index.ts +0 -80
  154. package/src/utils/retry.ts +0 -364
  155. package/src/utils/security.ts +0 -418
  156. package/src/utils/validation.ts +0 -540
  157. package/src/wallet/AutoWalletProvider.ts +0 -299
  158. package/src/wallet/EOAWalletProvider.ts +0 -69
  159. package/src/wallet/IWalletProvider.ts +0 -135
  160. package/src/wallet/aa/BundlerClient.ts +0 -274
  161. package/src/wallet/aa/DualNonceManager.ts +0 -173
  162. package/src/wallet/aa/PaymasterClient.ts +0 -174
  163. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  164. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  165. package/src/wallet/aa/constants.ts +0 -60
  166. package/src/wallet/keystore.ts +0 -240
@@ -1,731 +0,0 @@
1
- /**
2
- * X402Adapter - HTTP 402 Payment Required Protocol (Atomic Payments)
3
- *
4
- * Implements the x402 protocol for atomic, instant API payments.
5
- * NO escrow, NO state machine, NO disputes - just pay and receive.
6
- *
7
- * This is fundamentally different from ACTP:
8
- * - ACTP: escrow → state machine → disputes → explicit release
9
- * - x402: atomic payment → instant settlement → done
10
- *
11
- * Use x402 for:
12
- * - Simple API calls (pay-per-request)
13
- * - Instant delivery (response IS the delivery)
14
- * - Low-value, high-frequency transactions
15
- *
16
- * Use ACTP for:
17
- * - Complex services requiring verification
18
- * - High-value transactions needing dispute protection
19
- * - Multi-step deliveries
20
- *
21
- * @module adapters/X402Adapter
22
- */
23
-
24
- import { BaseAdapter, ValidationError } from './BaseAdapter';
25
- import { IAdapter, TransactionStatus, AdapterTransactionState } from './IAdapter';
26
- import {
27
- AdapterMetadata,
28
- UnifiedPayParams,
29
- UnifiedPayResult,
30
- } from '../types/adapter';
31
- import {
32
- X402PaymentHeaders,
33
- X402_HEADERS,
34
- X402_PROOF_HEADERS,
35
- X402Error,
36
- X402ErrorCode,
37
- X402Network,
38
- X402FeeBreakdown,
39
- isValidX402Network,
40
- } from '../types/x402';
41
-
42
- // ============================================================================
43
- // Types
44
- // ============================================================================
45
-
46
- /** Type for fetch function (cross-platform compatible) */
47
- export type FetchFunction = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
48
-
49
- /**
50
- * Transfer function for atomic payments (legacy direct transfer).
51
- *
52
- * @param to - Recipient address
53
- * @param amount - Amount in USDC wei (string)
54
- * @returns Transaction hash as proof
55
- */
56
- export type TransferFunction = (to: string, amount: string) => Promise<string>;
57
-
58
- /**
59
- * Approve function for USDC allowance (used with X402Relay).
60
- *
61
- * @param spender - Spender address (relay contract)
62
- * @param amount - Amount in USDC wei (string)
63
- * @returns Transaction hash
64
- */
65
- export type ApproveFunction = (spender: string, amount: string) => Promise<string>;
66
-
67
- /**
68
- * Relay pay function — calls X402Relay.payWithFee().
69
- *
70
- * @param provider - Provider address
71
- * @param grossAmount - Gross USDC amount (string)
72
- * @param serviceId - Service identifier (bytes32 hex)
73
- * @returns Transaction hash
74
- */
75
- export type RelayPayFunction = (provider: string, grossAmount: string, serviceId: string) => Promise<string>;
76
-
77
- /** Supported HTTP methods for x402 requests */
78
- export type X402HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
79
-
80
- /**
81
- * Extended payment parameters for x402 with full HTTP support.
82
- */
83
- export interface X402PayParams extends UnifiedPayParams {
84
- /** HTTP method (default: GET) */
85
- method?: X402HttpMethod;
86
-
87
- /** Custom request headers */
88
- headers?: Record<string, string>;
89
-
90
- /** Request body (string or object, will be JSON.stringify'd if object) */
91
- body?: string | Record<string, unknown>;
92
-
93
- /** Content-Type header (default: application/json for POST/PUT/PATCH with body) */
94
- contentType?: string;
95
- }
96
-
97
- /**
98
- * Configuration options for X402Adapter.
99
- *
100
- * For fee-enabled payments via X402Relay, provide relayAddress + approveFn + relayPayFn.
101
- * Without relay config, falls back to legacy direct transfer (no platform fee).
102
- */
103
- export interface X402AdapterConfig {
104
- /** Expected network for validation (must match server's X-Payment-Network) */
105
- expectedNetwork: X402Network;
106
-
107
- /** Transfer function for direct atomic payments (legacy fallback) */
108
- transferFn: TransferFunction;
109
-
110
- /** Request timeout in milliseconds (default: 30000) */
111
- requestTimeout?: number;
112
-
113
- /** Custom fetch function for testing (default: global fetch) */
114
- fetchFn?: FetchFunction;
115
-
116
- /** Default headers to include in all requests */
117
- defaultHeaders?: Record<string, string>;
118
-
119
- // --- X402Relay fee-splitting (optional, recommended) ---
120
-
121
- /** X402Relay contract address for on-chain fee splitting */
122
- relayAddress?: string;
123
-
124
- /** USDC approve function — required when relayAddress is set */
125
- approveFn?: ApproveFunction;
126
-
127
- /** Relay payWithFee function — required when relayAddress is set */
128
- relayPayFn?: RelayPayFunction;
129
-
130
- /** Platform fee in basis points (default: 100 = 1%). Read-only display hint. */
131
- platformFeeBps?: number;
132
- }
133
-
134
- /**
135
- * Atomic payment record (for getStatus lookups).
136
- */
137
- interface AtomicPaymentRecord {
138
- txHash: string;
139
- provider: string;
140
- requester: string;
141
- amount: string;
142
- timestamp: number;
143
- endpoint: string;
144
- feeBreakdown?: X402FeeBreakdown;
145
- }
146
-
147
- // ============================================================================
148
- // X402Adapter Implementation
149
- // ============================================================================
150
-
151
- /**
152
- * X402Adapter - Atomic HTTP payment protocol.
153
- *
154
- * Key characteristics:
155
- * - usesEscrow: false (direct payment)
156
- * - supportsDisputes: false (atomic = final)
157
- * - settlementMode: 'atomic' (instant)
158
- * - releaseRequired: false (no escrow to release)
159
- *
160
- * @example
161
- * ```typescript
162
- * const adapter = new X402Adapter(requesterAddress, {
163
- * expectedNetwork: 'base-sepolia',
164
- * transferFn: async (to, amount) => {
165
- * const tx = await usdcContract.transfer(to, amount);
166
- * return tx.hash;
167
- * },
168
- * });
169
- *
170
- * const result = await adapter.pay({
171
- * to: 'https://api.provider.com/service',
172
- * amount: '10', // Hint only, actual amount from 402
173
- * });
174
- *
175
- * // That's it! No release() needed.
176
- * console.log(result.response?.status); // 200
177
- * console.log(result.releaseRequired); // false
178
- * ```
179
- */
180
- export class X402Adapter extends BaseAdapter implements IAdapter {
181
- /**
182
- * Adapter metadata - atomic, no escrow.
183
- */
184
- public readonly metadata: AdapterMetadata = {
185
- id: 'x402',
186
- name: 'X402 Atomic Payment Adapter',
187
- usesEscrow: false,
188
- supportsDisputes: false,
189
- requiresIdentity: false,
190
- settlementMode: 'atomic',
191
- priority: 70,
192
- };
193
-
194
- private readonly timeout: number;
195
- private readonly fetchFn: FetchFunction;
196
- private readonly defaultHeaders: Record<string, string>;
197
- private readonly transferFn: TransferFunction;
198
-
199
- /** Local cache of payments for status lookups */
200
- private readonly payments = new Map<string, AtomicPaymentRecord>();
201
-
202
- /**
203
- * Creates a new X402Adapter instance.
204
- *
205
- * @param requesterAddress - The requester's Ethereum address
206
- * @param config - X402-specific configuration
207
- */
208
- constructor(
209
- requesterAddress: string,
210
- private config: X402AdapterConfig
211
- ) {
212
- super(requesterAddress);
213
- this.timeout = config.requestTimeout ?? 30000;
214
- this.fetchFn = config.fetchFn ?? fetch;
215
- this.defaultHeaders = config.defaultHeaders ?? {};
216
- this.transferFn = config.transferFn;
217
- }
218
-
219
- // ==========================================================================
220
- // IAdapter Implementation
221
- // ==========================================================================
222
-
223
- /**
224
- * Check if this adapter can handle the given parameters.
225
- *
226
- * X402Adapter handles HTTPS URLs only (security requirement).
227
- */
228
- canHandle(params: UnifiedPayParams): boolean {
229
- if (typeof params.to !== 'string') {
230
- return false;
231
- }
232
-
233
- try {
234
- const url = new URL(params.to);
235
- return url.protocol === 'https:';
236
- } catch {
237
- return false;
238
- }
239
- }
240
-
241
- /**
242
- * Validate parameters before execution.
243
- */
244
- validate(params: UnifiedPayParams): void {
245
- if (!this.canHandle(params)) {
246
- throw new ValidationError(
247
- `X402 requires HTTPS URL, got: "${params.to}". ` +
248
- `HTTP endpoints are not supported for security reasons.`
249
- );
250
- }
251
-
252
- const url = new URL(params.to);
253
-
254
- if (url.username || url.password) {
255
- throw new ValidationError(
256
- 'URL cannot contain embedded credentials (username:password).'
257
- );
258
- }
259
- }
260
-
261
- /**
262
- * Execute atomic x402 payment flow with full HTTP support.
263
- *
264
- * 1. Request endpoint → get 402
265
- * 2. Parse payment headers
266
- * 3. Execute atomic USDC transfer
267
- * 4. Retry with tx hash as proof (same method/headers/body)
268
- * 5. Return response (settlement complete!)
269
- *
270
- * @param params - Payment parameters with optional HTTP method, headers, body
271
- */
272
- async pay(params: UnifiedPayParams | X402PayParams): Promise<UnifiedPayResult> {
273
- this.validate(params);
274
-
275
- const endpoint = params.to;
276
- const x402Params = params as X402PayParams;
277
-
278
- // Extract HTTP options
279
- const method: X402HttpMethod = x402Params.method ?? 'GET';
280
- const requestHeaders = x402Params.headers ?? {};
281
- const requestBody = this.serializeBody(x402Params.body, x402Params.contentType);
282
- const contentType = x402Params.contentType ??
283
- (x402Params.body && method !== 'GET' ? 'application/json' : undefined);
284
-
285
- // Step 1: Initial request
286
- const initialResponse = await this.makeRequest(
287
- endpoint,
288
- method,
289
- requestHeaders,
290
- requestBody,
291
- contentType
292
- );
293
-
294
- // Step 2: Check response status
295
- if (initialResponse.status !== 402) {
296
- if (initialResponse.ok) {
297
- return this.createFreeServiceResult(params, initialResponse);
298
- }
299
- throw new X402Error(
300
- `Expected 402 Payment Required, got ${initialResponse.status}`,
301
- X402ErrorCode.NOT_402_RESPONSE,
302
- initialResponse
303
- );
304
- }
305
-
306
- // Step 3: Parse payment headers
307
- const paymentHeaders = this.parsePaymentHeaders(initialResponse);
308
-
309
- // Step 4: Validate network
310
- if (paymentHeaders.network !== this.config.expectedNetwork) {
311
- throw new X402Error(
312
- `Network mismatch: expected ${this.config.expectedNetwork}, got ${paymentHeaders.network}`,
313
- X402ErrorCode.NETWORK_MISMATCH,
314
- initialResponse
315
- );
316
- }
317
-
318
- // Step 5: Validate deadline
319
- const now = Math.floor(Date.now() / 1000);
320
- if (paymentHeaders.deadline <= now) {
321
- throw new X402Error(
322
- `Payment deadline has passed: ${new Date(paymentHeaders.deadline * 1000).toISOString()}`,
323
- X402ErrorCode.DEADLINE_PASSED,
324
- initialResponse
325
- );
326
- }
327
-
328
- // Step 6: ATOMIC PAYMENT - via relay (with fee) or direct transfer (legacy)
329
- const { txHash, feeBreakdown } = await this.executeAtomicPayment(paymentHeaders);
330
-
331
- // Step 7: Retry with proof (same method/headers/body + payment proof)
332
- const serviceResponse = await this.retryWithProof(
333
- endpoint,
334
- txHash,
335
- method,
336
- requestHeaders,
337
- requestBody,
338
- contentType
339
- );
340
-
341
- // Step 8: Cache payment record for status lookups
342
- this.payments.set(txHash, {
343
- txHash,
344
- provider: paymentHeaders.paymentAddress.toLowerCase(),
345
- requester: this.requesterAddress.toLowerCase(),
346
- amount: paymentHeaders.amount,
347
- timestamp: now,
348
- endpoint,
349
- feeBreakdown,
350
- });
351
-
352
- // Step 9: Return result - DONE! No release needed.
353
- return {
354
- txId: txHash,
355
- escrowId: null, // No escrow!
356
- adapter: this.metadata.id,
357
- state: 'COMMITTED', // Atomic = immediately settled
358
- success: true,
359
- amount: this.formatAmount(paymentHeaders.amount),
360
- response: serviceResponse,
361
- releaseRequired: false, // KEY DIFFERENCE from ACTP
362
- provider: paymentHeaders.paymentAddress.toLowerCase(),
363
- requester: this.requesterAddress.toLowerCase(),
364
- deadline: new Date(paymentHeaders.deadline * 1000).toISOString(),
365
- feeBreakdown,
366
- };
367
- }
368
-
369
- /**
370
- * Serialize request body to string.
371
- */
372
- private serializeBody(
373
- body: string | Record<string, unknown> | undefined,
374
- _contentType?: string
375
- ): string | undefined {
376
- if (body === undefined) return undefined;
377
- if (typeof body === 'string') return body;
378
- return JSON.stringify(body);
379
- }
380
-
381
- /**
382
- * Get payment status by transaction hash.
383
- *
384
- * For atomic payments, status is simple:
385
- * - If tx exists → SETTLED (atomic = instant settlement)
386
- */
387
- async getStatus(txId: string): Promise<TransactionStatus> {
388
- const record = this.payments.get(txId);
389
-
390
- if (!record) {
391
- throw new Error(`Payment ${txId} not found. X402 payments are atomic and stateless.`);
392
- }
393
-
394
- return {
395
- state: 'SETTLED' as AdapterTransactionState,
396
- canStartWork: false,
397
- canDeliver: false,
398
- canRelease: false,
399
- canDispute: false,
400
- amount: this.formatAmount(record.amount),
401
- provider: record.provider,
402
- requester: record.requester,
403
- };
404
- }
405
-
406
- /**
407
- * Not applicable for atomic payments.
408
- * @throws {Error} Always - x402 has no lifecycle
409
- */
410
- async startWork(_txId: string): Promise<void> {
411
- throw new Error(
412
- 'X402 is atomic - no lifecycle methods. ' +
413
- 'Payment and delivery happen atomically. Use ACTP for stateful transactions.'
414
- );
415
- }
416
-
417
- /**
418
- * Not applicable for atomic payments.
419
- * @throws {Error} Always - x402 has no lifecycle
420
- */
421
- async deliver(_txId: string, _proof?: string): Promise<void> {
422
- throw new Error(
423
- 'X402 is atomic - no lifecycle methods. ' +
424
- 'The HTTP response IS the delivery. Use ACTP for stateful transactions.'
425
- );
426
- }
427
-
428
- /**
429
- * Not applicable for atomic payments.
430
- * @throws {Error} Always - x402 has no escrow
431
- */
432
- async release(_escrowId: string, _attestationUID?: string): Promise<void> {
433
- throw new Error(
434
- 'X402 is atomic - no escrow to release. ' +
435
- 'Payment settled instantly. Use ACTP for escrow-based transactions.'
436
- );
437
- }
438
-
439
- // ==========================================================================
440
- // Private Helpers
441
- // ==========================================================================
442
-
443
- /**
444
- * Make an HTTP request with full options support.
445
- *
446
- * @param url - Request URL
447
- * @param method - HTTP method
448
- * @param customHeaders - Custom headers from request params
449
- * @param body - Request body (optional)
450
- * @param contentType - Content-Type header (optional)
451
- * @param proofHeaders - Payment proof headers for retry (optional)
452
- */
453
- private async makeRequest(
454
- url: string,
455
- method: X402HttpMethod,
456
- customHeaders: Record<string, string> = {},
457
- body?: string,
458
- contentType?: string,
459
- proofHeaders?: Record<string, string>
460
- ): Promise<Response> {
461
- const controller = new AbortController();
462
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
463
-
464
- try {
465
- // Build headers: defaults + custom + content-type + proof
466
- const headers = new Headers(this.defaultHeaders);
467
-
468
- // Add custom headers from request
469
- for (const [key, value] of Object.entries(customHeaders)) {
470
- headers.set(key, value);
471
- }
472
-
473
- // Add content-type if provided
474
- if (contentType) {
475
- headers.set('Content-Type', contentType);
476
- }
477
-
478
- // Add proof headers for retry
479
- if (proofHeaders) {
480
- for (const [key, value] of Object.entries(proofHeaders)) {
481
- headers.set(key, value);
482
- }
483
- }
484
-
485
- const init: RequestInit = {
486
- method,
487
- headers,
488
- signal: controller.signal,
489
- };
490
-
491
- // Add body for non-GET requests
492
- if (body && method !== 'GET') {
493
- init.body = body;
494
- }
495
-
496
- return await this.fetchFn(url, init);
497
- } finally {
498
- clearTimeout(timeoutId);
499
- }
500
- }
501
-
502
- /**
503
- * Parse X-Payment-* headers from 402 response.
504
- */
505
- private parsePaymentHeaders(response: Response): X402PaymentHeaders {
506
- const h = response.headers;
507
-
508
- const requiredHeader = h.get(X402_HEADERS.REQUIRED);
509
- if (requiredHeader?.toLowerCase() !== 'true') {
510
- throw new X402Error(
511
- `Missing or invalid ${X402_HEADERS.REQUIRED} header`,
512
- X402ErrorCode.MISSING_HEADERS,
513
- response
514
- );
515
- }
516
-
517
- const address = h.get(X402_HEADERS.ADDRESS);
518
- const amount = h.get(X402_HEADERS.AMOUNT);
519
- const network = h.get(X402_HEADERS.NETWORK);
520
- const token = h.get(X402_HEADERS.TOKEN);
521
- const deadline = h.get(X402_HEADERS.DEADLINE);
522
-
523
- if (!address) {
524
- throw new X402Error(`Missing ${X402_HEADERS.ADDRESS}`, X402ErrorCode.MISSING_HEADERS, response);
525
- }
526
- if (!amount) {
527
- throw new X402Error(`Missing ${X402_HEADERS.AMOUNT}`, X402ErrorCode.MISSING_HEADERS, response);
528
- }
529
- if (!network) {
530
- throw new X402Error(`Missing ${X402_HEADERS.NETWORK}`, X402ErrorCode.MISSING_HEADERS, response);
531
- }
532
- if (!token) {
533
- throw new X402Error(`Missing ${X402_HEADERS.TOKEN}`, X402ErrorCode.MISSING_HEADERS, response);
534
- }
535
- if (!deadline) {
536
- throw new X402Error(`Missing ${X402_HEADERS.DEADLINE}`, X402ErrorCode.MISSING_HEADERS, response);
537
- }
538
-
539
- // Validate address
540
- const validatedAddress = this.validatePaymentAddress(address, response);
541
-
542
- // Validate amount
543
- if (!/^\d+$/.test(amount)) {
544
- throw new X402Error(
545
- `Invalid ${X402_HEADERS.AMOUNT}: "${amount}"`,
546
- X402ErrorCode.INVALID_AMOUNT,
547
- response
548
- );
549
- }
550
-
551
- // Validate network
552
- if (!isValidX402Network(network)) {
553
- throw new X402Error(
554
- `Invalid ${X402_HEADERS.NETWORK}: "${network}"`,
555
- X402ErrorCode.INVALID_NETWORK,
556
- response
557
- );
558
- }
559
-
560
- // Validate token
561
- if (token.toUpperCase() !== 'USDC') {
562
- throw new X402Error(
563
- `Unsupported token: "${token}". Only USDC supported.`,
564
- X402ErrorCode.MISSING_HEADERS,
565
- response
566
- );
567
- }
568
-
569
- const deadlineNum = parseInt(deadline, 10);
570
- if (isNaN(deadlineNum) || deadlineNum <= 0) {
571
- throw new X402Error(
572
- `Invalid ${X402_HEADERS.DEADLINE}: "${deadline}"`,
573
- X402ErrorCode.MISSING_HEADERS,
574
- response
575
- );
576
- }
577
-
578
- return {
579
- required: true,
580
- paymentAddress: validatedAddress,
581
- amount,
582
- network,
583
- token: 'USDC',
584
- deadline: deadlineNum,
585
- serviceId: h.get(X402_HEADERS.SERVICE_ID) ?? undefined,
586
- };
587
- }
588
-
589
- /**
590
- * Validate payment address from header.
591
- */
592
- private validatePaymentAddress(address: string, response: Response): string {
593
- try {
594
- return this.validateAddress(address, X402_HEADERS.ADDRESS);
595
- } catch {
596
- throw new X402Error(
597
- `Invalid ${X402_HEADERS.ADDRESS}: "${address}"`,
598
- X402ErrorCode.INVALID_ADDRESS,
599
- response
600
- );
601
- }
602
- }
603
-
604
- /**
605
- * Execute atomic payment with fee splitting via X402Relay (if configured),
606
- * or direct transfer as legacy fallback.
607
- *
608
- * Relay flow: approve relay → relay.payWithFee(provider, gross, serviceId)
609
- * Legacy flow: transferFn(provider, amount) — no fee extraction
610
- */
611
- private async executeAtomicPayment(headers: X402PaymentHeaders): Promise<{
612
- txHash: string;
613
- feeBreakdown?: X402FeeBreakdown;
614
- }> {
615
- try {
616
- // Relay path: on-chain fee splitting
617
- if (this.config.relayAddress && this.config.approveFn && this.config.relayPayFn) {
618
- const grossAmount = headers.amount;
619
- const feeBps = this.config.platformFeeBps ?? 100;
620
- const MIN_FEE = 50_000n; // $0.05 USDC
621
-
622
- // Calculate fee: max(gross * bps / 10000, MIN_FEE)
623
- const grossBig = BigInt(grossAmount);
624
- const bpsFee = (grossBig * BigInt(feeBps)) / 10_000n;
625
- const fee = bpsFee > MIN_FEE ? bpsFee : MIN_FEE;
626
- const providerNet = grossBig - fee;
627
-
628
- // 1. Approve relay for gross amount
629
- await this.config.approveFn(this.config.relayAddress, grossAmount);
630
-
631
- // 2. Call relay.payWithFee
632
- const serviceId = headers.serviceId ?? '0x' + '0'.repeat(64);
633
- const txHash = await this.config.relayPayFn(
634
- headers.paymentAddress,
635
- grossAmount,
636
- serviceId
637
- );
638
-
639
- return {
640
- txHash,
641
- feeBreakdown: {
642
- grossAmount,
643
- providerNet: providerNet.toString(),
644
- platformFee: fee.toString(),
645
- feeBps,
646
- estimated: true,
647
- },
648
- };
649
- }
650
-
651
- // Legacy path: direct transfer, no fee
652
- const txHash = await this.transferFn(
653
- headers.paymentAddress,
654
- headers.amount
655
- );
656
- return { txHash };
657
- } catch (error) {
658
- throw new X402Error(
659
- `Atomic payment failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
660
- X402ErrorCode.PAYMENT_FAILED
661
- );
662
- }
663
- }
664
-
665
- /**
666
- * Retry request with payment proof (tx hash).
667
- * Uses the same HTTP method, headers, and body as the original request.
668
- *
669
- * @param endpoint - Request URL
670
- * @param txHash - Payment transaction hash as proof
671
- * @param method - Original HTTP method
672
- * @param customHeaders - Original custom headers
673
- * @param body - Original request body
674
- * @param contentType - Original content-type
675
- */
676
- private async retryWithProof(
677
- endpoint: string,
678
- txHash: string,
679
- method: X402HttpMethod = 'GET',
680
- customHeaders: Record<string, string> = {},
681
- body?: string,
682
- contentType?: string
683
- ): Promise<Response> {
684
- // Add payment proof headers
685
- const proofHeaders: Record<string, string> = {
686
- [X402_PROOF_HEADERS.TX_ID]: txHash,
687
- // No escrow ID for atomic payments
688
- };
689
-
690
- const response = await this.makeRequest(
691
- endpoint,
692
- method,
693
- customHeaders,
694
- body,
695
- contentType,
696
- proofHeaders
697
- );
698
-
699
- if (!response.ok) {
700
- throw new X402Error(
701
- `Retry failed: ${response.status} ${response.statusText}`,
702
- X402ErrorCode.RETRY_FAILED,
703
- response
704
- );
705
- }
706
-
707
- return response;
708
- }
709
-
710
- /**
711
- * Create result for free services (200 on initial request).
712
- */
713
- private createFreeServiceResult(
714
- params: UnifiedPayParams,
715
- response: Response
716
- ): UnifiedPayResult {
717
- return {
718
- txId: '0x' + '0'.repeat(64),
719
- escrowId: null,
720
- adapter: this.metadata.id,
721
- state: 'COMMITTED',
722
- success: true,
723
- amount: '0.00 USDC',
724
- response,
725
- releaseRequired: false,
726
- provider: '0x' + '0'.repeat(40),
727
- requester: this.requesterAddress.toLowerCase(),
728
- deadline: new Date(Date.now() + 86400000).toISOString(),
729
- };
730
- }
731
- }