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
@@ -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,35 @@ var MoltsPayClient = class {
191
259
  }
192
260
  const amount = Number(amountRaw) / 1e6;
193
261
  this.checkLimits(amount);
194
- console.log(`[MoltsPay] Signing payment: $${amount} USDC (gasless)`);
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
+ if (token === "USDT") {
274
+ const balances = await this.getBalance();
275
+ if (balances.native < 1e-4) {
276
+ throw new Error(
277
+ `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).`
278
+ );
279
+ }
280
+ console.log(`[MoltsPay] \u26A0\uFE0F USDT requires gas (~$0.01). Proceeding with payment...`);
281
+ } else {
282
+ console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
283
+ }
195
284
  const payTo = req.payTo || req.resource;
196
285
  if (!payTo) {
197
286
  throw new Error("Missing payTo address in payment requirements");
198
287
  }
199
- const authorization = await this.signEIP3009(payTo, amount, chain);
288
+ const authorization = await this.signEIP3009(payTo, amount, chain, token);
289
+ const tokenConfig = chain.tokens[token];
290
+ const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
200
291
  const payload = {
201
292
  x402Version: X402_VERSION,
202
293
  payload: authorization,
@@ -204,11 +295,11 @@ var MoltsPayClient = class {
204
295
  accepted: {
205
296
  scheme: "exact",
206
297
  network,
207
- asset: req.asset || chain.usdc,
298
+ asset: tokenConfig.address,
208
299
  amount: amountRaw,
209
300
  payTo,
210
301
  maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
211
- extra: req.extra || { name: "USD Coin", version: "2" }
302
+ extra: req.extra || { name: tokenName, version: "2" }
212
303
  }
213
304
  };
214
305
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
@@ -232,12 +323,14 @@ var MoltsPayClient = class {
232
323
  /**
233
324
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
234
325
  * This only signs - no on-chain transaction, no gas needed.
326
+ * Supports both USDC and USDT.
235
327
  */
236
- async signEIP3009(to, amount, chain) {
328
+ async signEIP3009(to, amount, chain, token = "USDC") {
237
329
  const validAfter = 0;
238
330
  const validBefore = Math.floor(Date.now() / 1e3) + 3600;
239
331
  const nonce = ethers.hexlify(ethers.randomBytes(32));
240
- const value = BigInt(Math.floor(amount * 1e6)).toString();
332
+ const tokenConfig = chain.tokens[token];
333
+ const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
241
334
  const authorization = {
242
335
  from: this.wallet.address,
243
336
  to,
@@ -246,11 +339,12 @@ var MoltsPayClient = class {
246
339
  validBefore: validBefore.toString(),
247
340
  nonce
248
341
  };
342
+ const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
249
343
  const domain = {
250
- name: "USD Coin",
344
+ name: tokenName,
251
345
  version: "2",
252
346
  chainId: chain.chainId,
253
- verifyingContract: chain.usdc
347
+ verifyingContract: tokenConfig.address
254
348
  };
255
349
  const types = {
256
350
  TransferWithAuthorization: [
@@ -385,7 +479,7 @@ var MoltsPayClient = class {
385
479
  return { address: wallet.address, configDir };
386
480
  }
387
481
  /**
388
- * Get wallet balance
482
+ * Get wallet balance (USDC, USDT, and native token)
389
483
  */
390
484
  async getBalance() {
391
485
  if (!this.wallet) {
@@ -398,12 +492,15 @@ var MoltsPayClient = class {
398
492
  throw new Error(`Unknown chain: ${this.config.chain}`);
399
493
  }
400
494
  const provider = new ethers.JsonRpcProvider(chain.rpc);
401
- const nativeBalance = await provider.getBalance(this.wallet.address);
402
- const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
403
- const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);
404
- const usdcBalance = await usdc.balanceOf(this.wallet.address);
495
+ const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
496
+ const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
497
+ provider.getBalance(this.wallet.address),
498
+ new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
499
+ new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
500
+ ]);
405
501
  return {
406
- usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),
502
+ usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
503
+ usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
407
504
  native: parseFloat(ethers.formatEther(nativeBalance))
408
505
  };
409
506
  }
@@ -863,16 +960,24 @@ var X402_VERSION3 = 2;
863
960
  var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
864
961
  var PAYMENT_HEADER2 = "x-payment";
865
962
  var PAYMENT_RESPONSE_HEADER = "x-payment-response";
866
- var USDC_ADDRESSES = {
867
- "eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
868
- // Base mainnet
869
- "eip155:84532": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
870
- // Base Sepolia
963
+ var TOKEN_ADDRESSES = {
964
+ "eip155:8453": {
965
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
966
+ USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
967
+ },
968
+ "eip155:84532": {
969
+ USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
970
+ USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
971
+ // Same as USDC on testnet
972
+ }
871
973
  };
872
- var USDC_DOMAIN = {
873
- name: "USD Coin",
874
- version: "2"
974
+ var TOKEN_DOMAINS = {
975
+ USDC: { name: "USD Coin", version: "2" },
976
+ USDT: { name: "Tether USD", version: "2" }
875
977
  };
978
+ function getAcceptedCurrencies(config) {
979
+ return config.acceptedCurrencies ?? [config.currency];
980
+ }
876
981
  function loadEnvFile2() {
877
982
  const envPaths = [
878
983
  path2.join(process.cwd(), ".env"),
@@ -1020,6 +1125,7 @@ var MoltsPayServer = class {
1020
1125
  description: s.description,
1021
1126
  price: s.price,
1022
1127
  currency: s.currency,
1128
+ acceptedCurrencies: getAcceptedCurrencies(s),
1023
1129
  input: s.input,
1024
1130
  output: s.output,
1025
1131
  available: this.skills.has(s.id)
@@ -1057,6 +1163,7 @@ var MoltsPayServer = class {
1057
1163
  description: s.description,
1058
1164
  price: s.price,
1059
1165
  currency: s.currency,
1166
+ acceptedCurrencies: getAcceptedCurrencies(s),
1060
1167
  input: s.input,
1061
1168
  output: s.output,
1062
1169
  available: this.skills.has(s.id)
@@ -1123,7 +1230,14 @@ var MoltsPayServer = class {
1123
1230
  if (!validation.valid) {
1124
1231
  return this.sendJson(res, 402, { error: validation.error });
1125
1232
  }
1126
- const requirements = this.buildPaymentRequirements(skill.config);
1233
+ const paymentToken = this.detectPaymentToken(payment);
1234
+ if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
1235
+ const accepted = getAcceptedCurrencies(skill.config);
1236
+ return this.sendJson(res, 402, {
1237
+ error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
1238
+ });
1239
+ }
1240
+ const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
1127
1241
  console.log(`[MoltsPay] Verifying payment...`);
1128
1242
  const verifyResult = await this.registry.verify(payment, requirements);
1129
1243
  if (!verifyResult.valid) {
@@ -1178,12 +1292,15 @@ var MoltsPayServer = class {
1178
1292
  }
1179
1293
  /**
1180
1294
  * Return 402 with x402 payment requirements (v2 format)
1295
+ * Includes requirements for all accepted currencies
1181
1296
  */
1182
1297
  sendPaymentRequired(config, res) {
1183
- const requirements = this.buildPaymentRequirements(config);
1298
+ const acceptedTokens = getAcceptedCurrencies(config);
1299
+ const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
1184
1300
  const paymentRequired = {
1185
1301
  x402Version: X402_VERSION3,
1186
- accepts: [requirements],
1302
+ accepts,
1303
+ acceptedCurrencies: acceptedTokens,
1187
1304
  resource: {
1188
1305
  url: `/execute?service=${config.id}`,
1189
1306
  description: `${config.name} - $${config.price} ${config.currency}`,
@@ -1198,6 +1315,7 @@ var MoltsPayServer = class {
1198
1315
  res.end(JSON.stringify({
1199
1316
  error: "Payment required",
1200
1317
  message: `Service requires $${config.price} ${config.currency}`,
1318
+ acceptedCurrencies: acceptedTokens,
1201
1319
  x402: paymentRequired
1202
1320
  }, null, 2));
1203
1321
  }
@@ -1220,20 +1338,47 @@ var MoltsPayServer = class {
1220
1338
  }
1221
1339
  /**
1222
1340
  * Build payment requirements for facilitator
1341
+ * Returns requirements for the primary currency (USDC by default)
1342
+ * Server accepts any of the acceptedCurrencies
1223
1343
  */
1224
- buildPaymentRequirements(config) {
1344
+ buildPaymentRequirements(config, token) {
1225
1345
  const amountInUnits = Math.floor(config.price * 1e6).toString();
1226
- const usdcAddress = USDC_ADDRESSES[this.networkId];
1346
+ const acceptedTokens = getAcceptedCurrencies(config);
1347
+ const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
1348
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1349
+ const tokenAddress = tokenAddresses[selectedToken];
1350
+ const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
1227
1351
  return {
1228
1352
  scheme: "exact",
1229
1353
  network: this.networkId,
1230
- asset: usdcAddress,
1354
+ asset: tokenAddress,
1231
1355
  amount: amountInUnits,
1232
1356
  payTo: this.manifest.provider.wallet,
1233
1357
  maxTimeoutSeconds: 300,
1234
- extra: USDC_DOMAIN
1358
+ extra: tokenDomain
1235
1359
  };
1236
1360
  }
1361
+ /**
1362
+ * Detect which token is being used in the payment
1363
+ */
1364
+ detectPaymentToken(payment) {
1365
+ const asset = payment.accepted?.asset || payment.payload?.asset;
1366
+ if (!asset) return void 0;
1367
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1368
+ for (const [symbol, address] of Object.entries(tokenAddresses)) {
1369
+ if (address.toLowerCase() === asset.toLowerCase()) {
1370
+ return symbol;
1371
+ }
1372
+ }
1373
+ return void 0;
1374
+ }
1375
+ /**
1376
+ * Check if payment token is accepted for service
1377
+ */
1378
+ isTokenAccepted(config, token) {
1379
+ const accepted = getAcceptedCurrencies(config);
1380
+ return accepted.includes(token);
1381
+ }
1237
1382
  async readBody(req) {
1238
1383
  return new Promise((resolve2, reject) => {
1239
1384
  let body = "";
@@ -1433,18 +1578,22 @@ var MoltsPayServer = class {
1433
1578
  /**
1434
1579
  * Build payment requirements for proxy endpoint (uses provided wallet)
1435
1580
  */
1436
- buildProxyPaymentRequirements(config, wallet) {
1581
+ buildProxyPaymentRequirements(config, wallet, token) {
1437
1582
  const amountInUnits = Math.floor(config.price * 1e6).toString();
1438
- const usdcAddress = USDC_ADDRESSES[this.networkId];
1583
+ const acceptedTokens = getAcceptedCurrencies(config);
1584
+ const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
1585
+ const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
1586
+ const tokenAddress = tokenAddresses[selectedToken];
1587
+ const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
1439
1588
  return {
1440
1589
  scheme: "exact",
1441
1590
  network: this.networkId,
1442
- asset: usdcAddress,
1591
+ asset: tokenAddress,
1443
1592
  amount: amountInUnits,
1444
1593
  payTo: wallet,
1445
1594
  // Use provided wallet, not manifest
1446
1595
  maxTimeoutSeconds: 300,
1447
- extra: USDC_DOMAIN
1596
+ extra: tokenDomain
1448
1597
  };
1449
1598
  }
1450
1599
  /**
@@ -1577,7 +1726,7 @@ program.command("status").description("Show wallet status and balance").option("
1577
1726
  return;
1578
1727
  }
1579
1728
  const config = client.getConfig();
1580
- let balance = { usdc: 0, native: 0 };
1729
+ let balance = { usdc: 0, usdt: 0, native: 0 };
1581
1730
  try {
1582
1731
  balance = await client.getBalance();
1583
1732
  } catch (err) {
@@ -1594,7 +1743,7 @@ program.command("status").description("Show wallet status and balance").option("
1594
1743
  console.log("\n\u{1F4CA} MoltsPay Status\n");
1595
1744
  console.log(` Wallet: ${client.address}`);
1596
1745
  console.log(` Chain: ${config.chain}`);
1597
- console.log(` Balance: ${balance.usdc.toFixed(2)} USDC`);
1746
+ console.log(` Balance: ${balance.usdc.toFixed(2)} USDC | ${balance.usdt.toFixed(2)} USDT`);
1598
1747
  console.log(` Native: ${balance.native.toFixed(6)} ETH`);
1599
1748
  console.log("");
1600
1749
  console.log(" Limits:");
@@ -1838,7 +1987,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
1838
1987
  process.exit(1);
1839
1988
  }
1840
1989
  });
1841
- 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) => {
1990
+ 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) => {
1842
1991
  const client = new MoltsPayClient();
1843
1992
  if (!client.isInitialized) {
1844
1993
  console.error("\u274C Wallet not initialized. Run: npx moltspay init");
@@ -1873,6 +2022,19 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
1873
2022
  process.exit(1);
1874
2023
  }
1875
2024
  const imageDisplay = params.image_url || (params.image_base64 ? `[local file: ${options.image}]` : null);
2025
+ const token = (options.token || "USDC").toUpperCase();
2026
+ if (token === "USDT") {
2027
+ const balance = await client.getBalance();
2028
+ if (balance.native < 1e-4) {
2029
+ console.log("\n\u26A0\uFE0F USDT requires a small amount of ETH for gas (~$0.01)");
2030
+ console.log(` Your ETH balance: ${balance.native.toFixed(6)} ETH`);
2031
+ console.log(" Please add a tiny amount of ETH to your wallet.\n");
2032
+ process.exit(1);
2033
+ }
2034
+ if (!options.json) {
2035
+ console.log("\n\u26A0\uFE0F Note: USDT payments require gas (~$0.01 on Base)");
2036
+ }
2037
+ }
1876
2038
  if (!options.json) {
1877
2039
  console.log(`
1878
2040
  \u{1F4B3} MoltsPay - Paying for service
@@ -1881,11 +2043,12 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
1881
2043
  console.log(` Service: ${service}`);
1882
2044
  console.log(` Prompt: ${params.prompt}`);
1883
2045
  if (imageDisplay) console.log(` Image: ${imageDisplay}`);
2046
+ console.log(` Token: ${token}`);
1884
2047
  console.log(` Wallet: ${client.address}`);
1885
2048
  console.log("");
1886
2049
  }
1887
2050
  try {
1888
- const result = await client.pay(server, service, params);
2051
+ const result = await client.pay(server, service, params, { token });
1889
2052
  if (options.json) {
1890
2053
  console.log(JSON.stringify(result));
1891
2054
  } else {