moltspay 0.9.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -48
- package/dist/cdp/index.js +1 -1
- package/dist/cdp/index.js.map +1 -1
- package/dist/cdp/index.mjs +1 -1
- package/dist/cdp/index.mjs.map +1 -1
- package/dist/chains/index.js +1 -1
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/index.mjs +1 -1
- package/dist/chains/index.mjs.map +1 -1
- package/dist/cli/index.js +229 -48
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +229 -48
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +11 -1
- package/dist/client/index.d.ts +11 -1
- package/dist/client/index.js +87 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +87 -7
- package/dist/client/index.mjs.map +1 -1
- package/dist/facilitators/index.js +1 -1
- package/dist/facilitators/index.js.map +1 -1
- package/dist/facilitators/index.mjs +1 -1
- package/dist/facilitators/index.mjs.map +1 -1
- package/dist/index.js +191 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -33
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +25 -5
- package/dist/server/index.d.ts +25 -5
- package/dist/server/index.js +104 -26
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +104 -26
- package/dist/server/index.mjs.map +1 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/index.js.map +1 -1
- package/dist/verify/index.mjs +1 -1
- package/dist/verify/index.mjs.map +1 -1
- package/dist/wallet/index.js +1 -1
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +1 -1
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schemas/moltspay.services.schema.json +58 -2
package/dist/cli/index.mjs
CHANGED
|
@@ -41,7 +41,7 @@ var CHAINS = {
|
|
|
41
41
|
polygon: {
|
|
42
42
|
name: "Polygon",
|
|
43
43
|
chainId: 137,
|
|
44
|
-
rpc: "https://polygon-rpc.com",
|
|
44
|
+
rpc: "https://polygon-bor-rpc.publicnode.com",
|
|
45
45
|
tokens: {
|
|
46
46
|
USDC: {
|
|
47
47
|
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
@@ -217,10 +217,14 @@ var MoltsPayClient = class {
|
|
|
217
217
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
218
218
|
}
|
|
219
219
|
console.log(`[MoltsPay] Requesting service: ${service}`);
|
|
220
|
+
const requestBody = { service, params };
|
|
221
|
+
if (options.chain) {
|
|
222
|
+
requestBody.chain = options.chain;
|
|
223
|
+
}
|
|
220
224
|
const initialRes = await fetch(`${serverUrl}/execute`, {
|
|
221
225
|
method: "POST",
|
|
222
226
|
headers: { "Content-Type": "application/json" },
|
|
223
|
-
body: JSON.stringify(
|
|
227
|
+
body: JSON.stringify(requestBody)
|
|
224
228
|
});
|
|
225
229
|
if (initialRes.status !== 402) {
|
|
226
230
|
const data = await initialRes.json();
|
|
@@ -247,11 +251,41 @@ var MoltsPayClient = class {
|
|
|
247
251
|
} catch {
|
|
248
252
|
throw new Error("Invalid x-payment-required header");
|
|
249
253
|
}
|
|
250
|
-
const
|
|
254
|
+
const networkToChainName = (network2) => {
|
|
255
|
+
const match = network2.match(/^eip155:(\d+)$/);
|
|
256
|
+
if (!match) return null;
|
|
257
|
+
const chainId = parseInt(match[1]);
|
|
258
|
+
if (chainId === 8453) return "base";
|
|
259
|
+
if (chainId === 137) return "polygon";
|
|
260
|
+
if (chainId === 84532) return "base_sepolia";
|
|
261
|
+
return null;
|
|
262
|
+
};
|
|
263
|
+
const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
|
|
264
|
+
let chainName;
|
|
265
|
+
const userSpecifiedChain = options.chain;
|
|
266
|
+
if (userSpecifiedChain) {
|
|
267
|
+
if (!serverChains.includes(userSpecifiedChain)) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Server doesn't accept '${userSpecifiedChain}'.
|
|
270
|
+
Server accepts: ${serverChains.join(", ")}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
chainName = userSpecifiedChain;
|
|
274
|
+
} else {
|
|
275
|
+
if (serverChains.length === 1 && serverChains[0] === "base") {
|
|
276
|
+
chainName = "base";
|
|
277
|
+
} else {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Server accepts: ${serverChains.join(", ")}
|
|
280
|
+
Please specify: --chain base or --chain polygon`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const chain = getChain(chainName);
|
|
251
285
|
const network = `eip155:${chain.chainId}`;
|
|
252
286
|
const req = requirements.find((r) => r.scheme === "exact" && r.network === network);
|
|
253
287
|
if (!req) {
|
|
254
|
-
throw new Error(`
|
|
288
|
+
throw new Error(`Failed to find payment requirement for ${chainName}`);
|
|
255
289
|
}
|
|
256
290
|
const amountRaw = req.amount || req.maxAmountRequired;
|
|
257
291
|
if (!amountRaw) {
|
|
@@ -270,7 +304,17 @@ var MoltsPayClient = class {
|
|
|
270
304
|
throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);
|
|
271
305
|
}
|
|
272
306
|
}
|
|
273
|
-
|
|
307
|
+
if (token === "USDT") {
|
|
308
|
+
const balances = await this.getBalance();
|
|
309
|
+
if (balances.native < 1e-4) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`USDT requires ETH for gas (~$0.01 on Base). Your ETH balance: ${balances.native.toFixed(6)} ETH. Please add a small amount of ETH to your wallet, or use USDC (gasless).`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
console.log(`[MoltsPay] \u26A0\uFE0F USDT requires gas (~$0.01). Proceeding with payment...`);
|
|
315
|
+
} else {
|
|
316
|
+
console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
|
|
317
|
+
}
|
|
274
318
|
const payTo = req.payTo || req.resource;
|
|
275
319
|
if (!payTo) {
|
|
276
320
|
throw new Error("Missing payTo address in payment requirements");
|
|
@@ -294,13 +338,17 @@ var MoltsPayClient = class {
|
|
|
294
338
|
};
|
|
295
339
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
296
340
|
console.log(`[MoltsPay] Sending request with payment...`);
|
|
341
|
+
const paidRequestBody = { service, params };
|
|
342
|
+
if (options.chain) {
|
|
343
|
+
paidRequestBody.chain = options.chain;
|
|
344
|
+
}
|
|
297
345
|
const paidRes = await fetch(`${serverUrl}/execute`, {
|
|
298
346
|
method: "POST",
|
|
299
347
|
headers: {
|
|
300
348
|
"Content-Type": "application/json",
|
|
301
349
|
[PAYMENT_HEADER]: paymentHeader
|
|
302
350
|
},
|
|
303
|
-
body: JSON.stringify(
|
|
351
|
+
body: JSON.stringify(paidRequestBody)
|
|
304
352
|
});
|
|
305
353
|
const result = await paidRes.json();
|
|
306
354
|
if (!paidRes.ok) {
|
|
@@ -469,7 +517,7 @@ var MoltsPayClient = class {
|
|
|
469
517
|
return { address: wallet.address, configDir };
|
|
470
518
|
}
|
|
471
519
|
/**
|
|
472
|
-
* Get wallet balance (USDC, USDT, and native token)
|
|
520
|
+
* Get wallet balance (USDC, USDT, and native token) on default chain
|
|
473
521
|
*/
|
|
474
522
|
async getBalance() {
|
|
475
523
|
if (!this.wallet) {
|
|
@@ -494,6 +542,38 @@ var MoltsPayClient = class {
|
|
|
494
542
|
native: parseFloat(ethers.formatEther(nativeBalance))
|
|
495
543
|
};
|
|
496
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Get wallet balances on all supported chains (Base + Polygon)
|
|
547
|
+
*/
|
|
548
|
+
async getAllBalances() {
|
|
549
|
+
if (!this.wallet) {
|
|
550
|
+
throw new Error("Client not initialized");
|
|
551
|
+
}
|
|
552
|
+
const supportedChains = ["base", "polygon"];
|
|
553
|
+
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
554
|
+
const results = {};
|
|
555
|
+
await Promise.all(
|
|
556
|
+
supportedChains.map(async (chainName) => {
|
|
557
|
+
try {
|
|
558
|
+
const chain = getChain(chainName);
|
|
559
|
+
const provider = new ethers.JsonRpcProvider(chain.rpc);
|
|
560
|
+
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
561
|
+
provider.getBalance(this.wallet.address),
|
|
562
|
+
new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
563
|
+
new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
564
|
+
]);
|
|
565
|
+
results[chainName] = {
|
|
566
|
+
usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
|
|
567
|
+
usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
|
|
568
|
+
native: parseFloat(ethers.formatEther(nativeBalance))
|
|
569
|
+
};
|
|
570
|
+
} catch (err) {
|
|
571
|
+
results[chainName] = { usdc: 0, usdt: 0, native: 0 };
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
);
|
|
575
|
+
return results;
|
|
576
|
+
}
|
|
497
577
|
};
|
|
498
578
|
|
|
499
579
|
// src/server/index.ts
|
|
@@ -558,7 +638,7 @@ var CDPFacilitator = class extends BaseFacilitator {
|
|
|
558
638
|
this.apiKeyId = config.apiKeyId || process.env.CDP_API_KEY_ID;
|
|
559
639
|
this.apiKeySecret = config.apiKeySecret || process.env.CDP_API_KEY_SECRET;
|
|
560
640
|
this.endpoint = this.useMainnet ? CDP_MAINNET_URL : CDP_TESTNET_URL;
|
|
561
|
-
this.supportedNetworks = this.useMainnet ? ["eip155:8453"] : ["eip155:8453", "eip155:84532"];
|
|
641
|
+
this.supportedNetworks = this.useMainnet ? ["eip155:8453", "eip155:137"] : ["eip155:8453", "eip155:84532", "eip155:137"];
|
|
562
642
|
if (this.useMainnet && (!this.apiKeyId || !this.apiKeySecret)) {
|
|
563
643
|
console.warn("[CDPFacilitator] WARNING: Mainnet mode but missing CDP credentials!");
|
|
564
644
|
console.warn("[CDPFacilitator] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET");
|
|
@@ -959,8 +1039,17 @@ var TOKEN_ADDRESSES = {
|
|
|
959
1039
|
USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
960
1040
|
USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
961
1041
|
// Same as USDC on testnet
|
|
1042
|
+
},
|
|
1043
|
+
"eip155:137": {
|
|
1044
|
+
USDC: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
1045
|
+
USDT: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F"
|
|
962
1046
|
}
|
|
963
1047
|
};
|
|
1048
|
+
var CHAIN_TO_NETWORK = {
|
|
1049
|
+
"base": "eip155:8453",
|
|
1050
|
+
"base_sepolia": "eip155:84532",
|
|
1051
|
+
"polygon": "eip155:137"
|
|
1052
|
+
};
|
|
964
1053
|
var TOKEN_DOMAINS = {
|
|
965
1054
|
USDC: { name: "USD Coin", version: "2" },
|
|
966
1055
|
USDT: { name: "Tether USD", version: "2" }
|
|
@@ -1026,11 +1115,17 @@ var MoltsPayServer = class {
|
|
|
1026
1115
|
};
|
|
1027
1116
|
this.registry = new FacilitatorRegistry(facilitatorConfig);
|
|
1028
1117
|
const primaryFacilitator = this.registry.get(facilitatorConfig.primary);
|
|
1029
|
-
const networkName = this.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
1030
1118
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
1031
1119
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
1032
1120
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
1033
|
-
|
|
1121
|
+
const chains = this.manifest.provider.chains;
|
|
1122
|
+
if (chains && chains.length > 0) {
|
|
1123
|
+
const chainNames = chains.map((c) => c.chain || c.network).join(", ");
|
|
1124
|
+
console.log(`[MoltsPay] Chains: ${chainNames} (multi-chain enabled)`);
|
|
1125
|
+
} else {
|
|
1126
|
+
const networkName = this.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
1127
|
+
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
1128
|
+
}
|
|
1034
1129
|
console.log(`[MoltsPay] Facilitator: ${primaryFacilitator.displayName} (${facilitatorConfig.strategy || "failover"})`);
|
|
1035
1130
|
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
1036
1131
|
}
|
|
@@ -1045,6 +1140,42 @@ var MoltsPayServer = class {
|
|
|
1045
1140
|
this.skills.set(serviceId, { id: serviceId, config, handler });
|
|
1046
1141
|
return this;
|
|
1047
1142
|
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Get all configured chains for this provider
|
|
1145
|
+
* Returns array of { network, wallet, tokens } for each chain
|
|
1146
|
+
*/
|
|
1147
|
+
getProviderChains() {
|
|
1148
|
+
const provider = this.manifest.provider;
|
|
1149
|
+
if (provider.chains && provider.chains.length > 0) {
|
|
1150
|
+
return provider.chains.map((c) => ({
|
|
1151
|
+
network: c.network || CHAIN_TO_NETWORK[c.chain] || "eip155:8453",
|
|
1152
|
+
wallet: c.wallet || provider.wallet,
|
|
1153
|
+
tokens: c.tokens || ["USDC"]
|
|
1154
|
+
}));
|
|
1155
|
+
}
|
|
1156
|
+
const chain = provider.chain || "base";
|
|
1157
|
+
const network = CHAIN_TO_NETWORK[chain] || this.networkId;
|
|
1158
|
+
return [{
|
|
1159
|
+
network,
|
|
1160
|
+
wallet: provider.wallet,
|
|
1161
|
+
tokens: ["USDC"]
|
|
1162
|
+
}];
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Get wallet address for a specific network
|
|
1166
|
+
*/
|
|
1167
|
+
getWalletForNetwork(network) {
|
|
1168
|
+
const chains = this.getProviderChains();
|
|
1169
|
+
const chain = chains.find((c) => c.network === network);
|
|
1170
|
+
return chain?.wallet || this.manifest.provider.wallet;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Check if a network is accepted by this provider
|
|
1174
|
+
*/
|
|
1175
|
+
isNetworkAccepted(network) {
|
|
1176
|
+
const chains = this.getProviderChains();
|
|
1177
|
+
return chains.some((c) => c.network === network);
|
|
1178
|
+
}
|
|
1048
1179
|
/**
|
|
1049
1180
|
* Start HTTP server
|
|
1050
1181
|
*/
|
|
@@ -1227,7 +1358,9 @@ var MoltsPayServer = class {
|
|
|
1227
1358
|
error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
|
|
1228
1359
|
});
|
|
1229
1360
|
}
|
|
1230
|
-
const
|
|
1361
|
+
const paymentNetwork = payment.accepted?.network || payment.network || this.networkId;
|
|
1362
|
+
const paymentWallet = this.getWalletForNetwork(paymentNetwork);
|
|
1363
|
+
const requirements = this.buildPaymentRequirements(skill.config, paymentNetwork, paymentWallet, paymentToken);
|
|
1231
1364
|
console.log(`[MoltsPay] Verifying payment...`);
|
|
1232
1365
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
1233
1366
|
if (!verifyResult.valid) {
|
|
@@ -1282,15 +1415,29 @@ var MoltsPayServer = class {
|
|
|
1282
1415
|
}
|
|
1283
1416
|
/**
|
|
1284
1417
|
* Return 402 with x402 payment requirements (v2 format)
|
|
1285
|
-
* Includes requirements for all accepted currencies
|
|
1418
|
+
* Includes requirements for all chains and all accepted currencies
|
|
1286
1419
|
*/
|
|
1287
1420
|
sendPaymentRequired(config, res) {
|
|
1288
1421
|
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1289
|
-
const
|
|
1422
|
+
const providerChains = this.getProviderChains();
|
|
1423
|
+
const accepts = [];
|
|
1424
|
+
for (const chainConfig of providerChains) {
|
|
1425
|
+
for (const token of acceptedTokens) {
|
|
1426
|
+
if (chainConfig.tokens.includes(token)) {
|
|
1427
|
+
accepts.push(this.buildPaymentRequirements(config, chainConfig.network, chainConfig.wallet, token));
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
const acceptedChains = providerChains.map((c) => {
|
|
1432
|
+
if (c.network === "eip155:8453") return "base";
|
|
1433
|
+
if (c.network === "eip155:137") return "polygon";
|
|
1434
|
+
return c.network;
|
|
1435
|
+
});
|
|
1290
1436
|
const paymentRequired = {
|
|
1291
1437
|
x402Version: X402_VERSION3,
|
|
1292
1438
|
accepts,
|
|
1293
1439
|
acceptedCurrencies: acceptedTokens,
|
|
1440
|
+
acceptedChains,
|
|
1294
1441
|
resource: {
|
|
1295
1442
|
url: `/execute?service=${config.id}`,
|
|
1296
1443
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
@@ -1306,6 +1453,7 @@ var MoltsPayServer = class {
|
|
|
1306
1453
|
error: "Payment required",
|
|
1307
1454
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
1308
1455
|
acceptedCurrencies: acceptedTokens,
|
|
1456
|
+
acceptedChains,
|
|
1309
1457
|
x402: paymentRequired
|
|
1310
1458
|
}, null, 2));
|
|
1311
1459
|
}
|
|
@@ -1317,46 +1465,50 @@ var MoltsPayServer = class {
|
|
|
1317
1465
|
return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };
|
|
1318
1466
|
}
|
|
1319
1467
|
const scheme = payment.accepted?.scheme || payment.scheme;
|
|
1320
|
-
const network = payment.accepted?.network || payment.network;
|
|
1468
|
+
const network = payment.accepted?.network || payment.network || this.networkId;
|
|
1321
1469
|
if (scheme !== "exact") {
|
|
1322
1470
|
return { valid: false, error: `Unsupported scheme: ${scheme}` };
|
|
1323
1471
|
}
|
|
1324
|
-
if (
|
|
1325
|
-
|
|
1472
|
+
if (!this.isNetworkAccepted(network)) {
|
|
1473
|
+
const acceptedChains = this.getProviderChains().map((c) => c.network).join(", ");
|
|
1474
|
+
return { valid: false, error: `Network not accepted: ${network}. Accepted: ${acceptedChains}` };
|
|
1326
1475
|
}
|
|
1327
1476
|
return { valid: true };
|
|
1328
1477
|
}
|
|
1329
1478
|
/**
|
|
1330
1479
|
* Build payment requirements for facilitator
|
|
1331
|
-
*
|
|
1332
|
-
* Server accepts any of the acceptedCurrencies
|
|
1480
|
+
* Now supports multi-chain: takes network and wallet as parameters
|
|
1333
1481
|
*/
|
|
1334
|
-
buildPaymentRequirements(config, token) {
|
|
1482
|
+
buildPaymentRequirements(config, network, wallet, token) {
|
|
1335
1483
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1336
1484
|
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1485
|
+
const selectedNetwork = network || this.networkId;
|
|
1486
|
+
const selectedWallet = wallet || this.manifest.provider.wallet;
|
|
1337
1487
|
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1338
|
-
const tokenAddresses = TOKEN_ADDRESSES[
|
|
1488
|
+
const tokenAddresses = TOKEN_ADDRESSES[selectedNetwork] || {};
|
|
1339
1489
|
const tokenAddress = tokenAddresses[selectedToken];
|
|
1340
1490
|
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1341
1491
|
return {
|
|
1342
1492
|
scheme: "exact",
|
|
1343
|
-
network:
|
|
1493
|
+
network: selectedNetwork,
|
|
1344
1494
|
asset: tokenAddress,
|
|
1345
1495
|
amount: amountInUnits,
|
|
1346
|
-
payTo:
|
|
1496
|
+
payTo: selectedWallet,
|
|
1347
1497
|
maxTimeoutSeconds: 300,
|
|
1348
1498
|
extra: tokenDomain
|
|
1349
1499
|
};
|
|
1350
1500
|
}
|
|
1351
1501
|
/**
|
|
1352
1502
|
* Detect which token is being used in the payment
|
|
1503
|
+
* Checks across all supported networks
|
|
1353
1504
|
*/
|
|
1354
1505
|
detectPaymentToken(payment) {
|
|
1355
1506
|
const asset = payment.accepted?.asset || payment.payload?.asset;
|
|
1356
1507
|
if (!asset) return void 0;
|
|
1357
|
-
const
|
|
1508
|
+
const paymentNetwork = payment.accepted?.network || payment.network || this.networkId;
|
|
1509
|
+
const tokenAddresses = TOKEN_ADDRESSES[paymentNetwork] || {};
|
|
1358
1510
|
for (const [symbol, address] of Object.entries(tokenAddresses)) {
|
|
1359
|
-
if (address.toLowerCase() === asset.toLowerCase()) {
|
|
1511
|
+
if (address && address.toLowerCase() === asset.toLowerCase()) {
|
|
1360
1512
|
return symbol;
|
|
1361
1513
|
}
|
|
1362
1514
|
}
|
|
@@ -1431,6 +1583,10 @@ var MoltsPayServer = class {
|
|
|
1431
1583
|
if (isNaN(amountNum) || amountNum <= 0) {
|
|
1432
1584
|
return this.sendJson(res, 400, { error: "Invalid amount" });
|
|
1433
1585
|
}
|
|
1586
|
+
const supportedChains = ["base", "polygon", "base_sepolia"];
|
|
1587
|
+
if (chain && !supportedChains.includes(chain)) {
|
|
1588
|
+
return this.sendJson(res, 400, { error: `Unsupported chain: ${chain}. Supported: ${supportedChains.join(", ")}` });
|
|
1589
|
+
}
|
|
1434
1590
|
const proxyConfig = {
|
|
1435
1591
|
id: serviceId || "proxy",
|
|
1436
1592
|
name: description || "Proxy Payment",
|
|
@@ -1442,9 +1598,9 @@ var MoltsPayServer = class {
|
|
|
1442
1598
|
input: {},
|
|
1443
1599
|
output: {}
|
|
1444
1600
|
};
|
|
1445
|
-
const requirements = this.buildProxyPaymentRequirements(proxyConfig, wallet);
|
|
1601
|
+
const requirements = this.buildProxyPaymentRequirements(proxyConfig, wallet, currency, chain);
|
|
1446
1602
|
if (!paymentHeader) {
|
|
1447
|
-
return this.sendProxyPaymentRequired(proxyConfig, wallet, memo, res);
|
|
1603
|
+
return this.sendProxyPaymentRequired(proxyConfig, wallet, memo, chain, res);
|
|
1448
1604
|
}
|
|
1449
1605
|
let payment;
|
|
1450
1606
|
try {
|
|
@@ -1461,8 +1617,9 @@ var MoltsPayServer = class {
|
|
|
1461
1617
|
if (scheme !== "exact") {
|
|
1462
1618
|
return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
|
|
1463
1619
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1620
|
+
const expectedNetwork = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
|
|
1621
|
+
if (network !== expectedNetwork) {
|
|
1622
|
+
return this.sendJson(res, 402, { error: `Network mismatch: expected ${expectedNetwork}, got ${network}` });
|
|
1466
1623
|
}
|
|
1467
1624
|
console.log(`[MoltsPay] /proxy: Verifying payment for ${wallet}...`);
|
|
1468
1625
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
@@ -1568,16 +1725,17 @@ var MoltsPayServer = class {
|
|
|
1568
1725
|
/**
|
|
1569
1726
|
* Build payment requirements for proxy endpoint (uses provided wallet)
|
|
1570
1727
|
*/
|
|
1571
|
-
buildProxyPaymentRequirements(config, wallet, token) {
|
|
1728
|
+
buildProxyPaymentRequirements(config, wallet, token, chain) {
|
|
1572
1729
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1573
1730
|
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1731
|
+
const networkId = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
|
|
1574
1732
|
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1575
|
-
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1733
|
+
const tokenAddresses = TOKEN_ADDRESSES[networkId] || TOKEN_ADDRESSES[this.networkId] || {};
|
|
1576
1734
|
const tokenAddress = tokenAddresses[selectedToken];
|
|
1577
1735
|
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1578
1736
|
return {
|
|
1579
1737
|
scheme: "exact",
|
|
1580
|
-
network:
|
|
1738
|
+
network: networkId,
|
|
1581
1739
|
asset: tokenAddress,
|
|
1582
1740
|
amount: amountInUnits,
|
|
1583
1741
|
payTo: wallet,
|
|
@@ -1589,8 +1747,8 @@ var MoltsPayServer = class {
|
|
|
1589
1747
|
/**
|
|
1590
1748
|
* Return 402 with x402 payment requirements for proxy endpoint
|
|
1591
1749
|
*/
|
|
1592
|
-
sendProxyPaymentRequired(config, wallet, memo, res) {
|
|
1593
|
-
const requirements = this.buildProxyPaymentRequirements(config, wallet);
|
|
1750
|
+
sendProxyPaymentRequired(config, wallet, memo, chain, res) {
|
|
1751
|
+
const requirements = this.buildProxyPaymentRequirements(config, wallet, config.currency, chain);
|
|
1594
1752
|
const paymentRequired = {
|
|
1595
1753
|
x402Version: X402_VERSION3,
|
|
1596
1754
|
accepts: [requirements],
|
|
@@ -1716,29 +1874,31 @@ program.command("status").description("Show wallet status and balance").option("
|
|
|
1716
1874
|
return;
|
|
1717
1875
|
}
|
|
1718
1876
|
const config = client.getConfig();
|
|
1719
|
-
let
|
|
1877
|
+
let allBalances = {};
|
|
1720
1878
|
try {
|
|
1721
|
-
|
|
1879
|
+
allBalances = await client.getAllBalances();
|
|
1722
1880
|
} catch (err) {
|
|
1723
|
-
console.error("Warning: Could not fetch
|
|
1881
|
+
console.error("Warning: Could not fetch balances:", err.message);
|
|
1724
1882
|
}
|
|
1725
1883
|
if (options.json) {
|
|
1726
1884
|
console.log(JSON.stringify({
|
|
1727
1885
|
address: client.address,
|
|
1728
|
-
|
|
1729
|
-
balance,
|
|
1886
|
+
balances: allBalances,
|
|
1730
1887
|
limits: config.limits
|
|
1731
1888
|
}, null, 2));
|
|
1732
1889
|
} else {
|
|
1733
|
-
console.log("\n\u{1F4CA} MoltsPay Status\n");
|
|
1734
|
-
console.log(`
|
|
1735
|
-
console.log(
|
|
1736
|
-
console.log(
|
|
1737
|
-
|
|
1890
|
+
console.log("\n\u{1F4CA} MoltsPay Wallet Status\n");
|
|
1891
|
+
console.log(` Address: ${client.address}`);
|
|
1892
|
+
console.log("");
|
|
1893
|
+
console.log(" Balances:");
|
|
1894
|
+
for (const [chainName, balance] of Object.entries(allBalances)) {
|
|
1895
|
+
const chainLabel = chainName.charAt(0).toUpperCase() + chainName.slice(1);
|
|
1896
|
+
console.log(` ${chainLabel.padEnd(10)} ${balance.usdc.toFixed(2)} USDC | ${balance.usdt.toFixed(2)} USDT`);
|
|
1897
|
+
}
|
|
1738
1898
|
console.log("");
|
|
1739
|
-
console.log(" Limits:");
|
|
1740
|
-
console.log(`
|
|
1741
|
-
console.log(`
|
|
1899
|
+
console.log(" Spending Limits:");
|
|
1900
|
+
console.log(` Per Transaction: $${config.limits.maxPerTx}`);
|
|
1901
|
+
console.log(` Daily: $${config.limits.maxPerDay}`);
|
|
1742
1902
|
console.log("");
|
|
1743
1903
|
}
|
|
1744
1904
|
});
|
|
@@ -1977,7 +2137,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
1977
2137
|
process.exit(1);
|
|
1978
2138
|
}
|
|
1979
2139
|
});
|
|
1980
|
-
program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
|
|
2140
|
+
program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--chain <chain>", "Chain to pay on (base or polygon). Required if server accepts multiple chains.").option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
|
|
1981
2141
|
const client = new MoltsPayClient();
|
|
1982
2142
|
if (!client.isInitialized) {
|
|
1983
2143
|
console.error("\u274C Wallet not initialized. Run: npx moltspay init");
|
|
@@ -2011,8 +2171,25 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
2011
2171
|
console.error("\u274C Missing prompt. Use --prompt or pass JSON params");
|
|
2012
2172
|
process.exit(1);
|
|
2013
2173
|
}
|
|
2174
|
+
const chain = options.chain?.toLowerCase();
|
|
2175
|
+
if (chain && !["base", "polygon"].includes(chain)) {
|
|
2176
|
+
console.error(`\u274C Unknown chain: ${chain}. Supported: base, polygon`);
|
|
2177
|
+
process.exit(1);
|
|
2178
|
+
}
|
|
2014
2179
|
const imageDisplay = params.image_url || (params.image_base64 ? `[local file: ${options.image}]` : null);
|
|
2015
2180
|
const token = (options.token || "USDC").toUpperCase();
|
|
2181
|
+
if (token === "USDT") {
|
|
2182
|
+
const balance = await client.getBalance();
|
|
2183
|
+
if (balance.native < 1e-4) {
|
|
2184
|
+
console.log("\n\u26A0\uFE0F USDT requires a small amount of ETH for gas (~$0.01)");
|
|
2185
|
+
console.log(` Your ETH balance: ${balance.native.toFixed(6)} ETH`);
|
|
2186
|
+
console.log(" Please add a tiny amount of ETH to your wallet.\n");
|
|
2187
|
+
process.exit(1);
|
|
2188
|
+
}
|
|
2189
|
+
if (!options.json) {
|
|
2190
|
+
console.log("\n\u26A0\uFE0F Note: USDT payments require gas (~$0.01 on Base)");
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2016
2193
|
if (!options.json) {
|
|
2017
2194
|
console.log(`
|
|
2018
2195
|
\u{1F4B3} MoltsPay - Paying for service
|
|
@@ -2021,12 +2198,16 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
2021
2198
|
console.log(` Service: ${service}`);
|
|
2022
2199
|
console.log(` Prompt: ${params.prompt}`);
|
|
2023
2200
|
if (imageDisplay) console.log(` Image: ${imageDisplay}`);
|
|
2201
|
+
console.log(` Chain: ${chain || "(auto)"}`);
|
|
2024
2202
|
console.log(` Token: ${token}`);
|
|
2025
2203
|
console.log(` Wallet: ${client.address}`);
|
|
2026
2204
|
console.log("");
|
|
2027
2205
|
}
|
|
2028
2206
|
try {
|
|
2029
|
-
const result = await client.pay(server, service, params, {
|
|
2207
|
+
const result = await client.pay(server, service, params, {
|
|
2208
|
+
token,
|
|
2209
|
+
chain
|
|
2210
|
+
});
|
|
2030
2211
|
if (options.json) {
|
|
2031
2212
|
console.log(JSON.stringify(result));
|
|
2032
2213
|
} else {
|