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
@@ -0,0 +1,615 @@
1
+ /**
2
+ * Contact & Address Book Management
3
+ *
4
+ * Secure storage for payment contacts with labels, validation, and search.
5
+ */
6
+
7
+ import crypto from 'crypto';
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+
11
+ export interface Contact {
12
+ id: string;
13
+ name: string;
14
+ alias?: string; // Short name for quick reference
15
+ email?: string;
16
+ phone?: string;
17
+
18
+ // Wallet addresses by chain
19
+ addresses: {
20
+ chain: string;
21
+ address: string;
22
+ label?: string;
23
+ verified: boolean;
24
+ addedAt: string;
25
+ }[];
26
+
27
+ // Payment preferences
28
+ defaultChain?: string;
29
+ defaultAddress?: string;
30
+
31
+ // Tags for organization
32
+ tags: string[];
33
+
34
+ // Transaction history summary
35
+ totalSent: string;
36
+ totalReceived: string;
37
+ lastTransactionAt?: string;
38
+ transactionCount: number;
39
+
40
+ // Notes
41
+ notes?: string;
42
+
43
+ // Metadata
44
+ createdAt: string;
45
+ updatedAt: string;
46
+ }
47
+
48
+ const DATA_DIR = process.env.USDC_DATA_DIR || './data';
49
+
50
+ /**
51
+ * Contact Manager
52
+ */
53
+ export class ContactManager {
54
+ private dataPath: string;
55
+
56
+ constructor(dataDir = DATA_DIR) {
57
+ this.dataPath = path.join(dataDir, 'contacts.json');
58
+ }
59
+
60
+ private async loadContacts(): Promise<Contact[]> {
61
+ try {
62
+ const data = await fs.readFile(this.dataPath, 'utf-8');
63
+ return JSON.parse(data);
64
+ } catch {
65
+ return [];
66
+ }
67
+ }
68
+
69
+ private async saveContacts(contacts: Contact[]): Promise<void> {
70
+ await fs.mkdir(path.dirname(this.dataPath), { recursive: true });
71
+ await fs.writeFile(this.dataPath, JSON.stringify(contacts, null, 2));
72
+ }
73
+
74
+ /**
75
+ * Add a new contact
76
+ */
77
+ async add(params: {
78
+ name: string;
79
+ alias?: string;
80
+ email?: string;
81
+ phone?: string;
82
+ addresses?: { chain: string; address: string; label?: string }[];
83
+ tags?: string[];
84
+ notes?: string;
85
+ }): Promise<Contact> {
86
+ const contacts = await this.loadContacts();
87
+
88
+ // Check for duplicate alias
89
+ if (params.alias) {
90
+ const existing = contacts.find(c =>
91
+ c.alias?.toLowerCase() === params.alias!.toLowerCase()
92
+ );
93
+ if (existing) {
94
+ throw new Error(`Alias "${params.alias}" already exists`);
95
+ }
96
+ }
97
+
98
+ const contact: Contact = {
99
+ id: crypto.randomUUID(),
100
+ name: params.name,
101
+ alias: params.alias,
102
+ email: params.email,
103
+ phone: params.phone,
104
+ addresses: (params.addresses || []).map(addr => ({
105
+ ...addr,
106
+ verified: this.isValidAddress(addr.address),
107
+ addedAt: new Date().toISOString(),
108
+ })),
109
+ tags: params.tags || [],
110
+ totalSent: '0',
111
+ totalReceived: '0',
112
+ transactionCount: 0,
113
+ notes: params.notes,
114
+ createdAt: new Date().toISOString(),
115
+ updatedAt: new Date().toISOString(),
116
+ };
117
+
118
+ // Set default address if provided
119
+ if (contact.addresses.length > 0) {
120
+ contact.defaultChain = contact.addresses[0].chain;
121
+ contact.defaultAddress = contact.addresses[0].address;
122
+ }
123
+
124
+ contacts.push(contact);
125
+ await this.saveContacts(contacts);
126
+
127
+ return contact;
128
+ }
129
+
130
+ /**
131
+ * Find contact by name, alias, or address
132
+ */
133
+ async find(query: string): Promise<Contact | null> {
134
+ const contacts = await this.loadContacts();
135
+ const lowerQuery = query.toLowerCase();
136
+
137
+ return contacts.find(c =>
138
+ c.name.toLowerCase() === lowerQuery ||
139
+ c.alias?.toLowerCase() === lowerQuery ||
140
+ c.addresses.some(a => a.address.toLowerCase() === lowerQuery)
141
+ ) || null;
142
+ }
143
+
144
+ /**
145
+ * Resolve a recipient (name, alias, or address) to an address
146
+ */
147
+ async resolveRecipient(recipient: string, chain?: string): Promise<{
148
+ contact?: Contact;
149
+ address: string;
150
+ chain: string;
151
+ } | null> {
152
+ // If it's already a valid address, return it
153
+ if (this.isValidAddress(recipient)) {
154
+ const contact = await this.findByAddress(recipient);
155
+ return {
156
+ contact: contact || undefined,
157
+ address: recipient,
158
+ chain: chain || 'ETH-SEPOLIA',
159
+ };
160
+ }
161
+
162
+ // Look up contact
163
+ const contact = await this.find(recipient);
164
+ if (!contact) {
165
+ return null;
166
+ }
167
+
168
+ // Find address for requested chain or default
169
+ const targetChain = chain || contact.defaultChain || 'ETH-SEPOLIA';
170
+ const addr = contact.addresses.find(a => a.chain === targetChain) ||
171
+ contact.addresses[0];
172
+
173
+ if (!addr) {
174
+ return null;
175
+ }
176
+
177
+ return {
178
+ contact,
179
+ address: addr.address,
180
+ chain: addr.chain,
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Search contacts
186
+ */
187
+ async search(query: string): Promise<Contact[]> {
188
+ const contacts = await this.loadContacts();
189
+ const lowerQuery = query.toLowerCase();
190
+
191
+ return contacts.filter(c =>
192
+ c.name.toLowerCase().includes(lowerQuery) ||
193
+ c.alias?.toLowerCase().includes(lowerQuery) ||
194
+ c.email?.toLowerCase().includes(lowerQuery) ||
195
+ c.tags.some(t => t.toLowerCase().includes(lowerQuery)) ||
196
+ c.notes?.toLowerCase().includes(lowerQuery)
197
+ );
198
+ }
199
+
200
+ /**
201
+ * List all contacts
202
+ */
203
+ async list(options?: {
204
+ tag?: string;
205
+ sortBy?: 'name' | 'recent' | 'frequent';
206
+ }): Promise<Contact[]> {
207
+ let contacts = await this.loadContacts();
208
+
209
+ if (options?.tag) {
210
+ contacts = contacts.filter(c =>
211
+ c.tags.some(t => t.toLowerCase() === options.tag!.toLowerCase())
212
+ );
213
+ }
214
+
215
+ switch (options?.sortBy) {
216
+ case 'recent':
217
+ contacts.sort((a, b) => {
218
+ const dateA = a.lastTransactionAt || a.createdAt;
219
+ const dateB = b.lastTransactionAt || b.createdAt;
220
+ return new Date(dateB).getTime() - new Date(dateA).getTime();
221
+ });
222
+ break;
223
+ case 'frequent':
224
+ contacts.sort((a, b) => b.transactionCount - a.transactionCount);
225
+ break;
226
+ case 'name':
227
+ default:
228
+ contacts.sort((a, b) => a.name.localeCompare(b.name));
229
+ }
230
+
231
+ return contacts;
232
+ }
233
+
234
+ /**
235
+ * Find contact by address
236
+ */
237
+ async findByAddress(address: string): Promise<Contact | null> {
238
+ const contacts = await this.loadContacts();
239
+ return contacts.find(c =>
240
+ c.addresses.some(a => a.address.toLowerCase() === address.toLowerCase())
241
+ ) || null;
242
+ }
243
+
244
+ /**
245
+ * Update contact
246
+ */
247
+ async update(id: string, updates: Partial<Omit<Contact, 'id' | 'createdAt'>>): Promise<Contact | null> {
248
+ const contacts = await this.loadContacts();
249
+ const contact = contacts.find(c => c.id === id);
250
+
251
+ if (contact) {
252
+ Object.assign(contact, updates, { updatedAt: new Date().toISOString() });
253
+ await this.saveContacts(contacts);
254
+ }
255
+
256
+ return contact || null;
257
+ }
258
+
259
+ /**
260
+ * Add address to contact
261
+ */
262
+ async addAddress(contactId: string, address: {
263
+ chain: string;
264
+ address: string;
265
+ label?: string;
266
+ }): Promise<Contact | null> {
267
+ const contacts = await this.loadContacts();
268
+ const contact = contacts.find(c => c.id === contactId);
269
+
270
+ if (contact) {
271
+ // Check if address already exists
272
+ if (contact.addresses.some(a =>
273
+ a.chain === address.chain &&
274
+ a.address.toLowerCase() === address.address.toLowerCase()
275
+ )) {
276
+ throw new Error('Address already exists for this chain');
277
+ }
278
+
279
+ contact.addresses.push({
280
+ ...address,
281
+ verified: this.isValidAddress(address.address),
282
+ addedAt: new Date().toISOString(),
283
+ });
284
+ contact.updatedAt = new Date().toISOString();
285
+ await this.saveContacts(contacts);
286
+ }
287
+
288
+ return contact || null;
289
+ }
290
+
291
+ /**
292
+ * Remove address from contact
293
+ */
294
+ async removeAddress(contactId: string, chain: string): Promise<Contact | null> {
295
+ const contacts = await this.loadContacts();
296
+ const contact = contacts.find(c => c.id === contactId);
297
+
298
+ if (contact) {
299
+ contact.addresses = contact.addresses.filter(a => a.chain !== chain);
300
+ contact.updatedAt = new Date().toISOString();
301
+
302
+ // Update default if removed
303
+ if (contact.defaultChain === chain && contact.addresses.length > 0) {
304
+ contact.defaultChain = contact.addresses[0].chain;
305
+ contact.defaultAddress = contact.addresses[0].address;
306
+ }
307
+
308
+ await this.saveContacts(contacts);
309
+ }
310
+
311
+ return contact || null;
312
+ }
313
+
314
+ /**
315
+ * Set default address for contact
316
+ */
317
+ async setDefault(contactId: string, chain: string): Promise<Contact | null> {
318
+ const contacts = await this.loadContacts();
319
+ const contact = contacts.find(c => c.id === contactId);
320
+
321
+ if (contact) {
322
+ const addr = contact.addresses.find(a => a.chain === chain);
323
+ if (addr) {
324
+ contact.defaultChain = addr.chain;
325
+ contact.defaultAddress = addr.address;
326
+ contact.updatedAt = new Date().toISOString();
327
+ await this.saveContacts(contacts);
328
+ }
329
+ }
330
+
331
+ return contact || null;
332
+ }
333
+
334
+ /**
335
+ * Add tags to contact
336
+ */
337
+ async addTags(contactId: string, tags: string[]): Promise<Contact | null> {
338
+ const contacts = await this.loadContacts();
339
+ const contact = contacts.find(c => c.id === contactId);
340
+
341
+ if (contact) {
342
+ const newTags = tags.filter(t => !contact.tags.includes(t));
343
+ contact.tags.push(...newTags);
344
+ contact.updatedAt = new Date().toISOString();
345
+ await this.saveContacts(contacts);
346
+ }
347
+
348
+ return contact || null;
349
+ }
350
+
351
+ /**
352
+ * Record a transaction with contact
353
+ */
354
+ async recordTransaction(address: string, type: 'sent' | 'received', amount: string): Promise<void> {
355
+ const contacts = await this.loadContacts();
356
+ const contact = contacts.find(c =>
357
+ c.addresses.some(a => a.address.toLowerCase() === address.toLowerCase())
358
+ );
359
+
360
+ if (contact) {
361
+ const amountNum = parseFloat(amount);
362
+ if (type === 'sent') {
363
+ contact.totalSent = (parseFloat(contact.totalSent) + amountNum).toString();
364
+ } else {
365
+ contact.totalReceived = (parseFloat(contact.totalReceived) + amountNum).toString();
366
+ }
367
+ contact.transactionCount++;
368
+ contact.lastTransactionAt = new Date().toISOString();
369
+ contact.updatedAt = new Date().toISOString();
370
+ await this.saveContacts(contacts);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Delete contact
376
+ */
377
+ async delete(id: string): Promise<boolean> {
378
+ const contacts = await this.loadContacts();
379
+ const index = contacts.findIndex(c => c.id === id);
380
+
381
+ if (index >= 0) {
382
+ contacts.splice(index, 1);
383
+ await this.saveContacts(contacts);
384
+ return true;
385
+ }
386
+
387
+ return false;
388
+ }
389
+
390
+ /**
391
+ * Export contacts to CSV
392
+ */
393
+ async exportCSV(): Promise<string> {
394
+ const contacts = await this.loadContacts();
395
+
396
+ const header = 'Name,Alias,Email,Chain,Address,Tags,Total Sent,Total Received\n';
397
+ const rows = contacts.flatMap(c =>
398
+ c.addresses.map(a =>
399
+ `"${c.name}","${c.alias || ''}","${c.email || ''}","${a.chain}","${a.address}","${c.tags.join(';')}","${c.totalSent}","${c.totalReceived}"`
400
+ )
401
+ );
402
+
403
+ return header + rows.join('\n');
404
+ }
405
+
406
+ /**
407
+ * Import contacts from CSV
408
+ */
409
+ async importCSV(csvContent: string): Promise<{ imported: number; skipped: number }> {
410
+ const lines = csvContent.split('\n').slice(1); // Skip header
411
+ let imported = 0;
412
+ let skipped = 0;
413
+
414
+ for (const line of lines) {
415
+ if (!line.trim()) continue;
416
+
417
+ try {
418
+ // Simple CSV parsing (doesn't handle all edge cases)
419
+ const parts = line.match(/(?:"[^"]*"|[^,])+/g) || [];
420
+ const [name, alias, email, chain, address, tags] = parts.map(p =>
421
+ p.replace(/^"|"$/g, '')
422
+ );
423
+
424
+ if (!name || !address) {
425
+ skipped++;
426
+ continue;
427
+ }
428
+
429
+ // Check if contact exists
430
+ let contact = await this.find(name);
431
+
432
+ if (contact) {
433
+ // Add address to existing contact
434
+ await this.addAddress(contact.id, { chain: chain || 'ETH-SEPOLIA', address });
435
+ } else {
436
+ // Create new contact
437
+ await this.add({
438
+ name,
439
+ alias: alias || undefined,
440
+ email: email || undefined,
441
+ addresses: [{ chain: chain || 'ETH-SEPOLIA', address }],
442
+ tags: tags ? tags.split(';') : [],
443
+ });
444
+ }
445
+ imported++;
446
+ } catch (err) {
447
+ skipped++;
448
+ }
449
+ }
450
+
451
+ return { imported, skipped };
452
+ }
453
+
454
+ /**
455
+ * Validate Ethereum address
456
+ */
457
+ private isValidAddress(address: string): boolean {
458
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * x402 Premium Contact Features
464
+ *
465
+ * Premium verification and risk assessment services
466
+ */
467
+ export interface PremiumContactFeatures {
468
+ onChainVerification?: boolean; // Verify address activity on-chain
469
+ riskAssessment?: boolean; // Calculate risk score
470
+ fraudDetection?: boolean; // Check for fraud patterns
471
+ transactionHistory?: boolean; // Full on-chain transaction history
472
+ }
473
+
474
+ export interface ContactVerificationResult {
475
+ contact: Contact;
476
+ onChainVerified: boolean;
477
+ riskScore?: {
478
+ score: number; // 0-100 (lower is better)
479
+ level: 'low' | 'medium' | 'high' | 'critical';
480
+ factors: string[];
481
+ };
482
+ fraudFlags?: string[];
483
+ transactionSummary?: {
484
+ totalVolume: string;
485
+ transactionCount: number;
486
+ firstSeen: string;
487
+ lastSeen: string;
488
+ interactedWith: number;
489
+ };
490
+ verified: boolean;
491
+ verifiedAt: string;
492
+ }
493
+
494
+ // Premium verification pricing (in USDC)
495
+ export const PREMIUM_CONTACT_PRICING = {
496
+ basicVerification: '0.05', // Basic on-chain verification
497
+ fullVerification: '0.10', // Full verification + risk score
498
+ fraudCheck: '0.15', // Fraud detection analysis
499
+ fullReport: '0.25', // Complete verification report
500
+ };
501
+
502
+ /**
503
+ * Generate x402 verification URL for contact
504
+ */
505
+ export function generateX402VerificationUrl(
506
+ contactId: string,
507
+ verificationType: 'basic' | 'full' | 'fraud' | 'report' = 'basic',
508
+ baseUrl?: string
509
+ ): string {
510
+ const base = baseUrl || process.env.X402_BASE_URL || 'https://api.lobster-pay.com';
511
+ return `${base}/contacts/${contactId}/verify/${verificationType}`;
512
+ }
513
+
514
+ /**
515
+ * Verify contact with premium features via x402
516
+ */
517
+ export async function verifyContactPremium(
518
+ contact: Contact,
519
+ features: PremiumContactFeatures,
520
+ x402Fetch: (url: string) => Promise<Response>
521
+ ): Promise<ContactVerificationResult> {
522
+ const result: ContactVerificationResult = {
523
+ contact,
524
+ onChainVerified: false,
525
+ verified: false,
526
+ verifiedAt: new Date().toISOString(),
527
+ };
528
+
529
+ try {
530
+ // Determine verification type based on features
531
+ let verificationType: 'basic' | 'full' | 'fraud' | 'report' = 'basic';
532
+
533
+ if (features.fraudDetection) {
534
+ verificationType = 'fraud';
535
+ } else if (features.onChainVerification && features.riskAssessment && features.transactionHistory) {
536
+ verificationType = 'report';
537
+ } else if (features.riskAssessment) {
538
+ verificationType = 'full';
539
+ }
540
+
541
+ // Call x402-protected verification endpoint
542
+ const url = generateX402VerificationUrl(contact.id, verificationType);
543
+ const response = await x402Fetch(url);
544
+
545
+ if (!response.ok) {
546
+ throw new Error(`Verification failed: ${response.statusText}`);
547
+ }
548
+
549
+ const data = await response.json();
550
+
551
+ // Parse verification results
552
+ result.onChainVerified = data.onChainVerified || false;
553
+ result.riskScore = data.riskScore;
554
+ result.fraudFlags = data.fraudFlags;
555
+ result.transactionSummary = data.transactionSummary;
556
+ result.verified = true;
557
+
558
+ } catch (error) {
559
+ console.error('Contact verification error:', error);
560
+ result.verified = false;
561
+ }
562
+
563
+ return result;
564
+ }
565
+
566
+ /**
567
+ * Mock verification for testing
568
+ * Simulates what a real x402-protected endpoint would return
569
+ */
570
+ export function mockContactVerification(contact: Contact): ContactVerificationResult {
571
+ // Generate mock on-chain data
572
+ const addressCount = contact.addresses.length;
573
+ const hasMultipleChains = addressCount > 1;
574
+
575
+ // Calculate mock risk score
576
+ let riskScore = 20; // Base low risk
577
+ if (addressCount === 0) riskScore += 40; // No addresses = higher risk
578
+ if (!contact.email && !contact.phone) riskScore += 20; // No contact info
579
+ if (contact.transactionCount === 0) riskScore += 30; // No transaction history
580
+
581
+ riskScore = Math.min(100, riskScore);
582
+
583
+ const riskLevel =
584
+ riskScore < 25 ? 'low' :
585
+ riskScore < 50 ? 'medium' :
586
+ riskScore < 75 ? 'high' : 'critical';
587
+
588
+ const factors: string[] = [];
589
+ if (addressCount === 0) factors.push('No wallet addresses on file');
590
+ if (contact.transactionCount === 0) factors.push('No transaction history');
591
+ if (!contact.email && !contact.phone) factors.push('No contact information');
592
+ if (hasMultipleChains) factors.push('Active on multiple chains (positive)');
593
+
594
+ return {
595
+ contact,
596
+ onChainVerified: addressCount > 0,
597
+ riskScore: {
598
+ score: riskScore,
599
+ level: riskLevel,
600
+ factors,
601
+ },
602
+ fraudFlags: riskScore > 70 ? ['High risk score', 'Limited verification data'] : [],
603
+ transactionSummary: {
604
+ totalVolume: contact.totalSent || '0',
605
+ transactionCount: contact.transactionCount,
606
+ firstSeen: contact.createdAt,
607
+ lastSeen: contact.lastTransactionAt || contact.createdAt,
608
+ interactedWith: Math.floor(Math.random() * 50) + 5,
609
+ },
610
+ verified: true,
611
+ verifiedAt: new Date().toISOString(),
612
+ };
613
+ }
614
+
615
+ export default ContactManager;
package/lib/easy.ts ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Easy Mode API - Simplified interface for Pay Lobster
3
+ */
4
+
5
+ import { LobsterAgent } from './agent';
6
+ import type { LobsterConfig } from './types';
7
+
8
+ /**
9
+ * Create a new Lobster agent with minimal configuration
10
+ */
11
+ export function createLobsterAgent(config: LobsterConfig = {}): LobsterAgent {
12
+ return new LobsterAgent(config);
13
+ }
14
+
15
+ /**
16
+ * Quick start - create and initialize an agent in one call
17
+ */
18
+ export async function quickStart(config: LobsterConfig = {}): Promise<LobsterAgent> {
19
+ const agent = createLobsterAgent(config);
20
+ await agent.initialize();
21
+ return agent;
22
+ }
23
+
24
+ /**
25
+ * One-liner to send USDC
26
+ */
27
+ export async function sendUSDC(
28
+ to: string,
29
+ amount: number,
30
+ config: LobsterConfig = {}
31
+ ): Promise<string> {
32
+ const agent = await quickStart(config);
33
+ const tx = await agent.send(to, amount);
34
+ return tx.id;
35
+ }
36
+
37
+ /**
38
+ * One-liner to check balance
39
+ */
40
+ export async function checkBalance(
41
+ address: string,
42
+ config: LobsterConfig = {}
43
+ ): Promise<string> {
44
+ const agent = createLobsterAgent({ ...config, walletId: address });
45
+ return agent.getBalance();
46
+ }