pay-lobster 1.0.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.
Files changed (120) hide show
  1. package/README.md +401 -0
  2. package/README.md.bak +401 -0
  3. package/dist/agent.d.ts +132 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +224 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/analytics.d.ts +120 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +345 -0
  10. package/dist/analytics.js.map +1 -0
  11. package/dist/approvals.d.ts +168 -0
  12. package/dist/approvals.d.ts.map +1 -0
  13. package/dist/approvals.js +406 -0
  14. package/dist/approvals.js.map +1 -0
  15. package/dist/circle-client.d.ts +152 -0
  16. package/dist/circle-client.d.ts.map +1 -0
  17. package/dist/circle-client.js +266 -0
  18. package/dist/circle-client.js.map +1 -0
  19. package/dist/commission.d.ts +191 -0
  20. package/dist/commission.d.ts.map +1 -0
  21. package/dist/commission.js +475 -0
  22. package/dist/commission.js.map +1 -0
  23. package/dist/condition-builder.d.ts +98 -0
  24. package/dist/condition-builder.d.ts.map +1 -0
  25. package/dist/condition-builder.js +193 -0
  26. package/dist/condition-builder.js.map +1 -0
  27. package/dist/contacts.d.ts +179 -0
  28. package/dist/contacts.d.ts.map +1 -0
  29. package/dist/contacts.js +445 -0
  30. package/dist/contacts.js.map +1 -0
  31. package/dist/easy.d.ts +22 -0
  32. package/dist/easy.d.ts.map +1 -0
  33. package/dist/easy.js +40 -0
  34. package/dist/easy.js.map +1 -0
  35. package/dist/erc8004/constants.d.ts +152 -0
  36. package/dist/erc8004/constants.d.ts.map +1 -0
  37. package/dist/erc8004/constants.js +114 -0
  38. package/dist/erc8004/constants.js.map +1 -0
  39. package/dist/erc8004/discovery.d.ts +84 -0
  40. package/dist/erc8004/discovery.d.ts.map +1 -0
  41. package/dist/erc8004/discovery.js +217 -0
  42. package/dist/erc8004/discovery.js.map +1 -0
  43. package/dist/erc8004/identity.d.ts +91 -0
  44. package/dist/erc8004/identity.d.ts.map +1 -0
  45. package/dist/erc8004/identity.js +250 -0
  46. package/dist/erc8004/identity.js.map +1 -0
  47. package/dist/erc8004/index.d.ts +147 -0
  48. package/dist/erc8004/index.d.ts.map +1 -0
  49. package/dist/erc8004/index.js +225 -0
  50. package/dist/erc8004/index.js.map +1 -0
  51. package/dist/erc8004/reputation.d.ts +133 -0
  52. package/dist/erc8004/reputation.d.ts.map +1 -0
  53. package/dist/erc8004/reputation.js +277 -0
  54. package/dist/erc8004/reputation.js.map +1 -0
  55. package/dist/escrow-templates.d.ts +38 -0
  56. package/dist/escrow-templates.d.ts.map +1 -0
  57. package/dist/escrow-templates.js +419 -0
  58. package/dist/escrow-templates.js.map +1 -0
  59. package/dist/escrow.d.ts +320 -0
  60. package/dist/escrow.d.ts.map +1 -0
  61. package/dist/escrow.js +854 -0
  62. package/dist/escrow.js.map +1 -0
  63. package/dist/index.d.ts +11 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +33 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/invoices.d.ts +212 -0
  68. package/dist/invoices.d.ts.map +1 -0
  69. package/dist/invoices.js +393 -0
  70. package/dist/invoices.js.map +1 -0
  71. package/dist/notifications.d.ts +141 -0
  72. package/dist/notifications.d.ts.map +1 -0
  73. package/dist/notifications.js +350 -0
  74. package/dist/notifications.js.map +1 -0
  75. package/dist/tips.d.ts +171 -0
  76. package/dist/tips.d.ts.map +1 -0
  77. package/dist/tips.js +390 -0
  78. package/dist/tips.js.map +1 -0
  79. package/dist/types.d.ts +100 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +6 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/x402-client.d.ts +127 -0
  84. package/dist/x402-client.d.ts.map +1 -0
  85. package/dist/x402-client.js +350 -0
  86. package/dist/x402-client.js.map +1 -0
  87. package/dist/x402-server.d.ts +133 -0
  88. package/dist/x402-server.d.ts.map +1 -0
  89. package/dist/x402-server.js +330 -0
  90. package/dist/x402-server.js.map +1 -0
  91. package/lib/agent.ts +273 -0
  92. package/lib/analytics.ts +474 -0
  93. package/lib/analytics.ts.bak +474 -0
  94. package/lib/approvals.ts +585 -0
  95. package/lib/approvals.ts.bak +585 -0
  96. package/lib/circle-client.ts +376 -0
  97. package/lib/circle-client.ts.bak +376 -0
  98. package/lib/commission.ts +680 -0
  99. package/lib/commission.ts.bak +680 -0
  100. package/lib/condition-builder.ts +223 -0
  101. package/lib/condition-builder.ts.bak +223 -0
  102. package/lib/contacts.ts +615 -0
  103. package/lib/contacts.ts.bak +615 -0
  104. package/lib/easy.ts +46 -0
  105. package/lib/easy.ts.bak +352 -0
  106. package/lib/erc8004/constants.ts +175 -0
  107. package/lib/erc8004/discovery.ts +299 -0
  108. package/lib/erc8004/identity.ts +327 -0
  109. package/lib/erc8004/index.ts +285 -0
  110. package/lib/erc8004/reputation.ts +368 -0
  111. package/lib/escrow-templates.ts +462 -0
  112. package/lib/escrow.ts +1216 -0
  113. package/lib/index.ts +13 -0
  114. package/lib/invoices.ts +588 -0
  115. package/lib/notifications.ts +484 -0
  116. package/lib/tips.ts +570 -0
  117. package/lib/types.ts +108 -0
  118. package/lib/x402-client.ts +471 -0
  119. package/lib/x402-server.ts +462 -0
  120. package/package.json +58 -0
package/lib/escrow.ts ADDED
@@ -0,0 +1,1216 @@
1
+ /**
2
+ * Universal Escrow Module (Escrow as a Service - EaaS)
3
+ *
4
+ * Smart contract-style escrow for multiple verticals:
5
+ * - Real estate (earnest money, security deposits, closing)
6
+ * - Freelance/Services (milestones, deliverables)
7
+ * - Commerce (purchases, trades, swaps)
8
+ * - P2P (OTC trades, loans)
9
+ * - Digital (licenses, subscriptions, content)
10
+ * - Custom (user-defined)
11
+ */
12
+
13
+ import crypto from 'crypto';
14
+ import fs from 'fs/promises';
15
+ import path from 'path';
16
+ import type { EscrowTemplate, EscrowVertical } from './escrow-templates';
17
+ import { getTemplate } from './escrow-templates';
18
+ import type { BuiltCondition } from './condition-builder';
19
+
20
+ export type EscrowType =
21
+ // Real Estate
22
+ | 'earnest_money'
23
+ | 'security_deposit'
24
+ | 'closing_funds'
25
+ // Freelance / Services
26
+ | 'milestone'
27
+ | 'freelance'
28
+ // Commerce
29
+ | 'purchase'
30
+ | 'trade'
31
+ // Custom
32
+ | 'general';
33
+
34
+ export type EscrowStatus =
35
+ | 'created'
36
+ | 'funded'
37
+ | 'pending_release'
38
+ | 'released'
39
+ | 'refunded'
40
+ | 'disputed'
41
+ | 'cancelled';
42
+
43
+ /**
44
+ * Standard roles across verticals
45
+ */
46
+ export type StandardRole =
47
+ | 'depositor' | 'recipient' // Universal
48
+ | 'buyer' | 'seller' // Commerce
49
+ | 'client' | 'provider' // Services
50
+ | 'landlord' | 'tenant' // Real estate
51
+ | 'agent' | 'title_company' // Real estate agents
52
+ | 'arbiter' | 'witness' // Dispute resolution
53
+ | 'lender' | 'borrower' // P2P lending
54
+ | 'party_a' | 'party_b' // Generic parties
55
+ | 'marketplace' | 'platform' // Third parties
56
+ | 'consultant' | 'contractor' | 'inspector' | 'vendor' | 'subscriber' | 'creator'; // Specific roles
57
+
58
+ export interface EscrowParty {
59
+ role: StandardRole | string; // Standard or custom role
60
+ name: string;
61
+ email?: string;
62
+ phone?: string;
63
+ walletAddress?: string;
64
+ sessionId?: string; // For Clawdbot agent approval
65
+ }
66
+
67
+ export interface EscrowCondition {
68
+ id: string;
69
+ description: string;
70
+ type:
71
+ // Real Estate
72
+ | 'inspection' | 'financing' | 'appraisal' | 'title' | 'closing' | 'move_out'
73
+ // Freelance/Milestones
74
+ | 'milestone' | 'delivery' | 'approval' | 'revision'
75
+ // Commerce
76
+ | 'shipping' | 'receipt' | 'verification'
77
+ // Document/Time-based
78
+ | 'document' | 'deadline'
79
+ // Custom
80
+ | 'custom';
81
+ status: 'pending' | 'satisfied' | 'waived' | 'failed';
82
+ deadline?: string;
83
+ satisfiedAt?: string;
84
+ satisfiedBy?: string;
85
+ evidence?: string; // URL or description of proof
86
+
87
+ // For milestone escrows - partial release
88
+ releaseAmount?: string; // Amount to release when this condition is met
89
+ releasePercentage?: string; // Or percentage of total
90
+
91
+ // Additional metadata
92
+ metadata?: Record<string, any>;
93
+ }
94
+
95
+ export interface Escrow {
96
+ id: string;
97
+ type: EscrowType;
98
+ status: EscrowStatus;
99
+
100
+ // Property/transaction info
101
+ property?: {
102
+ address: string;
103
+ city: string;
104
+ state: string;
105
+ zip: string;
106
+ mlsNumber?: string;
107
+ };
108
+
109
+ // Parties
110
+ parties: EscrowParty[];
111
+
112
+ // Funds
113
+ amount: string;
114
+ chain: string;
115
+ escrowAddress: string; // Smart contract or custody address
116
+ fundingTxHash?: string;
117
+ fundedAt?: string;
118
+
119
+ // Conditions for release
120
+ conditions: EscrowCondition[];
121
+ releaseRequires: 'all_conditions' | 'majority_approval' | 'any_party';
122
+
123
+ // Approvals for release
124
+ approvals: {
125
+ partyRole: string;
126
+ approved: boolean;
127
+ timestamp: string;
128
+ note?: string;
129
+ }[];
130
+ requiredApprovals: string[]; // Roles that must approve
131
+
132
+ // Release/refund
133
+ releaseTo?: string; // Address
134
+ releaseToRole?: string;
135
+ releaseTxHash?: string;
136
+ releasedAt?: string;
137
+
138
+ // Dispute
139
+ dispute?: {
140
+ raisedBy: string;
141
+ reason: string;
142
+ raisedAt: string;
143
+ resolution?: string;
144
+ resolvedAt?: string;
145
+ };
146
+
147
+ // Deadlines
148
+ fundingDeadline?: string;
149
+ closingDate?: string;
150
+ leaseEndDate?: string;
151
+
152
+ // Metadata
153
+ notes?: string;
154
+ documents: { name: string; url: string; uploadedAt: string }[];
155
+
156
+ createdAt: string;
157
+ updatedAt: string;
158
+ }
159
+
160
+ const DATA_DIR = process.env.USDC_DATA_DIR || './data';
161
+
162
+ /**
163
+ * Escrow Manager
164
+ */
165
+ export class EscrowManager {
166
+ private dataPath: string;
167
+
168
+ constructor(dataDir = DATA_DIR) {
169
+ this.dataPath = path.join(dataDir, 'escrows.json');
170
+ }
171
+
172
+ private async loadEscrows(): Promise<Escrow[]> {
173
+ try {
174
+ const data = await fs.readFile(this.dataPath, 'utf-8');
175
+ return JSON.parse(data);
176
+ } catch {
177
+ return [];
178
+ }
179
+ }
180
+
181
+ private async saveEscrows(escrows: Escrow[]): Promise<void> {
182
+ await fs.mkdir(path.dirname(this.dataPath), { recursive: true });
183
+ await fs.writeFile(this.dataPath, JSON.stringify(escrows, null, 2));
184
+ }
185
+
186
+ // ============ Escrow Creation ============
187
+
188
+ /**
189
+ * Create earnest money escrow
190
+ */
191
+ async createEarnestMoney(params: {
192
+ property: Escrow['property'];
193
+ amount: string;
194
+ chain: string;
195
+ buyer: Omit<EscrowParty, 'role'>;
196
+ seller: Omit<EscrowParty, 'role'>;
197
+ agent?: Omit<EscrowParty, 'role'>;
198
+ closingDate?: string;
199
+ conditions?: Omit<EscrowCondition, 'id' | 'status'>[];
200
+ }): Promise<Escrow> {
201
+ const escrows = await this.loadEscrows();
202
+
203
+ const parties: EscrowParty[] = [
204
+ { ...params.buyer, role: 'buyer' },
205
+ { ...params.seller, role: 'seller' },
206
+ ];
207
+ if (params.agent) {
208
+ parties.push({ ...params.agent, role: 'agent' });
209
+ }
210
+
211
+ // Default conditions for earnest money
212
+ const defaultConditions: EscrowCondition[] = [
213
+ {
214
+ id: crypto.randomUUID(),
215
+ description: 'Home inspection satisfactory',
216
+ type: 'inspection',
217
+ status: 'pending',
218
+ deadline: this.addDays(new Date(), 10).toISOString(),
219
+ },
220
+ {
221
+ id: crypto.randomUUID(),
222
+ description: 'Financing approved',
223
+ type: 'financing',
224
+ status: 'pending',
225
+ deadline: this.addDays(new Date(), 21).toISOString(),
226
+ },
227
+ {
228
+ id: crypto.randomUUID(),
229
+ description: 'Title clear',
230
+ type: 'title',
231
+ status: 'pending',
232
+ },
233
+ ];
234
+
235
+ const customConditions = (params.conditions || []).map(c => ({
236
+ ...c,
237
+ id: crypto.randomUUID(),
238
+ status: 'pending' as const,
239
+ }));
240
+
241
+ const escrow: Escrow = {
242
+ id: `EM-${Date.now().toString(36).toUpperCase()}`,
243
+ type: 'earnest_money',
244
+ status: 'created',
245
+ property: params.property,
246
+ parties,
247
+ amount: params.amount,
248
+ chain: params.chain,
249
+ escrowAddress: this.generateEscrowAddress(),
250
+ conditions: [...defaultConditions, ...customConditions],
251
+ releaseRequires: 'all_conditions',
252
+ approvals: [],
253
+ requiredApprovals: ['buyer', 'seller'],
254
+ closingDate: params.closingDate,
255
+ fundingDeadline: this.addDays(new Date(), 3).toISOString(),
256
+ documents: [],
257
+ createdAt: new Date().toISOString(),
258
+ updatedAt: new Date().toISOString(),
259
+ };
260
+
261
+ escrows.push(escrow);
262
+ await this.saveEscrows(escrows);
263
+
264
+ return escrow;
265
+ }
266
+
267
+ /**
268
+ * Create rental security deposit escrow
269
+ */
270
+ async createSecurityDeposit(params: {
271
+ property: Escrow['property'];
272
+ amount: string;
273
+ chain: string;
274
+ landlord: Omit<EscrowParty, 'role'>;
275
+ tenant: Omit<EscrowParty, 'role'>;
276
+ leaseEndDate: string;
277
+ }): Promise<Escrow> {
278
+ const escrows = await this.loadEscrows();
279
+
280
+ const escrow: Escrow = {
281
+ id: `SD-${Date.now().toString(36).toUpperCase()}`,
282
+ type: 'security_deposit',
283
+ status: 'created',
284
+ property: params.property,
285
+ parties: [
286
+ { ...params.landlord, role: 'landlord' },
287
+ { ...params.tenant, role: 'tenant' },
288
+ ],
289
+ amount: params.amount,
290
+ chain: params.chain,
291
+ escrowAddress: this.generateEscrowAddress(),
292
+ conditions: [
293
+ {
294
+ id: crypto.randomUUID(),
295
+ description: 'Lease term completed',
296
+ type: 'move_out',
297
+ status: 'pending',
298
+ deadline: params.leaseEndDate,
299
+ },
300
+ {
301
+ id: crypto.randomUUID(),
302
+ description: 'Move-out inspection passed',
303
+ type: 'inspection',
304
+ status: 'pending',
305
+ },
306
+ ],
307
+ releaseRequires: 'majority_approval',
308
+ approvals: [],
309
+ requiredApprovals: ['landlord'], // Landlord approval releases to tenant
310
+ leaseEndDate: params.leaseEndDate,
311
+ documents: [],
312
+ createdAt: new Date().toISOString(),
313
+ updatedAt: new Date().toISOString(),
314
+ };
315
+
316
+ escrows.push(escrow);
317
+ await this.saveEscrows(escrows);
318
+
319
+ return escrow;
320
+ }
321
+
322
+ /**
323
+ * Create general escrow
324
+ */
325
+ async createGeneral(params: {
326
+ amount: string;
327
+ chain: string;
328
+ depositor: Omit<EscrowParty, 'role'>;
329
+ recipient: Omit<EscrowParty, 'role'>;
330
+ conditions?: Omit<EscrowCondition, 'id' | 'status'>[];
331
+ description?: string;
332
+ }): Promise<Escrow> {
333
+ const escrows = await this.loadEscrows();
334
+
335
+ const escrow: Escrow = {
336
+ id: `GE-${Date.now().toString(36).toUpperCase()}`,
337
+ type: 'general',
338
+ status: 'created',
339
+ parties: [
340
+ { ...params.depositor, role: 'depositor' },
341
+ { ...params.recipient, role: 'recipient' },
342
+ ],
343
+ amount: params.amount,
344
+ chain: params.chain,
345
+ escrowAddress: this.generateEscrowAddress(),
346
+ conditions: (params.conditions || []).map(c => ({
347
+ ...c,
348
+ id: crypto.randomUUID(),
349
+ status: 'pending' as const,
350
+ })),
351
+ releaseRequires: 'all_conditions',
352
+ approvals: [],
353
+ requiredApprovals: ['depositor'],
354
+ notes: params.description,
355
+ documents: [],
356
+ createdAt: new Date().toISOString(),
357
+ updatedAt: new Date().toISOString(),
358
+ };
359
+
360
+ escrows.push(escrow);
361
+ await this.saveEscrows(escrows);
362
+
363
+ return escrow;
364
+ }
365
+
366
+ /**
367
+ * Create milestone escrow for freelance/service work
368
+ */
369
+ async createMilestone(params: {
370
+ amount: string;
371
+ chain: string;
372
+ client: Omit<EscrowParty, 'role'>;
373
+ freelancer: Omit<EscrowParty, 'role'>;
374
+ projectName: string;
375
+ milestones: {
376
+ description: string;
377
+ amount?: string; // Fixed amount for this milestone
378
+ percentage?: string; // Or percentage of total
379
+ deadline?: string;
380
+ }[];
381
+ }): Promise<Escrow> {
382
+ const escrows = await this.loadEscrows();
383
+ const totalAmount = parseFloat(params.amount);
384
+
385
+ // Convert milestones to conditions with release amounts
386
+ const conditions: EscrowCondition[] = params.milestones.map((m, i) => ({
387
+ id: crypto.randomUUID(),
388
+ description: m.description,
389
+ type: 'milestone' as const,
390
+ status: 'pending' as const,
391
+ deadline: m.deadline,
392
+ releaseAmount: m.amount,
393
+ releasePercentage: m.percentage,
394
+ }));
395
+
396
+ // Validate amounts/percentages add up
397
+ let totalAllocated = 0;
398
+ for (const cond of conditions) {
399
+ if (cond.releaseAmount) {
400
+ totalAllocated += parseFloat(cond.releaseAmount);
401
+ } else if (cond.releasePercentage) {
402
+ totalAllocated += totalAmount * (parseFloat(cond.releasePercentage) / 100);
403
+ }
404
+ }
405
+
406
+ if (Math.abs(totalAllocated - totalAmount) > 0.01) {
407
+ throw new Error(`Milestone amounts ($${totalAllocated.toFixed(2)}) don't match total ($${params.amount})`);
408
+ }
409
+
410
+ const escrow: Escrow = {
411
+ id: `MS-${Date.now().toString(36).toUpperCase()}`,
412
+ type: 'milestone',
413
+ status: 'created',
414
+ parties: [
415
+ { ...params.client, role: 'depositor' },
416
+ { ...params.freelancer, role: 'recipient' },
417
+ ],
418
+ amount: params.amount,
419
+ chain: params.chain,
420
+ escrowAddress: this.generateEscrowAddress(),
421
+ conditions,
422
+ releaseRequires: 'any_party', // Each milestone releases independently
423
+ approvals: [],
424
+ requiredApprovals: ['depositor'], // Client approves each milestone
425
+ notes: `Project: ${params.projectName}`,
426
+ documents: [],
427
+ createdAt: new Date().toISOString(),
428
+ updatedAt: new Date().toISOString(),
429
+ };
430
+
431
+ escrows.push(escrow);
432
+ await this.saveEscrows(escrows);
433
+
434
+ return escrow;
435
+ }
436
+
437
+ /**
438
+ * Create purchase escrow (buyer/seller goods transaction)
439
+ */
440
+ async createPurchase(params: {
441
+ amount: string;
442
+ chain: string;
443
+ buyer: Omit<EscrowParty, 'role'>;
444
+ seller: Omit<EscrowParty, 'role'>;
445
+ itemDescription: string;
446
+ requiresShipping?: boolean;
447
+ inspectionPeriodDays?: number;
448
+ }): Promise<Escrow> {
449
+ const escrows = await this.loadEscrows();
450
+
451
+ const conditions: EscrowCondition[] = [];
452
+
453
+ if (params.requiresShipping) {
454
+ conditions.push({
455
+ id: crypto.randomUUID(),
456
+ description: 'Item shipped by seller',
457
+ type: 'shipping',
458
+ status: 'pending',
459
+ });
460
+ conditions.push({
461
+ id: crypto.randomUUID(),
462
+ description: 'Item received by buyer',
463
+ type: 'receipt',
464
+ status: 'pending',
465
+ });
466
+ }
467
+
468
+ if (params.inspectionPeriodDays) {
469
+ conditions.push({
470
+ id: crypto.randomUUID(),
471
+ description: `Inspection period (${params.inspectionPeriodDays} days)`,
472
+ type: 'inspection',
473
+ status: 'pending',
474
+ deadline: this.addDays(new Date(), params.inspectionPeriodDays).toISOString(),
475
+ });
476
+ }
477
+
478
+ // Always require buyer approval
479
+ conditions.push({
480
+ id: crypto.randomUUID(),
481
+ description: 'Buyer approves release',
482
+ type: 'approval',
483
+ status: 'pending',
484
+ });
485
+
486
+ const escrow: Escrow = {
487
+ id: `PU-${Date.now().toString(36).toUpperCase()}`,
488
+ type: 'purchase',
489
+ status: 'created',
490
+ parties: [
491
+ { ...params.buyer, role: 'buyer' },
492
+ { ...params.seller, role: 'seller' },
493
+ ],
494
+ amount: params.amount,
495
+ chain: params.chain,
496
+ escrowAddress: this.generateEscrowAddress(),
497
+ conditions,
498
+ releaseRequires: 'all_conditions',
499
+ approvals: [],
500
+ requiredApprovals: ['buyer'],
501
+ notes: params.itemDescription,
502
+ documents: [],
503
+ createdAt: new Date().toISOString(),
504
+ updatedAt: new Date().toISOString(),
505
+ };
506
+
507
+ escrows.push(escrow);
508
+ await this.saveEscrows(escrows);
509
+
510
+ return escrow;
511
+ }
512
+
513
+ /**
514
+ * Release partial amount (for milestone escrows)
515
+ */
516
+ async releasePartial(
517
+ escrowId: string,
518
+ conditionId: string,
519
+ toAddress: string,
520
+ txHash: string
521
+ ): Promise<Escrow | null> {
522
+ const escrows = await this.loadEscrows();
523
+ const escrow = escrows.find(e => e.id === escrowId);
524
+
525
+ if (!escrow) return null;
526
+
527
+ const condition = escrow.conditions.find(c => c.id === conditionId);
528
+ if (!condition || condition.status !== 'satisfied') {
529
+ throw new Error('Condition must be satisfied before partial release');
530
+ }
531
+
532
+ // Calculate release amount
533
+ let releaseAmount: string;
534
+ if (condition.releaseAmount) {
535
+ releaseAmount = condition.releaseAmount;
536
+ } else if (condition.releasePercentage) {
537
+ const total = parseFloat(escrow.amount);
538
+ releaseAmount = (total * parseFloat(condition.releasePercentage) / 100).toFixed(2);
539
+ } else {
540
+ throw new Error('Condition has no release amount defined');
541
+ }
542
+
543
+ // Record the partial release
544
+ if (!escrow.releaseTo) {
545
+ escrow.releaseTo = toAddress;
546
+ escrow.releaseTxHash = txHash;
547
+ }
548
+
549
+ // Check if all milestones released
550
+ const allReleased = escrow.conditions
551
+ .filter(c => c.type === 'milestone')
552
+ .every(c => c.status === 'satisfied');
553
+
554
+ if (allReleased) {
555
+ escrow.status = 'released';
556
+ escrow.releasedAt = new Date().toISOString();
557
+ }
558
+
559
+ escrow.updatedAt = new Date().toISOString();
560
+ await this.saveEscrows(escrows);
561
+
562
+ return escrow;
563
+ }
564
+
565
+ // ============ Escrow Operations ============
566
+
567
+ /**
568
+ * Get escrow by ID
569
+ */
570
+ async get(id: string): Promise<Escrow | null> {
571
+ const escrows = await this.loadEscrows();
572
+ return escrows.find(e => e.id === id) || null;
573
+ }
574
+
575
+ /**
576
+ * List escrows with filters
577
+ */
578
+ async list(filters?: {
579
+ type?: EscrowType;
580
+ status?: EscrowStatus;
581
+ partyAddress?: string;
582
+ propertyAddress?: string;
583
+ }): Promise<Escrow[]> {
584
+ let escrows = await this.loadEscrows();
585
+
586
+ if (filters?.type) {
587
+ escrows = escrows.filter(e => e.type === filters.type);
588
+ }
589
+ if (filters?.status) {
590
+ escrows = escrows.filter(e => e.status === filters.status);
591
+ }
592
+ if (filters?.partyAddress) {
593
+ escrows = escrows.filter(e =>
594
+ e.parties.some(p => p.walletAddress?.toLowerCase() === filters.partyAddress!.toLowerCase())
595
+ );
596
+ }
597
+ if (filters?.propertyAddress) {
598
+ escrows = escrows.filter(e =>
599
+ e.property?.address.toLowerCase().includes(filters.propertyAddress!.toLowerCase())
600
+ );
601
+ }
602
+
603
+ return escrows.sort((a, b) =>
604
+ new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
605
+ );
606
+ }
607
+
608
+ /**
609
+ * Mark escrow as funded
610
+ */
611
+ async markFunded(id: string, txHash: string): Promise<Escrow | null> {
612
+ const escrows = await this.loadEscrows();
613
+ const escrow = escrows.find(e => e.id === id);
614
+
615
+ if (escrow && escrow.status === 'created') {
616
+ escrow.status = 'funded';
617
+ escrow.fundingTxHash = txHash;
618
+ escrow.fundedAt = new Date().toISOString();
619
+ escrow.updatedAt = new Date().toISOString();
620
+ await this.saveEscrows(escrows);
621
+ }
622
+
623
+ return escrow || null;
624
+ }
625
+
626
+ /**
627
+ * Satisfy a condition
628
+ */
629
+ async satisfyCondition(
630
+ escrowId: string,
631
+ conditionId: string,
632
+ satisfiedBy: string,
633
+ evidence?: string
634
+ ): Promise<Escrow | null> {
635
+ const escrows = await this.loadEscrows();
636
+ const escrow = escrows.find(e => e.id === escrowId);
637
+
638
+ if (!escrow) return null;
639
+
640
+ const condition = escrow.conditions.find(c => c.id === conditionId);
641
+ if (condition && condition.status === 'pending') {
642
+ condition.status = 'satisfied';
643
+ condition.satisfiedAt = new Date().toISOString();
644
+ condition.satisfiedBy = satisfiedBy;
645
+ condition.evidence = evidence;
646
+ escrow.updatedAt = new Date().toISOString();
647
+
648
+ // Check if all conditions satisfied
649
+ const allSatisfied = escrow.conditions.every(c =>
650
+ c.status === 'satisfied' || c.status === 'waived'
651
+ );
652
+ if (allSatisfied && escrow.status === 'funded') {
653
+ escrow.status = 'pending_release';
654
+ }
655
+
656
+ await this.saveEscrows(escrows);
657
+ }
658
+
659
+ return escrow;
660
+ }
661
+
662
+ /**
663
+ * Waive a condition
664
+ */
665
+ async waiveCondition(escrowId: string, conditionId: string, waivedBy: string): Promise<Escrow | null> {
666
+ const escrows = await this.loadEscrows();
667
+ const escrow = escrows.find(e => e.id === escrowId);
668
+
669
+ if (!escrow) return null;
670
+
671
+ const condition = escrow.conditions.find(c => c.id === conditionId);
672
+ if (condition && condition.status === 'pending') {
673
+ condition.status = 'waived';
674
+ condition.satisfiedAt = new Date().toISOString();
675
+ condition.satisfiedBy = waivedBy;
676
+ escrow.updatedAt = new Date().toISOString();
677
+ await this.saveEscrows(escrows);
678
+ }
679
+
680
+ return escrow;
681
+ }
682
+
683
+ /**
684
+ * Fail a condition (triggers refund flow)
685
+ */
686
+ async failCondition(escrowId: string, conditionId: string, reason: string): Promise<Escrow | null> {
687
+ const escrows = await this.loadEscrows();
688
+ const escrow = escrows.find(e => e.id === escrowId);
689
+
690
+ if (!escrow) return null;
691
+
692
+ const condition = escrow.conditions.find(c => c.id === conditionId);
693
+ if (condition && condition.status === 'pending') {
694
+ condition.status = 'failed';
695
+ condition.evidence = reason;
696
+ escrow.updatedAt = new Date().toISOString();
697
+
698
+ // Initiate refund process for earnest money
699
+ if (escrow.type === 'earnest_money') {
700
+ escrow.status = 'pending_release';
701
+ escrow.releaseToRole = 'buyer'; // Failed conditions = refund to buyer
702
+ }
703
+
704
+ await this.saveEscrows(escrows);
705
+ }
706
+
707
+ return escrow;
708
+ }
709
+
710
+ /**
711
+ * Submit approval for release
712
+ */
713
+ async approve(escrowId: string, partyRole: string, note?: string): Promise<Escrow | null> {
714
+ const escrows = await this.loadEscrows();
715
+ const escrow = escrows.find(e => e.id === escrowId);
716
+
717
+ if (!escrow) return null;
718
+
719
+ // Check if party is authorized
720
+ if (!escrow.requiredApprovals.includes(partyRole)) {
721
+ throw new Error(`Party role "${partyRole}" is not required for approval`);
722
+ }
723
+
724
+ // Check if already approved
725
+ if (escrow.approvals.some(a => a.partyRole === partyRole)) {
726
+ throw new Error('Already approved');
727
+ }
728
+
729
+ escrow.approvals.push({
730
+ partyRole,
731
+ approved: true,
732
+ timestamp: new Date().toISOString(),
733
+ note,
734
+ });
735
+ escrow.updatedAt = new Date().toISOString();
736
+
737
+ // Check if all required approvals received
738
+ const allApproved = escrow.requiredApprovals.every(role =>
739
+ escrow.approvals.some(a => a.partyRole === role && a.approved)
740
+ );
741
+
742
+ if (allApproved && (escrow.status === 'funded' || escrow.status === 'pending_release')) {
743
+ escrow.status = 'pending_release';
744
+ }
745
+
746
+ await this.saveEscrows(escrows);
747
+ return escrow;
748
+ }
749
+
750
+ /**
751
+ * Execute release
752
+ */
753
+ async release(escrowId: string, toAddress: string, txHash: string): Promise<Escrow | null> {
754
+ const escrows = await this.loadEscrows();
755
+ const escrow = escrows.find(e => e.id === escrowId);
756
+
757
+ if (escrow && escrow.status === 'pending_release') {
758
+ escrow.status = 'released';
759
+ escrow.releaseTo = toAddress;
760
+ escrow.releaseTxHash = txHash;
761
+ escrow.releasedAt = new Date().toISOString();
762
+ escrow.updatedAt = new Date().toISOString();
763
+ await this.saveEscrows(escrows);
764
+ }
765
+
766
+ return escrow || null;
767
+ }
768
+
769
+ /**
770
+ * Execute refund
771
+ */
772
+ async refund(escrowId: string, txHash: string): Promise<Escrow | null> {
773
+ const escrows = await this.loadEscrows();
774
+ const escrow = escrows.find(e => e.id === escrowId);
775
+
776
+ if (escrow && (escrow.status === 'funded' || escrow.status === 'pending_release')) {
777
+ // Find buyer/depositor address
778
+ const refundParty = escrow.parties.find(p =>
779
+ p.role === 'buyer' || p.role === 'tenant' || p.role === 'depositor'
780
+ );
781
+
782
+ escrow.status = 'refunded';
783
+ escrow.releaseTo = refundParty?.walletAddress;
784
+ escrow.releaseTxHash = txHash;
785
+ escrow.releasedAt = new Date().toISOString();
786
+ escrow.updatedAt = new Date().toISOString();
787
+ await this.saveEscrows(escrows);
788
+ }
789
+
790
+ return escrow || null;
791
+ }
792
+
793
+ /**
794
+ * Raise dispute
795
+ */
796
+ async raiseDispute(escrowId: string, raisedBy: string, reason: string): Promise<Escrow | null> {
797
+ const escrows = await this.loadEscrows();
798
+ const escrow = escrows.find(e => e.id === escrowId);
799
+
800
+ if (escrow && escrow.status === 'funded') {
801
+ escrow.status = 'disputed';
802
+ escrow.dispute = {
803
+ raisedBy,
804
+ reason,
805
+ raisedAt: new Date().toISOString(),
806
+ };
807
+ escrow.updatedAt = new Date().toISOString();
808
+ await this.saveEscrows(escrows);
809
+ }
810
+
811
+ return escrow || null;
812
+ }
813
+
814
+ /**
815
+ * Cancel escrow (before funding)
816
+ */
817
+ async cancel(escrowId: string): Promise<Escrow | null> {
818
+ const escrows = await this.loadEscrows();
819
+ const escrow = escrows.find(e => e.id === escrowId);
820
+
821
+ if (escrow && escrow.status === 'created') {
822
+ escrow.status = 'cancelled';
823
+ escrow.updatedAt = new Date().toISOString();
824
+ await this.saveEscrows(escrows);
825
+ }
826
+
827
+ return escrow || null;
828
+ }
829
+
830
+ /**
831
+ * Add document to escrow
832
+ */
833
+ async addDocument(escrowId: string, name: string, url: string): Promise<Escrow | null> {
834
+ const escrows = await this.loadEscrows();
835
+ const escrow = escrows.find(e => e.id === escrowId);
836
+
837
+ if (escrow) {
838
+ escrow.documents.push({
839
+ name,
840
+ url,
841
+ uploadedAt: new Date().toISOString(),
842
+ });
843
+ escrow.updatedAt = new Date().toISOString();
844
+ await this.saveEscrows(escrows);
845
+ }
846
+
847
+ return escrow || null;
848
+ }
849
+
850
+ // ============ UNIVERSAL ESCROW API (EaaS) ============
851
+
852
+ /**
853
+ * Create escrow from template
854
+ *
855
+ * @example
856
+ * await escrowManager.create({
857
+ * template: 'project_milestone',
858
+ * amount: '1000',
859
+ * chain: 'polygon',
860
+ * parties: [
861
+ * { role: 'client', name: 'Alice' },
862
+ * { role: 'provider', name: 'Bob' }
863
+ * ]
864
+ * })
865
+ */
866
+ async create(params: {
867
+ template: string;
868
+ amount: string;
869
+ chain: string;
870
+ parties: Omit<EscrowParty, 'role'> & { role: string }[];
871
+ customConditions?: Omit<EscrowCondition, 'id' | 'status'>[];
872
+ metadata?: Record<string, any>;
873
+ autoReleaseDays?: number;
874
+ }): Promise<Escrow> {
875
+ const template = getTemplate(params.template);
876
+ if (!template) {
877
+ throw new Error(`Template '${params.template}' not found`);
878
+ }
879
+
880
+ const escrows = await this.loadEscrows();
881
+
882
+ // Convert template conditions to EscrowCondition format
883
+ const conditions: EscrowCondition[] = template.conditions.map(c => ({
884
+ id: crypto.randomUUID(),
885
+ description: c.description,
886
+ type: c.type,
887
+ status: 'pending',
888
+ ...(c.deadline && { deadline: c.deadline }),
889
+ ...(c.releaseAmount && { releaseAmount: c.releaseAmount }),
890
+ ...(c.releasePercentage && { releasePercentage: c.releasePercentage }),
891
+ ...(c.metadata && { metadata: c.metadata }),
892
+ }));
893
+
894
+ // Add custom conditions if provided
895
+ const customConditions = (params.customConditions || []).map(c => ({
896
+ ...c,
897
+ id: crypto.randomUUID(),
898
+ status: 'pending' as const,
899
+ }));
900
+
901
+ const escrow: Escrow = {
902
+ id: `${template.vertical.toUpperCase().slice(0, 2)}-${Date.now().toString(36).toUpperCase()}`,
903
+ type: this.mapVerticalToType(template.vertical),
904
+ status: 'created',
905
+ parties: params.parties as EscrowParty[],
906
+ amount: params.amount,
907
+ chain: params.chain,
908
+ escrowAddress: this.generateEscrowAddress(),
909
+ conditions: [...conditions, ...customConditions],
910
+ releaseRequires: template.releaseRequires,
911
+ approvals: [],
912
+ requiredApprovals: template.recommendedPartyRoles,
913
+ documents: [],
914
+ notes: `Created from template: ${template.name}`,
915
+ createdAt: new Date().toISOString(),
916
+ updatedAt: new Date().toISOString(),
917
+ fundingDeadline: this.addDays(new Date(), 3).toISOString(),
918
+ };
919
+
920
+ // Add auto-release if specified
921
+ if (params.autoReleaseDays || template.autoReleaseDays) {
922
+ const days = params.autoReleaseDays || template.autoReleaseDays!;
923
+ conditions.push({
924
+ id: crypto.randomUUID(),
925
+ description: `Auto-release after ${days} days if no disputes`,
926
+ type: 'deadline',
927
+ status: 'pending',
928
+ deadline: this.addDays(new Date(), days).toISOString(),
929
+ });
930
+ }
931
+
932
+ escrows.push(escrow);
933
+ await this.saveEscrows(escrows);
934
+
935
+ return escrow;
936
+ }
937
+
938
+ /**
939
+ * Create custom escrow with builder conditions
940
+ *
941
+ * @example
942
+ * import { ConditionBuilder } from './condition-builder';
943
+ *
944
+ * await escrowManager.createCustom({
945
+ * amount: '5000',
946
+ * chain: 'ethereum',
947
+ * parties: [
948
+ * { role: 'buyer', name: 'Alice' },
949
+ * { role: 'seller', name: 'Bob' }
950
+ * ],
951
+ * conditions: [
952
+ * ConditionBuilder.milestone('Phase 1', 30),
953
+ * ConditionBuilder.milestone('Phase 2', 70),
954
+ * ],
955
+ * releaseRequires: 'condition_based'
956
+ * })
957
+ */
958
+ async createCustom(params: {
959
+ amount: string;
960
+ chain: string;
961
+ parties: Omit<EscrowParty, 'role'> & { role: string }[];
962
+ conditions: BuiltCondition[];
963
+ releaseRequires?: 'all_conditions' | 'majority_approval' | 'condition_based' | 'any_party';
964
+ requiredApprovals?: string[];
965
+ metadata?: Record<string, any>;
966
+ autoReleaseDays?: number;
967
+ }): Promise<Escrow> {
968
+ const escrows = await this.loadEscrows();
969
+
970
+ const conditions: EscrowCondition[] = params.conditions.map(c => ({
971
+ ...c,
972
+ status: 'pending',
973
+ }));
974
+
975
+ // Add auto-release if specified
976
+ if (params.autoReleaseDays) {
977
+ conditions.push({
978
+ id: crypto.randomUUID(),
979
+ description: `Auto-release after ${params.autoReleaseDays} days if no disputes`,
980
+ type: 'deadline',
981
+ status: 'pending',
982
+ deadline: this.addDays(new Date(), params.autoReleaseDays).toISOString(),
983
+ });
984
+ }
985
+
986
+ const escrow: Escrow = {
987
+ id: `CUSTOM-${Date.now().toString(36).toUpperCase()}`,
988
+ type: 'general',
989
+ status: 'created',
990
+ parties: params.parties as EscrowParty[],
991
+ amount: params.amount,
992
+ chain: params.chain,
993
+ escrowAddress: this.generateEscrowAddress(),
994
+ conditions,
995
+ releaseRequires: params.releaseRequires || 'all_conditions',
996
+ approvals: [],
997
+ requiredApprovals: params.requiredApprovals || params.parties.map(p => p.role),
998
+ documents: [],
999
+ createdAt: new Date().toISOString(),
1000
+ updatedAt: new Date().toISOString(),
1001
+ fundingDeadline: this.addDays(new Date(), 3).toISOString(),
1002
+ };
1003
+
1004
+ escrows.push(escrow);
1005
+ await this.saveEscrows(escrows);
1006
+
1007
+ return escrow;
1008
+ }
1009
+
1010
+ /**
1011
+ * Map vertical to legacy EscrowType
1012
+ */
1013
+ private mapVerticalToType(vertical: EscrowVertical): EscrowType {
1014
+ switch (vertical) {
1015
+ case 'real_estate': return 'earnest_money';
1016
+ case 'freelance': return 'milestone';
1017
+ case 'commerce': return 'purchase';
1018
+ case 'p2p': return 'trade';
1019
+ case 'digital': return 'purchase';
1020
+ case 'services': return 'freelance';
1021
+ default: return 'general';
1022
+ }
1023
+ }
1024
+
1025
+ // ============ Formatting ============
1026
+
1027
+ /**
1028
+ * Format escrow summary for display
1029
+ */
1030
+ formatEscrowSummary(escrow: Escrow): string {
1031
+ const statusEmoji = {
1032
+ created: '📝',
1033
+ funded: '💰',
1034
+ pending_release: '⏳',
1035
+ released: '✅',
1036
+ refunded: '↩️',
1037
+ disputed: '⚠️',
1038
+ cancelled: '❌',
1039
+ }[escrow.status];
1040
+
1041
+ const typeLabel = {
1042
+ earnest_money: 'Earnest Money',
1043
+ security_deposit: 'Security Deposit',
1044
+ closing_funds: 'Closing Funds',
1045
+ general: 'Escrow',
1046
+ }[escrow.type];
1047
+
1048
+ let summary = `${statusEmoji} **${typeLabel} ${escrow.id}**\n\n`;
1049
+
1050
+ if (escrow.property) {
1051
+ summary += `📍 ${escrow.property.address}, ${escrow.property.city}, ${escrow.property.state}\n`;
1052
+ }
1053
+
1054
+ summary += `💵 **$${escrow.amount} USDC** (${escrow.chain})\n`;
1055
+ summary += `Status: ${escrow.status.replace('_', ' ').toUpperCase()}\n\n`;
1056
+
1057
+ // Parties
1058
+ summary += `**Parties:**\n`;
1059
+ for (const party of escrow.parties) {
1060
+ summary += `• ${party.role}: ${party.name}\n`;
1061
+ }
1062
+ summary += '\n';
1063
+
1064
+ // Conditions
1065
+ if (escrow.conditions.length > 0) {
1066
+ summary += `**Conditions:**\n`;
1067
+ for (const cond of escrow.conditions) {
1068
+ const condEmoji = {
1069
+ pending: '⏳',
1070
+ satisfied: '✅',
1071
+ waived: '⏭️',
1072
+ failed: '❌',
1073
+ }[cond.status];
1074
+ summary += `${condEmoji} ${cond.description}\n`;
1075
+ }
1076
+ summary += '\n';
1077
+ }
1078
+
1079
+ // Approvals
1080
+ if (escrow.requiredApprovals.length > 0) {
1081
+ summary += `**Approvals:** `;
1082
+ const approved = escrow.approvals.filter(a => a.approved).map(a => a.partyRole);
1083
+ summary += `${approved.length}/${escrow.requiredApprovals.length} `;
1084
+ summary += `(${escrow.requiredApprovals.map(r => approved.includes(r) ? '✓' : '○').join('')})\n`;
1085
+ }
1086
+
1087
+ return summary;
1088
+ }
1089
+
1090
+ // ============ Helpers ============
1091
+
1092
+ private generateEscrowAddress(): string {
1093
+ // In production, this would deploy/derive a smart contract address
1094
+ // For now, generate a deterministic address
1095
+ return '0x' + crypto.randomBytes(20).toString('hex');
1096
+ }
1097
+
1098
+ private addDays(date: Date, days: number): Date {
1099
+ const result = new Date(date);
1100
+ result.setDate(result.getDate() + days);
1101
+ return result;
1102
+ }
1103
+ }
1104
+
1105
+ /**
1106
+ * x402 Premium Escrow Features
1107
+ *
1108
+ * Gate premium escrow operations behind x402 payments
1109
+ */
1110
+ export interface PremiumEscrowFeatures {
1111
+ yieldOptimization?: boolean; // Route funds through yield-bearing protocols
1112
+ insurance?: boolean; // Add escrow insurance coverage
1113
+ prioritySupport?: boolean; // Priority dispute resolution
1114
+ analytics?: boolean; // Advanced analytics and reporting
1115
+ }
1116
+
1117
+ export interface X402EscrowEndpoints {
1118
+ optimize: string; // Yield optimization endpoint
1119
+ insure: string; // Insurance coverage endpoint
1120
+ analytics: string; // Analytics endpoint
1121
+ support: string; // Priority support endpoint
1122
+ }
1123
+
1124
+ // Premium feature pricing (in USDC)
1125
+ export const PREMIUM_ESCROW_PRICING = {
1126
+ yieldOptimization: '0.25', // Enable yield-bearing escrow
1127
+ insurance: '0.50', // Add insurance coverage
1128
+ prioritySupport: '1.00', // Priority support package
1129
+ analytics: '0.10', // Advanced analytics
1130
+ };
1131
+
1132
+ /**
1133
+ * Generate x402 premium feature URLs for escrow
1134
+ */
1135
+ export function generateX402EscrowUrls(
1136
+ escrowId: string,
1137
+ baseUrl?: string
1138
+ ): X402EscrowEndpoints {
1139
+ const base = baseUrl || process.env.X402_BASE_URL || 'https://api.lobster-pay.com';
1140
+
1141
+ return {
1142
+ optimize: `${base}/escrow/${escrowId}/optimize`,
1143
+ insure: `${base}/escrow/${escrowId}/insure`,
1144
+ analytics: `${base}/escrow/${escrowId}/analytics`,
1145
+ support: `${base}/escrow/${escrowId}/support`,
1146
+ };
1147
+ }
1148
+
1149
+ /**
1150
+ * Example: Enable premium features on escrow
1151
+ */
1152
+ export async function enablePremiumFeatures(
1153
+ escrow: Escrow,
1154
+ features: PremiumEscrowFeatures,
1155
+ x402Fetch: (url: string) => Promise<Response>
1156
+ ): Promise<{ enabled: string[]; failed: string[] }> {
1157
+ const urls = generateX402EscrowUrls(escrow.id);
1158
+ const enabled: string[] = [];
1159
+ const failed: string[] = [];
1160
+
1161
+ if (features.yieldOptimization) {
1162
+ try {
1163
+ const response = await x402Fetch(urls.optimize);
1164
+ if (response.ok) {
1165
+ enabled.push('yieldOptimization');
1166
+ } else {
1167
+ failed.push('yieldOptimization');
1168
+ }
1169
+ } catch {
1170
+ failed.push('yieldOptimization');
1171
+ }
1172
+ }
1173
+
1174
+ if (features.insurance) {
1175
+ try {
1176
+ const response = await x402Fetch(urls.insure);
1177
+ if (response.ok) {
1178
+ enabled.push('insurance');
1179
+ } else {
1180
+ failed.push('insurance');
1181
+ }
1182
+ } catch {
1183
+ failed.push('insurance');
1184
+ }
1185
+ }
1186
+
1187
+ if (features.analytics) {
1188
+ try {
1189
+ const response = await x402Fetch(urls.analytics);
1190
+ if (response.ok) {
1191
+ enabled.push('analytics');
1192
+ } else {
1193
+ failed.push('analytics');
1194
+ }
1195
+ } catch {
1196
+ failed.push('analytics');
1197
+ }
1198
+ }
1199
+
1200
+ if (features.prioritySupport) {
1201
+ try {
1202
+ const response = await x402Fetch(urls.support);
1203
+ if (response.ok) {
1204
+ enabled.push('prioritySupport');
1205
+ } else {
1206
+ failed.push('prioritySupport');
1207
+ }
1208
+ } catch {
1209
+ failed.push('prioritySupport');
1210
+ }
1211
+ }
1212
+
1213
+ return { enabled, failed };
1214
+ }
1215
+
1216
+ export default EscrowManager;