@agirails/sdk 2.5.2 → 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 (172) 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/cli/utils/client.d.ts.map +1 -1
  20. package/dist/cli/utils/client.js +1 -0
  21. package/dist/cli/utils/client.js.map +1 -1
  22. package/dist/config/networks.d.ts +2 -2
  23. package/dist/config/networks.d.ts.map +1 -1
  24. package/dist/config/networks.js +27 -22
  25. package/dist/config/networks.js.map +1 -1
  26. package/dist/level0/request.d.ts.map +1 -1
  27. package/dist/level0/request.js +2 -1
  28. package/dist/level0/request.js.map +1 -1
  29. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  30. package/dist/runtime/BlockchainRuntime.js +11 -5
  31. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  32. package/dist/runtime/MockStateManager.d.ts.map +1 -1
  33. package/dist/runtime/MockStateManager.js +2 -1
  34. package/dist/runtime/MockStateManager.js.map +1 -1
  35. package/dist/utils/IPFSClient.d.ts +3 -1
  36. package/dist/utils/IPFSClient.d.ts.map +1 -1
  37. package/dist/utils/IPFSClient.js +27 -7
  38. package/dist/utils/IPFSClient.js.map +1 -1
  39. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.js +52 -18
  41. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  42. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  43. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  44. package/dist/wallet/SmartWalletRouter.js +212 -0
  45. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  46. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  47. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  48. package/dist/wallet/aa/DualNonceManager.js +100 -5
  49. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  50. package/package.json +3 -6
  51. package/src/ACTPClient.ts +0 -1579
  52. package/src/abi/ACTPKernel.json +0 -1356
  53. package/src/abi/AgentRegistry.json +0 -915
  54. package/src/abi/ERC20.json +0 -40
  55. package/src/abi/EscrowVault.json +0 -134
  56. package/src/abi/IdentityRegistry.json +0 -316
  57. package/src/adapters/AdapterRegistry.ts +0 -173
  58. package/src/adapters/AdapterRouter.ts +0 -416
  59. package/src/adapters/BaseAdapter.ts +0 -498
  60. package/src/adapters/BasicAdapter.ts +0 -514
  61. package/src/adapters/IAdapter.ts +0 -292
  62. package/src/adapters/StandardAdapter.ts +0 -555
  63. package/src/adapters/X402Adapter.ts +0 -731
  64. package/src/adapters/index.ts +0 -60
  65. package/src/builders/DeliveryProofBuilder.ts +0 -327
  66. package/src/builders/QuoteBuilder.ts +0 -483
  67. package/src/builders/index.ts +0 -17
  68. package/src/cli/commands/balance.ts +0 -110
  69. package/src/cli/commands/batch.ts +0 -487
  70. package/src/cli/commands/config.ts +0 -231
  71. package/src/cli/commands/deploy-check.ts +0 -364
  72. package/src/cli/commands/deploy-env.ts +0 -120
  73. package/src/cli/commands/diff.ts +0 -141
  74. package/src/cli/commands/init.ts +0 -469
  75. package/src/cli/commands/mint.ts +0 -116
  76. package/src/cli/commands/pay.ts +0 -113
  77. package/src/cli/commands/publish.ts +0 -475
  78. package/src/cli/commands/pull.ts +0 -124
  79. package/src/cli/commands/register.ts +0 -247
  80. package/src/cli/commands/simulate.ts +0 -345
  81. package/src/cli/commands/time.ts +0 -302
  82. package/src/cli/commands/tx.ts +0 -448
  83. package/src/cli/commands/watch.ts +0 -211
  84. package/src/cli/index.ts +0 -134
  85. package/src/cli/utils/client.ts +0 -251
  86. package/src/cli/utils/config.ts +0 -389
  87. package/src/cli/utils/output.ts +0 -465
  88. package/src/cli/utils/wallet.ts +0 -109
  89. package/src/config/agirailsmd.ts +0 -262
  90. package/src/config/networks.ts +0 -275
  91. package/src/config/pendingPublish.ts +0 -237
  92. package/src/config/publishPipeline.ts +0 -359
  93. package/src/config/syncOperations.ts +0 -279
  94. package/src/erc8004/ERC8004Bridge.ts +0 -462
  95. package/src/erc8004/ReputationReporter.ts +0 -468
  96. package/src/erc8004/index.ts +0 -61
  97. package/src/errors/index.ts +0 -427
  98. package/src/index.ts +0 -364
  99. package/src/level0/Provider.ts +0 -117
  100. package/src/level0/ServiceDirectory.ts +0 -131
  101. package/src/level0/index.ts +0 -10
  102. package/src/level0/provide.ts +0 -132
  103. package/src/level0/request.ts +0 -432
  104. package/src/level1/Agent.ts +0 -1426
  105. package/src/level1/index.ts +0 -10
  106. package/src/level1/pricing/PriceCalculator.ts +0 -255
  107. package/src/level1/pricing/PricingStrategy.ts +0 -198
  108. package/src/level1/types/Job.ts +0 -179
  109. package/src/level1/types/Options.ts +0 -291
  110. package/src/level1/types/index.ts +0 -8
  111. package/src/protocol/ACTPKernel.ts +0 -808
  112. package/src/protocol/AgentRegistry.ts +0 -559
  113. package/src/protocol/DIDManager.ts +0 -629
  114. package/src/protocol/DIDResolver.ts +0 -554
  115. package/src/protocol/EASHelper.ts +0 -378
  116. package/src/protocol/EscrowVault.ts +0 -255
  117. package/src/protocol/EventMonitor.ts +0 -204
  118. package/src/protocol/MessageSigner.ts +0 -510
  119. package/src/protocol/ProofGenerator.ts +0 -339
  120. package/src/protocol/QuoteBuilder.ts +0 -15
  121. package/src/registry/AgentRegistryClient.ts +0 -202
  122. package/src/runtime/BlockchainRuntime.ts +0 -1015
  123. package/src/runtime/IACTPRuntime.ts +0 -306
  124. package/src/runtime/MockRuntime.ts +0 -1298
  125. package/src/runtime/MockStateManager.ts +0 -576
  126. package/src/runtime/index.ts +0 -25
  127. package/src/runtime/types/MockState.ts +0 -237
  128. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  129. package/src/storage/ArweaveClient.ts +0 -946
  130. package/src/storage/FilebaseClient.ts +0 -790
  131. package/src/storage/index.ts +0 -96
  132. package/src/storage/types.ts +0 -348
  133. package/src/types/adapter.ts +0 -310
  134. package/src/types/agent.ts +0 -79
  135. package/src/types/did.ts +0 -223
  136. package/src/types/eip712.ts +0 -175
  137. package/src/types/erc8004.ts +0 -293
  138. package/src/types/escrow.ts +0 -27
  139. package/src/types/index.ts +0 -17
  140. package/src/types/message.ts +0 -145
  141. package/src/types/state.ts +0 -87
  142. package/src/types/transaction.ts +0 -69
  143. package/src/types/x402.ts +0 -251
  144. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  145. package/src/utils/Helpers.ts +0 -688
  146. package/src/utils/IPFSClient.ts +0 -368
  147. package/src/utils/Logger.ts +0 -484
  148. package/src/utils/NonceManager.ts +0 -591
  149. package/src/utils/RateLimiter.ts +0 -534
  150. package/src/utils/ReceivedNonceTracker.ts +0 -567
  151. package/src/utils/SDKLifecycle.ts +0 -416
  152. package/src/utils/SecureNonce.ts +0 -78
  153. package/src/utils/Semaphore.ts +0 -276
  154. package/src/utils/UsedAttestationTracker.ts +0 -385
  155. package/src/utils/canonicalJson.ts +0 -38
  156. package/src/utils/circuitBreaker.ts +0 -324
  157. package/src/utils/computeTypeHash.ts +0 -48
  158. package/src/utils/fsSafe.ts +0 -80
  159. package/src/utils/index.ts +0 -80
  160. package/src/utils/retry.ts +0 -364
  161. package/src/utils/security.ts +0 -418
  162. package/src/utils/validation.ts +0 -540
  163. package/src/wallet/AutoWalletProvider.ts +0 -299
  164. package/src/wallet/EOAWalletProvider.ts +0 -69
  165. package/src/wallet/IWalletProvider.ts +0 -135
  166. package/src/wallet/aa/BundlerClient.ts +0 -274
  167. package/src/wallet/aa/DualNonceManager.ts +0 -173
  168. package/src/wallet/aa/PaymasterClient.ts +0 -174
  169. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  170. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  171. package/src/wallet/aa/constants.ts +0 -60
  172. 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
- }