moltspay 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +16 -3
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +16 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +466 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +462 -9
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +200 -1
- package/dist/wallet/index.d.ts +200 -1
- package/dist/wallet/index.js +352 -0
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +348 -0
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as ChainName, a as ChainConfig, P as PaymentAgentConfig, b as CreateInvoiceParams, I as Invoice, V as VerifyOptions, c as VerifyResult, W as WalletBalance, A as AuditAction, d as AuditEntry } from './index-CyFg9s2m.js';
|
|
2
2
|
export { E as EIP712TypedData, e as PendingTransfer, f as PermitExecuteResult, g as PermitRequest, h as PermitSignature, S as SecureWalletConfig, i as SecurityLimits, T as TransferParams, j as TransferResult } from './index-CyFg9s2m.js';
|
|
3
|
-
export { CreateWalletOptions, CreateWalletResult, PermitData, PermitWallet, PermitWalletConfig, SecureWallet, TransferWithPermitParams, TransferWithPermitResult, Wallet, WalletData, createWallet, formatPermitRequest, getWalletAddress, loadWallet, walletExists } from './wallet/index.js';
|
|
3
|
+
export { AllowanceStatus, AllowanceWallet, AllowanceWalletConfig, CreateWalletOptions, CreateWalletResult, OwnerPermit, PermitData, PermitSigner, PermitWallet, PermitWalletConfig, SecureWallet, SignPermitConfig, SignPermitParams, SignPermitResult, SpendParams, SpendResult, TransferWithPermitParams, TransferWithPermitResult, Wallet, WalletData, createWallet, formatPermitRequest, generatePermitInstructions, getWalletAddress, loadWallet, signPermit, walletExists } from './wallet/index.js';
|
|
4
4
|
export { PermitPayment } from './permit/index.js';
|
|
5
5
|
export { CreateOrderParams, MemoryOrderStore, Order, OrderManager, OrderStatus, OrderStore } from './orders/index.js';
|
|
6
6
|
export { VerifyPaymentParams, VerifyPaymentResult, getTransactionStatus, verifyPayment, waitForTransaction } from './verify/index.js';
|
|
@@ -288,6 +288,32 @@ declare const SellerTemplates: {
|
|
|
288
288
|
* 10. End
|
|
289
289
|
*/
|
|
290
290
|
end(): string;
|
|
291
|
+
/**
|
|
292
|
+
* Offer Permit payment option
|
|
293
|
+
*/
|
|
294
|
+
offerPermitPayment(params: {
|
|
295
|
+
service: string;
|
|
296
|
+
price: number;
|
|
297
|
+
sellerAddress: string;
|
|
298
|
+
chain?: ChainName;
|
|
299
|
+
invoiceId: string;
|
|
300
|
+
}): string;
|
|
301
|
+
/**
|
|
302
|
+
* Confirm Permit received, executing payment
|
|
303
|
+
*/
|
|
304
|
+
executingPermit(invoiceId: string): string;
|
|
305
|
+
/**
|
|
306
|
+
* Permit payment successful
|
|
307
|
+
*/
|
|
308
|
+
permitPaymentReceived(params: {
|
|
309
|
+
amount: number;
|
|
310
|
+
txHash: string;
|
|
311
|
+
invoiceId: string;
|
|
312
|
+
}): string;
|
|
313
|
+
/**
|
|
314
|
+
* Permit payment failed
|
|
315
|
+
*/
|
|
316
|
+
permitPaymentFailed(error: string, invoiceId: string): string;
|
|
291
317
|
};
|
|
292
318
|
declare const BuyerTemplates: {
|
|
293
319
|
/**
|
|
@@ -351,10 +377,48 @@ declare const BuyerTemplates: {
|
|
|
351
377
|
deadlineHours?: number;
|
|
352
378
|
reason?: string;
|
|
353
379
|
}): string;
|
|
380
|
+
/**
|
|
381
|
+
* Confirm willing to pay via Permit
|
|
382
|
+
*/
|
|
383
|
+
confirmPermitPayment(): string;
|
|
384
|
+
/**
|
|
385
|
+
* Send signed Permit to Seller
|
|
386
|
+
*/
|
|
387
|
+
sendPermit(params: {
|
|
388
|
+
permit: {
|
|
389
|
+
owner: string;
|
|
390
|
+
spender: string;
|
|
391
|
+
value: string;
|
|
392
|
+
deadline: number;
|
|
393
|
+
nonce: number;
|
|
394
|
+
v: number;
|
|
395
|
+
r: string;
|
|
396
|
+
s: string;
|
|
397
|
+
};
|
|
398
|
+
invoiceId: string;
|
|
399
|
+
amount: number;
|
|
400
|
+
}): string;
|
|
401
|
+
/**
|
|
402
|
+
* Simplified Permit message (compact JSON for agent parsing)
|
|
403
|
+
*/
|
|
404
|
+
sendPermitCompact(params: {
|
|
405
|
+
permit: {
|
|
406
|
+
owner: string;
|
|
407
|
+
spender: string;
|
|
408
|
+
value: string;
|
|
409
|
+
deadline: number;
|
|
410
|
+
v: number;
|
|
411
|
+
r: string;
|
|
412
|
+
s: string;
|
|
413
|
+
};
|
|
414
|
+
invoiceId: string;
|
|
415
|
+
}): string;
|
|
354
416
|
};
|
|
355
417
|
declare const StatusMarkers: {
|
|
356
418
|
walletReady: string;
|
|
357
419
|
permitReady: (amount: number) => string;
|
|
420
|
+
permitSent: (invoiceId: string, amount: number) => string;
|
|
421
|
+
awaitingPermit: (invoiceId: string) => string;
|
|
358
422
|
paymentSent: (txHash: string, amount: number) => string;
|
|
359
423
|
paymentConfirmed: (txHash: string) => string;
|
|
360
424
|
delivered: (url: string, hash?: string) => string;
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
AllowanceWallet: () => AllowanceWallet,
|
|
33
34
|
AuditLog: () => AuditLog,
|
|
34
35
|
BuyerTemplates: () => BuyerTemplates,
|
|
35
36
|
CHAINS: () => CHAINS,
|
|
@@ -38,6 +39,7 @@ __export(src_exports, {
|
|
|
38
39
|
OrderManager: () => OrderManager,
|
|
39
40
|
PaymentAgent: () => PaymentAgent,
|
|
40
41
|
PermitPayment: () => PermitPayment,
|
|
42
|
+
PermitSigner: () => PermitSigner,
|
|
41
43
|
PermitWallet: () => PermitWallet,
|
|
42
44
|
SecureWallet: () => SecureWallet,
|
|
43
45
|
SellerTemplates: () => SellerTemplates,
|
|
@@ -51,6 +53,7 @@ __export(src_exports, {
|
|
|
51
53
|
formatReceiptText: () => formatReceiptText,
|
|
52
54
|
generatePaymentGuide: () => generatePaymentGuide,
|
|
53
55
|
generatePaymentReminder: () => generatePaymentReminder,
|
|
56
|
+
generatePermitInstructions: () => generatePermitInstructions,
|
|
54
57
|
generateReceipt: () => generateReceipt,
|
|
55
58
|
generateReceiptFromInvoice: () => generateReceiptFromInvoice,
|
|
56
59
|
generateWalletGuide: () => generateWalletGuide,
|
|
@@ -62,6 +65,7 @@ __export(src_exports, {
|
|
|
62
65
|
listChains: () => listChains,
|
|
63
66
|
loadWallet: () => loadWallet,
|
|
64
67
|
parseStatusMarker: () => parseStatusMarker,
|
|
68
|
+
signPermit: () => signPermit,
|
|
65
69
|
verifyPayment: () => verifyPayment,
|
|
66
70
|
waitForTransaction: () => waitForTransaction,
|
|
67
71
|
walletExists: () => walletExists
|
|
@@ -1213,8 +1217,352 @@ After signing, send { v, r, s, deadline } to the Agent.
|
|
|
1213
1217
|
\u26A0\uFE0F Note: This authorization only allows the Agent to spend up to ${amount} USDC from your wallet. Your private key is never exposed.`;
|
|
1214
1218
|
}
|
|
1215
1219
|
|
|
1216
|
-
// src/
|
|
1220
|
+
// src/wallet/signPermit.ts
|
|
1217
1221
|
var import_ethers5 = require("ethers");
|
|
1222
|
+
async function signPermit(config, params) {
|
|
1223
|
+
const chain = config.chain || "base";
|
|
1224
|
+
const chainConfig = getChain(chain);
|
|
1225
|
+
const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
|
|
1226
|
+
if (!privateKey) {
|
|
1227
|
+
throw new Error("privateKey is required");
|
|
1228
|
+
}
|
|
1229
|
+
const rpcUrl = config.rpcUrl || chainConfig.rpc;
|
|
1230
|
+
const provider = new import_ethers5.ethers.JsonRpcProvider(rpcUrl);
|
|
1231
|
+
const wallet = new import_ethers5.ethers.Wallet(privateKey, provider);
|
|
1232
|
+
const usdcContract = new import_ethers5.ethers.Contract(chainConfig.usdc, ERC20_ABI, provider);
|
|
1233
|
+
const nonce = Number(await usdcContract.nonces(wallet.address));
|
|
1234
|
+
let deadline;
|
|
1235
|
+
if (!params.deadline) {
|
|
1236
|
+
deadline = Math.floor(Date.now() / 1e3) + 30 * 60;
|
|
1237
|
+
} else if (params.deadline < 1e6) {
|
|
1238
|
+
deadline = Math.floor(Date.now() / 1e3) + params.deadline * 60;
|
|
1239
|
+
} else {
|
|
1240
|
+
deadline = params.deadline;
|
|
1241
|
+
}
|
|
1242
|
+
const value = BigInt(Math.floor(params.amount * 1e6)).toString();
|
|
1243
|
+
const domain = {
|
|
1244
|
+
name: "USD Coin",
|
|
1245
|
+
version: "2",
|
|
1246
|
+
chainId: chainConfig.chainId,
|
|
1247
|
+
verifyingContract: chainConfig.usdc
|
|
1248
|
+
};
|
|
1249
|
+
const types = {
|
|
1250
|
+
Permit: [
|
|
1251
|
+
{ name: "owner", type: "address" },
|
|
1252
|
+
{ name: "spender", type: "address" },
|
|
1253
|
+
{ name: "value", type: "uint256" },
|
|
1254
|
+
{ name: "nonce", type: "uint256" },
|
|
1255
|
+
{ name: "deadline", type: "uint256" }
|
|
1256
|
+
]
|
|
1257
|
+
};
|
|
1258
|
+
const message = {
|
|
1259
|
+
owner: wallet.address,
|
|
1260
|
+
spender: params.spender,
|
|
1261
|
+
value,
|
|
1262
|
+
nonce,
|
|
1263
|
+
deadline
|
|
1264
|
+
};
|
|
1265
|
+
const signature = await wallet.signTypedData(domain, types, message);
|
|
1266
|
+
const sig = import_ethers5.ethers.Signature.from(signature);
|
|
1267
|
+
return {
|
|
1268
|
+
owner: wallet.address,
|
|
1269
|
+
spender: params.spender,
|
|
1270
|
+
value,
|
|
1271
|
+
deadline,
|
|
1272
|
+
nonce,
|
|
1273
|
+
v: sig.v,
|
|
1274
|
+
r: sig.r,
|
|
1275
|
+
s: sig.s
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
var PermitSigner = class {
|
|
1279
|
+
config;
|
|
1280
|
+
constructor(config) {
|
|
1281
|
+
this.config = config;
|
|
1282
|
+
}
|
|
1283
|
+
async sign(params) {
|
|
1284
|
+
return signPermit(this.config, params);
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// src/wallet/AllowanceWallet.ts
|
|
1289
|
+
var import_ethers6 = require("ethers");
|
|
1290
|
+
var PERMIT_ABI2 = [
|
|
1291
|
+
...ERC20_ABI,
|
|
1292
|
+
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
|
|
1293
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
1294
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1295
|
+
"function nonces(address owner) view returns (uint256)"
|
|
1296
|
+
];
|
|
1297
|
+
var AllowanceWallet = class {
|
|
1298
|
+
chain;
|
|
1299
|
+
chainConfig;
|
|
1300
|
+
address;
|
|
1301
|
+
// Agent's address
|
|
1302
|
+
wallet;
|
|
1303
|
+
provider;
|
|
1304
|
+
usdcContract;
|
|
1305
|
+
/** Stored permits from owners */
|
|
1306
|
+
permits = /* @__PURE__ */ new Map();
|
|
1307
|
+
constructor(config) {
|
|
1308
|
+
this.chain = config.chain || "base_sepolia";
|
|
1309
|
+
this.chainConfig = getChain(this.chain);
|
|
1310
|
+
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1311
|
+
this.provider = new import_ethers6.ethers.JsonRpcProvider(rpcUrl);
|
|
1312
|
+
this.wallet = new import_ethers6.ethers.Wallet(config.privateKey, this.provider);
|
|
1313
|
+
this.address = this.wallet.address;
|
|
1314
|
+
this.usdcContract = new import_ethers6.ethers.Contract(
|
|
1315
|
+
this.chainConfig.usdc,
|
|
1316
|
+
PERMIT_ABI2,
|
|
1317
|
+
this.wallet
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Store a Permit received from Owner
|
|
1322
|
+
* Call this when Owner sends you a signed Permit
|
|
1323
|
+
*/
|
|
1324
|
+
storePermit(permit) {
|
|
1325
|
+
const ownerLower = permit.owner.toLowerCase();
|
|
1326
|
+
this.permits.set(ownerLower, permit);
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Get stored Permit for an owner
|
|
1330
|
+
*/
|
|
1331
|
+
getPermit(owner) {
|
|
1332
|
+
return this.permits.get(owner.toLowerCase());
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Check allowance status with an owner
|
|
1336
|
+
*/
|
|
1337
|
+
async checkAllowance(owner) {
|
|
1338
|
+
const ownerAddress = import_ethers6.ethers.getAddress(owner);
|
|
1339
|
+
const [allowance, ownerBalance, agentGasBalance] = await Promise.all([
|
|
1340
|
+
this.usdcContract.allowance(ownerAddress, this.address),
|
|
1341
|
+
this.usdcContract.balanceOf(ownerAddress),
|
|
1342
|
+
this.provider.getBalance(this.address)
|
|
1343
|
+
]);
|
|
1344
|
+
const allowanceNum = Number(allowance) / 1e6;
|
|
1345
|
+
const hasGas = Number(import_ethers6.ethers.formatEther(agentGasBalance)) >= 1e-4;
|
|
1346
|
+
return {
|
|
1347
|
+
owner: ownerAddress,
|
|
1348
|
+
agent: this.address,
|
|
1349
|
+
allowance: allowanceNum.toFixed(2),
|
|
1350
|
+
ownerBalance: (Number(ownerBalance) / 1e6).toFixed(2),
|
|
1351
|
+
agentGasBalance: import_ethers6.ethers.formatEther(agentGasBalance),
|
|
1352
|
+
canSpend: allowanceNum > 0 && hasGas,
|
|
1353
|
+
chain: this.chainConfig.name
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Spend from Owner's wallet using Permit allowance
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* ```typescript
|
|
1361
|
+
* const agent = new AllowanceWallet({
|
|
1362
|
+
* chain: 'base',
|
|
1363
|
+
* privateKey: process.env.AGENT_KEY // Only needs gas
|
|
1364
|
+
* });
|
|
1365
|
+
*
|
|
1366
|
+
* // Owner gave us a Permit
|
|
1367
|
+
* agent.storePermit(ownerPermit);
|
|
1368
|
+
*
|
|
1369
|
+
* // Spend to pay for a service
|
|
1370
|
+
* const result = await agent.spend({
|
|
1371
|
+
* to: '0xServiceProvider...',
|
|
1372
|
+
* amount: 2.99,
|
|
1373
|
+
* });
|
|
1374
|
+
* ```
|
|
1375
|
+
*/
|
|
1376
|
+
async spend(params) {
|
|
1377
|
+
const { to, amount, permit } = params;
|
|
1378
|
+
try {
|
|
1379
|
+
const toAddress = import_ethers6.ethers.getAddress(to);
|
|
1380
|
+
const amountWei = BigInt(Math.floor(amount * 1e6));
|
|
1381
|
+
let ownerPermit = permit;
|
|
1382
|
+
let ownerAddress;
|
|
1383
|
+
if (ownerPermit) {
|
|
1384
|
+
ownerAddress = import_ethers6.ethers.getAddress(ownerPermit.owner);
|
|
1385
|
+
this.storePermit(ownerPermit);
|
|
1386
|
+
} else {
|
|
1387
|
+
for (const [owner, p] of this.permits) {
|
|
1388
|
+
const allowance = await this.usdcContract.allowance(owner, this.address);
|
|
1389
|
+
if (BigInt(allowance) >= amountWei) {
|
|
1390
|
+
ownerAddress = import_ethers6.ethers.getAddress(owner);
|
|
1391
|
+
ownerPermit = p;
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (!ownerPermit) {
|
|
1396
|
+
return {
|
|
1397
|
+
success: false,
|
|
1398
|
+
error: "No valid permit found. Ask Owner to sign a Permit first.",
|
|
1399
|
+
from: "",
|
|
1400
|
+
to: toAddress,
|
|
1401
|
+
amount
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1406
|
+
if (BigInt(currentAllowance) < amountWei) {
|
|
1407
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1408
|
+
if (ownerPermit.deadline < now) {
|
|
1409
|
+
return {
|
|
1410
|
+
success: false,
|
|
1411
|
+
error: `Permit expired at ${new Date(ownerPermit.deadline * 1e3).toISOString()}. Ask Owner for a new Permit.`,
|
|
1412
|
+
from: ownerAddress,
|
|
1413
|
+
to: toAddress,
|
|
1414
|
+
amount
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
const currentNonce = await this.usdcContract.nonces(ownerAddress);
|
|
1418
|
+
if (Number(currentNonce) !== ownerPermit.nonce) {
|
|
1419
|
+
return {
|
|
1420
|
+
success: false,
|
|
1421
|
+
error: `Permit nonce mismatch (expected ${ownerPermit.nonce}, got ${currentNonce}). Owner may have used this permit or signed a new one.`,
|
|
1422
|
+
from: ownerAddress,
|
|
1423
|
+
to: toAddress,
|
|
1424
|
+
amount
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
console.log("[AllowanceWallet] Submitting permit on-chain...");
|
|
1428
|
+
const permitTx = await this.usdcContract.permit(
|
|
1429
|
+
ownerAddress,
|
|
1430
|
+
this.address,
|
|
1431
|
+
ownerPermit.value,
|
|
1432
|
+
ownerPermit.deadline,
|
|
1433
|
+
ownerPermit.v,
|
|
1434
|
+
ownerPermit.r,
|
|
1435
|
+
ownerPermit.s
|
|
1436
|
+
);
|
|
1437
|
+
await permitTx.wait();
|
|
1438
|
+
console.log("[AllowanceWallet] Permit submitted:", permitTx.hash);
|
|
1439
|
+
}
|
|
1440
|
+
console.log("[AllowanceWallet] Executing transferFrom...");
|
|
1441
|
+
const tx = await this.usdcContract.transferFrom(
|
|
1442
|
+
ownerAddress,
|
|
1443
|
+
toAddress,
|
|
1444
|
+
amountWei
|
|
1445
|
+
);
|
|
1446
|
+
const receipt = await tx.wait();
|
|
1447
|
+
const newAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1448
|
+
return {
|
|
1449
|
+
success: true,
|
|
1450
|
+
txHash: tx.hash,
|
|
1451
|
+
from: ownerAddress,
|
|
1452
|
+
to: toAddress,
|
|
1453
|
+
amount,
|
|
1454
|
+
remainingAllowance: (Number(newAllowance) / 1e6).toFixed(2),
|
|
1455
|
+
explorerUrl: `${this.chainConfig.explorerTx}${tx.hash}`
|
|
1456
|
+
};
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
const message = error.message;
|
|
1459
|
+
if (message.includes("ERC20InsufficientAllowance")) {
|
|
1460
|
+
return {
|
|
1461
|
+
success: false,
|
|
1462
|
+
error: "Insufficient allowance. Ask Owner to sign a new Permit with higher amount.",
|
|
1463
|
+
from: "",
|
|
1464
|
+
to,
|
|
1465
|
+
amount
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
if (message.includes("ERC20InsufficientBalance")) {
|
|
1469
|
+
return {
|
|
1470
|
+
success: false,
|
|
1471
|
+
error: "Owner's wallet has insufficient USDC balance.",
|
|
1472
|
+
from: "",
|
|
1473
|
+
to,
|
|
1474
|
+
amount
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
return {
|
|
1478
|
+
success: false,
|
|
1479
|
+
error: message,
|
|
1480
|
+
from: "",
|
|
1481
|
+
to,
|
|
1482
|
+
amount
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Get Agent's gas balance (ETH)
|
|
1488
|
+
*/
|
|
1489
|
+
async getGasBalance() {
|
|
1490
|
+
const balance = await this.provider.getBalance(this.address);
|
|
1491
|
+
return import_ethers6.ethers.formatEther(balance);
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
function generatePermitInstructions(params) {
|
|
1495
|
+
const { ownerAddress, agentAddress, amount, deadlineHours = 24, chain = "base" } = params;
|
|
1496
|
+
const chainConfig = getChain(chain);
|
|
1497
|
+
const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
|
|
1498
|
+
const value = BigInt(Math.floor(amount * 1e6)).toString();
|
|
1499
|
+
const eip712Domain = {
|
|
1500
|
+
name: "USD Coin",
|
|
1501
|
+
version: "2",
|
|
1502
|
+
chainId: chainConfig.chainId,
|
|
1503
|
+
verifyingContract: chainConfig.usdc
|
|
1504
|
+
};
|
|
1505
|
+
const typedData = {
|
|
1506
|
+
types: {
|
|
1507
|
+
EIP712Domain: [
|
|
1508
|
+
{ name: "name", type: "string" },
|
|
1509
|
+
{ name: "version", type: "string" },
|
|
1510
|
+
{ name: "chainId", type: "uint256" },
|
|
1511
|
+
{ name: "verifyingContract", type: "address" }
|
|
1512
|
+
],
|
|
1513
|
+
Permit: [
|
|
1514
|
+
{ name: "owner", type: "address" },
|
|
1515
|
+
{ name: "spender", type: "address" },
|
|
1516
|
+
{ name: "value", type: "uint256" },
|
|
1517
|
+
{ name: "nonce", type: "uint256" },
|
|
1518
|
+
{ name: "deadline", type: "uint256" }
|
|
1519
|
+
]
|
|
1520
|
+
},
|
|
1521
|
+
primaryType: "Permit",
|
|
1522
|
+
domain: eip712Domain,
|
|
1523
|
+
message: {
|
|
1524
|
+
owner: ownerAddress,
|
|
1525
|
+
spender: agentAddress,
|
|
1526
|
+
value,
|
|
1527
|
+
nonce: "<GET_FROM_USDC_CONTRACT>",
|
|
1528
|
+
// Owner needs to query this
|
|
1529
|
+
deadline
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
const instructions = `
|
|
1533
|
+
\u{1F510} **Grant USDC Spending Allowance to Your Agent**
|
|
1534
|
+
|
|
1535
|
+
Your Agent (${agentAddress}) is requesting permission to spend up to ${amount} USDC from your wallet.
|
|
1536
|
+
|
|
1537
|
+
**What this does:**
|
|
1538
|
+
- Allows your Agent to pay for services on your behalf
|
|
1539
|
+
- Your USDC stays in YOUR wallet until spent
|
|
1540
|
+
- Agent can only spend up to the authorized amount
|
|
1541
|
+
- Expires in ${deadlineHours} hours
|
|
1542
|
+
- You can revoke anytime by moving your USDC
|
|
1543
|
+
|
|
1544
|
+
**How to sign (MetaMask / any web3 wallet):**
|
|
1545
|
+
|
|
1546
|
+
1. Go to https://etherscan.io/address/${chainConfig.usdc}#readContract
|
|
1547
|
+
2. Query \`nonces(${ownerAddress})\` to get your current nonce
|
|
1548
|
+
3. Use eth_signTypedData_v4 with the data below (replace nonce)
|
|
1549
|
+
4. Send the signature {v, r, s, deadline, nonce} to your Agent
|
|
1550
|
+
|
|
1551
|
+
**Chain:** ${chainConfig.name}
|
|
1552
|
+
**USDC Contract:** ${chainConfig.usdc}
|
|
1553
|
+
|
|
1554
|
+
**EIP-712 Typed Data:**
|
|
1555
|
+
\`\`\`json
|
|
1556
|
+
${JSON.stringify(typedData, null, 2)}
|
|
1557
|
+
\`\`\`
|
|
1558
|
+
|
|
1559
|
+
\u26A0\uFE0F Never share your private key. This signature only authorizes spending, not wallet access.
|
|
1560
|
+
`;
|
|
1561
|
+
return { instructions, typedData, eip712Domain };
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// src/permit/Permit.ts
|
|
1565
|
+
var import_ethers7 = require("ethers");
|
|
1218
1566
|
var PermitPayment = class {
|
|
1219
1567
|
chain;
|
|
1220
1568
|
chainConfig;
|
|
@@ -1227,13 +1575,13 @@ var PermitPayment = class {
|
|
|
1227
1575
|
this.chainConfig = getChain(this.chain);
|
|
1228
1576
|
this.spenderAddress = config.spenderAddress || process.env.PAYMENT_AGENT_WALLET || "";
|
|
1229
1577
|
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1230
|
-
this.provider = new
|
|
1578
|
+
this.provider = new import_ethers7.ethers.JsonRpcProvider(rpcUrl);
|
|
1231
1579
|
const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
|
|
1232
1580
|
if (privateKey) {
|
|
1233
|
-
this.wallet = new
|
|
1581
|
+
this.wallet = new import_ethers7.ethers.Wallet(privateKey, this.provider);
|
|
1234
1582
|
this.spenderAddress = this.wallet.address;
|
|
1235
1583
|
}
|
|
1236
|
-
this.usdcContract = new
|
|
1584
|
+
this.usdcContract = new import_ethers7.ethers.Contract(
|
|
1237
1585
|
this.chainConfig.usdc,
|
|
1238
1586
|
ERC20_ABI,
|
|
1239
1587
|
this.wallet || this.provider
|
|
@@ -1516,8 +1864,8 @@ var OrderManager = class {
|
|
|
1516
1864
|
};
|
|
1517
1865
|
|
|
1518
1866
|
// src/verify/index.ts
|
|
1519
|
-
var
|
|
1520
|
-
var TRANSFER_EVENT_TOPIC =
|
|
1867
|
+
var import_ethers8 = require("ethers");
|
|
1868
|
+
var TRANSFER_EVENT_TOPIC = import_ethers8.ethers.id("Transfer(address,address,uint256)");
|
|
1521
1869
|
async function verifyPayment(params) {
|
|
1522
1870
|
const { txHash, expectedAmount, expectedTo } = params;
|
|
1523
1871
|
let chain;
|
|
@@ -1534,7 +1882,7 @@ async function verifyPayment(params) {
|
|
|
1534
1882
|
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
1535
1883
|
}
|
|
1536
1884
|
try {
|
|
1537
|
-
const provider = new
|
|
1885
|
+
const provider = new import_ethers8.ethers.JsonRpcProvider(chain.rpc);
|
|
1538
1886
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1539
1887
|
if (!receipt) {
|
|
1540
1888
|
return { verified: false, error: "Transaction not found or not confirmed" };
|
|
@@ -1594,7 +1942,7 @@ async function getTransactionStatus(txHash, chain = "base") {
|
|
|
1594
1942
|
return { status: "not_found" };
|
|
1595
1943
|
}
|
|
1596
1944
|
try {
|
|
1597
|
-
const provider = new
|
|
1945
|
+
const provider = new import_ethers8.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
1598
1946
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1599
1947
|
if (!receipt) {
|
|
1600
1948
|
const tx = await provider.getTransaction(txHash);
|
|
@@ -1631,7 +1979,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
|
|
|
1631
1979
|
} catch (e) {
|
|
1632
1980
|
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };
|
|
1633
1981
|
}
|
|
1634
|
-
const provider = new
|
|
1982
|
+
const provider = new import_ethers8.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
1635
1983
|
try {
|
|
1636
1984
|
const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
|
|
1637
1985
|
if (!receipt) {
|
|
@@ -2054,6 +2402,58 @@ ${receipt.delivery?.url ? `- Delivery: ${receipt.delivery.url}` : ""}
|
|
|
2054
2402
|
*/
|
|
2055
2403
|
end() {
|
|
2056
2404
|
return `Thank you for your purchase. This transaction is complete. Feel free to reach out if you need modifications or new services.`;
|
|
2405
|
+
},
|
|
2406
|
+
// ============ Permit Payment (Seller receives Permit from Client) ============
|
|
2407
|
+
/**
|
|
2408
|
+
* Offer Permit payment option
|
|
2409
|
+
*/
|
|
2410
|
+
offerPermitPayment(params) {
|
|
2411
|
+
const { service, price, sellerAddress, chain = "base", invoiceId } = params;
|
|
2412
|
+
const chainConfig = getChain(chain);
|
|
2413
|
+
return `Quote:
|
|
2414
|
+
- Service: ${service}
|
|
2415
|
+
- Price: ${price} USDC
|
|
2416
|
+
- Chain: ${chainConfig.name}
|
|
2417
|
+
- Invoice: ${invoiceId}
|
|
2418
|
+
|
|
2419
|
+
You can pay by signing a Permit authorizing my wallet to collect payment:
|
|
2420
|
+
- Spender (me): \`${sellerAddress}\`
|
|
2421
|
+
- Amount: ${price} USDC
|
|
2422
|
+
|
|
2423
|
+
Sign the Permit and send me the data. I'll then execute the collection.
|
|
2424
|
+
[status:awaiting_permit invoice=${invoiceId}]`;
|
|
2425
|
+
},
|
|
2426
|
+
/**
|
|
2427
|
+
* Confirm Permit received, executing payment
|
|
2428
|
+
*/
|
|
2429
|
+
executingPermit(invoiceId) {
|
|
2430
|
+
return `Permit received for invoice ${invoiceId}. Executing payment collection now...`;
|
|
2431
|
+
},
|
|
2432
|
+
/**
|
|
2433
|
+
* Permit payment successful
|
|
2434
|
+
*/
|
|
2435
|
+
permitPaymentReceived(params) {
|
|
2436
|
+
const { amount, txHash, invoiceId } = params;
|
|
2437
|
+
return `Payment collected successfully!
|
|
2438
|
+
- Invoice: ${invoiceId}
|
|
2439
|
+
- Amount: ${amount} USDC
|
|
2440
|
+
- Tx Hash: \`${txHash}\`
|
|
2441
|
+
|
|
2442
|
+
Starting to process your request now.
|
|
2443
|
+
[status:payment_confirmed tx=${txHash}]`;
|
|
2444
|
+
},
|
|
2445
|
+
/**
|
|
2446
|
+
* Permit payment failed
|
|
2447
|
+
*/
|
|
2448
|
+
permitPaymentFailed(error, invoiceId) {
|
|
2449
|
+
return `Failed to collect payment for invoice ${invoiceId}: ${error}
|
|
2450
|
+
|
|
2451
|
+
Please check:
|
|
2452
|
+
- Permit signature is valid
|
|
2453
|
+
- Permit hasn't expired
|
|
2454
|
+
- Your wallet has sufficient USDC balance
|
|
2455
|
+
|
|
2456
|
+
You can re-sign and send a new Permit.`;
|
|
2057
2457
|
}
|
|
2058
2458
|
};
|
|
2059
2459
|
var BuyerTemplates = {
|
|
@@ -2146,11 +2546,46 @@ ${requirements}`;
|
|
|
2146
2546
|
const { amount, agentAddress, deadlineHours = 24, reason } = params;
|
|
2147
2547
|
return `Boss, I need a USDC spending allowance (Permit) for ${reason || "purchasing services"}.
|
|
2148
2548
|
Please authorize ${amount} USDC to my wallet ${agentAddress}, valid for ${deadlineHours} hours.`;
|
|
2549
|
+
},
|
|
2550
|
+
// ============ Permit Payment (Client signs Permit to Seller) ============
|
|
2551
|
+
/**
|
|
2552
|
+
* Confirm willing to pay via Permit
|
|
2553
|
+
*/
|
|
2554
|
+
confirmPermitPayment() {
|
|
2555
|
+
return `Confirmed. I'll sign a Permit authorizing you to collect the payment.`;
|
|
2556
|
+
},
|
|
2557
|
+
/**
|
|
2558
|
+
* Send signed Permit to Seller
|
|
2559
|
+
*/
|
|
2560
|
+
sendPermit(params) {
|
|
2561
|
+
const { permit, invoiceId, amount } = params;
|
|
2562
|
+
return `Payment authorized via Permit.
|
|
2563
|
+
|
|
2564
|
+
Invoice: ${invoiceId}
|
|
2565
|
+
Amount: ${amount} USDC
|
|
2566
|
+
|
|
2567
|
+
Permit Data:
|
|
2568
|
+
\`\`\`json
|
|
2569
|
+
${JSON.stringify(permit, null, 2)}
|
|
2570
|
+
\`\`\`
|
|
2571
|
+
|
|
2572
|
+
[status:permit_sent invoice=${invoiceId} amount=${amount}]`;
|
|
2573
|
+
},
|
|
2574
|
+
/**
|
|
2575
|
+
* Simplified Permit message (compact JSON for agent parsing)
|
|
2576
|
+
*/
|
|
2577
|
+
sendPermitCompact(params) {
|
|
2578
|
+
const { permit, invoiceId } = params;
|
|
2579
|
+
return `Permit signed for invoice ${invoiceId}:
|
|
2580
|
+
${JSON.stringify(permit)}
|
|
2581
|
+
[status:permit_sent invoice=${invoiceId}]`;
|
|
2149
2582
|
}
|
|
2150
2583
|
};
|
|
2151
2584
|
var StatusMarkers = {
|
|
2152
2585
|
walletReady: "[status:wallet_ready]",
|
|
2153
2586
|
permitReady: (amount) => `[status:permit_ready USDC=${amount}]`,
|
|
2587
|
+
permitSent: (invoiceId, amount) => `[status:permit_sent invoice=${invoiceId} amount=${amount}]`,
|
|
2588
|
+
awaitingPermit: (invoiceId) => `[status:awaiting_permit invoice=${invoiceId}]`,
|
|
2154
2589
|
paymentSent: (txHash, amount) => `[status:payment_sent tx=${txHash} amount=${amount} USDC]`,
|
|
2155
2590
|
paymentConfirmed: (txHash) => `[status:payment_confirmed tx=${txHash}]`,
|
|
2156
2591
|
delivered: (url, hash) => `[status:delivered url=${url}${hash ? ` hash=${hash}` : ""}]`,
|
|
@@ -2170,6 +2605,24 @@ function parseStatusMarker(message) {
|
|
|
2170
2605
|
data: { amount: amountMatch?.[1] || "0" }
|
|
2171
2606
|
};
|
|
2172
2607
|
}
|
|
2608
|
+
if (content.startsWith("permit_sent")) {
|
|
2609
|
+
const invoiceMatch = content.match(/invoice=(\S+)/);
|
|
2610
|
+
const amountMatch = content.match(/amount=(\d+(?:\.\d+)?)/);
|
|
2611
|
+
return {
|
|
2612
|
+
type: "permit_sent",
|
|
2613
|
+
data: {
|
|
2614
|
+
invoiceId: invoiceMatch?.[1] || "",
|
|
2615
|
+
amount: amountMatch?.[1] || "0"
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
if (content.startsWith("awaiting_permit")) {
|
|
2620
|
+
const invoiceMatch = content.match(/invoice=(\S+)/);
|
|
2621
|
+
return {
|
|
2622
|
+
type: "awaiting_permit",
|
|
2623
|
+
data: { invoiceId: invoiceMatch?.[1] || "" }
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2173
2626
|
if (content.startsWith("payment_sent")) {
|
|
2174
2627
|
const txMatch = content.match(/tx=(\S+)/);
|
|
2175
2628
|
const amountMatch = content.match(/amount=(\d+(?:\.\d+)?)/);
|
|
@@ -2214,6 +2667,7 @@ function parseStatusMarker(message) {
|
|
|
2214
2667
|
}
|
|
2215
2668
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2216
2669
|
0 && (module.exports = {
|
|
2670
|
+
AllowanceWallet,
|
|
2217
2671
|
AuditLog,
|
|
2218
2672
|
BuyerTemplates,
|
|
2219
2673
|
CHAINS,
|
|
@@ -2222,6 +2676,7 @@ function parseStatusMarker(message) {
|
|
|
2222
2676
|
OrderManager,
|
|
2223
2677
|
PaymentAgent,
|
|
2224
2678
|
PermitPayment,
|
|
2679
|
+
PermitSigner,
|
|
2225
2680
|
PermitWallet,
|
|
2226
2681
|
SecureWallet,
|
|
2227
2682
|
SellerTemplates,
|
|
@@ -2235,6 +2690,7 @@ function parseStatusMarker(message) {
|
|
|
2235
2690
|
formatReceiptText,
|
|
2236
2691
|
generatePaymentGuide,
|
|
2237
2692
|
generatePaymentReminder,
|
|
2693
|
+
generatePermitInstructions,
|
|
2238
2694
|
generateReceipt,
|
|
2239
2695
|
generateReceiptFromInvoice,
|
|
2240
2696
|
generateWalletGuide,
|
|
@@ -2246,6 +2702,7 @@ function parseStatusMarker(message) {
|
|
|
2246
2702
|
listChains,
|
|
2247
2703
|
loadWallet,
|
|
2248
2704
|
parseStatusMarker,
|
|
2705
|
+
signPermit,
|
|
2249
2706
|
verifyPayment,
|
|
2250
2707
|
waitForTransaction,
|
|
2251
2708
|
walletExists
|