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.
Files changed (51) hide show
  1. package/.env.example +7 -0
  2. package/dist/cdp/index.d.mts +1 -1
  3. package/dist/cdp/index.d.ts +1 -1
  4. package/dist/cdp/index.js +63 -0
  5. package/dist/cdp/index.js.map +1 -1
  6. package/dist/cdp/index.mjs +63 -0
  7. package/dist/cdp/index.mjs.map +1 -1
  8. package/dist/chains/index.d.mts +9 -5
  9. package/dist/chains/index.d.ts +9 -5
  10. package/dist/chains/index.js +85 -0
  11. package/dist/chains/index.js.map +1 -1
  12. package/dist/chains/index.mjs +83 -0
  13. package/dist/chains/index.mjs.map +1 -1
  14. package/dist/cli/index.js +234 -41
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/index.mjs +234 -41
  17. package/dist/cli/index.mjs.map +1 -1
  18. package/dist/client/index.d.mts +18 -3
  19. package/dist/client/index.d.ts +18 -3
  20. package/dist/client/index.js +102 -15
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/client/index.mjs +102 -15
  23. package/dist/client/index.mjs.map +1 -1
  24. package/dist/{index-Dg8n6wdW.d.mts → index-B3v8IWjM.d.mts} +11 -1
  25. package/dist/{index-Dg8n6wdW.d.ts → index-B3v8IWjM.d.ts} +11 -1
  26. package/dist/index.d.mts +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +248 -45
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +248 -45
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/server/index.d.mts +23 -1
  33. package/dist/server/index.d.ts +23 -1
  34. package/dist/server/index.js +126 -22
  35. package/dist/server/index.js.map +1 -1
  36. package/dist/server/index.mjs +126 -22
  37. package/dist/server/index.mjs.map +1 -1
  38. package/dist/verify/index.d.mts +7 -0
  39. package/dist/verify/index.d.ts +7 -0
  40. package/dist/verify/index.js +83 -8
  41. package/dist/verify/index.js.map +1 -1
  42. package/dist/verify/index.mjs +83 -8
  43. package/dist/verify/index.mjs.map +1 -1
  44. package/dist/wallet/index.d.mts +16 -8
  45. package/dist/wallet/index.d.ts +16 -8
  46. package/dist/wallet/index.js +114 -18
  47. package/dist/wallet/index.js.map +1 -1
  48. package/dist/wallet/index.mjs +114 -18
  49. package/dist/wallet/index.mjs.map +1 -1
  50. package/package.json +1 -1
  51. 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
- console.log(`[MoltsPay] Signing payment: $${amount} USDC (gasless)`);
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: req.asset || chain.usdc,
311
+ asset: tokenConfig.address,
231
312
  amount: amountRaw,
232
313
  payTo,
233
314
  maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
234
- extra: req.extra || { name: "USD Coin", version: "2" }
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 value = BigInt(Math.floor(amount * 1e6)).toString();
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: "USD Coin",
357
+ name: tokenName,
274
358
  version: "2",
275
359
  chainId: chain.chainId,
276
- verifyingContract: chain.usdc
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 nativeBalance = await provider.getBalance(this.wallet.address);
425
- const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
426
- const usdc = new import_ethers.ethers.Contract(chain.usdc, usdcAbi, provider);
427
- const usdcBalance = await usdc.balanceOf(this.wallet.address);
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, 6)),
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 USDC_ADDRESSES = {
890
- "eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
891
- // Base mainnet
892
- "eip155:84532": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
893
- // Base Sepolia
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 USDC_DOMAIN = {
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 requirements = this.buildPaymentRequirements(skill.config);
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
- console.log(`[MoltsPay] Executing skill: ${service}`);
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 skill.handler(params || {});
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 requirements = this.buildPaymentRequirements(config);
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: [requirements],
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 usdcAddress = USDC_ADDRESSES[this.networkId];
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: usdcAddress,
1367
+ asset: tokenAddress,
1208
1368
  amount: amountInUnits,
1209
1369
  payTo: this.manifest.provider.wallet,
1210
1370
  maxTimeoutSeconds: 300,
1211
- extra: USDC_DOMAIN
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 skill.handler(params || {});
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 usdcAddress = USDC_ADDRESSES[this.networkId];
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: usdcAddress,
1604
+ asset: tokenAddress,
1414
1605
  amount: amountInUnits,
1415
1606
  payTo: wallet,
1416
1607
  // Use provided wallet, not manifest
1417
1608
  maxTimeoutSeconds: 300,
1418
- extra: USDC_DOMAIN
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 {