moltspay 0.9.3 → 0.9.6
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/.env.example +7 -0
- package/dist/cdp/index.d.mts +1 -1
- package/dist/cdp/index.d.ts +1 -1
- package/dist/cdp/index.js +63 -0
- package/dist/cdp/index.js.map +1 -1
- package/dist/cdp/index.mjs +63 -0
- package/dist/cdp/index.mjs.map +1 -1
- package/dist/chains/index.d.mts +9 -5
- package/dist/chains/index.d.ts +9 -5
- package/dist/chains/index.js +85 -0
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/index.mjs +83 -0
- package/dist/chains/index.mjs.map +1 -1
- package/dist/cli/index.js +234 -41
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +234 -41
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +18 -3
- package/dist/client/index.d.ts +18 -3
- package/dist/client/index.js +102 -15
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +102 -15
- package/dist/client/index.mjs.map +1 -1
- package/dist/{index-Dg8n6wdW.d.mts → index-B3v8IWjM.d.mts} +11 -1
- package/dist/{index-Dg8n6wdW.d.ts → index-B3v8IWjM.d.ts} +11 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +248 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +248 -45
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +23 -1
- package/dist/server/index.d.ts +23 -1
- package/dist/server/index.js +126 -22
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +126 -22
- package/dist/server/index.mjs.map +1 -1
- package/dist/verify/index.d.mts +7 -0
- package/dist/verify/index.d.ts +7 -0
- package/dist/verify/index.js +83 -8
- package/dist/verify/index.js.map +1 -1
- package/dist/verify/index.mjs +83 -8
- package/dist/verify/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +16 -8
- package/dist/wallet/index.d.ts +16 -8
- package/dist/wallet/index.js +114 -18
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +114 -18
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schemas/moltspay.services.schema.json +13 -3
package/dist/cli/index.js
CHANGED
|
@@ -43,7 +43,20 @@ var CHAINS = {
|
|
|
43
43
|
name: "Base",
|
|
44
44
|
chainId: 8453,
|
|
45
45
|
rpc: "https://mainnet.base.org",
|
|
46
|
+
tokens: {
|
|
47
|
+
USDC: {
|
|
48
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
49
|
+
decimals: 6,
|
|
50
|
+
symbol: "USDC"
|
|
51
|
+
},
|
|
52
|
+
USDT: {
|
|
53
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
54
|
+
decimals: 6,
|
|
55
|
+
symbol: "USDT"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
46
58
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
59
|
+
// deprecated, for backward compat
|
|
47
60
|
explorer: "https://basescan.org/address/",
|
|
48
61
|
explorerTx: "https://basescan.org/tx/",
|
|
49
62
|
avgBlockTime: 2
|
|
@@ -52,6 +65,18 @@ var CHAINS = {
|
|
|
52
65
|
name: "Polygon",
|
|
53
66
|
chainId: 137,
|
|
54
67
|
rpc: "https://polygon-rpc.com",
|
|
68
|
+
tokens: {
|
|
69
|
+
USDC: {
|
|
70
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
71
|
+
decimals: 6,
|
|
72
|
+
symbol: "USDC"
|
|
73
|
+
},
|
|
74
|
+
USDT: {
|
|
75
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
76
|
+
decimals: 6,
|
|
77
|
+
symbol: "USDT"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
55
80
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
56
81
|
explorer: "https://polygonscan.com/address/",
|
|
57
82
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -61,6 +86,18 @@ var CHAINS = {
|
|
|
61
86
|
name: "Ethereum",
|
|
62
87
|
chainId: 1,
|
|
63
88
|
rpc: "https://eth.llamarpc.com",
|
|
89
|
+
tokens: {
|
|
90
|
+
USDC: {
|
|
91
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
92
|
+
decimals: 6,
|
|
93
|
+
symbol: "USDC"
|
|
94
|
+
},
|
|
95
|
+
USDT: {
|
|
96
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
97
|
+
decimals: 6,
|
|
98
|
+
symbol: "USDT"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
64
101
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
65
102
|
explorer: "https://etherscan.io/address/",
|
|
66
103
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -71,6 +108,19 @@ var CHAINS = {
|
|
|
71
108
|
name: "Base Sepolia",
|
|
72
109
|
chainId: 84532,
|
|
73
110
|
rpc: "https://sepolia.base.org",
|
|
111
|
+
tokens: {
|
|
112
|
+
USDC: {
|
|
113
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
114
|
+
decimals: 6,
|
|
115
|
+
symbol: "USDC"
|
|
116
|
+
},
|
|
117
|
+
USDT: {
|
|
118
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
119
|
+
// Same as USDC on testnet (no official USDT)
|
|
120
|
+
decimals: 6,
|
|
121
|
+
symbol: "USDT"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
74
124
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
75
125
|
explorer: "https://sepolia.basescan.org/address/",
|
|
76
126
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -80,6 +130,19 @@ var CHAINS = {
|
|
|
80
130
|
name: "Sepolia",
|
|
81
131
|
chainId: 11155111,
|
|
82
132
|
rpc: "https://rpc.sepolia.org",
|
|
133
|
+
tokens: {
|
|
134
|
+
USDC: {
|
|
135
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
136
|
+
decimals: 6,
|
|
137
|
+
symbol: "USDC"
|
|
138
|
+
},
|
|
139
|
+
USDT: {
|
|
140
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
141
|
+
// Same as USDC on testnet
|
|
142
|
+
decimals: 6,
|
|
143
|
+
symbol: "USDT"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
83
146
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
84
147
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
85
148
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -166,8 +229,13 @@ var MoltsPayClient = class {
|
|
|
166
229
|
*
|
|
167
230
|
* This is GASLESS for the client - server pays gas to claim payment.
|
|
168
231
|
* This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.
|
|
232
|
+
*
|
|
233
|
+
* @param serverUrl - Server URL
|
|
234
|
+
* @param service - Service ID
|
|
235
|
+
* @param params - Service parameters
|
|
236
|
+
* @param options - Payment options (token selection)
|
|
169
237
|
*/
|
|
170
|
-
async pay(serverUrl, service, params) {
|
|
238
|
+
async pay(serverUrl, service, params, options = {}) {
|
|
171
239
|
if (!this.wallet || !this.walletData) {
|
|
172
240
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
173
241
|
}
|
|
@@ -214,12 +282,25 @@ var MoltsPayClient = class {
|
|
|
214
282
|
}
|
|
215
283
|
const amount = Number(amountRaw) / 1e6;
|
|
216
284
|
this.checkLimits(amount);
|
|
217
|
-
|
|
285
|
+
let token = options.token || "USDC";
|
|
286
|
+
if (options.autoSelect) {
|
|
287
|
+
const balances = await this.getBalance();
|
|
288
|
+
if (balances.usdc >= amount) {
|
|
289
|
+
token = "USDC";
|
|
290
|
+
} else if (balances.usdt >= amount) {
|
|
291
|
+
token = "USDT";
|
|
292
|
+
} else {
|
|
293
|
+
throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
|
|
218
297
|
const payTo = req.payTo || req.resource;
|
|
219
298
|
if (!payTo) {
|
|
220
299
|
throw new Error("Missing payTo address in payment requirements");
|
|
221
300
|
}
|
|
222
|
-
const authorization = await this.signEIP3009(payTo, amount, chain);
|
|
301
|
+
const authorization = await this.signEIP3009(payTo, amount, chain, token);
|
|
302
|
+
const tokenConfig = chain.tokens[token];
|
|
303
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
223
304
|
const payload = {
|
|
224
305
|
x402Version: X402_VERSION,
|
|
225
306
|
payload: authorization,
|
|
@@ -227,11 +308,11 @@ var MoltsPayClient = class {
|
|
|
227
308
|
accepted: {
|
|
228
309
|
scheme: "exact",
|
|
229
310
|
network,
|
|
230
|
-
asset:
|
|
311
|
+
asset: tokenConfig.address,
|
|
231
312
|
amount: amountRaw,
|
|
232
313
|
payTo,
|
|
233
314
|
maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
|
|
234
|
-
extra: req.extra || { name:
|
|
315
|
+
extra: req.extra || { name: tokenName, version: "2" }
|
|
235
316
|
}
|
|
236
317
|
};
|
|
237
318
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
@@ -255,12 +336,14 @@ var MoltsPayClient = class {
|
|
|
255
336
|
/**
|
|
256
337
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
257
338
|
* This only signs - no on-chain transaction, no gas needed.
|
|
339
|
+
* Supports both USDC and USDT.
|
|
258
340
|
*/
|
|
259
|
-
async signEIP3009(to, amount, chain) {
|
|
341
|
+
async signEIP3009(to, amount, chain, token = "USDC") {
|
|
260
342
|
const validAfter = 0;
|
|
261
343
|
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
262
344
|
const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
|
|
263
|
-
const
|
|
345
|
+
const tokenConfig = chain.tokens[token];
|
|
346
|
+
const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
264
347
|
const authorization = {
|
|
265
348
|
from: this.wallet.address,
|
|
266
349
|
to,
|
|
@@ -269,11 +352,12 @@ var MoltsPayClient = class {
|
|
|
269
352
|
validBefore: validBefore.toString(),
|
|
270
353
|
nonce
|
|
271
354
|
};
|
|
355
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
272
356
|
const domain = {
|
|
273
|
-
name:
|
|
357
|
+
name: tokenName,
|
|
274
358
|
version: "2",
|
|
275
359
|
chainId: chain.chainId,
|
|
276
|
-
verifyingContract:
|
|
360
|
+
verifyingContract: tokenConfig.address
|
|
277
361
|
};
|
|
278
362
|
const types = {
|
|
279
363
|
TransferWithAuthorization: [
|
|
@@ -408,7 +492,7 @@ var MoltsPayClient = class {
|
|
|
408
492
|
return { address: wallet.address, configDir };
|
|
409
493
|
}
|
|
410
494
|
/**
|
|
411
|
-
* Get wallet balance
|
|
495
|
+
* Get wallet balance (USDC, USDT, and native token)
|
|
412
496
|
*/
|
|
413
497
|
async getBalance() {
|
|
414
498
|
if (!this.wallet) {
|
|
@@ -421,12 +505,15 @@ var MoltsPayClient = class {
|
|
|
421
505
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
422
506
|
}
|
|
423
507
|
const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
|
|
424
|
-
const
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
508
|
+
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
509
|
+
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
510
|
+
provider.getBalance(this.wallet.address),
|
|
511
|
+
new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
512
|
+
new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
513
|
+
]);
|
|
428
514
|
return {
|
|
429
|
-
usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance,
|
|
515
|
+
usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
|
|
516
|
+
usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
|
|
430
517
|
native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
|
|
431
518
|
};
|
|
432
519
|
}
|
|
@@ -886,16 +973,24 @@ var X402_VERSION3 = 2;
|
|
|
886
973
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
887
974
|
var PAYMENT_HEADER2 = "x-payment";
|
|
888
975
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
889
|
-
var
|
|
890
|
-
"eip155:8453":
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
976
|
+
var TOKEN_ADDRESSES = {
|
|
977
|
+
"eip155:8453": {
|
|
978
|
+
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
979
|
+
USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
|
|
980
|
+
},
|
|
981
|
+
"eip155:84532": {
|
|
982
|
+
USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
983
|
+
USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
984
|
+
// Same as USDC on testnet
|
|
985
|
+
}
|
|
894
986
|
};
|
|
895
|
-
var
|
|
896
|
-
name: "USD Coin",
|
|
897
|
-
version: "2"
|
|
987
|
+
var TOKEN_DOMAINS = {
|
|
988
|
+
USDC: { name: "USD Coin", version: "2" },
|
|
989
|
+
USDT: { name: "Tether USD", version: "2" }
|
|
898
990
|
};
|
|
991
|
+
function getAcceptedCurrencies(config) {
|
|
992
|
+
return config.acceptedCurrencies ?? [config.currency];
|
|
993
|
+
}
|
|
899
994
|
function loadEnvFile2() {
|
|
900
995
|
const envPaths = [
|
|
901
996
|
path2.join(process.cwd(), ".env"),
|
|
@@ -1007,6 +1102,9 @@ var MoltsPayServer = class {
|
|
|
1007
1102
|
if (url.pathname === "/services" && req.method === "GET") {
|
|
1008
1103
|
return this.handleGetServices(res);
|
|
1009
1104
|
}
|
|
1105
|
+
if (url.pathname === "/.well-known/agent-services.json" && req.method === "GET") {
|
|
1106
|
+
return this.handleAgentServicesDiscovery(res);
|
|
1107
|
+
}
|
|
1010
1108
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
1011
1109
|
return await this.handleHealthCheck(res);
|
|
1012
1110
|
}
|
|
@@ -1030,6 +1128,44 @@ var MoltsPayServer = class {
|
|
|
1030
1128
|
this.sendJson(res, 500, { error: err.message || "Internal error" });
|
|
1031
1129
|
}
|
|
1032
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* GET /.well-known/agent-services.json - Standard discovery endpoint
|
|
1133
|
+
*/
|
|
1134
|
+
handleAgentServicesDiscovery(res) {
|
|
1135
|
+
const services = this.manifest.services.map((s) => ({
|
|
1136
|
+
id: s.id,
|
|
1137
|
+
name: s.name,
|
|
1138
|
+
description: s.description,
|
|
1139
|
+
price: s.price,
|
|
1140
|
+
currency: s.currency,
|
|
1141
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
1142
|
+
input: s.input,
|
|
1143
|
+
output: s.output,
|
|
1144
|
+
available: this.skills.has(s.id)
|
|
1145
|
+
}));
|
|
1146
|
+
this.sendJson(res, 200, {
|
|
1147
|
+
version: "1.0",
|
|
1148
|
+
provider: {
|
|
1149
|
+
name: this.manifest.provider.name,
|
|
1150
|
+
description: this.manifest.provider.description,
|
|
1151
|
+
wallet: this.manifest.provider.wallet,
|
|
1152
|
+
chain: this.manifest.provider.chain || "base"
|
|
1153
|
+
},
|
|
1154
|
+
services,
|
|
1155
|
+
endpoints: {
|
|
1156
|
+
services: "/services",
|
|
1157
|
+
execute: "/execute",
|
|
1158
|
+
health: "/health"
|
|
1159
|
+
},
|
|
1160
|
+
payment: {
|
|
1161
|
+
protocol: "x402",
|
|
1162
|
+
version: X402_VERSION3,
|
|
1163
|
+
network: this.networkId,
|
|
1164
|
+
schemes: ["exact"],
|
|
1165
|
+
mainnet: this.useMainnet
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1033
1169
|
/**
|
|
1034
1170
|
* GET /services - List available services
|
|
1035
1171
|
*/
|
|
@@ -1040,6 +1176,7 @@ var MoltsPayServer = class {
|
|
|
1040
1176
|
description: s.description,
|
|
1041
1177
|
price: s.price,
|
|
1042
1178
|
currency: s.currency,
|
|
1179
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
1043
1180
|
input: s.input,
|
|
1044
1181
|
output: s.output,
|
|
1045
1182
|
available: this.skills.has(s.id)
|
|
@@ -1106,7 +1243,14 @@ var MoltsPayServer = class {
|
|
|
1106
1243
|
if (!validation.valid) {
|
|
1107
1244
|
return this.sendJson(res, 402, { error: validation.error });
|
|
1108
1245
|
}
|
|
1109
|
-
const
|
|
1246
|
+
const paymentToken = this.detectPaymentToken(payment);
|
|
1247
|
+
if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
|
|
1248
|
+
const accepted = getAcceptedCurrencies(skill.config);
|
|
1249
|
+
return this.sendJson(res, 402, {
|
|
1250
|
+
error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
|
|
1110
1254
|
console.log(`[MoltsPay] Verifying payment...`);
|
|
1111
1255
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
1112
1256
|
if (!verifyResult.valid) {
|
|
@@ -1116,10 +1260,16 @@ var MoltsPayServer = class {
|
|
|
1116
1260
|
});
|
|
1117
1261
|
}
|
|
1118
1262
|
console.log(`[MoltsPay] Verified by ${verifyResult.facilitator}`);
|
|
1119
|
-
|
|
1263
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
1264
|
+
console.log(`[MoltsPay] Executing skill: ${service} (timeout: ${timeoutSeconds}s)`);
|
|
1120
1265
|
let result;
|
|
1121
1266
|
try {
|
|
1122
|
-
result = await
|
|
1267
|
+
result = await Promise.race([
|
|
1268
|
+
skill.handler(params || {}),
|
|
1269
|
+
new Promise(
|
|
1270
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
1271
|
+
)
|
|
1272
|
+
]);
|
|
1123
1273
|
} catch (err) {
|
|
1124
1274
|
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
1125
1275
|
return this.sendJson(res, 500, {
|
|
@@ -1155,12 +1305,15 @@ var MoltsPayServer = class {
|
|
|
1155
1305
|
}
|
|
1156
1306
|
/**
|
|
1157
1307
|
* Return 402 with x402 payment requirements (v2 format)
|
|
1308
|
+
* Includes requirements for all accepted currencies
|
|
1158
1309
|
*/
|
|
1159
1310
|
sendPaymentRequired(config, res) {
|
|
1160
|
-
const
|
|
1311
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1312
|
+
const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
|
|
1161
1313
|
const paymentRequired = {
|
|
1162
1314
|
x402Version: X402_VERSION3,
|
|
1163
|
-
accepts
|
|
1315
|
+
accepts,
|
|
1316
|
+
acceptedCurrencies: acceptedTokens,
|
|
1164
1317
|
resource: {
|
|
1165
1318
|
url: `/execute?service=${config.id}`,
|
|
1166
1319
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
@@ -1175,6 +1328,7 @@ var MoltsPayServer = class {
|
|
|
1175
1328
|
res.end(JSON.stringify({
|
|
1176
1329
|
error: "Payment required",
|
|
1177
1330
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
1331
|
+
acceptedCurrencies: acceptedTokens,
|
|
1178
1332
|
x402: paymentRequired
|
|
1179
1333
|
}, null, 2));
|
|
1180
1334
|
}
|
|
@@ -1197,20 +1351,47 @@ var MoltsPayServer = class {
|
|
|
1197
1351
|
}
|
|
1198
1352
|
/**
|
|
1199
1353
|
* Build payment requirements for facilitator
|
|
1354
|
+
* Returns requirements for the primary currency (USDC by default)
|
|
1355
|
+
* Server accepts any of the acceptedCurrencies
|
|
1200
1356
|
*/
|
|
1201
|
-
buildPaymentRequirements(config) {
|
|
1357
|
+
buildPaymentRequirements(config, token) {
|
|
1202
1358
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1203
|
-
const
|
|
1359
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1360
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1361
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1362
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
1363
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1204
1364
|
return {
|
|
1205
1365
|
scheme: "exact",
|
|
1206
1366
|
network: this.networkId,
|
|
1207
|
-
asset:
|
|
1367
|
+
asset: tokenAddress,
|
|
1208
1368
|
amount: amountInUnits,
|
|
1209
1369
|
payTo: this.manifest.provider.wallet,
|
|
1210
1370
|
maxTimeoutSeconds: 300,
|
|
1211
|
-
extra:
|
|
1371
|
+
extra: tokenDomain
|
|
1212
1372
|
};
|
|
1213
1373
|
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Detect which token is being used in the payment
|
|
1376
|
+
*/
|
|
1377
|
+
detectPaymentToken(payment) {
|
|
1378
|
+
const asset = payment.accepted?.asset || payment.payload?.asset;
|
|
1379
|
+
if (!asset) return void 0;
|
|
1380
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1381
|
+
for (const [symbol, address] of Object.entries(tokenAddresses)) {
|
|
1382
|
+
if (address.toLowerCase() === asset.toLowerCase()) {
|
|
1383
|
+
return symbol;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return void 0;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Check if payment token is accepted for service
|
|
1390
|
+
*/
|
|
1391
|
+
isTokenAccepted(config, token) {
|
|
1392
|
+
const accepted = getAcceptedCurrencies(config);
|
|
1393
|
+
return accepted.includes(token);
|
|
1394
|
+
}
|
|
1214
1395
|
async readBody(req) {
|
|
1215
1396
|
return new Promise((resolve2, reject) => {
|
|
1216
1397
|
let body = "";
|
|
@@ -1328,9 +1509,15 @@ var MoltsPayServer = class {
|
|
|
1328
1509
|
error: `Service not found: ${service}`
|
|
1329
1510
|
});
|
|
1330
1511
|
}
|
|
1512
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
1331
1513
|
let result;
|
|
1332
1514
|
try {
|
|
1333
|
-
result = await
|
|
1515
|
+
result = await Promise.race([
|
|
1516
|
+
skill.handler(params || {}),
|
|
1517
|
+
new Promise(
|
|
1518
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
1519
|
+
)
|
|
1520
|
+
]);
|
|
1334
1521
|
console.log(`[MoltsPay] /proxy: Skill succeeded, now settling payment...`);
|
|
1335
1522
|
} catch (err) {
|
|
1336
1523
|
console.error(`[MoltsPay] /proxy: Skill failed: ${err.message} - NOT settling`);
|
|
@@ -1404,18 +1591,22 @@ var MoltsPayServer = class {
|
|
|
1404
1591
|
/**
|
|
1405
1592
|
* Build payment requirements for proxy endpoint (uses provided wallet)
|
|
1406
1593
|
*/
|
|
1407
|
-
buildProxyPaymentRequirements(config, wallet) {
|
|
1594
|
+
buildProxyPaymentRequirements(config, wallet, token) {
|
|
1408
1595
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1409
|
-
const
|
|
1596
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1597
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1598
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1599
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
1600
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1410
1601
|
return {
|
|
1411
1602
|
scheme: "exact",
|
|
1412
1603
|
network: this.networkId,
|
|
1413
|
-
asset:
|
|
1604
|
+
asset: tokenAddress,
|
|
1414
1605
|
amount: amountInUnits,
|
|
1415
1606
|
payTo: wallet,
|
|
1416
1607
|
// Use provided wallet, not manifest
|
|
1417
1608
|
maxTimeoutSeconds: 300,
|
|
1418
|
-
extra:
|
|
1609
|
+
extra: tokenDomain
|
|
1419
1610
|
};
|
|
1420
1611
|
}
|
|
1421
1612
|
/**
|
|
@@ -1548,7 +1739,7 @@ program.command("status").description("Show wallet status and balance").option("
|
|
|
1548
1739
|
return;
|
|
1549
1740
|
}
|
|
1550
1741
|
const config = client.getConfig();
|
|
1551
|
-
let balance = { usdc: 0, native: 0 };
|
|
1742
|
+
let balance = { usdc: 0, usdt: 0, native: 0 };
|
|
1552
1743
|
try {
|
|
1553
1744
|
balance = await client.getBalance();
|
|
1554
1745
|
} catch (err) {
|
|
@@ -1565,7 +1756,7 @@ program.command("status").description("Show wallet status and balance").option("
|
|
|
1565
1756
|
console.log("\n\u{1F4CA} MoltsPay Status\n");
|
|
1566
1757
|
console.log(` Wallet: ${client.address}`);
|
|
1567
1758
|
console.log(` Chain: ${config.chain}`);
|
|
1568
|
-
console.log(` Balance: ${balance.usdc.toFixed(2)} USDC`);
|
|
1759
|
+
console.log(` Balance: ${balance.usdc.toFixed(2)} USDC | ${balance.usdt.toFixed(2)} USDT`);
|
|
1569
1760
|
console.log(` Native: ${balance.native.toFixed(6)} ETH`);
|
|
1570
1761
|
console.log("");
|
|
1571
1762
|
console.log(" Limits:");
|
|
@@ -1809,7 +2000,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
1809
2000
|
process.exit(1);
|
|
1810
2001
|
}
|
|
1811
2002
|
});
|
|
1812
|
-
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("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
|
|
2003
|
+
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) => {
|
|
1813
2004
|
const client = new MoltsPayClient();
|
|
1814
2005
|
if (!client.isInitialized) {
|
|
1815
2006
|
console.error("\u274C Wallet not initialized. Run: npx moltspay init");
|
|
@@ -1844,6 +2035,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
1844
2035
|
process.exit(1);
|
|
1845
2036
|
}
|
|
1846
2037
|
const imageDisplay = params.image_url || (params.image_base64 ? `[local file: ${options.image}]` : null);
|
|
2038
|
+
const token = (options.token || "USDC").toUpperCase();
|
|
1847
2039
|
if (!options.json) {
|
|
1848
2040
|
console.log(`
|
|
1849
2041
|
\u{1F4B3} MoltsPay - Paying for service
|
|
@@ -1852,11 +2044,12 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
1852
2044
|
console.log(` Service: ${service}`);
|
|
1853
2045
|
console.log(` Prompt: ${params.prompt}`);
|
|
1854
2046
|
if (imageDisplay) console.log(` Image: ${imageDisplay}`);
|
|
2047
|
+
console.log(` Token: ${token}`);
|
|
1855
2048
|
console.log(` Wallet: ${client.address}`);
|
|
1856
2049
|
console.log("");
|
|
1857
2050
|
}
|
|
1858
2051
|
try {
|
|
1859
|
-
const result = await client.pay(server, service, params);
|
|
2052
|
+
const result = await client.pay(server, service, params, { token });
|
|
1860
2053
|
if (options.json) {
|
|
1861
2054
|
console.log(JSON.stringify(result));
|
|
1862
2055
|
} else {
|