moltspay 0.9.5 → 0.9.7

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/README.md +68 -45
  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 +201 -38
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/index.mjs +201 -38
  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 +112 -15
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/client/index.mjs +112 -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 +203 -42
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +203 -42
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/server/index.d.mts +19 -1
  33. package/dist/server/index.d.ts +19 -1
  34. package/dist/server/index.js +71 -19
  35. package/dist/server/index.js.map +1 -1
  36. package/dist/server/index.mjs +71 -19
  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,35 @@ 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
+ if (token === "USDT") {
297
+ const balances = await this.getBalance();
298
+ if (balances.native < 1e-4) {
299
+ throw new Error(
300
+ `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).`
301
+ );
302
+ }
303
+ console.log(`[MoltsPay] \u26A0\uFE0F USDT requires gas (~$0.01). Proceeding with payment...`);
304
+ } else {
305
+ console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
306
+ }
218
307
  const payTo = req.payTo || req.resource;
219
308
  if (!payTo) {
220
309
  throw new Error("Missing payTo address in payment requirements");
221
310
  }
222
- const authorization = await this.signEIP3009(payTo, amount, chain);
311
+ const authorization = await this.signEIP3009(payTo, amount, chain, token);
312
+ const tokenConfig = chain.tokens[token];
313
+ const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
223
314
  const payload = {
224
315
  x402Version: X402_VERSION,
225
316
  payload: authorization,
@@ -227,11 +318,11 @@ var MoltsPayClient = class {
227
318
  accepted: {
228
319
  scheme: "exact",
229
320
  network,
230
- asset: req.asset || chain.usdc,
321
+ asset: tokenConfig.address,
231
322
  amount: amountRaw,
232
323
  payTo,
233
324
  maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
234
- extra: req.extra || { name: "USD Coin", version: "2" }
325
+ extra: req.extra || { name: tokenName, version: "2" }
235
326
  }
236
327
  };
237
328
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
@@ -255,12 +346,14 @@ var MoltsPayClient = class {
255
346
  /**
256
347
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
257
348
  * This only signs - no on-chain transaction, no gas needed.
349
+ * Supports both USDC and USDT.
258
350
  */
259
- async signEIP3009(to, amount, chain) {
351
+ async signEIP3009(to, amount, chain, token = "USDC") {
260
352
  const validAfter = 0;
261
353
  const validBefore = Math.floor(Date.now() / 1e3) + 3600;
262
354
  const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
263
- const value = BigInt(Math.floor(amount * 1e6)).toString();
355
+ const tokenConfig = chain.tokens[token];
356
+ const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
264
357
  const authorization = {
265
358
  from: this.wallet.address,
266
359
  to,
@@ -269,11 +362,12 @@ var MoltsPayClient = class {
269
362
  validBefore: validBefore.toString(),
270
363
  nonce
271
364
  };
365
+ const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
272
366
  const domain = {
273
- name: "USD Coin",
367
+ name: tokenName,
274
368
  version: "2",
275
369
  chainId: chain.chainId,
276
- verifyingContract: chain.usdc
370
+ verifyingContract: tokenConfig.address
277
371
  };
278
372
  const types = {
279
373
  TransferWithAuthorization: [
@@ -408,7 +502,7 @@ var MoltsPayClient = class {
408
502
  return { address: wallet.address, configDir };
409
503
  }
410
504
  /**
411
- * Get wallet balance
505
+ * Get wallet balance (USDC, USDT, and native token)
412
506
  */
413
507
  async getBalance() {
414
508
  if (!this.wallet) {
@@ -421,12 +515,15 @@ var MoltsPayClient = class {
421
515
  throw new Error(`Unknown chain: ${this.config.chain}`);
422
516
  }
423
517
  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);
518
+ const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
519
+ const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
520
+ provider.getBalance(this.wallet.address),
521
+ new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
522
+ new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
523
+ ]);
428
524
  return {
429
- usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, 6)),
525
+ usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
526
+ usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
430
527
  native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
431
528
  };
432
529
  }
@@ -886,16 +983,24 @@ var X402_VERSION3 = 2;
886
983
  var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
887
984
  var PAYMENT_HEADER2 = "x-payment";
888
985
  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
986
+ var TOKEN_ADDRESSES = {
987
+ "eip155:8453": {
988
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
989
+ USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
990
+ },
991
+ "eip155:84532": {
992
+ USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
993
+ USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
994
+ // Same as USDC on testnet
995
+ }
894
996
  };
895
- var USDC_DOMAIN = {
896
- name: "USD Coin",
897
- version: "2"
997
+ var TOKEN_DOMAINS = {
998
+ USDC: { name: "USD Coin", version: "2" },
999
+ USDT: { name: "Tether USD", version: "2" }
898
1000
  };
1001
+ function getAcceptedCurrencies(config) {
1002
+ return config.acceptedCurrencies ?? [config.currency];
1003
+ }
899
1004
  function loadEnvFile2() {
900
1005
  const envPaths = [
901
1006
  path2.join(process.cwd(), ".env"),
@@ -1043,6 +1148,7 @@ var MoltsPayServer = class {
1043
1148
  description: s.description,
1044
1149
  price: s.price,
1045
1150
  currency: s.currency,
1151
+ acceptedCurrencies: getAcceptedCurrencies(s),
1046
1152
  input: s.input,
1047
1153
  output: s.output,
1048
1154
  available: this.skills.has(s.id)
@@ -1080,6 +1186,7 @@ var MoltsPayServer = class {
1080
1186
  description: s.description,
1081
1187
  price: s.price,
1082
1188
  currency: s.currency,
1189
+ acceptedCurrencies: getAcceptedCurrencies(s),
1083
1190
  input: s.input,
1084
1191
  output: s.output,
1085
1192
  available: this.skills.has(s.id)
@@ -1146,7 +1253,14 @@ var MoltsPayServer = class {
1146
1253
  if (!validation.valid) {
1147
1254
  return this.sendJson(res, 402, { error: validation.error });
1148
1255
  }
1149
- const requirements = this.buildPaymentRequirements(skill.config);
1256
+ const paymentToken = this.detectPaymentToken(payment);
1257
+ if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
1258
+ const accepted = getAcceptedCurrencies(skill.config);
1259
+ return this.sendJson(res, 402, {
1260
+ error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
1261
+ });
1262
+ }
1263
+ const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
1150
1264
  console.log(`[MoltsPay] Verifying payment...`);
1151
1265
  const verifyResult = await this.registry.verify(payment, requirements);
1152
1266
  if (!verifyResult.valid) {
@@ -1201,12 +1315,15 @@ var MoltsPayServer = class {
1201
1315
  }
1202
1316
  /**
1203
1317
  * Return 402 with x402 payment requirements (v2 format)
1318
+ * Includes requirements for all accepted currencies
1204
1319
  */
1205
1320
  sendPaymentRequired(config, res) {
1206
- const requirements = this.buildPaymentRequirements(config);
1321
+ const acceptedTokens = getAcceptedCurrencies(config);
1322
+ const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
1207
1323
  const paymentRequired = {
1208
1324
  x402Version: X402_VERSION3,
1209
- accepts: [requirements],
1325
+ accepts,
1326
+ acceptedCurrencies: acceptedTokens,
1210
1327
  resource: {
1211
1328
  url: `/execute?service=${config.id}`,
1212
1329
  description: `${config.name} - $${config.price} ${config.currency}`,
@@ -1221,6 +1338,7 @@ var MoltsPayServer = class {
1221
1338
  res.end(JSON.stringify({
1222
1339
  error: "Payment required",
1223
1340
  message: `Service requires $${config.price} ${config.currency}`,
1341
+ acceptedCurrencies: acceptedTokens,
1224
1342
  x402: paymentRequired
1225
1343
  }, null, 2));
1226
1344
  }
@@ -1243,20 +1361,47 @@ var MoltsPayServer = class {
1243
1361
  }
1244
1362
  /**
1245
1363
  * Build payment requirements for facilitator
1364
+ * Returns requirements for the primary currency (USDC by default)
1365
+ * Server accepts any of the acceptedCurrencies
1246
1366
  */
1247
- buildPaymentRequirements(config) {
1367
+ buildPaymentRequirements(config, token) {
1248
1368
  const amountInUnits = Math.floor(config.price * 1e6).toString();
1249
- const usdcAddress = USDC_ADDRESSES[this.networkId];
1369
+ const acceptedTokens = getAcceptedCurrencies(config);
1370
+ const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
1371
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1372
+ const tokenAddress = tokenAddresses[selectedToken];
1373
+ const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
1250
1374
  return {
1251
1375
  scheme: "exact",
1252
1376
  network: this.networkId,
1253
- asset: usdcAddress,
1377
+ asset: tokenAddress,
1254
1378
  amount: amountInUnits,
1255
1379
  payTo: this.manifest.provider.wallet,
1256
1380
  maxTimeoutSeconds: 300,
1257
- extra: USDC_DOMAIN
1381
+ extra: tokenDomain
1258
1382
  };
1259
1383
  }
1384
+ /**
1385
+ * Detect which token is being used in the payment
1386
+ */
1387
+ detectPaymentToken(payment) {
1388
+ const asset = payment.accepted?.asset || payment.payload?.asset;
1389
+ if (!asset) return void 0;
1390
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1391
+ for (const [symbol, address] of Object.entries(tokenAddresses)) {
1392
+ if (address.toLowerCase() === asset.toLowerCase()) {
1393
+ return symbol;
1394
+ }
1395
+ }
1396
+ return void 0;
1397
+ }
1398
+ /**
1399
+ * Check if payment token is accepted for service
1400
+ */
1401
+ isTokenAccepted(config, token) {
1402
+ const accepted = getAcceptedCurrencies(config);
1403
+ return accepted.includes(token);
1404
+ }
1260
1405
  async readBody(req) {
1261
1406
  return new Promise((resolve2, reject) => {
1262
1407
  let body = "";
@@ -1456,18 +1601,22 @@ var MoltsPayServer = class {
1456
1601
  /**
1457
1602
  * Build payment requirements for proxy endpoint (uses provided wallet)
1458
1603
  */
1459
- buildProxyPaymentRequirements(config, wallet) {
1604
+ buildProxyPaymentRequirements(config, wallet, token) {
1460
1605
  const amountInUnits = Math.floor(config.price * 1e6).toString();
1461
- const usdcAddress = USDC_ADDRESSES[this.networkId];
1606
+ const acceptedTokens = getAcceptedCurrencies(config);
1607
+ const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
1608
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1609
+ const tokenAddress = tokenAddresses[selectedToken];
1610
+ const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
1462
1611
  return {
1463
1612
  scheme: "exact",
1464
1613
  network: this.networkId,
1465
- asset: usdcAddress,
1614
+ asset: tokenAddress,
1466
1615
  amount: amountInUnits,
1467
1616
  payTo: wallet,
1468
1617
  // Use provided wallet, not manifest
1469
1618
  maxTimeoutSeconds: 300,
1470
- extra: USDC_DOMAIN
1619
+ extra: tokenDomain
1471
1620
  };
1472
1621
  }
1473
1622
  /**
@@ -1600,7 +1749,7 @@ program.command("status").description("Show wallet status and balance").option("
1600
1749
  return;
1601
1750
  }
1602
1751
  const config = client.getConfig();
1603
- let balance = { usdc: 0, native: 0 };
1752
+ let balance = { usdc: 0, usdt: 0, native: 0 };
1604
1753
  try {
1605
1754
  balance = await client.getBalance();
1606
1755
  } catch (err) {
@@ -1617,7 +1766,7 @@ program.command("status").description("Show wallet status and balance").option("
1617
1766
  console.log("\n\u{1F4CA} MoltsPay Status\n");
1618
1767
  console.log(` Wallet: ${client.address}`);
1619
1768
  console.log(` Chain: ${config.chain}`);
1620
- console.log(` Balance: ${balance.usdc.toFixed(2)} USDC`);
1769
+ console.log(` Balance: ${balance.usdc.toFixed(2)} USDC | ${balance.usdt.toFixed(2)} USDT`);
1621
1770
  console.log(` Native: ${balance.native.toFixed(6)} ETH`);
1622
1771
  console.log("");
1623
1772
  console.log(" Limits:");
@@ -1861,7 +2010,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
1861
2010
  process.exit(1);
1862
2011
  }
1863
2012
  });
1864
- 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) => {
2013
+ 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) => {
1865
2014
  const client = new MoltsPayClient();
1866
2015
  if (!client.isInitialized) {
1867
2016
  console.error("\u274C Wallet not initialized. Run: npx moltspay init");
@@ -1896,6 +2045,19 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
1896
2045
  process.exit(1);
1897
2046
  }
1898
2047
  const imageDisplay = params.image_url || (params.image_base64 ? `[local file: ${options.image}]` : null);
2048
+ const token = (options.token || "USDC").toUpperCase();
2049
+ if (token === "USDT") {
2050
+ const balance = await client.getBalance();
2051
+ if (balance.native < 1e-4) {
2052
+ console.log("\n\u26A0\uFE0F USDT requires a small amount of ETH for gas (~$0.01)");
2053
+ console.log(` Your ETH balance: ${balance.native.toFixed(6)} ETH`);
2054
+ console.log(" Please add a tiny amount of ETH to your wallet.\n");
2055
+ process.exit(1);
2056
+ }
2057
+ if (!options.json) {
2058
+ console.log("\n\u26A0\uFE0F Note: USDT payments require gas (~$0.01 on Base)");
2059
+ }
2060
+ }
1899
2061
  if (!options.json) {
1900
2062
  console.log(`
1901
2063
  \u{1F4B3} MoltsPay - Paying for service
@@ -1904,11 +2066,12 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
1904
2066
  console.log(` Service: ${service}`);
1905
2067
  console.log(` Prompt: ${params.prompt}`);
1906
2068
  if (imageDisplay) console.log(` Image: ${imageDisplay}`);
2069
+ console.log(` Token: ${token}`);
1907
2070
  console.log(` Wallet: ${client.address}`);
1908
2071
  console.log("");
1909
2072
  }
1910
2073
  try {
1911
- const result = await client.pay(server, service, params);
2074
+ const result = await client.pay(server, service, params, { token });
1912
2075
  if (options.json) {
1913
2076
  console.log(JSON.stringify(result));
1914
2077
  } else {