moltspay 0.5.4 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +0 -124
  2. package/dist/cdp/index.d.mts +111 -0
  3. package/dist/cdp/index.d.ts +111 -0
  4. package/dist/cdp/index.js +30655 -0
  5. package/dist/cdp/index.js.map +1 -0
  6. package/dist/cdp/index.mjs +30631 -0
  7. package/dist/cdp/index.mjs.map +1 -0
  8. package/dist/chains/index.d.mts +1 -1
  9. package/dist/chains/index.d.ts +1 -1
  10. package/dist/cli/index.js +990 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/index.mjs +967 -0
  13. package/dist/cli/index.mjs.map +1 -0
  14. package/dist/client/index.d.mts +134 -0
  15. package/dist/client/index.d.ts +134 -0
  16. package/dist/client/index.js +331 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/client/index.mjs +296 -0
  19. package/dist/client/index.mjs.map +1 -0
  20. package/dist/createWallet-D53qu7ie.d.mts +77 -0
  21. package/dist/createWallet-D53qu7ie.d.ts +77 -0
  22. package/dist/index-Dg8n6wdW.d.mts +32 -0
  23. package/dist/index-Dg8n6wdW.d.ts +32 -0
  24. package/dist/index.d.mts +6 -1483
  25. package/dist/index.d.ts +6 -1483
  26. package/dist/index.js +31039 -4254
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +31042 -4203
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/server/index.d.mts +120 -0
  31. package/dist/server/index.d.ts +120 -0
  32. package/dist/server/index.js +418 -0
  33. package/dist/server/index.js.map +1 -0
  34. package/dist/server/index.mjs +393 -0
  35. package/dist/server/index.mjs.map +1 -0
  36. package/dist/wallet/index.d.mts +3 -451
  37. package/dist/wallet/index.d.ts +3 -451
  38. package/dist/wallet/index.js +5 -1021
  39. package/dist/wallet/index.js.map +1 -1
  40. package/dist/wallet/index.mjs +16 -1015
  41. package/dist/wallet/index.mjs.map +1 -1
  42. package/package.json +19 -19
  43. package/dist/cli.js +0 -1984
  44. package/dist/cli.js.map +0 -1
  45. package/dist/cli.mjs +0 -1969
  46. package/dist/cli.mjs.map +0 -1
  47. package/dist/guide/index.d.mts +0 -39
  48. package/dist/guide/index.d.ts +0 -39
  49. package/dist/guide/index.js +0 -181
  50. package/dist/guide/index.js.map +0 -1
  51. package/dist/guide/index.mjs +0 -152
  52. package/dist/guide/index.mjs.map +0 -1
  53. package/dist/index-CyFg9s2m.d.mts +0 -161
  54. package/dist/index-CyFg9s2m.d.ts +0 -161
  55. package/dist/orders/index.d.mts +0 -97
  56. package/dist/orders/index.d.ts +0 -97
  57. package/dist/orders/index.js +0 -162
  58. package/dist/orders/index.js.map +0 -1
  59. package/dist/orders/index.mjs +0 -136
  60. package/dist/orders/index.mjs.map +0 -1
  61. package/dist/permit/index.d.mts +0 -49
  62. package/dist/permit/index.d.ts +0 -49
  63. package/dist/permit/index.js +0 -273
  64. package/dist/permit/index.js.map +0 -1
  65. package/dist/permit/index.mjs +0 -246
  66. package/dist/permit/index.mjs.map +0 -1
  67. /package/dist/{cli.d.mts → cli/index.d.mts} +0 -0
  68. /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,30 +15,15 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/wallet/index.ts
31
21
  var wallet_exports = {};
32
22
  __export(wallet_exports, {
33
- AllowanceWallet: () => AllowanceWallet,
34
- PermitSigner: () => PermitSigner,
35
- PermitWallet: () => PermitWallet,
36
- SecureWallet: () => SecureWallet,
37
23
  Wallet: () => Wallet,
38
24
  createWallet: () => createWallet,
39
- formatPermitRequest: () => formatPermitRequest,
40
- generatePermitInstructions: () => generatePermitInstructions,
41
25
  getWalletAddress: () => getWalletAddress,
42
26
  loadWallet: () => loadWallet,
43
- signPermit: () => signPermit,
44
27
  walletExists: () => walletExists
45
28
  });
46
29
  module.exports = __toCommonJS(wallet_exports);
@@ -216,407 +199,6 @@ var Wallet = class {
216
199
  }
217
200
  };
218
201
 
219
- // src/audit/AuditLog.ts
220
- var fs = __toESM(require("fs"));
221
- var path = __toESM(require("path"));
222
- var crypto = __toESM(require("crypto"));
223
- var AuditLog = class {
224
- basePath;
225
- lastHash = "0000000000000000";
226
- constructor(basePath) {
227
- this.basePath = basePath || path.join(process.cwd(), "data", "audit");
228
- this.ensureDir();
229
- this.loadLastHash();
230
- }
231
- /**
232
- * Record audit log
233
- */
234
- async log(params) {
235
- const now = /* @__PURE__ */ new Date();
236
- const entry = {
237
- timestamp: now.getTime() / 1e3,
238
- datetime: now.toISOString(),
239
- action: params.action,
240
- request_id: params.request_id,
241
- from: params.from,
242
- to: params.to,
243
- amount: params.amount,
244
- tx_hash: params.tx_hash,
245
- reason: params.reason,
246
- requester: params.requester,
247
- prev_hash: this.lastHash,
248
- hash: "",
249
- // Filled after calculation
250
- metadata: params.metadata
251
- };
252
- entry.hash = this.calculateHash(entry);
253
- this.lastHash = entry.hash;
254
- const filePath = this.getFilePath(now);
255
- const line = JSON.stringify(entry) + "\n";
256
- fs.appendFileSync(filePath, line, "utf-8");
257
- return entry;
258
- }
259
- /**
260
- * Read logs for specified date
261
- */
262
- read(date) {
263
- const filePath = this.getFilePath(date || /* @__PURE__ */ new Date());
264
- if (!fs.existsSync(filePath)) {
265
- return [];
266
- }
267
- const content = fs.readFileSync(filePath, "utf-8");
268
- const lines = content.trim().split("\n").filter(Boolean);
269
- return lines.map((line) => JSON.parse(line));
270
- }
271
- /**
272
- * Verify log integrity
273
- */
274
- verify(date) {
275
- const entries = this.read(date);
276
- const errors = [];
277
- for (let i = 0; i < entries.length; i++) {
278
- const entry = entries[i];
279
- const expectedHash = this.calculateHash(entry);
280
- if (entry.hash !== expectedHash) {
281
- errors.push(`Entry ${i}: hash mismatch (expected ${expectedHash}, got ${entry.hash})`);
282
- }
283
- if (i > 0 && entry.prev_hash !== entries[i - 1].hash) {
284
- errors.push(`Entry ${i}: prev_hash mismatch`);
285
- }
286
- }
287
- return { valid: errors.length === 0, errors };
288
- }
289
- /**
290
- * Search logs
291
- */
292
- search(filter) {
293
- const results = [];
294
- const startDate = filter.startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
295
- const endDate = filter.endDate || /* @__PURE__ */ new Date();
296
- const current = new Date(startDate);
297
- while (current <= endDate) {
298
- const entries = this.read(current);
299
- for (const entry of entries) {
300
- let match = true;
301
- if (filter.action && entry.action !== filter.action) match = false;
302
- if (filter.request_id && entry.request_id !== filter.request_id) match = false;
303
- if (filter.from && entry.from?.toLowerCase() !== filter.from.toLowerCase()) match = false;
304
- if (filter.to && entry.to?.toLowerCase() !== filter.to.toLowerCase()) match = false;
305
- if (match) {
306
- results.push(entry);
307
- }
308
- }
309
- current.setDate(current.getDate() + 1);
310
- }
311
- return results;
312
- }
313
- /**
314
- * Get log file path
315
- */
316
- getFilePath(date) {
317
- const dateStr = date.toISOString().slice(0, 10);
318
- return path.join(this.basePath, `audit_${dateStr}.jsonl`);
319
- }
320
- /**
321
- * Calculate entry hash
322
- */
323
- calculateHash(entry) {
324
- const data = {
325
- timestamp: entry.timestamp,
326
- action: entry.action,
327
- request_id: entry.request_id,
328
- from: entry.from,
329
- to: entry.to,
330
- amount: entry.amount,
331
- tx_hash: entry.tx_hash,
332
- prev_hash: entry.prev_hash
333
- };
334
- const str = JSON.stringify(data);
335
- return crypto.createHash("sha256").update(str).digest("hex").slice(0, 16);
336
- }
337
- /**
338
- * Load last log entry hash
339
- */
340
- loadLastHash() {
341
- const today = /* @__PURE__ */ new Date();
342
- for (let i = 0; i < 2; i++) {
343
- const date = new Date(today);
344
- date.setDate(date.getDate() - i);
345
- const entries = this.read(date);
346
- if (entries.length > 0) {
347
- this.lastHash = entries[entries.length - 1].hash;
348
- return;
349
- }
350
- }
351
- }
352
- /**
353
- * Ensure directory exists
354
- */
355
- ensureDir() {
356
- if (!fs.existsSync(this.basePath)) {
357
- fs.mkdirSync(this.basePath, { recursive: true });
358
- }
359
- }
360
- };
361
-
362
- // src/wallet/SecureWallet.ts
363
- var DEFAULT_LIMITS = {
364
- singleMax: 100,
365
- // Single max $100
366
- dailyMax: 1e3,
367
- // Daily max $1000
368
- requireWhitelist: true
369
- };
370
- var SecureWallet = class {
371
- wallet;
372
- limits;
373
- whitelist;
374
- auditLog;
375
- dailyTotal = 0;
376
- dailyDate = "";
377
- pendingTransfers = /* @__PURE__ */ new Map();
378
- constructor(config = {}) {
379
- this.wallet = new Wallet({
380
- chain: config.chain,
381
- privateKey: config.privateKey
382
- });
383
- this.limits = { ...DEFAULT_LIMITS, ...config.limits };
384
- this.whitelist = new Set((config.whitelist || []).map((a) => a.toLowerCase()));
385
- this.auditLog = new AuditLog(config.auditPath);
386
- }
387
- /**
388
- * Get wallet address
389
- */
390
- get address() {
391
- return this.wallet.address;
392
- }
393
- /**
394
- * Get balance
395
- */
396
- async getBalance() {
397
- return this.wallet.getBalance();
398
- }
399
- /**
400
- * Secure transfer (with limit and whitelist checks)
401
- *
402
- * Supports two calling methods:
403
- * - transfer({ to, amount, reason?, requester? })
404
- * - transfer(to, amount)
405
- */
406
- async transfer(paramsOrTo, amountArg) {
407
- let params;
408
- if (typeof paramsOrTo === "string") {
409
- params = {
410
- to: paramsOrTo,
411
- amount: typeof amountArg === "string" ? parseFloat(amountArg) : amountArg || 0
412
- };
413
- } else {
414
- params = paramsOrTo;
415
- }
416
- const { to, amount, reason, requester } = params;
417
- const toAddress = to.toLowerCase();
418
- const requestId = `tr_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
419
- await this.auditLog.log({
420
- action: "transfer_request",
421
- request_id: requestId,
422
- from: this.wallet.address,
423
- to,
424
- amount,
425
- reason,
426
- requester
427
- });
428
- if (this.limits.requireWhitelist && !this.whitelist.has(toAddress)) {
429
- await this.auditLog.log({
430
- action: "transfer_failed",
431
- request_id: requestId,
432
- metadata: { error: "Address not in whitelist" }
433
- });
434
- return { success: false, error: `Address not in whitelist: ${to}` };
435
- }
436
- if (amount > this.limits.singleMax) {
437
- const pending = {
438
- id: requestId,
439
- to,
440
- amount,
441
- reason,
442
- requester,
443
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
444
- status: "pending"
445
- };
446
- this.pendingTransfers.set(requestId, pending);
447
- await this.auditLog.log({
448
- action: "transfer_request",
449
- request_id: requestId,
450
- metadata: { pending: true, reason: "Exceeds single limit" }
451
- });
452
- return {
453
- success: false,
454
- error: `Amount ${amount} exceeds single limit ${this.limits.singleMax}. Pending approval: ${requestId}`
455
- };
456
- }
457
- this.updateDailyTotal();
458
- if (this.dailyTotal + amount > this.limits.dailyMax) {
459
- const pending = {
460
- id: requestId,
461
- to,
462
- amount,
463
- reason,
464
- requester,
465
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
466
- status: "pending"
467
- };
468
- this.pendingTransfers.set(requestId, pending);
469
- await this.auditLog.log({
470
- action: "transfer_request",
471
- request_id: requestId,
472
- metadata: { pending: true, reason: "Exceeds daily limit" }
473
- });
474
- return {
475
- success: false,
476
- error: `Daily limit would be exceeded (${this.dailyTotal} + ${amount} > ${this.limits.dailyMax}). Pending approval: ${requestId}`
477
- };
478
- }
479
- const result = await this.wallet.transfer(to, amount);
480
- if (result.success) {
481
- this.dailyTotal += amount;
482
- await this.auditLog.log({
483
- action: "transfer_executed",
484
- request_id: requestId,
485
- from: this.wallet.address,
486
- to,
487
- amount,
488
- tx_hash: result.tx_hash,
489
- reason,
490
- requester
491
- });
492
- } else {
493
- await this.auditLog.log({
494
- action: "transfer_failed",
495
- request_id: requestId,
496
- metadata: { error: result.error }
497
- });
498
- }
499
- return result;
500
- }
501
- /**
502
- * Approve pending transfer
503
- */
504
- async approve(requestId, approver) {
505
- const pending = this.pendingTransfers.get(requestId);
506
- if (!pending) {
507
- return { success: false, error: `Pending transfer not found: ${requestId}` };
508
- }
509
- if (pending.status !== "pending") {
510
- return { success: false, error: `Transfer already ${pending.status}` };
511
- }
512
- await this.auditLog.log({
513
- action: "transfer_approved",
514
- request_id: requestId,
515
- metadata: { approver }
516
- });
517
- pending.status = "approved";
518
- const result = await this.wallet.transfer(pending.to, pending.amount);
519
- if (result.success) {
520
- pending.status = "executed";
521
- this.dailyTotal += pending.amount;
522
- await this.auditLog.log({
523
- action: "transfer_executed",
524
- request_id: requestId,
525
- from: this.wallet.address,
526
- to: pending.to,
527
- amount: pending.amount,
528
- tx_hash: result.tx_hash,
529
- reason: pending.reason,
530
- requester: pending.requester,
531
- metadata: { approved_by: approver }
532
- });
533
- } else {
534
- await this.auditLog.log({
535
- action: "transfer_failed",
536
- request_id: requestId,
537
- metadata: { error: result.error }
538
- });
539
- }
540
- return result;
541
- }
542
- /**
543
- * Reject pending transfer
544
- */
545
- async reject(requestId, rejecter, reason) {
546
- const pending = this.pendingTransfers.get(requestId);
547
- if (!pending) {
548
- throw new Error(`Pending transfer not found: ${requestId}`);
549
- }
550
- pending.status = "rejected";
551
- await this.auditLog.log({
552
- action: "transfer_rejected",
553
- request_id: requestId,
554
- metadata: { rejected_by: rejecter, reason }
555
- });
556
- }
557
- /**
558
- * Add to whitelist
559
- */
560
- async addToWhitelist(address, addedBy) {
561
- const addr = address.toLowerCase();
562
- this.whitelist.add(addr);
563
- await this.auditLog.log({
564
- action: "whitelist_add",
565
- request_id: `wl_${Date.now()}`,
566
- to: address,
567
- metadata: { added_by: addedBy }
568
- });
569
- }
570
- /**
571
- * Remove from whitelist
572
- */
573
- async removeFromWhitelist(address, removedBy) {
574
- const addr = address.toLowerCase();
575
- this.whitelist.delete(addr);
576
- await this.auditLog.log({
577
- action: "whitelist_remove",
578
- request_id: `wl_${Date.now()}`,
579
- to: address,
580
- metadata: { removed_by: removedBy }
581
- });
582
- }
583
- /**
584
- * Check if address is whitelisted
585
- */
586
- isWhitelisted(address) {
587
- return this.whitelist.has(address.toLowerCase());
588
- }
589
- /**
590
- * Get pending transfers list
591
- */
592
- getPendingTransfers() {
593
- return Array.from(this.pendingTransfers.values()).filter((p) => p.status === "pending");
594
- }
595
- /**
596
- * Get current limit config
597
- */
598
- getLimits() {
599
- return { ...this.limits };
600
- }
601
- /**
602
- * Get daily used amount
603
- */
604
- getDailyUsed() {
605
- this.updateDailyTotal();
606
- return this.dailyTotal;
607
- }
608
- /**
609
- * Update daily limit counter
610
- */
611
- updateDailyTotal() {
612
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
613
- if (this.dailyDate !== today) {
614
- this.dailyDate = today;
615
- this.dailyTotal = 0;
616
- }
617
- }
618
- };
619
-
620
202
  // src/wallet/createWallet.ts
621
203
  var import_ethers2 = require("ethers");
622
204
  var import_fs = require("fs");
@@ -718,625 +300,27 @@ function loadWallet(options = {}) {
718
300
  }
719
301
  }
720
302
  function getWalletAddress(storagePath) {
721
- const path2 = storagePath || (0, import_path.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
722
- if (!(0, import_fs.existsSync)(path2)) {
303
+ const path = storagePath || (0, import_path.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
304
+ if (!(0, import_fs.existsSync)(path)) {
723
305
  return null;
724
306
  }
725
307
  try {
726
- const data = JSON.parse((0, import_fs.readFileSync)(path2, "utf8"));
308
+ const data = JSON.parse((0, import_fs.readFileSync)(path, "utf8"));
727
309
  return data.address;
728
310
  } catch {
729
311
  return null;
730
312
  }
731
313
  }
732
314
  function walletExists(storagePath) {
733
- const path2 = storagePath || (0, import_path.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
734
- return (0, import_fs.existsSync)(path2);
735
- }
736
-
737
- // src/wallet/PermitWallet.ts
738
- var import_ethers3 = require("ethers");
739
- var PERMIT_ABI = [
740
- ...ERC20_ABI,
741
- "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
742
- "function transferFrom(address from, address to, uint256 amount) returns (bool)",
743
- "function allowance(address owner, address spender) view returns (uint256)"
744
- ];
745
- var PermitWallet = class {
746
- chain;
747
- chainConfig;
748
- address;
749
- wallet;
750
- provider;
751
- usdcContract;
752
- constructor(config = {}) {
753
- this.chain = config.chain || "base_sepolia";
754
- this.chainConfig = getChain(this.chain);
755
- let privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
756
- if (!privateKey && config.walletPath) {
757
- const loaded = loadWallet({
758
- storagePath: config.walletPath,
759
- password: config.walletPassword
760
- });
761
- if (!loaded.success || !loaded.privateKey) {
762
- throw new Error(loaded.error || "Failed to load wallet");
763
- }
764
- privateKey = loaded.privateKey;
765
- }
766
- if (!privateKey) {
767
- throw new Error("privateKey is required. Set via config, env var, or walletPath.");
768
- }
769
- const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
770
- this.provider = new import_ethers3.ethers.JsonRpcProvider(rpcUrl);
771
- this.wallet = new import_ethers3.ethers.Wallet(privateKey, this.provider);
772
- this.address = this.wallet.address;
773
- this.usdcContract = new import_ethers3.ethers.Contract(
774
- this.chainConfig.usdc,
775
- PERMIT_ABI,
776
- this.wallet
777
- );
778
- }
779
- /**
780
- * Check if Permit is valid (current allowance)
781
- */
782
- async checkPermitAllowance(owner) {
783
- const allowance = await this.usdcContract.allowance(owner, this.address);
784
- return (Number(allowance) / 1e6).toFixed(2);
785
- }
786
- /**
787
- * Pay using Permit authorization
788
- *
789
- * Flow:
790
- * 1. Call permit() to record Boss's authorization in the contract
791
- * 2. Call transferFrom() to transfer from Boss's wallet to recipient
792
- *
793
- * @example
794
- * ```typescript
795
- * const wallet = new PermitWallet({ chain: 'base' });
796
- *
797
- * // Boss-signed permit data
798
- * const permit = {
799
- * owner: '0xBOSS...',
800
- * spender: wallet.address,
801
- * value: '10000000', // 10 USDC
802
- * deadline: 1234567890,
803
- * v: 27,
804
- * r: '0x...',
805
- * s: '0x...'
806
- * };
807
- *
808
- * const result = await wallet.transferWithPermit({
809
- * to: '0xSELLER...',
810
- * amount: 3.99,
811
- * permit
812
- * });
813
- * ```
814
- */
815
- async transferWithPermit(params) {
816
- const { to, amount, permit } = params;
817
- try {
818
- const toAddress = import_ethers3.ethers.getAddress(to);
819
- const ownerAddress = import_ethers3.ethers.getAddress(permit.owner);
820
- if (import_ethers3.ethers.getAddress(permit.spender).toLowerCase() !== this.address.toLowerCase()) {
821
- return {
822
- success: false,
823
- error: `Permit spender (${permit.spender}) doesn't match wallet address (${this.address})`
824
- };
825
- }
826
- const now = Math.floor(Date.now() / 1e3);
827
- if (permit.deadline < now) {
828
- return {
829
- success: false,
830
- error: `Permit expired at ${new Date(permit.deadline * 1e3).toISOString()}`
831
- };
832
- }
833
- const amountWei = BigInt(Math.floor(amount * 1e6));
834
- const permitValue = BigInt(permit.value);
835
- if (amountWei > permitValue) {
836
- return {
837
- success: false,
838
- error: `Permit value (${Number(permitValue) / 1e6} USDC) < transfer amount (${amount} USDC)`
839
- };
840
- }
841
- const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
842
- let permitTxHash;
843
- if (BigInt(currentAllowance) < amountWei) {
844
- console.log("Executing permit...");
845
- const permitTx = await this.usdcContract.permit(
846
- ownerAddress,
847
- this.address,
848
- permitValue,
849
- permit.deadline,
850
- permit.v,
851
- permit.r,
852
- permit.s
853
- );
854
- const permitReceipt = await permitTx.wait();
855
- if (permitReceipt.status !== 1) {
856
- return {
857
- success: false,
858
- error: "Permit transaction failed",
859
- permitTxHash: permitTx.hash
860
- };
861
- }
862
- permitTxHash = permitTx.hash;
863
- console.log("Permit executed:", permitTxHash);
864
- }
865
- console.log("Executing transferFrom...");
866
- const transferTx = await this.usdcContract.transferFrom(
867
- ownerAddress,
868
- toAddress,
869
- amountWei
870
- );
871
- const transferReceipt = await transferTx.wait();
872
- if (transferReceipt.status === 1) {
873
- return {
874
- success: true,
875
- tx_hash: transferTx.hash,
876
- permitTxHash,
877
- transferTxHash: transferTx.hash,
878
- from: ownerAddress,
879
- to: toAddress,
880
- amount,
881
- gas_used: Number(transferReceipt.gasUsed),
882
- block_number: transferReceipt.blockNumber,
883
- explorer_url: `${this.chainConfig.explorerTx}${transferTx.hash}`
884
- };
885
- } else {
886
- return {
887
- success: false,
888
- error: "TransferFrom transaction failed",
889
- tx_hash: transferTx.hash,
890
- permitTxHash
891
- };
892
- }
893
- } catch (error) {
894
- const message = error.message;
895
- if (message.includes("ERC20InsufficientAllowance")) {
896
- return {
897
- success: false,
898
- error: "Insufficient allowance. Permit may have been used or expired."
899
- };
900
- }
901
- if (message.includes("ERC20InsufficientBalance")) {
902
- return {
903
- success: false,
904
- error: "Boss wallet has insufficient USDC balance."
905
- };
906
- }
907
- if (message.includes("InvalidSignature") || message.includes("invalid signature")) {
908
- return {
909
- success: false,
910
- error: "Invalid permit signature. Ask Boss to re-sign."
911
- };
912
- }
913
- return {
914
- success: false,
915
- error: message
916
- };
917
- }
918
- }
919
- /**
920
- * Get ETH balance (for gas)
921
- */
922
- async getGasBalance() {
923
- const balance = await this.provider.getBalance(this.address);
924
- return import_ethers3.ethers.formatEther(balance);
925
- }
926
- /**
927
- * Check if there's enough gas
928
- */
929
- async hasEnoughGas(minEth = 1e-3) {
930
- const balance = await this.getGasBalance();
931
- return parseFloat(balance) >= minEth;
932
- }
933
- };
934
- function formatPermitRequest(params) {
935
- const { agentAddress, amount, deadlineHours = 24, chain = "base", reason } = params;
936
- const chainConfig = getChain(chain);
937
- const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
938
- const value = BigInt(Math.floor(amount * 1e6)).toString();
939
- return `\u{1F510} **USDC Spending Allowance Request**
940
-
941
- ${reason ? `**Purpose:** ${reason}
942
- ` : ""}
943
- **Authorization Details:**
944
- - Authorized address (Agent): \`${agentAddress}\`
945
- - Amount: ${amount} USDC
946
- - Valid for: ${deadlineHours} hours
947
- - Chain: ${chainConfig.name}
948
-
949
- **Please sign the following EIP-2612 Permit with your wallet:**
950
-
951
- \`\`\`json
952
- {
953
- "types": {
954
- "Permit": [
955
- { "name": "owner", "type": "address" },
956
- { "name": "spender", "type": "address" },
957
- { "name": "value", "type": "uint256" },
958
- { "name": "nonce", "type": "uint256" },
959
- { "name": "deadline", "type": "uint256" }
960
- ]
961
- },
962
- "primaryType": "Permit",
963
- "domain": {
964
- "name": "USD Coin",
965
- "version": "2",
966
- "chainId": ${chainConfig.chainId},
967
- "verifyingContract": "${chainConfig.usdc}"
968
- },
969
- "message": {
970
- "owner": "<YOUR_WALLET_ADDRESS>",
971
- "spender": "${agentAddress}",
972
- "value": "${value}",
973
- "nonce": "<GET_FROM_CONTRACT>",
974
- "deadline": ${deadline}
975
- }
976
- }
977
- \`\`\`
978
-
979
- After signing, send { v, r, s, deadline } to the Agent.
980
-
981
- \u26A0\uFE0F Note: This authorization only allows the Agent to spend up to ${amount} USDC from your wallet. Your private key is never exposed.`;
982
- }
983
-
984
- // src/wallet/signPermit.ts
985
- var import_ethers4 = require("ethers");
986
- async function signPermit(config, params) {
987
- const chain = config.chain || "base";
988
- const chainConfig = getChain(chain);
989
- const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
990
- if (!privateKey) {
991
- throw new Error("privateKey is required");
992
- }
993
- const rpcUrl = config.rpcUrl || chainConfig.rpc;
994
- const provider = new import_ethers4.ethers.JsonRpcProvider(rpcUrl);
995
- const wallet = new import_ethers4.ethers.Wallet(privateKey, provider);
996
- const usdcContract = new import_ethers4.ethers.Contract(chainConfig.usdc, ERC20_ABI, provider);
997
- const nonce = Number(await usdcContract.nonces(wallet.address));
998
- let deadline;
999
- if (!params.deadline) {
1000
- deadline = Math.floor(Date.now() / 1e3) + 30 * 60;
1001
- } else if (params.deadline < 1e6) {
1002
- deadline = Math.floor(Date.now() / 1e3) + params.deadline * 60;
1003
- } else {
1004
- deadline = params.deadline;
1005
- }
1006
- const value = BigInt(Math.floor(params.amount * 1e6)).toString();
1007
- const domain = {
1008
- name: "USD Coin",
1009
- version: "2",
1010
- chainId: chainConfig.chainId,
1011
- verifyingContract: chainConfig.usdc
1012
- };
1013
- const types = {
1014
- Permit: [
1015
- { name: "owner", type: "address" },
1016
- { name: "spender", type: "address" },
1017
- { name: "value", type: "uint256" },
1018
- { name: "nonce", type: "uint256" },
1019
- { name: "deadline", type: "uint256" }
1020
- ]
1021
- };
1022
- const message = {
1023
- owner: wallet.address,
1024
- spender: params.spender,
1025
- value,
1026
- nonce,
1027
- deadline
1028
- };
1029
- const signature = await wallet.signTypedData(domain, types, message);
1030
- const sig = import_ethers4.ethers.Signature.from(signature);
1031
- return {
1032
- owner: wallet.address,
1033
- spender: params.spender,
1034
- value,
1035
- deadline,
1036
- nonce,
1037
- v: sig.v,
1038
- r: sig.r,
1039
- s: sig.s
1040
- };
1041
- }
1042
- var PermitSigner = class {
1043
- config;
1044
- constructor(config) {
1045
- this.config = config;
1046
- }
1047
- async sign(params) {
1048
- return signPermit(this.config, params);
1049
- }
1050
- };
1051
-
1052
- // src/wallet/AllowanceWallet.ts
1053
- var import_ethers5 = require("ethers");
1054
- var PERMIT_ABI2 = [
1055
- ...ERC20_ABI,
1056
- "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
1057
- "function transferFrom(address from, address to, uint256 amount) returns (bool)",
1058
- "function allowance(address owner, address spender) view returns (uint256)",
1059
- "function nonces(address owner) view returns (uint256)"
1060
- ];
1061
- var AllowanceWallet = class {
1062
- chain;
1063
- chainConfig;
1064
- address;
1065
- // Agent's address
1066
- wallet;
1067
- provider;
1068
- usdcContract;
1069
- /** Stored permits from owners */
1070
- permits = /* @__PURE__ */ new Map();
1071
- constructor(config) {
1072
- this.chain = config.chain || "base_sepolia";
1073
- this.chainConfig = getChain(this.chain);
1074
- const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
1075
- this.provider = new import_ethers5.ethers.JsonRpcProvider(rpcUrl);
1076
- this.wallet = new import_ethers5.ethers.Wallet(config.privateKey, this.provider);
1077
- this.address = this.wallet.address;
1078
- this.usdcContract = new import_ethers5.ethers.Contract(
1079
- this.chainConfig.usdc,
1080
- PERMIT_ABI2,
1081
- this.wallet
1082
- );
1083
- }
1084
- /**
1085
- * Store a Permit received from Owner
1086
- * Call this when Owner sends you a signed Permit
1087
- */
1088
- storePermit(permit) {
1089
- const ownerLower = permit.owner.toLowerCase();
1090
- this.permits.set(ownerLower, permit);
1091
- }
1092
- /**
1093
- * Get stored Permit for an owner
1094
- */
1095
- getPermit(owner) {
1096
- return this.permits.get(owner.toLowerCase());
1097
- }
1098
- /**
1099
- * Check allowance status with an owner
1100
- */
1101
- async checkAllowance(owner) {
1102
- const ownerAddress = import_ethers5.ethers.getAddress(owner);
1103
- const [allowance, ownerBalance, agentGasBalance] = await Promise.all([
1104
- this.usdcContract.allowance(ownerAddress, this.address),
1105
- this.usdcContract.balanceOf(ownerAddress),
1106
- this.provider.getBalance(this.address)
1107
- ]);
1108
- const allowanceNum = Number(allowance) / 1e6;
1109
- const hasGas = Number(import_ethers5.ethers.formatEther(agentGasBalance)) >= 1e-4;
1110
- return {
1111
- owner: ownerAddress,
1112
- agent: this.address,
1113
- allowance: allowanceNum.toFixed(2),
1114
- ownerBalance: (Number(ownerBalance) / 1e6).toFixed(2),
1115
- agentGasBalance: import_ethers5.ethers.formatEther(agentGasBalance),
1116
- canSpend: allowanceNum > 0 && hasGas,
1117
- chain: this.chainConfig.name
1118
- };
1119
- }
1120
- /**
1121
- * Spend from Owner's wallet using Permit allowance
1122
- *
1123
- * @example
1124
- * ```typescript
1125
- * const agent = new AllowanceWallet({
1126
- * chain: 'base',
1127
- * privateKey: process.env.AGENT_KEY // Only needs gas
1128
- * });
1129
- *
1130
- * // Owner gave us a Permit
1131
- * agent.storePermit(ownerPermit);
1132
- *
1133
- * // Spend to pay for a service
1134
- * const result = await agent.spend({
1135
- * to: '0xServiceProvider...',
1136
- * amount: 2.99,
1137
- * });
1138
- * ```
1139
- */
1140
- async spend(params) {
1141
- const { to, amount, permit } = params;
1142
- try {
1143
- const toAddress = import_ethers5.ethers.getAddress(to);
1144
- const amountWei = BigInt(Math.floor(amount * 1e6));
1145
- let ownerPermit = permit;
1146
- let ownerAddress;
1147
- if (ownerPermit) {
1148
- ownerAddress = import_ethers5.ethers.getAddress(ownerPermit.owner);
1149
- this.storePermit(ownerPermit);
1150
- } else {
1151
- for (const [owner, p] of this.permits) {
1152
- const allowance = await this.usdcContract.allowance(owner, this.address);
1153
- if (BigInt(allowance) >= amountWei) {
1154
- ownerAddress = import_ethers5.ethers.getAddress(owner);
1155
- ownerPermit = p;
1156
- break;
1157
- }
1158
- }
1159
- if (!ownerPermit) {
1160
- return {
1161
- success: false,
1162
- error: "No valid permit found. Ask Owner to sign a Permit first.",
1163
- from: "",
1164
- to: toAddress,
1165
- amount
1166
- };
1167
- }
1168
- }
1169
- const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
1170
- if (BigInt(currentAllowance) < amountWei) {
1171
- const now = Math.floor(Date.now() / 1e3);
1172
- if (ownerPermit.deadline < now) {
1173
- return {
1174
- success: false,
1175
- error: `Permit expired at ${new Date(ownerPermit.deadline * 1e3).toISOString()}. Ask Owner for a new Permit.`,
1176
- from: ownerAddress,
1177
- to: toAddress,
1178
- amount
1179
- };
1180
- }
1181
- const currentNonce = await this.usdcContract.nonces(ownerAddress);
1182
- if (Number(currentNonce) !== ownerPermit.nonce) {
1183
- return {
1184
- success: false,
1185
- error: `Permit nonce mismatch (expected ${ownerPermit.nonce}, got ${currentNonce}). Owner may have used this permit or signed a new one.`,
1186
- from: ownerAddress,
1187
- to: toAddress,
1188
- amount
1189
- };
1190
- }
1191
- console.log("[AllowanceWallet] Submitting permit on-chain...");
1192
- const permitTx = await this.usdcContract.permit(
1193
- ownerAddress,
1194
- this.address,
1195
- ownerPermit.value,
1196
- ownerPermit.deadline,
1197
- ownerPermit.v,
1198
- ownerPermit.r,
1199
- ownerPermit.s
1200
- );
1201
- await permitTx.wait();
1202
- console.log("[AllowanceWallet] Permit submitted:", permitTx.hash);
1203
- }
1204
- console.log("[AllowanceWallet] Executing transferFrom...");
1205
- const tx = await this.usdcContract.transferFrom(
1206
- ownerAddress,
1207
- toAddress,
1208
- amountWei
1209
- );
1210
- const receipt = await tx.wait();
1211
- const newAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
1212
- return {
1213
- success: true,
1214
- txHash: tx.hash,
1215
- from: ownerAddress,
1216
- to: toAddress,
1217
- amount,
1218
- remainingAllowance: (Number(newAllowance) / 1e6).toFixed(2),
1219
- explorerUrl: `${this.chainConfig.explorerTx}${tx.hash}`
1220
- };
1221
- } catch (error) {
1222
- const message = error.message;
1223
- if (message.includes("ERC20InsufficientAllowance")) {
1224
- return {
1225
- success: false,
1226
- error: "Insufficient allowance. Ask Owner to sign a new Permit with higher amount.",
1227
- from: "",
1228
- to,
1229
- amount
1230
- };
1231
- }
1232
- if (message.includes("ERC20InsufficientBalance")) {
1233
- return {
1234
- success: false,
1235
- error: "Owner's wallet has insufficient USDC balance.",
1236
- from: "",
1237
- to,
1238
- amount
1239
- };
1240
- }
1241
- return {
1242
- success: false,
1243
- error: message,
1244
- from: "",
1245
- to,
1246
- amount
1247
- };
1248
- }
1249
- }
1250
- /**
1251
- * Get Agent's gas balance (ETH)
1252
- */
1253
- async getGasBalance() {
1254
- const balance = await this.provider.getBalance(this.address);
1255
- return import_ethers5.ethers.formatEther(balance);
1256
- }
1257
- };
1258
- function generatePermitInstructions(params) {
1259
- const { ownerAddress, agentAddress, amount, deadlineHours = 24, chain = "base" } = params;
1260
- const chainConfig = getChain(chain);
1261
- const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
1262
- const value = BigInt(Math.floor(amount * 1e6)).toString();
1263
- const eip712Domain = {
1264
- name: "USD Coin",
1265
- version: "2",
1266
- chainId: chainConfig.chainId,
1267
- verifyingContract: chainConfig.usdc
1268
- };
1269
- const typedData = {
1270
- types: {
1271
- EIP712Domain: [
1272
- { name: "name", type: "string" },
1273
- { name: "version", type: "string" },
1274
- { name: "chainId", type: "uint256" },
1275
- { name: "verifyingContract", type: "address" }
1276
- ],
1277
- Permit: [
1278
- { name: "owner", type: "address" },
1279
- { name: "spender", type: "address" },
1280
- { name: "value", type: "uint256" },
1281
- { name: "nonce", type: "uint256" },
1282
- { name: "deadline", type: "uint256" }
1283
- ]
1284
- },
1285
- primaryType: "Permit",
1286
- domain: eip712Domain,
1287
- message: {
1288
- owner: ownerAddress,
1289
- spender: agentAddress,
1290
- value,
1291
- nonce: "<GET_FROM_USDC_CONTRACT>",
1292
- // Owner needs to query this
1293
- deadline
1294
- }
1295
- };
1296
- const instructions = `
1297
- \u{1F510} **Grant USDC Spending Allowance to Your Agent**
1298
-
1299
- Your Agent (${agentAddress}) is requesting permission to spend up to ${amount} USDC from your wallet.
1300
-
1301
- **What this does:**
1302
- - Allows your Agent to pay for services on your behalf
1303
- - Your USDC stays in YOUR wallet until spent
1304
- - Agent can only spend up to the authorized amount
1305
- - Expires in ${deadlineHours} hours
1306
- - You can revoke anytime by moving your USDC
1307
-
1308
- **How to sign (MetaMask / any web3 wallet):**
1309
-
1310
- 1. Go to https://etherscan.io/address/${chainConfig.usdc}#readContract
1311
- 2. Query \`nonces(${ownerAddress})\` to get your current nonce
1312
- 3. Use eth_signTypedData_v4 with the data below (replace nonce)
1313
- 4. Send the signature {v, r, s, deadline, nonce} to your Agent
1314
-
1315
- **Chain:** ${chainConfig.name}
1316
- **USDC Contract:** ${chainConfig.usdc}
1317
-
1318
- **EIP-712 Typed Data:**
1319
- \`\`\`json
1320
- ${JSON.stringify(typedData, null, 2)}
1321
- \`\`\`
1322
-
1323
- \u26A0\uFE0F Never share your private key. This signature only authorizes spending, not wallet access.
1324
- `;
1325
- return { instructions, typedData, eip712Domain };
315
+ const path = storagePath || (0, import_path.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
316
+ return (0, import_fs.existsSync)(path);
1326
317
  }
1327
318
  // Annotate the CommonJS export names for ESM import in node:
1328
319
  0 && (module.exports = {
1329
- AllowanceWallet,
1330
- PermitSigner,
1331
- PermitWallet,
1332
- SecureWallet,
1333
320
  Wallet,
1334
321
  createWallet,
1335
- formatPermitRequest,
1336
- generatePermitInstructions,
1337
322
  getWalletAddress,
1338
323
  loadWallet,
1339
- signPermit,
1340
324
  walletExists
1341
325
  });
1342
326
  //# sourceMappingURL=index.js.map