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.mjs
CHANGED
|
@@ -20,7 +20,20 @@ var CHAINS = {
|
|
|
20
20
|
name: "Base",
|
|
21
21
|
chainId: 8453,
|
|
22
22
|
rpc: "https://mainnet.base.org",
|
|
23
|
+
tokens: {
|
|
24
|
+
USDC: {
|
|
25
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
26
|
+
decimals: 6,
|
|
27
|
+
symbol: "USDC"
|
|
28
|
+
},
|
|
29
|
+
USDT: {
|
|
30
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
31
|
+
decimals: 6,
|
|
32
|
+
symbol: "USDT"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
23
35
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
36
|
+
// deprecated, for backward compat
|
|
24
37
|
explorer: "https://basescan.org/address/",
|
|
25
38
|
explorerTx: "https://basescan.org/tx/",
|
|
26
39
|
avgBlockTime: 2
|
|
@@ -29,6 +42,18 @@ var CHAINS = {
|
|
|
29
42
|
name: "Polygon",
|
|
30
43
|
chainId: 137,
|
|
31
44
|
rpc: "https://polygon-rpc.com",
|
|
45
|
+
tokens: {
|
|
46
|
+
USDC: {
|
|
47
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
48
|
+
decimals: 6,
|
|
49
|
+
symbol: "USDC"
|
|
50
|
+
},
|
|
51
|
+
USDT: {
|
|
52
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
53
|
+
decimals: 6,
|
|
54
|
+
symbol: "USDT"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
32
57
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
33
58
|
explorer: "https://polygonscan.com/address/",
|
|
34
59
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -38,6 +63,18 @@ var CHAINS = {
|
|
|
38
63
|
name: "Ethereum",
|
|
39
64
|
chainId: 1,
|
|
40
65
|
rpc: "https://eth.llamarpc.com",
|
|
66
|
+
tokens: {
|
|
67
|
+
USDC: {
|
|
68
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
69
|
+
decimals: 6,
|
|
70
|
+
symbol: "USDC"
|
|
71
|
+
},
|
|
72
|
+
USDT: {
|
|
73
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
74
|
+
decimals: 6,
|
|
75
|
+
symbol: "USDT"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
41
78
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
42
79
|
explorer: "https://etherscan.io/address/",
|
|
43
80
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -48,6 +85,19 @@ var CHAINS = {
|
|
|
48
85
|
name: "Base Sepolia",
|
|
49
86
|
chainId: 84532,
|
|
50
87
|
rpc: "https://sepolia.base.org",
|
|
88
|
+
tokens: {
|
|
89
|
+
USDC: {
|
|
90
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
91
|
+
decimals: 6,
|
|
92
|
+
symbol: "USDC"
|
|
93
|
+
},
|
|
94
|
+
USDT: {
|
|
95
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
96
|
+
// Same as USDC on testnet (no official USDT)
|
|
97
|
+
decimals: 6,
|
|
98
|
+
symbol: "USDT"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
51
101
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
52
102
|
explorer: "https://sepolia.basescan.org/address/",
|
|
53
103
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -57,6 +107,19 @@ var CHAINS = {
|
|
|
57
107
|
name: "Sepolia",
|
|
58
108
|
chainId: 11155111,
|
|
59
109
|
rpc: "https://rpc.sepolia.org",
|
|
110
|
+
tokens: {
|
|
111
|
+
USDC: {
|
|
112
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
113
|
+
decimals: 6,
|
|
114
|
+
symbol: "USDC"
|
|
115
|
+
},
|
|
116
|
+
USDT: {
|
|
117
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
118
|
+
// Same as USDC on testnet
|
|
119
|
+
decimals: 6,
|
|
120
|
+
symbol: "USDT"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
60
123
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
61
124
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
62
125
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -143,8 +206,13 @@ var MoltsPayClient = class {
|
|
|
143
206
|
*
|
|
144
207
|
* This is GASLESS for the client - server pays gas to claim payment.
|
|
145
208
|
* This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.
|
|
209
|
+
*
|
|
210
|
+
* @param serverUrl - Server URL
|
|
211
|
+
* @param service - Service ID
|
|
212
|
+
* @param params - Service parameters
|
|
213
|
+
* @param options - Payment options (token selection)
|
|
146
214
|
*/
|
|
147
|
-
async pay(serverUrl, service, params) {
|
|
215
|
+
async pay(serverUrl, service, params, options = {}) {
|
|
148
216
|
if (!this.wallet || !this.walletData) {
|
|
149
217
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
150
218
|
}
|
|
@@ -191,12 +259,25 @@ var MoltsPayClient = class {
|
|
|
191
259
|
}
|
|
192
260
|
const amount = Number(amountRaw) / 1e6;
|
|
193
261
|
this.checkLimits(amount);
|
|
194
|
-
|
|
262
|
+
let token = options.token || "USDC";
|
|
263
|
+
if (options.autoSelect) {
|
|
264
|
+
const balances = await this.getBalance();
|
|
265
|
+
if (balances.usdc >= amount) {
|
|
266
|
+
token = "USDC";
|
|
267
|
+
} else if (balances.usdt >= amount) {
|
|
268
|
+
token = "USDT";
|
|
269
|
+
} else {
|
|
270
|
+
throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
|
|
195
274
|
const payTo = req.payTo || req.resource;
|
|
196
275
|
if (!payTo) {
|
|
197
276
|
throw new Error("Missing payTo address in payment requirements");
|
|
198
277
|
}
|
|
199
|
-
const authorization = await this.signEIP3009(payTo, amount, chain);
|
|
278
|
+
const authorization = await this.signEIP3009(payTo, amount, chain, token);
|
|
279
|
+
const tokenConfig = chain.tokens[token];
|
|
280
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
200
281
|
const payload = {
|
|
201
282
|
x402Version: X402_VERSION,
|
|
202
283
|
payload: authorization,
|
|
@@ -204,11 +285,11 @@ var MoltsPayClient = class {
|
|
|
204
285
|
accepted: {
|
|
205
286
|
scheme: "exact",
|
|
206
287
|
network,
|
|
207
|
-
asset:
|
|
288
|
+
asset: tokenConfig.address,
|
|
208
289
|
amount: amountRaw,
|
|
209
290
|
payTo,
|
|
210
291
|
maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
|
|
211
|
-
extra: req.extra || { name:
|
|
292
|
+
extra: req.extra || { name: tokenName, version: "2" }
|
|
212
293
|
}
|
|
213
294
|
};
|
|
214
295
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
@@ -232,12 +313,14 @@ var MoltsPayClient = class {
|
|
|
232
313
|
/**
|
|
233
314
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
234
315
|
* This only signs - no on-chain transaction, no gas needed.
|
|
316
|
+
* Supports both USDC and USDT.
|
|
235
317
|
*/
|
|
236
|
-
async signEIP3009(to, amount, chain) {
|
|
318
|
+
async signEIP3009(to, amount, chain, token = "USDC") {
|
|
237
319
|
const validAfter = 0;
|
|
238
320
|
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
239
321
|
const nonce = ethers.hexlify(ethers.randomBytes(32));
|
|
240
|
-
const
|
|
322
|
+
const tokenConfig = chain.tokens[token];
|
|
323
|
+
const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
241
324
|
const authorization = {
|
|
242
325
|
from: this.wallet.address,
|
|
243
326
|
to,
|
|
@@ -246,11 +329,12 @@ var MoltsPayClient = class {
|
|
|
246
329
|
validBefore: validBefore.toString(),
|
|
247
330
|
nonce
|
|
248
331
|
};
|
|
332
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
249
333
|
const domain = {
|
|
250
|
-
name:
|
|
334
|
+
name: tokenName,
|
|
251
335
|
version: "2",
|
|
252
336
|
chainId: chain.chainId,
|
|
253
|
-
verifyingContract:
|
|
337
|
+
verifyingContract: tokenConfig.address
|
|
254
338
|
};
|
|
255
339
|
const types = {
|
|
256
340
|
TransferWithAuthorization: [
|
|
@@ -385,7 +469,7 @@ var MoltsPayClient = class {
|
|
|
385
469
|
return { address: wallet.address, configDir };
|
|
386
470
|
}
|
|
387
471
|
/**
|
|
388
|
-
* Get wallet balance
|
|
472
|
+
* Get wallet balance (USDC, USDT, and native token)
|
|
389
473
|
*/
|
|
390
474
|
async getBalance() {
|
|
391
475
|
if (!this.wallet) {
|
|
@@ -398,12 +482,15 @@ var MoltsPayClient = class {
|
|
|
398
482
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
399
483
|
}
|
|
400
484
|
const provider = new ethers.JsonRpcProvider(chain.rpc);
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
485
|
+
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
486
|
+
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
487
|
+
provider.getBalance(this.wallet.address),
|
|
488
|
+
new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
489
|
+
new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
490
|
+
]);
|
|
405
491
|
return {
|
|
406
|
-
usdc: parseFloat(ethers.formatUnits(usdcBalance,
|
|
492
|
+
usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
|
|
493
|
+
usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
|
|
407
494
|
native: parseFloat(ethers.formatEther(nativeBalance))
|
|
408
495
|
};
|
|
409
496
|
}
|
|
@@ -863,16 +950,24 @@ var X402_VERSION3 = 2;
|
|
|
863
950
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
864
951
|
var PAYMENT_HEADER2 = "x-payment";
|
|
865
952
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
866
|
-
var
|
|
867
|
-
"eip155:8453":
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
953
|
+
var TOKEN_ADDRESSES = {
|
|
954
|
+
"eip155:8453": {
|
|
955
|
+
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
956
|
+
USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
|
|
957
|
+
},
|
|
958
|
+
"eip155:84532": {
|
|
959
|
+
USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
960
|
+
USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
961
|
+
// Same as USDC on testnet
|
|
962
|
+
}
|
|
871
963
|
};
|
|
872
|
-
var
|
|
873
|
-
name: "USD Coin",
|
|
874
|
-
version: "2"
|
|
964
|
+
var TOKEN_DOMAINS = {
|
|
965
|
+
USDC: { name: "USD Coin", version: "2" },
|
|
966
|
+
USDT: { name: "Tether USD", version: "2" }
|
|
875
967
|
};
|
|
968
|
+
function getAcceptedCurrencies(config) {
|
|
969
|
+
return config.acceptedCurrencies ?? [config.currency];
|
|
970
|
+
}
|
|
876
971
|
function loadEnvFile2() {
|
|
877
972
|
const envPaths = [
|
|
878
973
|
path2.join(process.cwd(), ".env"),
|
|
@@ -984,6 +1079,9 @@ var MoltsPayServer = class {
|
|
|
984
1079
|
if (url.pathname === "/services" && req.method === "GET") {
|
|
985
1080
|
return this.handleGetServices(res);
|
|
986
1081
|
}
|
|
1082
|
+
if (url.pathname === "/.well-known/agent-services.json" && req.method === "GET") {
|
|
1083
|
+
return this.handleAgentServicesDiscovery(res);
|
|
1084
|
+
}
|
|
987
1085
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
988
1086
|
return await this.handleHealthCheck(res);
|
|
989
1087
|
}
|
|
@@ -1007,6 +1105,44 @@ var MoltsPayServer = class {
|
|
|
1007
1105
|
this.sendJson(res, 500, { error: err.message || "Internal error" });
|
|
1008
1106
|
}
|
|
1009
1107
|
}
|
|
1108
|
+
/**
|
|
1109
|
+
* GET /.well-known/agent-services.json - Standard discovery endpoint
|
|
1110
|
+
*/
|
|
1111
|
+
handleAgentServicesDiscovery(res) {
|
|
1112
|
+
const services = this.manifest.services.map((s) => ({
|
|
1113
|
+
id: s.id,
|
|
1114
|
+
name: s.name,
|
|
1115
|
+
description: s.description,
|
|
1116
|
+
price: s.price,
|
|
1117
|
+
currency: s.currency,
|
|
1118
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
1119
|
+
input: s.input,
|
|
1120
|
+
output: s.output,
|
|
1121
|
+
available: this.skills.has(s.id)
|
|
1122
|
+
}));
|
|
1123
|
+
this.sendJson(res, 200, {
|
|
1124
|
+
version: "1.0",
|
|
1125
|
+
provider: {
|
|
1126
|
+
name: this.manifest.provider.name,
|
|
1127
|
+
description: this.manifest.provider.description,
|
|
1128
|
+
wallet: this.manifest.provider.wallet,
|
|
1129
|
+
chain: this.manifest.provider.chain || "base"
|
|
1130
|
+
},
|
|
1131
|
+
services,
|
|
1132
|
+
endpoints: {
|
|
1133
|
+
services: "/services",
|
|
1134
|
+
execute: "/execute",
|
|
1135
|
+
health: "/health"
|
|
1136
|
+
},
|
|
1137
|
+
payment: {
|
|
1138
|
+
protocol: "x402",
|
|
1139
|
+
version: X402_VERSION3,
|
|
1140
|
+
network: this.networkId,
|
|
1141
|
+
schemes: ["exact"],
|
|
1142
|
+
mainnet: this.useMainnet
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1010
1146
|
/**
|
|
1011
1147
|
* GET /services - List available services
|
|
1012
1148
|
*/
|
|
@@ -1017,6 +1153,7 @@ var MoltsPayServer = class {
|
|
|
1017
1153
|
description: s.description,
|
|
1018
1154
|
price: s.price,
|
|
1019
1155
|
currency: s.currency,
|
|
1156
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
1020
1157
|
input: s.input,
|
|
1021
1158
|
output: s.output,
|
|
1022
1159
|
available: this.skills.has(s.id)
|
|
@@ -1083,7 +1220,14 @@ var MoltsPayServer = class {
|
|
|
1083
1220
|
if (!validation.valid) {
|
|
1084
1221
|
return this.sendJson(res, 402, { error: validation.error });
|
|
1085
1222
|
}
|
|
1086
|
-
const
|
|
1223
|
+
const paymentToken = this.detectPaymentToken(payment);
|
|
1224
|
+
if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
|
|
1225
|
+
const accepted = getAcceptedCurrencies(skill.config);
|
|
1226
|
+
return this.sendJson(res, 402, {
|
|
1227
|
+
error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
|
|
1087
1231
|
console.log(`[MoltsPay] Verifying payment...`);
|
|
1088
1232
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
1089
1233
|
if (!verifyResult.valid) {
|
|
@@ -1093,10 +1237,16 @@ var MoltsPayServer = class {
|
|
|
1093
1237
|
});
|
|
1094
1238
|
}
|
|
1095
1239
|
console.log(`[MoltsPay] Verified by ${verifyResult.facilitator}`);
|
|
1096
|
-
|
|
1240
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
1241
|
+
console.log(`[MoltsPay] Executing skill: ${service} (timeout: ${timeoutSeconds}s)`);
|
|
1097
1242
|
let result;
|
|
1098
1243
|
try {
|
|
1099
|
-
result = await
|
|
1244
|
+
result = await Promise.race([
|
|
1245
|
+
skill.handler(params || {}),
|
|
1246
|
+
new Promise(
|
|
1247
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
1248
|
+
)
|
|
1249
|
+
]);
|
|
1100
1250
|
} catch (err) {
|
|
1101
1251
|
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
1102
1252
|
return this.sendJson(res, 500, {
|
|
@@ -1132,12 +1282,15 @@ var MoltsPayServer = class {
|
|
|
1132
1282
|
}
|
|
1133
1283
|
/**
|
|
1134
1284
|
* Return 402 with x402 payment requirements (v2 format)
|
|
1285
|
+
* Includes requirements for all accepted currencies
|
|
1135
1286
|
*/
|
|
1136
1287
|
sendPaymentRequired(config, res) {
|
|
1137
|
-
const
|
|
1288
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1289
|
+
const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
|
|
1138
1290
|
const paymentRequired = {
|
|
1139
1291
|
x402Version: X402_VERSION3,
|
|
1140
|
-
accepts
|
|
1292
|
+
accepts,
|
|
1293
|
+
acceptedCurrencies: acceptedTokens,
|
|
1141
1294
|
resource: {
|
|
1142
1295
|
url: `/execute?service=${config.id}`,
|
|
1143
1296
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
@@ -1152,6 +1305,7 @@ var MoltsPayServer = class {
|
|
|
1152
1305
|
res.end(JSON.stringify({
|
|
1153
1306
|
error: "Payment required",
|
|
1154
1307
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
1308
|
+
acceptedCurrencies: acceptedTokens,
|
|
1155
1309
|
x402: paymentRequired
|
|
1156
1310
|
}, null, 2));
|
|
1157
1311
|
}
|
|
@@ -1174,20 +1328,47 @@ var MoltsPayServer = class {
|
|
|
1174
1328
|
}
|
|
1175
1329
|
/**
|
|
1176
1330
|
* Build payment requirements for facilitator
|
|
1331
|
+
* Returns requirements for the primary currency (USDC by default)
|
|
1332
|
+
* Server accepts any of the acceptedCurrencies
|
|
1177
1333
|
*/
|
|
1178
|
-
buildPaymentRequirements(config) {
|
|
1334
|
+
buildPaymentRequirements(config, token) {
|
|
1179
1335
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1180
|
-
const
|
|
1336
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1337
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1338
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1339
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
1340
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1181
1341
|
return {
|
|
1182
1342
|
scheme: "exact",
|
|
1183
1343
|
network: this.networkId,
|
|
1184
|
-
asset:
|
|
1344
|
+
asset: tokenAddress,
|
|
1185
1345
|
amount: amountInUnits,
|
|
1186
1346
|
payTo: this.manifest.provider.wallet,
|
|
1187
1347
|
maxTimeoutSeconds: 300,
|
|
1188
|
-
extra:
|
|
1348
|
+
extra: tokenDomain
|
|
1189
1349
|
};
|
|
1190
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Detect which token is being used in the payment
|
|
1353
|
+
*/
|
|
1354
|
+
detectPaymentToken(payment) {
|
|
1355
|
+
const asset = payment.accepted?.asset || payment.payload?.asset;
|
|
1356
|
+
if (!asset) return void 0;
|
|
1357
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1358
|
+
for (const [symbol, address] of Object.entries(tokenAddresses)) {
|
|
1359
|
+
if (address.toLowerCase() === asset.toLowerCase()) {
|
|
1360
|
+
return symbol;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return void 0;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Check if payment token is accepted for service
|
|
1367
|
+
*/
|
|
1368
|
+
isTokenAccepted(config, token) {
|
|
1369
|
+
const accepted = getAcceptedCurrencies(config);
|
|
1370
|
+
return accepted.includes(token);
|
|
1371
|
+
}
|
|
1191
1372
|
async readBody(req) {
|
|
1192
1373
|
return new Promise((resolve2, reject) => {
|
|
1193
1374
|
let body = "";
|
|
@@ -1305,9 +1486,15 @@ var MoltsPayServer = class {
|
|
|
1305
1486
|
error: `Service not found: ${service}`
|
|
1306
1487
|
});
|
|
1307
1488
|
}
|
|
1489
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
1308
1490
|
let result;
|
|
1309
1491
|
try {
|
|
1310
|
-
result = await
|
|
1492
|
+
result = await Promise.race([
|
|
1493
|
+
skill.handler(params || {}),
|
|
1494
|
+
new Promise(
|
|
1495
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
1496
|
+
)
|
|
1497
|
+
]);
|
|
1311
1498
|
console.log(`[MoltsPay] /proxy: Skill succeeded, now settling payment...`);
|
|
1312
1499
|
} catch (err) {
|
|
1313
1500
|
console.error(`[MoltsPay] /proxy: Skill failed: ${err.message} - NOT settling`);
|
|
@@ -1381,18 +1568,22 @@ var MoltsPayServer = class {
|
|
|
1381
1568
|
/**
|
|
1382
1569
|
* Build payment requirements for proxy endpoint (uses provided wallet)
|
|
1383
1570
|
*/
|
|
1384
|
-
buildProxyPaymentRequirements(config, wallet) {
|
|
1571
|
+
buildProxyPaymentRequirements(config, wallet, token) {
|
|
1385
1572
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
1386
|
-
const
|
|
1573
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
1574
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
1575
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
1576
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
1577
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
1387
1578
|
return {
|
|
1388
1579
|
scheme: "exact",
|
|
1389
1580
|
network: this.networkId,
|
|
1390
|
-
asset:
|
|
1581
|
+
asset: tokenAddress,
|
|
1391
1582
|
amount: amountInUnits,
|
|
1392
1583
|
payTo: wallet,
|
|
1393
1584
|
// Use provided wallet, not manifest
|
|
1394
1585
|
maxTimeoutSeconds: 300,
|
|
1395
|
-
extra:
|
|
1586
|
+
extra: tokenDomain
|
|
1396
1587
|
};
|
|
1397
1588
|
}
|
|
1398
1589
|
/**
|
|
@@ -1525,7 +1716,7 @@ program.command("status").description("Show wallet status and balance").option("
|
|
|
1525
1716
|
return;
|
|
1526
1717
|
}
|
|
1527
1718
|
const config = client.getConfig();
|
|
1528
|
-
let balance = { usdc: 0, native: 0 };
|
|
1719
|
+
let balance = { usdc: 0, usdt: 0, native: 0 };
|
|
1529
1720
|
try {
|
|
1530
1721
|
balance = await client.getBalance();
|
|
1531
1722
|
} catch (err) {
|
|
@@ -1542,7 +1733,7 @@ program.command("status").description("Show wallet status and balance").option("
|
|
|
1542
1733
|
console.log("\n\u{1F4CA} MoltsPay Status\n");
|
|
1543
1734
|
console.log(` Wallet: ${client.address}`);
|
|
1544
1735
|
console.log(` Chain: ${config.chain}`);
|
|
1545
|
-
console.log(` Balance: ${balance.usdc.toFixed(2)} USDC`);
|
|
1736
|
+
console.log(` Balance: ${balance.usdc.toFixed(2)} USDC | ${balance.usdt.toFixed(2)} USDT`);
|
|
1546
1737
|
console.log(` Native: ${balance.native.toFixed(6)} ETH`);
|
|
1547
1738
|
console.log("");
|
|
1548
1739
|
console.log(" Limits:");
|
|
@@ -1786,7 +1977,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
1786
1977
|
process.exit(1);
|
|
1787
1978
|
}
|
|
1788
1979
|
});
|
|
1789
|
-
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) => {
|
|
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) => {
|
|
1790
1981
|
const client = new MoltsPayClient();
|
|
1791
1982
|
if (!client.isInitialized) {
|
|
1792
1983
|
console.error("\u274C Wallet not initialized. Run: npx moltspay init");
|
|
@@ -1821,6 +2012,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
1821
2012
|
process.exit(1);
|
|
1822
2013
|
}
|
|
1823
2014
|
const imageDisplay = params.image_url || (params.image_base64 ? `[local file: ${options.image}]` : null);
|
|
2015
|
+
const token = (options.token || "USDC").toUpperCase();
|
|
1824
2016
|
if (!options.json) {
|
|
1825
2017
|
console.log(`
|
|
1826
2018
|
\u{1F4B3} MoltsPay - Paying for service
|
|
@@ -1829,11 +2021,12 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
1829
2021
|
console.log(` Service: ${service}`);
|
|
1830
2022
|
console.log(` Prompt: ${params.prompt}`);
|
|
1831
2023
|
if (imageDisplay) console.log(` Image: ${imageDisplay}`);
|
|
2024
|
+
console.log(` Token: ${token}`);
|
|
1832
2025
|
console.log(` Wallet: ${client.address}`);
|
|
1833
2026
|
console.log("");
|
|
1834
2027
|
}
|
|
1835
2028
|
try {
|
|
1836
|
-
const result = await client.pay(server, service, params);
|
|
2029
|
+
const result = await client.pay(server, service, params, { token });
|
|
1837
2030
|
if (options.json) {
|
|
1838
2031
|
console.log(JSON.stringify(result));
|
|
1839
2032
|
} else {
|