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.mjs
CHANGED
|
@@ -1143,8 +1143,352 @@ After signing, send { v, r, s, deadline } to the Agent.
|
|
|
1143
1143
|
\u26A0\uFE0F Note: This authorization only allows the Agent to spend up to ${amount} USDC from your wallet. Your private key is never exposed.`;
|
|
1144
1144
|
}
|
|
1145
1145
|
|
|
1146
|
-
// src/
|
|
1146
|
+
// src/wallet/signPermit.ts
|
|
1147
1147
|
import { ethers as ethers5 } from "ethers";
|
|
1148
|
+
async function signPermit(config, params) {
|
|
1149
|
+
const chain = config.chain || "base";
|
|
1150
|
+
const chainConfig = getChain(chain);
|
|
1151
|
+
const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
|
|
1152
|
+
if (!privateKey) {
|
|
1153
|
+
throw new Error("privateKey is required");
|
|
1154
|
+
}
|
|
1155
|
+
const rpcUrl = config.rpcUrl || chainConfig.rpc;
|
|
1156
|
+
const provider = new ethers5.JsonRpcProvider(rpcUrl);
|
|
1157
|
+
const wallet = new ethers5.Wallet(privateKey, provider);
|
|
1158
|
+
const usdcContract = new ethers5.Contract(chainConfig.usdc, ERC20_ABI, provider);
|
|
1159
|
+
const nonce = Number(await usdcContract.nonces(wallet.address));
|
|
1160
|
+
let deadline;
|
|
1161
|
+
if (!params.deadline) {
|
|
1162
|
+
deadline = Math.floor(Date.now() / 1e3) + 30 * 60;
|
|
1163
|
+
} else if (params.deadline < 1e6) {
|
|
1164
|
+
deadline = Math.floor(Date.now() / 1e3) + params.deadline * 60;
|
|
1165
|
+
} else {
|
|
1166
|
+
deadline = params.deadline;
|
|
1167
|
+
}
|
|
1168
|
+
const value = BigInt(Math.floor(params.amount * 1e6)).toString();
|
|
1169
|
+
const domain = {
|
|
1170
|
+
name: "USD Coin",
|
|
1171
|
+
version: "2",
|
|
1172
|
+
chainId: chainConfig.chainId,
|
|
1173
|
+
verifyingContract: chainConfig.usdc
|
|
1174
|
+
};
|
|
1175
|
+
const types = {
|
|
1176
|
+
Permit: [
|
|
1177
|
+
{ name: "owner", type: "address" },
|
|
1178
|
+
{ name: "spender", type: "address" },
|
|
1179
|
+
{ name: "value", type: "uint256" },
|
|
1180
|
+
{ name: "nonce", type: "uint256" },
|
|
1181
|
+
{ name: "deadline", type: "uint256" }
|
|
1182
|
+
]
|
|
1183
|
+
};
|
|
1184
|
+
const message = {
|
|
1185
|
+
owner: wallet.address,
|
|
1186
|
+
spender: params.spender,
|
|
1187
|
+
value,
|
|
1188
|
+
nonce,
|
|
1189
|
+
deadline
|
|
1190
|
+
};
|
|
1191
|
+
const signature = await wallet.signTypedData(domain, types, message);
|
|
1192
|
+
const sig = ethers5.Signature.from(signature);
|
|
1193
|
+
return {
|
|
1194
|
+
owner: wallet.address,
|
|
1195
|
+
spender: params.spender,
|
|
1196
|
+
value,
|
|
1197
|
+
deadline,
|
|
1198
|
+
nonce,
|
|
1199
|
+
v: sig.v,
|
|
1200
|
+
r: sig.r,
|
|
1201
|
+
s: sig.s
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
var PermitSigner = class {
|
|
1205
|
+
config;
|
|
1206
|
+
constructor(config) {
|
|
1207
|
+
this.config = config;
|
|
1208
|
+
}
|
|
1209
|
+
async sign(params) {
|
|
1210
|
+
return signPermit(this.config, params);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
// src/wallet/AllowanceWallet.ts
|
|
1215
|
+
import { ethers as ethers6 } from "ethers";
|
|
1216
|
+
var PERMIT_ABI2 = [
|
|
1217
|
+
...ERC20_ABI,
|
|
1218
|
+
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
|
|
1219
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
1220
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1221
|
+
"function nonces(address owner) view returns (uint256)"
|
|
1222
|
+
];
|
|
1223
|
+
var AllowanceWallet = class {
|
|
1224
|
+
chain;
|
|
1225
|
+
chainConfig;
|
|
1226
|
+
address;
|
|
1227
|
+
// Agent's address
|
|
1228
|
+
wallet;
|
|
1229
|
+
provider;
|
|
1230
|
+
usdcContract;
|
|
1231
|
+
/** Stored permits from owners */
|
|
1232
|
+
permits = /* @__PURE__ */ new Map();
|
|
1233
|
+
constructor(config) {
|
|
1234
|
+
this.chain = config.chain || "base_sepolia";
|
|
1235
|
+
this.chainConfig = getChain(this.chain);
|
|
1236
|
+
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1237
|
+
this.provider = new ethers6.JsonRpcProvider(rpcUrl);
|
|
1238
|
+
this.wallet = new ethers6.Wallet(config.privateKey, this.provider);
|
|
1239
|
+
this.address = this.wallet.address;
|
|
1240
|
+
this.usdcContract = new ethers6.Contract(
|
|
1241
|
+
this.chainConfig.usdc,
|
|
1242
|
+
PERMIT_ABI2,
|
|
1243
|
+
this.wallet
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Store a Permit received from Owner
|
|
1248
|
+
* Call this when Owner sends you a signed Permit
|
|
1249
|
+
*/
|
|
1250
|
+
storePermit(permit) {
|
|
1251
|
+
const ownerLower = permit.owner.toLowerCase();
|
|
1252
|
+
this.permits.set(ownerLower, permit);
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Get stored Permit for an owner
|
|
1256
|
+
*/
|
|
1257
|
+
getPermit(owner) {
|
|
1258
|
+
return this.permits.get(owner.toLowerCase());
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Check allowance status with an owner
|
|
1262
|
+
*/
|
|
1263
|
+
async checkAllowance(owner) {
|
|
1264
|
+
const ownerAddress = ethers6.getAddress(owner);
|
|
1265
|
+
const [allowance, ownerBalance, agentGasBalance] = await Promise.all([
|
|
1266
|
+
this.usdcContract.allowance(ownerAddress, this.address),
|
|
1267
|
+
this.usdcContract.balanceOf(ownerAddress),
|
|
1268
|
+
this.provider.getBalance(this.address)
|
|
1269
|
+
]);
|
|
1270
|
+
const allowanceNum = Number(allowance) / 1e6;
|
|
1271
|
+
const hasGas = Number(ethers6.formatEther(agentGasBalance)) >= 1e-4;
|
|
1272
|
+
return {
|
|
1273
|
+
owner: ownerAddress,
|
|
1274
|
+
agent: this.address,
|
|
1275
|
+
allowance: allowanceNum.toFixed(2),
|
|
1276
|
+
ownerBalance: (Number(ownerBalance) / 1e6).toFixed(2),
|
|
1277
|
+
agentGasBalance: ethers6.formatEther(agentGasBalance),
|
|
1278
|
+
canSpend: allowanceNum > 0 && hasGas,
|
|
1279
|
+
chain: this.chainConfig.name
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Spend from Owner's wallet using Permit allowance
|
|
1284
|
+
*
|
|
1285
|
+
* @example
|
|
1286
|
+
* ```typescript
|
|
1287
|
+
* const agent = new AllowanceWallet({
|
|
1288
|
+
* chain: 'base',
|
|
1289
|
+
* privateKey: process.env.AGENT_KEY // Only needs gas
|
|
1290
|
+
* });
|
|
1291
|
+
*
|
|
1292
|
+
* // Owner gave us a Permit
|
|
1293
|
+
* agent.storePermit(ownerPermit);
|
|
1294
|
+
*
|
|
1295
|
+
* // Spend to pay for a service
|
|
1296
|
+
* const result = await agent.spend({
|
|
1297
|
+
* to: '0xServiceProvider...',
|
|
1298
|
+
* amount: 2.99,
|
|
1299
|
+
* });
|
|
1300
|
+
* ```
|
|
1301
|
+
*/
|
|
1302
|
+
async spend(params) {
|
|
1303
|
+
const { to, amount, permit } = params;
|
|
1304
|
+
try {
|
|
1305
|
+
const toAddress = ethers6.getAddress(to);
|
|
1306
|
+
const amountWei = BigInt(Math.floor(amount * 1e6));
|
|
1307
|
+
let ownerPermit = permit;
|
|
1308
|
+
let ownerAddress;
|
|
1309
|
+
if (ownerPermit) {
|
|
1310
|
+
ownerAddress = ethers6.getAddress(ownerPermit.owner);
|
|
1311
|
+
this.storePermit(ownerPermit);
|
|
1312
|
+
} else {
|
|
1313
|
+
for (const [owner, p] of this.permits) {
|
|
1314
|
+
const allowance = await this.usdcContract.allowance(owner, this.address);
|
|
1315
|
+
if (BigInt(allowance) >= amountWei) {
|
|
1316
|
+
ownerAddress = ethers6.getAddress(owner);
|
|
1317
|
+
ownerPermit = p;
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (!ownerPermit) {
|
|
1322
|
+
return {
|
|
1323
|
+
success: false,
|
|
1324
|
+
error: "No valid permit found. Ask Owner to sign a Permit first.",
|
|
1325
|
+
from: "",
|
|
1326
|
+
to: toAddress,
|
|
1327
|
+
amount
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1332
|
+
if (BigInt(currentAllowance) < amountWei) {
|
|
1333
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1334
|
+
if (ownerPermit.deadline < now) {
|
|
1335
|
+
return {
|
|
1336
|
+
success: false,
|
|
1337
|
+
error: `Permit expired at ${new Date(ownerPermit.deadline * 1e3).toISOString()}. Ask Owner for a new Permit.`,
|
|
1338
|
+
from: ownerAddress,
|
|
1339
|
+
to: toAddress,
|
|
1340
|
+
amount
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
const currentNonce = await this.usdcContract.nonces(ownerAddress);
|
|
1344
|
+
if (Number(currentNonce) !== ownerPermit.nonce) {
|
|
1345
|
+
return {
|
|
1346
|
+
success: false,
|
|
1347
|
+
error: `Permit nonce mismatch (expected ${ownerPermit.nonce}, got ${currentNonce}). Owner may have used this permit or signed a new one.`,
|
|
1348
|
+
from: ownerAddress,
|
|
1349
|
+
to: toAddress,
|
|
1350
|
+
amount
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
console.log("[AllowanceWallet] Submitting permit on-chain...");
|
|
1354
|
+
const permitTx = await this.usdcContract.permit(
|
|
1355
|
+
ownerAddress,
|
|
1356
|
+
this.address,
|
|
1357
|
+
ownerPermit.value,
|
|
1358
|
+
ownerPermit.deadline,
|
|
1359
|
+
ownerPermit.v,
|
|
1360
|
+
ownerPermit.r,
|
|
1361
|
+
ownerPermit.s
|
|
1362
|
+
);
|
|
1363
|
+
await permitTx.wait();
|
|
1364
|
+
console.log("[AllowanceWallet] Permit submitted:", permitTx.hash);
|
|
1365
|
+
}
|
|
1366
|
+
console.log("[AllowanceWallet] Executing transferFrom...");
|
|
1367
|
+
const tx = await this.usdcContract.transferFrom(
|
|
1368
|
+
ownerAddress,
|
|
1369
|
+
toAddress,
|
|
1370
|
+
amountWei
|
|
1371
|
+
);
|
|
1372
|
+
const receipt = await tx.wait();
|
|
1373
|
+
const newAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
|
|
1374
|
+
return {
|
|
1375
|
+
success: true,
|
|
1376
|
+
txHash: tx.hash,
|
|
1377
|
+
from: ownerAddress,
|
|
1378
|
+
to: toAddress,
|
|
1379
|
+
amount,
|
|
1380
|
+
remainingAllowance: (Number(newAllowance) / 1e6).toFixed(2),
|
|
1381
|
+
explorerUrl: `${this.chainConfig.explorerTx}${tx.hash}`
|
|
1382
|
+
};
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
const message = error.message;
|
|
1385
|
+
if (message.includes("ERC20InsufficientAllowance")) {
|
|
1386
|
+
return {
|
|
1387
|
+
success: false,
|
|
1388
|
+
error: "Insufficient allowance. Ask Owner to sign a new Permit with higher amount.",
|
|
1389
|
+
from: "",
|
|
1390
|
+
to,
|
|
1391
|
+
amount
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
if (message.includes("ERC20InsufficientBalance")) {
|
|
1395
|
+
return {
|
|
1396
|
+
success: false,
|
|
1397
|
+
error: "Owner's wallet has insufficient USDC balance.",
|
|
1398
|
+
from: "",
|
|
1399
|
+
to,
|
|
1400
|
+
amount
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
success: false,
|
|
1405
|
+
error: message,
|
|
1406
|
+
from: "",
|
|
1407
|
+
to,
|
|
1408
|
+
amount
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Get Agent's gas balance (ETH)
|
|
1414
|
+
*/
|
|
1415
|
+
async getGasBalance() {
|
|
1416
|
+
const balance = await this.provider.getBalance(this.address);
|
|
1417
|
+
return ethers6.formatEther(balance);
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
function generatePermitInstructions(params) {
|
|
1421
|
+
const { ownerAddress, agentAddress, amount, deadlineHours = 24, chain = "base" } = params;
|
|
1422
|
+
const chainConfig = getChain(chain);
|
|
1423
|
+
const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
|
|
1424
|
+
const value = BigInt(Math.floor(amount * 1e6)).toString();
|
|
1425
|
+
const eip712Domain = {
|
|
1426
|
+
name: "USD Coin",
|
|
1427
|
+
version: "2",
|
|
1428
|
+
chainId: chainConfig.chainId,
|
|
1429
|
+
verifyingContract: chainConfig.usdc
|
|
1430
|
+
};
|
|
1431
|
+
const typedData = {
|
|
1432
|
+
types: {
|
|
1433
|
+
EIP712Domain: [
|
|
1434
|
+
{ name: "name", type: "string" },
|
|
1435
|
+
{ name: "version", type: "string" },
|
|
1436
|
+
{ name: "chainId", type: "uint256" },
|
|
1437
|
+
{ name: "verifyingContract", type: "address" }
|
|
1438
|
+
],
|
|
1439
|
+
Permit: [
|
|
1440
|
+
{ name: "owner", type: "address" },
|
|
1441
|
+
{ name: "spender", type: "address" },
|
|
1442
|
+
{ name: "value", type: "uint256" },
|
|
1443
|
+
{ name: "nonce", type: "uint256" },
|
|
1444
|
+
{ name: "deadline", type: "uint256" }
|
|
1445
|
+
]
|
|
1446
|
+
},
|
|
1447
|
+
primaryType: "Permit",
|
|
1448
|
+
domain: eip712Domain,
|
|
1449
|
+
message: {
|
|
1450
|
+
owner: ownerAddress,
|
|
1451
|
+
spender: agentAddress,
|
|
1452
|
+
value,
|
|
1453
|
+
nonce: "<GET_FROM_USDC_CONTRACT>",
|
|
1454
|
+
// Owner needs to query this
|
|
1455
|
+
deadline
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
const instructions = `
|
|
1459
|
+
\u{1F510} **Grant USDC Spending Allowance to Your Agent**
|
|
1460
|
+
|
|
1461
|
+
Your Agent (${agentAddress}) is requesting permission to spend up to ${amount} USDC from your wallet.
|
|
1462
|
+
|
|
1463
|
+
**What this does:**
|
|
1464
|
+
- Allows your Agent to pay for services on your behalf
|
|
1465
|
+
- Your USDC stays in YOUR wallet until spent
|
|
1466
|
+
- Agent can only spend up to the authorized amount
|
|
1467
|
+
- Expires in ${deadlineHours} hours
|
|
1468
|
+
- You can revoke anytime by moving your USDC
|
|
1469
|
+
|
|
1470
|
+
**How to sign (MetaMask / any web3 wallet):**
|
|
1471
|
+
|
|
1472
|
+
1. Go to https://etherscan.io/address/${chainConfig.usdc}#readContract
|
|
1473
|
+
2. Query \`nonces(${ownerAddress})\` to get your current nonce
|
|
1474
|
+
3. Use eth_signTypedData_v4 with the data below (replace nonce)
|
|
1475
|
+
4. Send the signature {v, r, s, deadline, nonce} to your Agent
|
|
1476
|
+
|
|
1477
|
+
**Chain:** ${chainConfig.name}
|
|
1478
|
+
**USDC Contract:** ${chainConfig.usdc}
|
|
1479
|
+
|
|
1480
|
+
**EIP-712 Typed Data:**
|
|
1481
|
+
\`\`\`json
|
|
1482
|
+
${JSON.stringify(typedData, null, 2)}
|
|
1483
|
+
\`\`\`
|
|
1484
|
+
|
|
1485
|
+
\u26A0\uFE0F Never share your private key. This signature only authorizes spending, not wallet access.
|
|
1486
|
+
`;
|
|
1487
|
+
return { instructions, typedData, eip712Domain };
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/permit/Permit.ts
|
|
1491
|
+
import { ethers as ethers7 } from "ethers";
|
|
1148
1492
|
var PermitPayment = class {
|
|
1149
1493
|
chain;
|
|
1150
1494
|
chainConfig;
|
|
@@ -1157,13 +1501,13 @@ var PermitPayment = class {
|
|
|
1157
1501
|
this.chainConfig = getChain(this.chain);
|
|
1158
1502
|
this.spenderAddress = config.spenderAddress || process.env.PAYMENT_AGENT_WALLET || "";
|
|
1159
1503
|
const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
|
|
1160
|
-
this.provider = new
|
|
1504
|
+
this.provider = new ethers7.JsonRpcProvider(rpcUrl);
|
|
1161
1505
|
const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
|
|
1162
1506
|
if (privateKey) {
|
|
1163
|
-
this.wallet = new
|
|
1507
|
+
this.wallet = new ethers7.Wallet(privateKey, this.provider);
|
|
1164
1508
|
this.spenderAddress = this.wallet.address;
|
|
1165
1509
|
}
|
|
1166
|
-
this.usdcContract = new
|
|
1510
|
+
this.usdcContract = new ethers7.Contract(
|
|
1167
1511
|
this.chainConfig.usdc,
|
|
1168
1512
|
ERC20_ABI,
|
|
1169
1513
|
this.wallet || this.provider
|
|
@@ -1446,8 +1790,8 @@ var OrderManager = class {
|
|
|
1446
1790
|
};
|
|
1447
1791
|
|
|
1448
1792
|
// src/verify/index.ts
|
|
1449
|
-
import { ethers as
|
|
1450
|
-
var TRANSFER_EVENT_TOPIC =
|
|
1793
|
+
import { ethers as ethers8 } from "ethers";
|
|
1794
|
+
var TRANSFER_EVENT_TOPIC = ethers8.id("Transfer(address,address,uint256)");
|
|
1451
1795
|
async function verifyPayment(params) {
|
|
1452
1796
|
const { txHash, expectedAmount, expectedTo } = params;
|
|
1453
1797
|
let chain;
|
|
@@ -1464,7 +1808,7 @@ async function verifyPayment(params) {
|
|
|
1464
1808
|
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
1465
1809
|
}
|
|
1466
1810
|
try {
|
|
1467
|
-
const provider = new
|
|
1811
|
+
const provider = new ethers8.JsonRpcProvider(chain.rpc);
|
|
1468
1812
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1469
1813
|
if (!receipt) {
|
|
1470
1814
|
return { verified: false, error: "Transaction not found or not confirmed" };
|
|
@@ -1524,7 +1868,7 @@ async function getTransactionStatus(txHash, chain = "base") {
|
|
|
1524
1868
|
return { status: "not_found" };
|
|
1525
1869
|
}
|
|
1526
1870
|
try {
|
|
1527
|
-
const provider = new
|
|
1871
|
+
const provider = new ethers8.JsonRpcProvider(chainConfig.rpc);
|
|
1528
1872
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1529
1873
|
if (!receipt) {
|
|
1530
1874
|
const tx = await provider.getTransaction(txHash);
|
|
@@ -1561,7 +1905,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
|
|
|
1561
1905
|
} catch (e) {
|
|
1562
1906
|
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };
|
|
1563
1907
|
}
|
|
1564
|
-
const provider = new
|
|
1908
|
+
const provider = new ethers8.JsonRpcProvider(chainConfig.rpc);
|
|
1565
1909
|
try {
|
|
1566
1910
|
const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
|
|
1567
1911
|
if (!receipt) {
|
|
@@ -1984,6 +2328,58 @@ ${receipt.delivery?.url ? `- Delivery: ${receipt.delivery.url}` : ""}
|
|
|
1984
2328
|
*/
|
|
1985
2329
|
end() {
|
|
1986
2330
|
return `Thank you for your purchase. This transaction is complete. Feel free to reach out if you need modifications or new services.`;
|
|
2331
|
+
},
|
|
2332
|
+
// ============ Permit Payment (Seller receives Permit from Client) ============
|
|
2333
|
+
/**
|
|
2334
|
+
* Offer Permit payment option
|
|
2335
|
+
*/
|
|
2336
|
+
offerPermitPayment(params) {
|
|
2337
|
+
const { service, price, sellerAddress, chain = "base", invoiceId } = params;
|
|
2338
|
+
const chainConfig = getChain(chain);
|
|
2339
|
+
return `Quote:
|
|
2340
|
+
- Service: ${service}
|
|
2341
|
+
- Price: ${price} USDC
|
|
2342
|
+
- Chain: ${chainConfig.name}
|
|
2343
|
+
- Invoice: ${invoiceId}
|
|
2344
|
+
|
|
2345
|
+
You can pay by signing a Permit authorizing my wallet to collect payment:
|
|
2346
|
+
- Spender (me): \`${sellerAddress}\`
|
|
2347
|
+
- Amount: ${price} USDC
|
|
2348
|
+
|
|
2349
|
+
Sign the Permit and send me the data. I'll then execute the collection.
|
|
2350
|
+
[status:awaiting_permit invoice=${invoiceId}]`;
|
|
2351
|
+
},
|
|
2352
|
+
/**
|
|
2353
|
+
* Confirm Permit received, executing payment
|
|
2354
|
+
*/
|
|
2355
|
+
executingPermit(invoiceId) {
|
|
2356
|
+
return `Permit received for invoice ${invoiceId}. Executing payment collection now...`;
|
|
2357
|
+
},
|
|
2358
|
+
/**
|
|
2359
|
+
* Permit payment successful
|
|
2360
|
+
*/
|
|
2361
|
+
permitPaymentReceived(params) {
|
|
2362
|
+
const { amount, txHash, invoiceId } = params;
|
|
2363
|
+
return `Payment collected successfully!
|
|
2364
|
+
- Invoice: ${invoiceId}
|
|
2365
|
+
- Amount: ${amount} USDC
|
|
2366
|
+
- Tx Hash: \`${txHash}\`
|
|
2367
|
+
|
|
2368
|
+
Starting to process your request now.
|
|
2369
|
+
[status:payment_confirmed tx=${txHash}]`;
|
|
2370
|
+
},
|
|
2371
|
+
/**
|
|
2372
|
+
* Permit payment failed
|
|
2373
|
+
*/
|
|
2374
|
+
permitPaymentFailed(error, invoiceId) {
|
|
2375
|
+
return `Failed to collect payment for invoice ${invoiceId}: ${error}
|
|
2376
|
+
|
|
2377
|
+
Please check:
|
|
2378
|
+
- Permit signature is valid
|
|
2379
|
+
- Permit hasn't expired
|
|
2380
|
+
- Your wallet has sufficient USDC balance
|
|
2381
|
+
|
|
2382
|
+
You can re-sign and send a new Permit.`;
|
|
1987
2383
|
}
|
|
1988
2384
|
};
|
|
1989
2385
|
var BuyerTemplates = {
|
|
@@ -2076,11 +2472,46 @@ ${requirements}`;
|
|
|
2076
2472
|
const { amount, agentAddress, deadlineHours = 24, reason } = params;
|
|
2077
2473
|
return `Boss, I need a USDC spending allowance (Permit) for ${reason || "purchasing services"}.
|
|
2078
2474
|
Please authorize ${amount} USDC to my wallet ${agentAddress}, valid for ${deadlineHours} hours.`;
|
|
2475
|
+
},
|
|
2476
|
+
// ============ Permit Payment (Client signs Permit to Seller) ============
|
|
2477
|
+
/**
|
|
2478
|
+
* Confirm willing to pay via Permit
|
|
2479
|
+
*/
|
|
2480
|
+
confirmPermitPayment() {
|
|
2481
|
+
return `Confirmed. I'll sign a Permit authorizing you to collect the payment.`;
|
|
2482
|
+
},
|
|
2483
|
+
/**
|
|
2484
|
+
* Send signed Permit to Seller
|
|
2485
|
+
*/
|
|
2486
|
+
sendPermit(params) {
|
|
2487
|
+
const { permit, invoiceId, amount } = params;
|
|
2488
|
+
return `Payment authorized via Permit.
|
|
2489
|
+
|
|
2490
|
+
Invoice: ${invoiceId}
|
|
2491
|
+
Amount: ${amount} USDC
|
|
2492
|
+
|
|
2493
|
+
Permit Data:
|
|
2494
|
+
\`\`\`json
|
|
2495
|
+
${JSON.stringify(permit, null, 2)}
|
|
2496
|
+
\`\`\`
|
|
2497
|
+
|
|
2498
|
+
[status:permit_sent invoice=${invoiceId} amount=${amount}]`;
|
|
2499
|
+
},
|
|
2500
|
+
/**
|
|
2501
|
+
* Simplified Permit message (compact JSON for agent parsing)
|
|
2502
|
+
*/
|
|
2503
|
+
sendPermitCompact(params) {
|
|
2504
|
+
const { permit, invoiceId } = params;
|
|
2505
|
+
return `Permit signed for invoice ${invoiceId}:
|
|
2506
|
+
${JSON.stringify(permit)}
|
|
2507
|
+
[status:permit_sent invoice=${invoiceId}]`;
|
|
2079
2508
|
}
|
|
2080
2509
|
};
|
|
2081
2510
|
var StatusMarkers = {
|
|
2082
2511
|
walletReady: "[status:wallet_ready]",
|
|
2083
2512
|
permitReady: (amount) => `[status:permit_ready USDC=${amount}]`,
|
|
2513
|
+
permitSent: (invoiceId, amount) => `[status:permit_sent invoice=${invoiceId} amount=${amount}]`,
|
|
2514
|
+
awaitingPermit: (invoiceId) => `[status:awaiting_permit invoice=${invoiceId}]`,
|
|
2084
2515
|
paymentSent: (txHash, amount) => `[status:payment_sent tx=${txHash} amount=${amount} USDC]`,
|
|
2085
2516
|
paymentConfirmed: (txHash) => `[status:payment_confirmed tx=${txHash}]`,
|
|
2086
2517
|
delivered: (url, hash) => `[status:delivered url=${url}${hash ? ` hash=${hash}` : ""}]`,
|
|
@@ -2100,6 +2531,24 @@ function parseStatusMarker(message) {
|
|
|
2100
2531
|
data: { amount: amountMatch?.[1] || "0" }
|
|
2101
2532
|
};
|
|
2102
2533
|
}
|
|
2534
|
+
if (content.startsWith("permit_sent")) {
|
|
2535
|
+
const invoiceMatch = content.match(/invoice=(\S+)/);
|
|
2536
|
+
const amountMatch = content.match(/amount=(\d+(?:\.\d+)?)/);
|
|
2537
|
+
return {
|
|
2538
|
+
type: "permit_sent",
|
|
2539
|
+
data: {
|
|
2540
|
+
invoiceId: invoiceMatch?.[1] || "",
|
|
2541
|
+
amount: amountMatch?.[1] || "0"
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
if (content.startsWith("awaiting_permit")) {
|
|
2546
|
+
const invoiceMatch = content.match(/invoice=(\S+)/);
|
|
2547
|
+
return {
|
|
2548
|
+
type: "awaiting_permit",
|
|
2549
|
+
data: { invoiceId: invoiceMatch?.[1] || "" }
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2103
2552
|
if (content.startsWith("payment_sent")) {
|
|
2104
2553
|
const txMatch = content.match(/tx=(\S+)/);
|
|
2105
2554
|
const amountMatch = content.match(/amount=(\d+(?:\.\d+)?)/);
|
|
@@ -2143,6 +2592,7 @@ function parseStatusMarker(message) {
|
|
|
2143
2592
|
return { type: "unknown", data: { raw: content } };
|
|
2144
2593
|
}
|
|
2145
2594
|
export {
|
|
2595
|
+
AllowanceWallet,
|
|
2146
2596
|
AuditLog,
|
|
2147
2597
|
BuyerTemplates,
|
|
2148
2598
|
CHAINS,
|
|
@@ -2151,6 +2601,7 @@ export {
|
|
|
2151
2601
|
OrderManager,
|
|
2152
2602
|
PaymentAgent,
|
|
2153
2603
|
PermitPayment,
|
|
2604
|
+
PermitSigner,
|
|
2154
2605
|
PermitWallet,
|
|
2155
2606
|
SecureWallet,
|
|
2156
2607
|
SellerTemplates,
|
|
@@ -2164,6 +2615,7 @@ export {
|
|
|
2164
2615
|
formatReceiptText,
|
|
2165
2616
|
generatePaymentGuide,
|
|
2166
2617
|
generatePaymentReminder,
|
|
2618
|
+
generatePermitInstructions,
|
|
2167
2619
|
generateReceipt,
|
|
2168
2620
|
generateReceiptFromInvoice,
|
|
2169
2621
|
generateWalletGuide,
|
|
@@ -2175,6 +2627,7 @@ export {
|
|
|
2175
2627
|
listChains,
|
|
2176
2628
|
loadWallet,
|
|
2177
2629
|
parseStatusMarker,
|
|
2630
|
+
signPermit,
|
|
2178
2631
|
verifyPayment,
|
|
2179
2632
|
waitForTransaction,
|
|
2180
2633
|
walletExists
|