moltspay 0.2.3 → 0.2.5
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.
- package/dist/cli.js +581 -91
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +585 -93
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +112 -2
- package/dist/index.d.ts +112 -2
- package/dist/index.js +640 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +648 -59
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +130 -1
- package/dist/wallet/index.d.ts +130 -1
- package/dist/wallet/index.js +280 -0
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +278 -0
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -280,8 +280,317 @@ After payment, reply with your tx hash:
|
|
|
280
280
|
}
|
|
281
281
|
};
|
|
282
282
|
|
|
283
|
-
// src/
|
|
283
|
+
// src/agent/AgentWallet.ts
|
|
284
284
|
import { ethers as ethers2 } from "ethers";
|
|
285
|
+
import * as fs from "fs";
|
|
286
|
+
import * as path from "path";
|
|
287
|
+
var PERMIT_ABI = [
|
|
288
|
+
...ERC20_ABI,
|
|
289
|
+
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
|
|
290
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
291
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
292
|
+
"function nonces(address owner) view returns (uint256)"
|
|
293
|
+
];
|
|
294
|
+
var AgentWallet = class {
|
|
295
|
+
chain;
|
|
296
|
+
chainConfig;
|
|
297
|
+
storageDir;
|
|
298
|
+
_address = null;
|
|
299
|
+
_privateKey = null;
|
|
300
|
+
_wallet = null;
|
|
301
|
+
_provider = null;
|
|
302
|
+
_permits = /* @__PURE__ */ new Map();
|
|
303
|
+
constructor(config = {}) {
|
|
304
|
+
this.chain = config.chain || "base";
|
|
305
|
+
this.chainConfig = getChain(this.chain);
|
|
306
|
+
this.storageDir = config.storageDir || this.getDefaultStorageDir();
|
|
307
|
+
this.ensureInitialized();
|
|
308
|
+
}
|
|
309
|
+
getDefaultStorageDir() {
|
|
310
|
+
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
311
|
+
return path.join(home, ".moltspay");
|
|
312
|
+
}
|
|
313
|
+
getWalletPath() {
|
|
314
|
+
return path.join(this.storageDir, "wallet.json");
|
|
315
|
+
}
|
|
316
|
+
getPermitsPath() {
|
|
317
|
+
return path.join(this.storageDir, "permits.json");
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Auto-initialize: create wallet if not exists
|
|
321
|
+
* This is called automatically in constructor
|
|
322
|
+
*/
|
|
323
|
+
ensureInitialized() {
|
|
324
|
+
if (!fs.existsSync(this.storageDir)) {
|
|
325
|
+
fs.mkdirSync(this.storageDir, { recursive: true });
|
|
326
|
+
}
|
|
327
|
+
const walletPath = this.getWalletPath();
|
|
328
|
+
if (fs.existsSync(walletPath)) {
|
|
329
|
+
const data = JSON.parse(fs.readFileSync(walletPath, "utf-8"));
|
|
330
|
+
this._address = data.address;
|
|
331
|
+
this._privateKey = data.privateKey;
|
|
332
|
+
} else {
|
|
333
|
+
const wallet = ethers2.Wallet.createRandom();
|
|
334
|
+
this._address = wallet.address;
|
|
335
|
+
this._privateKey = wallet.privateKey;
|
|
336
|
+
fs.writeFileSync(walletPath, JSON.stringify({
|
|
337
|
+
address: this._address,
|
|
338
|
+
privateKey: this._privateKey,
|
|
339
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
340
|
+
chain: this.chain
|
|
341
|
+
}, null, 2), { mode: 384 });
|
|
342
|
+
}
|
|
343
|
+
const permitsPath = this.getPermitsPath();
|
|
344
|
+
if (fs.existsSync(permitsPath)) {
|
|
345
|
+
const permits = JSON.parse(fs.readFileSync(permitsPath, "utf-8"));
|
|
346
|
+
for (const [owner, permit] of Object.entries(permits)) {
|
|
347
|
+
this._permits.set(owner.toLowerCase(), permit);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/** Agent's address (auto-generated on first use) */
|
|
352
|
+
get address() {
|
|
353
|
+
return this._address;
|
|
354
|
+
}
|
|
355
|
+
get wallet() {
|
|
356
|
+
if (!this._wallet) {
|
|
357
|
+
this._wallet = new ethers2.Wallet(this._privateKey, this.provider);
|
|
358
|
+
}
|
|
359
|
+
return this._wallet;
|
|
360
|
+
}
|
|
361
|
+
get provider() {
|
|
362
|
+
if (!this._provider) {
|
|
363
|
+
this._provider = new ethers2.JsonRpcProvider(this.chainConfig.rpc);
|
|
364
|
+
}
|
|
365
|
+
return this._provider;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Store a Permit from Owner
|
|
369
|
+
*/
|
|
370
|
+
storePermit(permit) {
|
|
371
|
+
const ownerLower = permit.owner.toLowerCase();
|
|
372
|
+
this._permits.set(ownerLower, permit);
|
|
373
|
+
const permitsPath = this.getPermitsPath();
|
|
374
|
+
const permits = {};
|
|
375
|
+
for (const [owner, p] of this._permits) {
|
|
376
|
+
permits[owner] = p;
|
|
377
|
+
}
|
|
378
|
+
fs.writeFileSync(permitsPath, JSON.stringify(permits, null, 2));
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get stored permit for an owner
|
|
382
|
+
*/
|
|
383
|
+
getPermit(owner) {
|
|
384
|
+
return this._permits.get(owner.toLowerCase());
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Check allowance from an owner
|
|
388
|
+
*/
|
|
389
|
+
async checkAllowance(owner) {
|
|
390
|
+
const usdcContract = new ethers2.Contract(
|
|
391
|
+
this.chainConfig.usdc,
|
|
392
|
+
PERMIT_ABI,
|
|
393
|
+
this.provider
|
|
394
|
+
);
|
|
395
|
+
const ownerAddress = ethers2.getAddress(owner);
|
|
396
|
+
const [allowance, balance] = await Promise.all([
|
|
397
|
+
usdcContract.allowance(ownerAddress, this.address),
|
|
398
|
+
usdcContract.balanceOf(ownerAddress)
|
|
399
|
+
]);
|
|
400
|
+
return {
|
|
401
|
+
allowance: (Number(allowance) / 1e6).toFixed(2),
|
|
402
|
+
ownerBalance: (Number(balance) / 1e6).toFixed(2),
|
|
403
|
+
canSpend: Number(allowance) > 0
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Spend USDC from Owner's wallet
|
|
408
|
+
*
|
|
409
|
+
* @param to - Recipient (service provider)
|
|
410
|
+
* @param amount - Amount in USDC
|
|
411
|
+
* @param permit - Optional, uses stored permit if not provided
|
|
412
|
+
*/
|
|
413
|
+
async spend(to, amount, permit) {
|
|
414
|
+
const toAddress = ethers2.getAddress(to);
|
|
415
|
+
const amountWei = BigInt(Math.floor(amount * 1e6));
|
|
416
|
+
let usePermit = permit;
|
|
417
|
+
let ownerAddress;
|
|
418
|
+
if (usePermit) {
|
|
419
|
+
ownerAddress = ethers2.getAddress(usePermit.owner);
|
|
420
|
+
this.storePermit(usePermit);
|
|
421
|
+
} else {
|
|
422
|
+
const usdcContract = new ethers2.Contract(
|
|
423
|
+
this.chainConfig.usdc,
|
|
424
|
+
PERMIT_ABI,
|
|
425
|
+
this.provider
|
|
426
|
+
);
|
|
427
|
+
for (const [owner, p] of this._permits) {
|
|
428
|
+
const allowance = await usdcContract.allowance(owner, this.address);
|
|
429
|
+
if (BigInt(allowance) >= amountWei) {
|
|
430
|
+
ownerAddress = ethers2.getAddress(owner);
|
|
431
|
+
usePermit = p;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (!usePermit) {
|
|
436
|
+
return {
|
|
437
|
+
success: false,
|
|
438
|
+
error: "No valid permit. Ask Owner to authorize spending first.",
|
|
439
|
+
from: "",
|
|
440
|
+
to: toAddress,
|
|
441
|
+
amount
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const usdcContract = new ethers2.Contract(
|
|
447
|
+
this.chainConfig.usdc,
|
|
448
|
+
PERMIT_ABI,
|
|
449
|
+
this.wallet
|
|
450
|
+
);
|
|
451
|
+
const currentAllowance = await usdcContract.allowance(ownerAddress, this.address);
|
|
452
|
+
if (BigInt(currentAllowance) < amountWei) {
|
|
453
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
454
|
+
if (usePermit.deadline < now) {
|
|
455
|
+
return {
|
|
456
|
+
success: false,
|
|
457
|
+
error: "Permit expired. Ask Owner for a new authorization.",
|
|
458
|
+
from: ownerAddress,
|
|
459
|
+
to: toAddress,
|
|
460
|
+
amount
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const permitTx = await usdcContract.permit(
|
|
464
|
+
ownerAddress,
|
|
465
|
+
this.address,
|
|
466
|
+
usePermit.value,
|
|
467
|
+
usePermit.deadline,
|
|
468
|
+
usePermit.v,
|
|
469
|
+
usePermit.r,
|
|
470
|
+
usePermit.s
|
|
471
|
+
);
|
|
472
|
+
await permitTx.wait();
|
|
473
|
+
}
|
|
474
|
+
const tx = await usdcContract.transferFrom(ownerAddress, toAddress, amountWei);
|
|
475
|
+
await tx.wait();
|
|
476
|
+
const newAllowance = await usdcContract.allowance(ownerAddress, this.address);
|
|
477
|
+
return {
|
|
478
|
+
success: true,
|
|
479
|
+
txHash: tx.hash,
|
|
480
|
+
from: ownerAddress,
|
|
481
|
+
to: toAddress,
|
|
482
|
+
amount,
|
|
483
|
+
remainingAllowance: (Number(newAllowance) / 1e6).toFixed(2),
|
|
484
|
+
explorerUrl: `${this.chainConfig.explorerTx}${tx.hash}`
|
|
485
|
+
};
|
|
486
|
+
} catch (error) {
|
|
487
|
+
return {
|
|
488
|
+
success: false,
|
|
489
|
+
error: error.message,
|
|
490
|
+
from: ownerAddress,
|
|
491
|
+
to: toAddress,
|
|
492
|
+
amount
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Get gas balance (ETH needed for transactions)
|
|
498
|
+
*/
|
|
499
|
+
async getGasBalance() {
|
|
500
|
+
const balance = await this.provider.getBalance(this.address);
|
|
501
|
+
return ethers2.formatEther(balance);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Check if agent has enough gas
|
|
505
|
+
*/
|
|
506
|
+
async hasGas(minEth = 5e-4) {
|
|
507
|
+
const balance = await this.getGasBalance();
|
|
508
|
+
return parseFloat(balance) >= minEth;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Generate authorization request for Owner
|
|
512
|
+
* Owner can sign this with CLI (ethers) or MetaMask
|
|
513
|
+
*/
|
|
514
|
+
async generateAuthRequest(params) {
|
|
515
|
+
const { ownerAddress, amount, expiresInHours = 168 } = params;
|
|
516
|
+
const deadline = Math.floor(Date.now() / 1e3) + expiresInHours * 3600;
|
|
517
|
+
const value = BigInt(Math.floor(amount * 1e6)).toString();
|
|
518
|
+
const usdcContract = new ethers2.Contract(
|
|
519
|
+
this.chainConfig.usdc,
|
|
520
|
+
PERMIT_ABI,
|
|
521
|
+
this.provider
|
|
522
|
+
);
|
|
523
|
+
const nonce = Number(await usdcContract.nonces(ownerAddress));
|
|
524
|
+
const domain = {
|
|
525
|
+
name: "USD Coin",
|
|
526
|
+
version: "2",
|
|
527
|
+
chainId: this.chainConfig.chainId,
|
|
528
|
+
verifyingContract: this.chainConfig.usdc
|
|
529
|
+
};
|
|
530
|
+
const types = {
|
|
531
|
+
Permit: [
|
|
532
|
+
{ name: "owner", type: "address" },
|
|
533
|
+
{ name: "spender", type: "address" },
|
|
534
|
+
{ name: "value", type: "uint256" },
|
|
535
|
+
{ name: "nonce", type: "uint256" },
|
|
536
|
+
{ name: "deadline", type: "uint256" }
|
|
537
|
+
]
|
|
538
|
+
};
|
|
539
|
+
const permitMessage = {
|
|
540
|
+
owner: ownerAddress,
|
|
541
|
+
spender: this.address,
|
|
542
|
+
value,
|
|
543
|
+
nonce,
|
|
544
|
+
deadline
|
|
545
|
+
};
|
|
546
|
+
const typedData = {
|
|
547
|
+
types: { ...types, EIP712Domain: [
|
|
548
|
+
{ name: "name", type: "string" },
|
|
549
|
+
{ name: "version", type: "string" },
|
|
550
|
+
{ name: "chainId", type: "uint256" },
|
|
551
|
+
{ name: "verifyingContract", type: "address" }
|
|
552
|
+
] },
|
|
553
|
+
primaryType: "Permit",
|
|
554
|
+
domain,
|
|
555
|
+
message: permitMessage
|
|
556
|
+
};
|
|
557
|
+
const cliCommand = `npx moltspay sign-permit \\
|
|
558
|
+
--owner ${ownerAddress} \\
|
|
559
|
+
--spender ${this.address} \\
|
|
560
|
+
--amount ${amount} \\
|
|
561
|
+
--deadline ${deadline} \\
|
|
562
|
+
--nonce ${nonce} \\
|
|
563
|
+
--chain ${this.chain}`;
|
|
564
|
+
const message = `\u{1F510} Authorization Request
|
|
565
|
+
|
|
566
|
+
I need permission to spend up to ${amount} USDC from your wallet.
|
|
567
|
+
|
|
568
|
+
**Details:**
|
|
569
|
+
- Your wallet: ${ownerAddress}
|
|
570
|
+
- My address: ${this.address}
|
|
571
|
+
- Amount: ${amount} USDC
|
|
572
|
+
- Expires: ${new Date(deadline * 1e3).toISOString()}
|
|
573
|
+
- Chain: ${this.chainConfig.name}
|
|
574
|
+
|
|
575
|
+
**Option 1: Sign with CLI** (if you have the private key)
|
|
576
|
+
\`\`\`
|
|
577
|
+
${cliCommand}
|
|
578
|
+
\`\`\`
|
|
579
|
+
|
|
580
|
+
**Option 2: Sign with MetaMask**
|
|
581
|
+
Visit: https://moltspay.vercel.app/permit?data=${encodeURIComponent(JSON.stringify(typedData))}
|
|
582
|
+
|
|
583
|
+
After signing, send me the signature (v, r, s).`;
|
|
584
|
+
return { message, typedData, cliCommand };
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
function getAgentAddress(config) {
|
|
588
|
+
const wallet = new AgentWallet(config);
|
|
589
|
+
return wallet.address;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/wallet/Wallet.ts
|
|
593
|
+
import { ethers as ethers3 } from "ethers";
|
|
285
594
|
var Wallet = class {
|
|
286
595
|
chain;
|
|
287
596
|
chainConfig;
|
|
@@ -297,10 +606,10 @@ var Wallet = class {
|
|
|
297
606
|
throw new Error("privateKey is required. Set via config or PAYMENT_AGENT_PRIVATE_KEY env var.");
|
|
298
607
|
}
|
|
299
608
|
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
300
|
-
this.provider = new
|
|
301
|
-
this.wallet = new
|
|
609
|
+
this.provider = new ethers3.JsonRpcProvider(rpcUrl);
|
|
610
|
+
this.wallet = new ethers3.Wallet(privateKey, this.provider);
|
|
302
611
|
this.address = this.wallet.address;
|
|
303
|
-
this.usdcContract = new
|
|
612
|
+
this.usdcContract = new ethers3.Contract(
|
|
304
613
|
this.chainConfig.usdc,
|
|
305
614
|
ERC20_ABI,
|
|
306
615
|
this.wallet
|
|
@@ -316,7 +625,7 @@ var Wallet = class {
|
|
|
316
625
|
]);
|
|
317
626
|
return {
|
|
318
627
|
address: this.address,
|
|
319
|
-
eth:
|
|
628
|
+
eth: ethers3.formatEther(ethBalance),
|
|
320
629
|
usdc: (Number(usdcBalance) / 1e6).toFixed(2),
|
|
321
630
|
chain: this.chain
|
|
322
631
|
};
|
|
@@ -326,7 +635,7 @@ var Wallet = class {
|
|
|
326
635
|
*/
|
|
327
636
|
async transfer(to, amount) {
|
|
328
637
|
try {
|
|
329
|
-
to =
|
|
638
|
+
to = ethers3.getAddress(to);
|
|
330
639
|
const amountWei = BigInt(Math.floor(amount * 1e6));
|
|
331
640
|
const balance = await this.usdcContract.balanceOf(this.address);
|
|
332
641
|
if (BigInt(balance) < amountWei) {
|
|
@@ -367,7 +676,7 @@ var Wallet = class {
|
|
|
367
676
|
*/
|
|
368
677
|
async getEthBalance() {
|
|
369
678
|
const balance = await this.provider.getBalance(this.address);
|
|
370
|
-
return
|
|
679
|
+
return ethers3.formatEther(balance);
|
|
371
680
|
}
|
|
372
681
|
/**
|
|
373
682
|
* Get USDC balance
|
|
@@ -379,14 +688,14 @@ var Wallet = class {
|
|
|
379
688
|
};
|
|
380
689
|
|
|
381
690
|
// src/audit/AuditLog.ts
|
|
382
|
-
import * as
|
|
383
|
-
import * as
|
|
691
|
+
import * as fs2 from "fs";
|
|
692
|
+
import * as path2 from "path";
|
|
384
693
|
import * as crypto from "crypto";
|
|
385
694
|
var AuditLog = class {
|
|
386
695
|
basePath;
|
|
387
696
|
lastHash = "0000000000000000";
|
|
388
697
|
constructor(basePath) {
|
|
389
|
-
this.basePath = basePath ||
|
|
698
|
+
this.basePath = basePath || path2.join(process.cwd(), "data", "audit");
|
|
390
699
|
this.ensureDir();
|
|
391
700
|
this.loadLastHash();
|
|
392
701
|
}
|
|
@@ -415,7 +724,7 @@ var AuditLog = class {
|
|
|
415
724
|
this.lastHash = entry.hash;
|
|
416
725
|
const filePath = this.getFilePath(now);
|
|
417
726
|
const line = JSON.stringify(entry) + "\n";
|
|
418
|
-
|
|
727
|
+
fs2.appendFileSync(filePath, line, "utf-8");
|
|
419
728
|
return entry;
|
|
420
729
|
}
|
|
421
730
|
/**
|
|
@@ -423,10 +732,10 @@ var AuditLog = class {
|
|
|
423
732
|
*/
|
|
424
733
|
read(date) {
|
|
425
734
|
const filePath = this.getFilePath(date || /* @__PURE__ */ new Date());
|
|
426
|
-
if (!
|
|
735
|
+
if (!fs2.existsSync(filePath)) {
|
|
427
736
|
return [];
|
|
428
737
|
}
|
|
429
|
-
const content =
|
|
738
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
430
739
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
431
740
|
return lines.map((line) => JSON.parse(line));
|
|
432
741
|
}
|
|
@@ -477,7 +786,7 @@ var AuditLog = class {
|
|
|
477
786
|
*/
|
|
478
787
|
getFilePath(date) {
|
|
479
788
|
const dateStr = date.toISOString().slice(0, 10);
|
|
480
|
-
return
|
|
789
|
+
return path2.join(this.basePath, `audit_${dateStr}.jsonl`);
|
|
481
790
|
}
|
|
482
791
|
/**
|
|
483
792
|
* Calculate entry hash
|
|
@@ -515,8 +824,8 @@ var AuditLog = class {
|
|
|
515
824
|
* Ensure directory exists
|
|
516
825
|
*/
|
|
517
826
|
ensureDir() {
|
|
518
|
-
if (!
|
|
519
|
-
|
|
827
|
+
if (!fs2.existsSync(this.basePath)) {
|
|
828
|
+
fs2.mkdirSync(this.basePath, { recursive: true });
|
|
520
829
|
}
|
|
521
830
|
}
|
|
522
831
|
};
|
|
@@ -780,11 +1089,11 @@ var SecureWallet = class {
|
|
|
780
1089
|
};
|
|
781
1090
|
|
|
782
1091
|
// src/wallet/createWallet.ts
|
|
783
|
-
import { ethers as
|
|
784
|
-
import { writeFileSync, readFileSync as
|
|
785
|
-
import { join as
|
|
1092
|
+
import { ethers as ethers4 } from "ethers";
|
|
1093
|
+
import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
1094
|
+
import { join as join3, dirname } from "path";
|
|
786
1095
|
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
787
|
-
var DEFAULT_STORAGE_DIR =
|
|
1096
|
+
var DEFAULT_STORAGE_DIR = join3(process.env.HOME || "~", ".moltspay");
|
|
788
1097
|
var DEFAULT_STORAGE_FILE = "wallet.json";
|
|
789
1098
|
function encryptPrivateKey(privateKey, password) {
|
|
790
1099
|
const salt = randomBytes(16);
|
|
@@ -807,10 +1116,10 @@ function decryptPrivateKey(encrypted, password, iv, salt) {
|
|
|
807
1116
|
return decrypted;
|
|
808
1117
|
}
|
|
809
1118
|
function createWallet(options = {}) {
|
|
810
|
-
const storagePath = options.storagePath ||
|
|
811
|
-
if (
|
|
1119
|
+
const storagePath = options.storagePath || join3(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
1120
|
+
if (existsSync3(storagePath) && !options.overwrite) {
|
|
812
1121
|
try {
|
|
813
|
-
const existing = JSON.parse(
|
|
1122
|
+
const existing = JSON.parse(readFileSync3(storagePath, "utf8"));
|
|
814
1123
|
return {
|
|
815
1124
|
success: true,
|
|
816
1125
|
address: existing.address,
|
|
@@ -825,7 +1134,7 @@ function createWallet(options = {}) {
|
|
|
825
1134
|
}
|
|
826
1135
|
}
|
|
827
1136
|
try {
|
|
828
|
-
const wallet =
|
|
1137
|
+
const wallet = ethers4.Wallet.createRandom();
|
|
829
1138
|
const walletData = {
|
|
830
1139
|
address: wallet.address,
|
|
831
1140
|
label: options.label,
|
|
@@ -842,10 +1151,10 @@ function createWallet(options = {}) {
|
|
|
842
1151
|
walletData.privateKey = wallet.privateKey;
|
|
843
1152
|
}
|
|
844
1153
|
const dir = dirname(storagePath);
|
|
845
|
-
if (!
|
|
846
|
-
|
|
1154
|
+
if (!existsSync3(dir)) {
|
|
1155
|
+
mkdirSync3(dir, { recursive: true });
|
|
847
1156
|
}
|
|
848
|
-
|
|
1157
|
+
writeFileSync2(storagePath, JSON.stringify(walletData, null, 2), { mode: 384 });
|
|
849
1158
|
return {
|
|
850
1159
|
success: true,
|
|
851
1160
|
address: wallet.address,
|
|
@@ -860,12 +1169,12 @@ function createWallet(options = {}) {
|
|
|
860
1169
|
}
|
|
861
1170
|
}
|
|
862
1171
|
function loadWallet(options = {}) {
|
|
863
|
-
const storagePath = options.storagePath ||
|
|
864
|
-
if (!
|
|
1172
|
+
const storagePath = options.storagePath || join3(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
1173
|
+
if (!existsSync3(storagePath)) {
|
|
865
1174
|
return { success: false, error: "Wallet not found. Run createWallet() first." };
|
|
866
1175
|
}
|
|
867
1176
|
try {
|
|
868
|
-
const data = JSON.parse(
|
|
1177
|
+
const data = JSON.parse(readFileSync3(storagePath, "utf8"));
|
|
869
1178
|
if (data.encrypted) {
|
|
870
1179
|
if (!options.password) {
|
|
871
1180
|
return { success: false, error: "Wallet is encrypted. Password required." };
|
|
@@ -880,25 +1189,25 @@ function loadWallet(options = {}) {
|
|
|
880
1189
|
}
|
|
881
1190
|
}
|
|
882
1191
|
function getWalletAddress(storagePath) {
|
|
883
|
-
const
|
|
884
|
-
if (!
|
|
1192
|
+
const path3 = storagePath || join3(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
1193
|
+
if (!existsSync3(path3)) {
|
|
885
1194
|
return null;
|
|
886
1195
|
}
|
|
887
1196
|
try {
|
|
888
|
-
const data = JSON.parse(
|
|
1197
|
+
const data = JSON.parse(readFileSync3(path3, "utf8"));
|
|
889
1198
|
return data.address;
|
|
890
1199
|
} catch {
|
|
891
1200
|
return null;
|
|
892
1201
|
}
|
|
893
1202
|
}
|
|
894
1203
|
function walletExists(storagePath) {
|
|
895
|
-
const
|
|
896
|
-
return
|
|
1204
|
+
const path3 = storagePath || join3(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
1205
|
+
return existsSync3(path3);
|
|
897
1206
|
}
|
|
898
1207
|
|
|
899
1208
|
// src/wallet/PermitWallet.ts
|
|
900
|
-
import { ethers as
|
|
901
|
-
var
|
|
1209
|
+
import { ethers as ethers5 } from "ethers";
|
|
1210
|
+
var PERMIT_ABI2 = [
|
|
902
1211
|
...ERC20_ABI,
|
|
903
1212
|
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
|
|
904
1213
|
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
@@ -929,12 +1238,12 @@ var PermitWallet = class {
|
|
|
929
1238
|
throw new Error("privateKey is required. Set via config, env var, or walletPath.");
|
|
930
1239
|
}
|
|
931
1240
|
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
932
|
-
this.provider = new
|
|
933
|
-
this.wallet = new
|
|
1241
|
+
this.provider = new ethers5.JsonRpcProvider(rpcUrl);
|
|
1242
|
+
this.wallet = new ethers5.Wallet(privateKey, this.provider);
|
|
934
1243
|
this.address = this.wallet.address;
|
|
935
|
-
this.usdcContract = new
|
|
1244
|
+
this.usdcContract = new ethers5.Contract(
|
|
936
1245
|
this.chainConfig.usdc,
|
|
937
|
-
|
|
1246
|
+
PERMIT_ABI2,
|
|
938
1247
|
this.wallet
|
|
939
1248
|
);
|
|
940
1249
|
}
|
|
@@ -977,9 +1286,9 @@ var PermitWallet = class {
|
|
|
977
1286
|
async transferWithPermit(params) {
|
|
978
1287
|
const { to, amount, permit } = params;
|
|
979
1288
|
try {
|
|
980
|
-
const toAddress =
|
|
981
|
-
const ownerAddress =
|
|
982
|
-
if (
|
|
1289
|
+
const toAddress = ethers5.getAddress(to);
|
|
1290
|
+
const ownerAddress = ethers5.getAddress(permit.owner);
|
|
1291
|
+
if (ethers5.getAddress(permit.spender).toLowerCase() !== this.address.toLowerCase()) {
|
|
983
1292
|
return {
|
|
984
1293
|
success: false,
|
|
985
1294
|
error: `Permit spender (${permit.spender}) doesn't match wallet address (${this.address})`
|
|
@@ -1083,7 +1392,7 @@ var PermitWallet = class {
|
|
|
1083
1392
|
*/
|
|
1084
1393
|
async getGasBalance() {
|
|
1085
1394
|
const balance = await this.provider.getBalance(this.address);
|
|
1086
|
-
return
|
|
1395
|
+
return ethers5.formatEther(balance);
|
|
1087
1396
|
}
|
|
1088
1397
|
/**
|
|
1089
1398
|
* Check if there's enough gas
|
|
@@ -1144,7 +1453,7 @@ After signing, send { v, r, s, deadline } to the Agent.
|
|
|
1144
1453
|
}
|
|
1145
1454
|
|
|
1146
1455
|
// src/wallet/signPermit.ts
|
|
1147
|
-
import { ethers as
|
|
1456
|
+
import { ethers as ethers6 } from "ethers";
|
|
1148
1457
|
async function signPermit(config, params) {
|
|
1149
1458
|
const chain = config.chain || "base";
|
|
1150
1459
|
const chainConfig = getChain(chain);
|
|
@@ -1153,9 +1462,9 @@ async function signPermit(config, params) {
|
|
|
1153
1462
|
throw new Error("privateKey is required");
|
|
1154
1463
|
}
|
|
1155
1464
|
const rpcUrl = config.rpcUrl || chainConfig.rpc;
|
|
1156
|
-
const provider = new
|
|
1157
|
-
const wallet = new
|
|
1158
|
-
const usdcContract = new
|
|
1465
|
+
const provider = new ethers6.JsonRpcProvider(rpcUrl);
|
|
1466
|
+
const wallet = new ethers6.Wallet(privateKey, provider);
|
|
1467
|
+
const usdcContract = new ethers6.Contract(chainConfig.usdc, ERC20_ABI, provider);
|
|
1159
1468
|
const nonce = Number(await usdcContract.nonces(wallet.address));
|
|
1160
1469
|
let deadline;
|
|
1161
1470
|
if (!params.deadline) {
|
|
@@ -1189,7 +1498,7 @@ async function signPermit(config, params) {
|
|
|
1189
1498
|
deadline
|
|
1190
1499
|
};
|
|
1191
1500
|
const signature = await wallet.signTypedData(domain, types, message);
|
|
1192
|
-
const sig =
|
|
1501
|
+
const sig = ethers6.Signature.from(signature);
|
|
1193
1502
|
return {
|
|
1194
1503
|
owner: wallet.address,
|
|
1195
1504
|
spender: params.spender,
|
|
@@ -1211,8 +1520,284 @@ var PermitSigner = class {
|
|
|
1211
1520
|
}
|
|
1212
1521
|
};
|
|
1213
1522
|
|
|
1523
|
+
// src/wallet/AllowanceWallet.ts
|
|
1524
|
+
import { ethers as ethers7 } from "ethers";
|
|
1525
|
+
var PERMIT_ABI3 = [
|
|
1526
|
+
...ERC20_ABI,
|
|
1527
|
+
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
|
|
1528
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
1529
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1530
|
+
"function nonces(address owner) view returns (uint256)"
|
|
1531
|
+
];
|
|
1532
|
+
var AllowanceWallet = class {
|
|
1533
|
+
chain;
|
|
1534
|
+
chainConfig;
|
|
1535
|
+
address;
|
|
1536
|
+
// Agent's address
|
|
1537
|
+
wallet;
|
|
1538
|
+
provider;
|
|
1539
|
+
usdcContract;
|
|
1540
|
+
/** Stored permits from owners */
|
|
1541
|
+
permits = /* @__PURE__ */ new Map();
|
|
1542
|
+
constructor(config) {
|
|
1543
|
+
this.chain = config.chain || "base_sepolia";
|
|
1544
|
+
this.chainConfig = getChain(this.chain);
|
|
1545
|
+
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1546
|
+
this.provider = new ethers7.JsonRpcProvider(rpcUrl);
|
|
1547
|
+
this.wallet = new ethers7.Wallet(config.privateKey, this.provider);
|
|
1548
|
+
this.address = this.wallet.address;
|
|
1549
|
+
this.usdcContract = new ethers7.Contract(
|
|
1550
|
+
this.chainConfig.usdc,
|
|
1551
|
+
PERMIT_ABI3,
|
|
1552
|
+
this.wallet
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Store a Permit received from Owner
|
|
1557
|
+
* Call this when Owner sends you a signed Permit
|
|
1558
|
+
*/
|
|
1559
|
+
storePermit(permit) {
|
|
1560
|
+
const ownerLower = permit.owner.toLowerCase();
|
|
1561
|
+
this.permits.set(ownerLower, permit);
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Get stored Permit for an owner
|
|
1565
|
+
*/
|
|
1566
|
+
getPermit(owner) {
|
|
1567
|
+
return this.permits.get(owner.toLowerCase());
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Check allowance status with an owner
|
|
1571
|
+
*/
|
|
1572
|
+
async checkAllowance(owner) {
|
|
1573
|
+
const ownerAddress = ethers7.getAddress(owner);
|
|
1574
|
+
const [allowance, ownerBalance, agentGasBalance] = await Promise.all([
|
|
1575
|
+
this.usdcContract.allowance(ownerAddress, this.address),
|
|
1576
|
+
this.usdcContract.balanceOf(ownerAddress),
|
|
1577
|
+
this.provider.getBalance(this.address)
|
|
1578
|
+
]);
|
|
1579
|
+
const allowanceNum = Number(allowance) / 1e6;
|
|
1580
|
+
const hasGas = Number(ethers7.formatEther(agentGasBalance)) >= 1e-4;
|
|
1581
|
+
return {
|
|
1582
|
+
owner: ownerAddress,
|
|
1583
|
+
agent: this.address,
|
|
1584
|
+
allowance: allowanceNum.toFixed(2),
|
|
1585
|
+
ownerBalance: (Number(ownerBalance) / 1e6).toFixed(2),
|
|
1586
|
+
agentGasBalance: ethers7.formatEther(agentGasBalance),
|
|
1587
|
+
canSpend: allowanceNum > 0 && hasGas,
|
|
1588
|
+
chain: this.chainConfig.name
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Spend from Owner's wallet using Permit allowance
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* ```typescript
|
|
1596
|
+
* const agent = new AllowanceWallet({
|
|
1597
|
+
* chain: 'base',
|
|
1598
|
+
* privateKey: process.env.AGENT_KEY // Only needs gas
|
|
1599
|
+
* });
|
|
1600
|
+
*
|
|
1601
|
+
* // Owner gave us a Permit
|
|
1602
|
+
* agent.storePermit(ownerPermit);
|
|
1603
|
+
*
|
|
1604
|
+
* // Spend to pay for a service
|
|
1605
|
+
* const result = await agent.spend({
|
|
1606
|
+
* to: '0xServiceProvider...',
|
|
1607
|
+
* amount: 2.99,
|
|
1608
|
+
* });
|
|
1609
|
+
* ```
|
|
1610
|
+
*/
|
|
1611
|
+
async spend(params) {
|
|
1612
|
+
const { to, amount, permit } = params;
|
|
1613
|
+
try {
|
|
1614
|
+
const toAddress = ethers7.getAddress(to);
|
|
1615
|
+
const amountWei = BigInt(Math.floor(amount * 1e6));
|
|
1616
|
+
let ownerPermit = permit;
|
|
1617
|
+
let ownerAddress;
|
|
1618
|
+
if (ownerPermit) {
|
|
1619
|
+
ownerAddress = ethers7.getAddress(ownerPermit.owner);
|
|
1620
|
+
this.storePermit(ownerPermit);
|
|
1621
|
+
} else {
|
|
1622
|
+
for (const [owner, p] of this.permits) {
|
|
1623
|
+
const allowance = await this.usdcContract.allowance(owner, this.address);
|
|
1624
|
+
if (BigInt(allowance) >= amountWei) {
|
|
1625
|
+
ownerAddress = ethers7.getAddress(owner);
|
|
1626
|
+
ownerPermit = p;
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
if (!ownerPermit) {
|
|
1631
|
+
return {
|
|
1632
|
+
success: false,
|
|
1633
|
+
error: "No valid permit found. Ask Owner to sign a Permit first.",
|
|
1634
|
+
from: "",
|
|
1635
|
+
to: toAddress,
|
|
1636
|
+
amount
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1641
|
+
if (BigInt(currentAllowance) < amountWei) {
|
|
1642
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1643
|
+
if (ownerPermit.deadline < now) {
|
|
1644
|
+
return {
|
|
1645
|
+
success: false,
|
|
1646
|
+
error: `Permit expired at ${new Date(ownerPermit.deadline * 1e3).toISOString()}. Ask Owner for a new Permit.`,
|
|
1647
|
+
from: ownerAddress,
|
|
1648
|
+
to: toAddress,
|
|
1649
|
+
amount
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
const currentNonce = await this.usdcContract.nonces(ownerAddress);
|
|
1653
|
+
if (Number(currentNonce) !== ownerPermit.nonce) {
|
|
1654
|
+
return {
|
|
1655
|
+
success: false,
|
|
1656
|
+
error: `Permit nonce mismatch (expected ${ownerPermit.nonce}, got ${currentNonce}). Owner may have used this permit or signed a new one.`,
|
|
1657
|
+
from: ownerAddress,
|
|
1658
|
+
to: toAddress,
|
|
1659
|
+
amount
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
console.log("[AllowanceWallet] Submitting permit on-chain...");
|
|
1663
|
+
const permitTx = await this.usdcContract.permit(
|
|
1664
|
+
ownerAddress,
|
|
1665
|
+
this.address,
|
|
1666
|
+
ownerPermit.value,
|
|
1667
|
+
ownerPermit.deadline,
|
|
1668
|
+
ownerPermit.v,
|
|
1669
|
+
ownerPermit.r,
|
|
1670
|
+
ownerPermit.s
|
|
1671
|
+
);
|
|
1672
|
+
await permitTx.wait();
|
|
1673
|
+
console.log("[AllowanceWallet] Permit submitted:", permitTx.hash);
|
|
1674
|
+
}
|
|
1675
|
+
console.log("[AllowanceWallet] Executing transferFrom...");
|
|
1676
|
+
const tx = await this.usdcContract.transferFrom(
|
|
1677
|
+
ownerAddress,
|
|
1678
|
+
toAddress,
|
|
1679
|
+
amountWei
|
|
1680
|
+
);
|
|
1681
|
+
const receipt = await tx.wait();
|
|
1682
|
+
const newAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1683
|
+
return {
|
|
1684
|
+
success: true,
|
|
1685
|
+
txHash: tx.hash,
|
|
1686
|
+
from: ownerAddress,
|
|
1687
|
+
to: toAddress,
|
|
1688
|
+
amount,
|
|
1689
|
+
remainingAllowance: (Number(newAllowance) / 1e6).toFixed(2),
|
|
1690
|
+
explorerUrl: `${this.chainConfig.explorerTx}${tx.hash}`
|
|
1691
|
+
};
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
const message = error.message;
|
|
1694
|
+
if (message.includes("ERC20InsufficientAllowance")) {
|
|
1695
|
+
return {
|
|
1696
|
+
success: false,
|
|
1697
|
+
error: "Insufficient allowance. Ask Owner to sign a new Permit with higher amount.",
|
|
1698
|
+
from: "",
|
|
1699
|
+
to,
|
|
1700
|
+
amount
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
if (message.includes("ERC20InsufficientBalance")) {
|
|
1704
|
+
return {
|
|
1705
|
+
success: false,
|
|
1706
|
+
error: "Owner's wallet has insufficient USDC balance.",
|
|
1707
|
+
from: "",
|
|
1708
|
+
to,
|
|
1709
|
+
amount
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
return {
|
|
1713
|
+
success: false,
|
|
1714
|
+
error: message,
|
|
1715
|
+
from: "",
|
|
1716
|
+
to,
|
|
1717
|
+
amount
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Get Agent's gas balance (ETH)
|
|
1723
|
+
*/
|
|
1724
|
+
async getGasBalance() {
|
|
1725
|
+
const balance = await this.provider.getBalance(this.address);
|
|
1726
|
+
return ethers7.formatEther(balance);
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
function generatePermitInstructions(params) {
|
|
1730
|
+
const { ownerAddress, agentAddress, amount, deadlineHours = 24, chain = "base" } = params;
|
|
1731
|
+
const chainConfig = getChain(chain);
|
|
1732
|
+
const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
|
|
1733
|
+
const value = BigInt(Math.floor(amount * 1e6)).toString();
|
|
1734
|
+
const eip712Domain = {
|
|
1735
|
+
name: "USD Coin",
|
|
1736
|
+
version: "2",
|
|
1737
|
+
chainId: chainConfig.chainId,
|
|
1738
|
+
verifyingContract: chainConfig.usdc
|
|
1739
|
+
};
|
|
1740
|
+
const typedData = {
|
|
1741
|
+
types: {
|
|
1742
|
+
EIP712Domain: [
|
|
1743
|
+
{ name: "name", type: "string" },
|
|
1744
|
+
{ name: "version", type: "string" },
|
|
1745
|
+
{ name: "chainId", type: "uint256" },
|
|
1746
|
+
{ name: "verifyingContract", type: "address" }
|
|
1747
|
+
],
|
|
1748
|
+
Permit: [
|
|
1749
|
+
{ name: "owner", type: "address" },
|
|
1750
|
+
{ name: "spender", type: "address" },
|
|
1751
|
+
{ name: "value", type: "uint256" },
|
|
1752
|
+
{ name: "nonce", type: "uint256" },
|
|
1753
|
+
{ name: "deadline", type: "uint256" }
|
|
1754
|
+
]
|
|
1755
|
+
},
|
|
1756
|
+
primaryType: "Permit",
|
|
1757
|
+
domain: eip712Domain,
|
|
1758
|
+
message: {
|
|
1759
|
+
owner: ownerAddress,
|
|
1760
|
+
spender: agentAddress,
|
|
1761
|
+
value,
|
|
1762
|
+
nonce: "<GET_FROM_USDC_CONTRACT>",
|
|
1763
|
+
// Owner needs to query this
|
|
1764
|
+
deadline
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1767
|
+
const instructions = `
|
|
1768
|
+
\u{1F510} **Grant USDC Spending Allowance to Your Agent**
|
|
1769
|
+
|
|
1770
|
+
Your Agent (${agentAddress}) is requesting permission to spend up to ${amount} USDC from your wallet.
|
|
1771
|
+
|
|
1772
|
+
**What this does:**
|
|
1773
|
+
- Allows your Agent to pay for services on your behalf
|
|
1774
|
+
- Your USDC stays in YOUR wallet until spent
|
|
1775
|
+
- Agent can only spend up to the authorized amount
|
|
1776
|
+
- Expires in ${deadlineHours} hours
|
|
1777
|
+
- You can revoke anytime by moving your USDC
|
|
1778
|
+
|
|
1779
|
+
**How to sign (MetaMask / any web3 wallet):**
|
|
1780
|
+
|
|
1781
|
+
1. Go to https://etherscan.io/address/${chainConfig.usdc}#readContract
|
|
1782
|
+
2. Query \`nonces(${ownerAddress})\` to get your current nonce
|
|
1783
|
+
3. Use eth_signTypedData_v4 with the data below (replace nonce)
|
|
1784
|
+
4. Send the signature {v, r, s, deadline, nonce} to your Agent
|
|
1785
|
+
|
|
1786
|
+
**Chain:** ${chainConfig.name}
|
|
1787
|
+
**USDC Contract:** ${chainConfig.usdc}
|
|
1788
|
+
|
|
1789
|
+
**EIP-712 Typed Data:**
|
|
1790
|
+
\`\`\`json
|
|
1791
|
+
${JSON.stringify(typedData, null, 2)}
|
|
1792
|
+
\`\`\`
|
|
1793
|
+
|
|
1794
|
+
\u26A0\uFE0F Never share your private key. This signature only authorizes spending, not wallet access.
|
|
1795
|
+
`;
|
|
1796
|
+
return { instructions, typedData, eip712Domain };
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1214
1799
|
// src/permit/Permit.ts
|
|
1215
|
-
import { ethers as
|
|
1800
|
+
import { ethers as ethers8 } from "ethers";
|
|
1216
1801
|
var PermitPayment = class {
|
|
1217
1802
|
chain;
|
|
1218
1803
|
chainConfig;
|
|
@@ -1225,13 +1810,13 @@ var PermitPayment = class {
|
|
|
1225
1810
|
this.chainConfig = getChain(this.chain);
|
|
1226
1811
|
this.spenderAddress = config.spenderAddress || process.env.PAYMENT_AGENT_WALLET || "";
|
|
1227
1812
|
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1228
|
-
this.provider = new
|
|
1813
|
+
this.provider = new ethers8.JsonRpcProvider(rpcUrl);
|
|
1229
1814
|
const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
|
|
1230
1815
|
if (privateKey) {
|
|
1231
|
-
this.wallet = new
|
|
1816
|
+
this.wallet = new ethers8.Wallet(privateKey, this.provider);
|
|
1232
1817
|
this.spenderAddress = this.wallet.address;
|
|
1233
1818
|
}
|
|
1234
|
-
this.usdcContract = new
|
|
1819
|
+
this.usdcContract = new ethers8.Contract(
|
|
1235
1820
|
this.chainConfig.usdc,
|
|
1236
1821
|
ERC20_ABI,
|
|
1237
1822
|
this.wallet || this.provider
|
|
@@ -1514,8 +2099,8 @@ var OrderManager = class {
|
|
|
1514
2099
|
};
|
|
1515
2100
|
|
|
1516
2101
|
// src/verify/index.ts
|
|
1517
|
-
import { ethers as
|
|
1518
|
-
var TRANSFER_EVENT_TOPIC =
|
|
2102
|
+
import { ethers as ethers9 } from "ethers";
|
|
2103
|
+
var TRANSFER_EVENT_TOPIC = ethers9.id("Transfer(address,address,uint256)");
|
|
1519
2104
|
async function verifyPayment(params) {
|
|
1520
2105
|
const { txHash, expectedAmount, expectedTo } = params;
|
|
1521
2106
|
let chain;
|
|
@@ -1532,7 +2117,7 @@ async function verifyPayment(params) {
|
|
|
1532
2117
|
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
1533
2118
|
}
|
|
1534
2119
|
try {
|
|
1535
|
-
const provider = new
|
|
2120
|
+
const provider = new ethers9.JsonRpcProvider(chain.rpc);
|
|
1536
2121
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1537
2122
|
if (!receipt) {
|
|
1538
2123
|
return { verified: false, error: "Transaction not found or not confirmed" };
|
|
@@ -1592,7 +2177,7 @@ async function getTransactionStatus(txHash, chain = "base") {
|
|
|
1592
2177
|
return { status: "not_found" };
|
|
1593
2178
|
}
|
|
1594
2179
|
try {
|
|
1595
|
-
const provider = new
|
|
2180
|
+
const provider = new ethers9.JsonRpcProvider(chainConfig.rpc);
|
|
1596
2181
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1597
2182
|
if (!receipt) {
|
|
1598
2183
|
const tx = await provider.getTransaction(txHash);
|
|
@@ -1629,7 +2214,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
|
|
|
1629
2214
|
} catch (e) {
|
|
1630
2215
|
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };
|
|
1631
2216
|
}
|
|
1632
|
-
const provider = new
|
|
2217
|
+
const provider = new ethers9.JsonRpcProvider(chainConfig.rpc);
|
|
1633
2218
|
try {
|
|
1634
2219
|
const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
|
|
1635
2220
|
if (!receipt) {
|
|
@@ -2316,6 +2901,8 @@ function parseStatusMarker(message) {
|
|
|
2316
2901
|
return { type: "unknown", data: { raw: content } };
|
|
2317
2902
|
}
|
|
2318
2903
|
export {
|
|
2904
|
+
AgentWallet,
|
|
2905
|
+
AllowanceWallet,
|
|
2319
2906
|
AuditLog,
|
|
2320
2907
|
BuyerTemplates,
|
|
2321
2908
|
CHAINS,
|
|
@@ -2338,9 +2925,11 @@ export {
|
|
|
2338
2925
|
formatReceiptText,
|
|
2339
2926
|
generatePaymentGuide,
|
|
2340
2927
|
generatePaymentReminder,
|
|
2928
|
+
generatePermitInstructions,
|
|
2341
2929
|
generateReceipt,
|
|
2342
2930
|
generateReceiptFromInvoice,
|
|
2343
2931
|
generateWalletGuide,
|
|
2932
|
+
getAgentAddress,
|
|
2344
2933
|
getChain,
|
|
2345
2934
|
getChainById,
|
|
2346
2935
|
getTransactionStatus,
|