moltspay 0.5.4 → 0.7.1

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 (68) hide show
  1. package/README.md +0 -124
  2. package/dist/cdp/index.d.mts +111 -0
  3. package/dist/cdp/index.d.ts +111 -0
  4. package/dist/cdp/index.js +30655 -0
  5. package/dist/cdp/index.js.map +1 -0
  6. package/dist/cdp/index.mjs +30631 -0
  7. package/dist/cdp/index.mjs.map +1 -0
  8. package/dist/chains/index.d.mts +1 -1
  9. package/dist/chains/index.d.ts +1 -1
  10. package/dist/cli/index.js +990 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/index.mjs +967 -0
  13. package/dist/cli/index.mjs.map +1 -0
  14. package/dist/client/index.d.mts +134 -0
  15. package/dist/client/index.d.ts +134 -0
  16. package/dist/client/index.js +331 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/client/index.mjs +296 -0
  19. package/dist/client/index.mjs.map +1 -0
  20. package/dist/createWallet-D53qu7ie.d.mts +77 -0
  21. package/dist/createWallet-D53qu7ie.d.ts +77 -0
  22. package/dist/index-Dg8n6wdW.d.mts +32 -0
  23. package/dist/index-Dg8n6wdW.d.ts +32 -0
  24. package/dist/index.d.mts +6 -1483
  25. package/dist/index.d.ts +6 -1483
  26. package/dist/index.js +31039 -4254
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +31042 -4203
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/server/index.d.mts +120 -0
  31. package/dist/server/index.d.ts +120 -0
  32. package/dist/server/index.js +418 -0
  33. package/dist/server/index.js.map +1 -0
  34. package/dist/server/index.mjs +393 -0
  35. package/dist/server/index.mjs.map +1 -0
  36. package/dist/wallet/index.d.mts +3 -451
  37. package/dist/wallet/index.d.ts +3 -451
  38. package/dist/wallet/index.js +5 -1021
  39. package/dist/wallet/index.js.map +1 -1
  40. package/dist/wallet/index.mjs +16 -1015
  41. package/dist/wallet/index.mjs.map +1 -1
  42. package/package.json +19 -19
  43. package/dist/cli.js +0 -1984
  44. package/dist/cli.js.map +0 -1
  45. package/dist/cli.mjs +0 -1969
  46. package/dist/cli.mjs.map +0 -1
  47. package/dist/guide/index.d.mts +0 -39
  48. package/dist/guide/index.d.ts +0 -39
  49. package/dist/guide/index.js +0 -181
  50. package/dist/guide/index.js.map +0 -1
  51. package/dist/guide/index.mjs +0 -152
  52. package/dist/guide/index.mjs.map +0 -1
  53. package/dist/index-CyFg9s2m.d.mts +0 -161
  54. package/dist/index-CyFg9s2m.d.ts +0 -161
  55. package/dist/orders/index.d.mts +0 -97
  56. package/dist/orders/index.d.ts +0 -97
  57. package/dist/orders/index.js +0 -162
  58. package/dist/orders/index.js.map +0 -1
  59. package/dist/orders/index.mjs +0 -136
  60. package/dist/orders/index.mjs.map +0 -1
  61. package/dist/permit/index.d.mts +0 -49
  62. package/dist/permit/index.d.ts +0 -49
  63. package/dist/permit/index.js +0 -273
  64. package/dist/permit/index.js.map +0 -1
  65. package/dist/permit/index.mjs +0 -246
  66. package/dist/permit/index.mjs.map +0 -1
  67. /package/dist/{cli.d.mts → cli/index.d.mts} +0 -0
  68. /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
@@ -0,0 +1,331 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/client/index.ts
31
+ var client_exports = {};
32
+ __export(client_exports, {
33
+ MoltsPayClient: () => MoltsPayClient
34
+ });
35
+ module.exports = __toCommonJS(client_exports);
36
+ var import_fs = require("fs");
37
+ var import_os = require("os");
38
+ var import_path = require("path");
39
+ var import_ethers = require("ethers");
40
+
41
+ // src/chains/index.ts
42
+ var CHAINS = {
43
+ // ============ Mainnet ============
44
+ base: {
45
+ name: "Base",
46
+ chainId: 8453,
47
+ rpc: "https://mainnet.base.org",
48
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
49
+ explorer: "https://basescan.org/address/",
50
+ explorerTx: "https://basescan.org/tx/",
51
+ avgBlockTime: 2
52
+ },
53
+ polygon: {
54
+ name: "Polygon",
55
+ chainId: 137,
56
+ rpc: "https://polygon-rpc.com",
57
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
58
+ explorer: "https://polygonscan.com/address/",
59
+ explorerTx: "https://polygonscan.com/tx/",
60
+ avgBlockTime: 2
61
+ },
62
+ ethereum: {
63
+ name: "Ethereum",
64
+ chainId: 1,
65
+ rpc: "https://eth.llamarpc.com",
66
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
67
+ explorer: "https://etherscan.io/address/",
68
+ explorerTx: "https://etherscan.io/tx/",
69
+ avgBlockTime: 12
70
+ },
71
+ // ============ Testnet ============
72
+ base_sepolia: {
73
+ name: "Base Sepolia",
74
+ chainId: 84532,
75
+ rpc: "https://sepolia.base.org",
76
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
77
+ explorer: "https://sepolia.basescan.org/address/",
78
+ explorerTx: "https://sepolia.basescan.org/tx/",
79
+ avgBlockTime: 2
80
+ },
81
+ sepolia: {
82
+ name: "Sepolia",
83
+ chainId: 11155111,
84
+ rpc: "https://rpc.sepolia.org",
85
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
86
+ explorer: "https://sepolia.etherscan.io/address/",
87
+ explorerTx: "https://sepolia.etherscan.io/tx/",
88
+ avgBlockTime: 12
89
+ }
90
+ };
91
+ function getChain(name) {
92
+ const config = CHAINS[name];
93
+ if (!config) {
94
+ throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
95
+ }
96
+ return config;
97
+ }
98
+
99
+ // src/client/index.ts
100
+ var DEFAULT_CONFIG = {
101
+ chain: "base",
102
+ limits: {
103
+ maxPerTx: 100,
104
+ maxPerDay: 1e3
105
+ }
106
+ };
107
+ var MoltsPayClient = class {
108
+ configDir;
109
+ config;
110
+ walletData = null;
111
+ wallet = null;
112
+ todaySpending = 0;
113
+ lastSpendingReset = 0;
114
+ constructor(options = {}) {
115
+ this.configDir = options.configDir || (0, import_path.join)((0, import_os.homedir)(), ".moltspay");
116
+ this.config = this.loadConfig();
117
+ this.walletData = this.loadWallet();
118
+ if (this.walletData) {
119
+ this.wallet = new import_ethers.Wallet(this.walletData.privateKey);
120
+ }
121
+ }
122
+ /**
123
+ * Check if client is initialized (has wallet)
124
+ */
125
+ get isInitialized() {
126
+ return this.wallet !== null;
127
+ }
128
+ /**
129
+ * Get wallet address
130
+ */
131
+ get address() {
132
+ return this.wallet?.address || null;
133
+ }
134
+ /**
135
+ * Get current config
136
+ */
137
+ getConfig() {
138
+ return { ...this.config };
139
+ }
140
+ /**
141
+ * Update config
142
+ */
143
+ updateConfig(updates) {
144
+ if (updates.maxPerTx !== void 0) {
145
+ this.config.limits.maxPerTx = updates.maxPerTx;
146
+ }
147
+ if (updates.maxPerDay !== void 0) {
148
+ this.config.limits.maxPerDay = updates.maxPerDay;
149
+ }
150
+ this.saveConfig();
151
+ }
152
+ /**
153
+ * Get services from a provider
154
+ */
155
+ async getServices(serverUrl) {
156
+ const res = await fetch(`${serverUrl}/services`);
157
+ if (!res.ok) {
158
+ throw new Error(`Failed to get services: ${res.statusText}`);
159
+ }
160
+ return res.json();
161
+ }
162
+ /**
163
+ * Pay for a service and get the result
164
+ */
165
+ async pay(serverUrl, service, params) {
166
+ if (!this.wallet) {
167
+ throw new Error("Client not initialized. Run: npx moltspay init");
168
+ }
169
+ const payRes = await fetch(`${serverUrl}/pay`, {
170
+ method: "POST",
171
+ headers: { "Content-Type": "application/json" },
172
+ body: JSON.stringify({ service, params })
173
+ });
174
+ if (payRes.status !== 402) {
175
+ const err = await payRes.json();
176
+ throw new Error(err.error || "Unexpected response");
177
+ }
178
+ const paymentReq = await payRes.json();
179
+ const { payment } = paymentReq;
180
+ this.checkLimits(payment.amount);
181
+ console.log(`[MoltsPay] Paying $${payment.amount} ${payment.currency} to ${payment.wallet}`);
182
+ const txHash = await this.executePayment(payment);
183
+ console.log(`[MoltsPay] Payment tx: ${txHash}`);
184
+ const verifyRes = await fetch(`${serverUrl}/verify`, {
185
+ method: "POST",
186
+ headers: { "Content-Type": "application/json" },
187
+ body: JSON.stringify({
188
+ chargeId: payment.chargeId,
189
+ txHash
190
+ })
191
+ });
192
+ if (!verifyRes.ok) {
193
+ const err = await verifyRes.json();
194
+ throw new Error(err.error || "Verification failed");
195
+ }
196
+ const result = await verifyRes.json();
197
+ this.recordSpending(payment.amount);
198
+ return result.result;
199
+ }
200
+ /**
201
+ * Check spending limits
202
+ */
203
+ checkLimits(amount) {
204
+ if (amount > this.config.limits.maxPerTx) {
205
+ throw new Error(
206
+ `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`
207
+ );
208
+ }
209
+ const today = (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0);
210
+ if (today > this.lastSpendingReset) {
211
+ this.todaySpending = 0;
212
+ this.lastSpendingReset = today;
213
+ }
214
+ if (this.todaySpending + amount > this.config.limits.maxPerDay) {
215
+ throw new Error(
216
+ `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`
217
+ );
218
+ }
219
+ }
220
+ /**
221
+ * Record spending
222
+ */
223
+ recordSpending(amount) {
224
+ this.todaySpending += amount;
225
+ }
226
+ /**
227
+ * Execute payment on-chain
228
+ */
229
+ async executePayment(payment) {
230
+ let chain;
231
+ try {
232
+ chain = getChain(payment.chain);
233
+ } catch {
234
+ throw new Error(`Unknown chain: ${payment.chain}`);
235
+ }
236
+ const { ethers } = await import("ethers");
237
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
238
+ const signer = new ethers.Wallet(this.walletData.privateKey, provider);
239
+ const usdcAddress = chain.usdc;
240
+ const usdcAbi = [
241
+ "function transfer(address to, uint256 amount) returns (bool)",
242
+ "function balanceOf(address account) view returns (uint256)"
243
+ ];
244
+ const usdc = new ethers.Contract(usdcAddress, usdcAbi, signer);
245
+ const amountInUnits = ethers.parseUnits(payment.amount.toString(), 6);
246
+ const balance = await usdc.balanceOf(this.wallet.address);
247
+ if (balance < amountInUnits) {
248
+ throw new Error(
249
+ `Insufficient USDC balance: ${ethers.formatUnits(balance, 6)} < ${payment.amount}`
250
+ );
251
+ }
252
+ const tx = await usdc.transfer(payment.wallet, amountInUnits);
253
+ const receipt = await tx.wait();
254
+ return receipt.hash;
255
+ }
256
+ // --- Config & Wallet Management ---
257
+ loadConfig() {
258
+ const configPath = (0, import_path.join)(this.configDir, "config.json");
259
+ if ((0, import_fs.existsSync)(configPath)) {
260
+ const content = (0, import_fs.readFileSync)(configPath, "utf-8");
261
+ return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
262
+ }
263
+ return { ...DEFAULT_CONFIG };
264
+ }
265
+ saveConfig() {
266
+ (0, import_fs.mkdirSync)(this.configDir, { recursive: true });
267
+ const configPath = (0, import_path.join)(this.configDir, "config.json");
268
+ (0, import_fs.writeFileSync)(configPath, JSON.stringify(this.config, null, 2));
269
+ }
270
+ loadWallet() {
271
+ const walletPath = (0, import_path.join)(this.configDir, "wallet.json");
272
+ if ((0, import_fs.existsSync)(walletPath)) {
273
+ const content = (0, import_fs.readFileSync)(walletPath, "utf-8");
274
+ return JSON.parse(content);
275
+ }
276
+ return null;
277
+ }
278
+ /**
279
+ * Initialize a new wallet (called by CLI)
280
+ */
281
+ static init(configDir, options) {
282
+ (0, import_fs.mkdirSync)(configDir, { recursive: true });
283
+ const wallet = import_ethers.Wallet.createRandom();
284
+ const walletData = {
285
+ address: wallet.address,
286
+ privateKey: wallet.privateKey,
287
+ createdAt: Date.now()
288
+ };
289
+ const walletPath = (0, import_path.join)(configDir, "wallet.json");
290
+ (0, import_fs.writeFileSync)(walletPath, JSON.stringify(walletData, null, 2));
291
+ const config = {
292
+ chain: options.chain,
293
+ limits: {
294
+ maxPerTx: options.maxPerTx,
295
+ maxPerDay: options.maxPerDay
296
+ }
297
+ };
298
+ const configPath = (0, import_path.join)(configDir, "config.json");
299
+ (0, import_fs.writeFileSync)(configPath, JSON.stringify(config, null, 2));
300
+ return { address: wallet.address, configDir };
301
+ }
302
+ /**
303
+ * Get wallet balance
304
+ */
305
+ async getBalance() {
306
+ if (!this.wallet) {
307
+ throw new Error("Client not initialized");
308
+ }
309
+ let chain;
310
+ try {
311
+ chain = getChain(this.config.chain);
312
+ } catch {
313
+ throw new Error(`Unknown chain: ${this.config.chain}`);
314
+ }
315
+ const { ethers } = await import("ethers");
316
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
317
+ const nativeBalance = await provider.getBalance(this.wallet.address);
318
+ const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
319
+ const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);
320
+ const usdcBalance = await usdc.balanceOf(this.wallet.address);
321
+ return {
322
+ usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),
323
+ native: parseFloat(ethers.formatEther(nativeBalance))
324
+ };
325
+ }
326
+ };
327
+ // Annotate the CommonJS export names for ESM import in node:
328
+ 0 && (module.exports = {
329
+ MoltsPayClient
330
+ });
331
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Client - Pay for AI Agent services\n * \n * Usage:\n * const client = new MoltsPayClient(); // Loads from ~/.moltspay/\n * const services = await client.getServices('http://provider:3000');\n * const result = await client.pay('http://provider:3000', 'text-to-video', { prompt: '...' });\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Wallet } from 'ethers';\nimport { getChain, type ChainName } from '../chains/index.js';\nimport {\n ClientConfig,\n WalletData,\n PaymentRequired,\n ServicesResponse,\n VerifyResponse,\n MoltsPayClientOptions,\n} from './types.js';\n\nexport * from './types.js';\n\nconst DEFAULT_CONFIG: ClientConfig = {\n chain: 'base',\n limits: {\n maxPerTx: 100,\n maxPerDay: 1000,\n },\n};\n\nexport class MoltsPayClient {\n private configDir: string;\n private config: ClientConfig;\n private walletData: WalletData | null = null;\n private wallet: Wallet | null = null;\n private todaySpending: number = 0;\n private lastSpendingReset: number = 0;\n\n constructor(options: MoltsPayClientOptions = {}) {\n this.configDir = options.configDir || join(homedir(), '.moltspay');\n this.config = this.loadConfig();\n this.walletData = this.loadWallet();\n \n if (this.walletData) {\n this.wallet = new Wallet(this.walletData.privateKey);\n }\n }\n\n /**\n * Check if client is initialized (has wallet)\n */\n get isInitialized(): boolean {\n return this.wallet !== null;\n }\n\n /**\n * Get wallet address\n */\n get address(): string | null {\n return this.wallet?.address || null;\n }\n\n /**\n * Get current config\n */\n getConfig(): ClientConfig {\n return { ...this.config };\n }\n\n /**\n * Update config\n */\n updateConfig(updates: Partial<ClientConfig['limits']>): void {\n if (updates.maxPerTx !== undefined) {\n this.config.limits.maxPerTx = updates.maxPerTx;\n }\n if (updates.maxPerDay !== undefined) {\n this.config.limits.maxPerDay = updates.maxPerDay;\n }\n this.saveConfig();\n }\n\n /**\n * Get services from a provider\n */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n const res = await fetch(`${serverUrl}/services`);\n if (!res.ok) {\n throw new Error(`Failed to get services: ${res.statusText}`);\n }\n return res.json() as Promise<ServicesResponse>;\n }\n\n /**\n * Pay for a service and get the result\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, any>\n ): Promise<Record<string, any>> {\n if (!this.wallet) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Step 1: Request payment info\n const payRes = await fetch(`${serverUrl}/pay`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service, params }),\n });\n\n if (payRes.status !== 402) {\n const err = await payRes.json() as { error?: string };\n throw new Error(err.error || 'Unexpected response');\n }\n\n const paymentReq = await payRes.json() as PaymentRequired;\n const { payment } = paymentReq;\n\n // Step 2: Check limits\n this.checkLimits(payment.amount);\n\n // Step 3: Execute payment on-chain\n console.log(`[MoltsPay] Paying $${payment.amount} ${payment.currency} to ${payment.wallet}`);\n const txHash = await this.executePayment(payment);\n console.log(`[MoltsPay] Payment tx: ${txHash}`);\n\n // Step 4: Verify and get result\n const verifyRes = await fetch(`${serverUrl}/verify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n chargeId: payment.chargeId,\n txHash,\n }),\n });\n\n if (!verifyRes.ok) {\n const err = await verifyRes.json() as { error?: string };\n throw new Error(err.error || 'Verification failed');\n }\n\n const result = await verifyRes.json() as VerifyResponse;\n \n // Update spending tracking\n this.recordSpending(payment.amount);\n \n return result.result;\n }\n\n /**\n * Check spending limits\n */\n private checkLimits(amount: number): void {\n // Check per-tx limit\n if (amount > this.config.limits.maxPerTx) {\n throw new Error(\n `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`\n );\n }\n\n // Reset daily spending if new day\n const today = new Date().setHours(0, 0, 0, 0);\n if (today > this.lastSpendingReset) {\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n }\n\n // Check daily limit\n if (this.todaySpending + amount > this.config.limits.maxPerDay) {\n throw new Error(\n `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`\n );\n }\n }\n\n /**\n * Record spending\n */\n private recordSpending(amount: number): void {\n this.todaySpending += amount;\n }\n\n /**\n * Execute payment on-chain\n */\n private async executePayment(payment: PaymentRequired['payment']): Promise<string> {\n let chain;\n try {\n chain = getChain(payment.chain as ChainName);\n } catch {\n throw new Error(`Unknown chain: ${payment.chain}`);\n }\n\n // For now, we'll use a simple USDC transfer\n // In production, this would connect to the actual chain\n const { ethers } = await import('ethers');\n \n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const signer = new ethers.Wallet(this.walletData!.privateKey, provider);\n\n // USDC contract (Base mainnet)\n const usdcAddress = chain.usdc;\n const usdcAbi = [\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function balanceOf(address account) view returns (uint256)',\n ];\n \n const usdc = new ethers.Contract(usdcAddress, usdcAbi, signer);\n\n // Convert amount to USDC decimals (6)\n const amountInUnits = ethers.parseUnits(payment.amount.toString(), 6);\n\n // Check balance\n const balance = await usdc.balanceOf(this.wallet!.address);\n if (balance < amountInUnits) {\n throw new Error(\n `Insufficient USDC balance: ${ethers.formatUnits(balance, 6)} < ${payment.amount}`\n );\n }\n\n // Send transaction\n const tx = await usdc.transfer(payment.wallet, amountInUnits);\n const receipt = await tx.wait();\n\n return receipt.hash;\n }\n\n // --- Config & Wallet Management ---\n\n private loadConfig(): ClientConfig {\n const configPath = join(this.configDir, 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) };\n }\n return { ...DEFAULT_CONFIG };\n }\n\n private saveConfig(): void {\n mkdirSync(this.configDir, { recursive: true });\n const configPath = join(this.configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n }\n\n private loadWallet(): WalletData | null {\n const walletPath = join(this.configDir, 'wallet.json');\n if (existsSync(walletPath)) {\n const content = readFileSync(walletPath, 'utf-8');\n return JSON.parse(content);\n }\n return null;\n }\n\n /**\n * Initialize a new wallet (called by CLI)\n */\n static init(\n configDir: string,\n options: { chain: string; maxPerTx: number; maxPerDay: number }\n ): { address: string; configDir: string } {\n mkdirSync(configDir, { recursive: true });\n\n // Create wallet\n const wallet = Wallet.createRandom();\n const walletData: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: Date.now(),\n };\n\n // Save wallet\n const walletPath = join(configDir, 'wallet.json');\n writeFileSync(walletPath, JSON.stringify(walletData, null, 2));\n\n // Save config\n const config: ClientConfig = {\n chain: options.chain,\n limits: {\n maxPerTx: options.maxPerTx,\n maxPerDay: options.maxPerDay,\n },\n };\n const configPath = join(configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n return { address: wallet.address, configDir };\n }\n\n /**\n * Get wallet balance\n */\n async getBalance(): Promise<{ usdc: number; native: number }> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n let chain;\n try {\n chain = getChain(this.config.chain as ChainName);\n } catch {\n throw new Error(`Unknown chain: ${this.config.chain}`);\n }\n\n const { ethers } = await import('ethers');\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n\n // Get native balance\n const nativeBalance = await provider.getBalance(this.wallet.address);\n\n // Get USDC balance\n const usdcAbi = ['function balanceOf(address) view returns (uint256)'];\n const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);\n const usdcBalance = await usdc.balanceOf(this.wallet.address);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName } from '../types/index.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-rpc.com',\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n ethereum: {\n name: 'Ethereum',\n chainId: 1,\n rpc: 'https://eth.llamarpc.com',\n usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n explorer: 'https://etherscan.io/address/',\n explorerTx: 'https://etherscan.io/tx/',\n avgBlockTime: 12,\n },\n\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n sepolia: {\n name: 'Sepolia',\n chainId: 11155111,\n rpc: 'https://rpc.sepolia.org',\n usdc: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n explorer: 'https://sepolia.etherscan.io/address/',\n explorerTx: 'https://sepolia.etherscan.io/tx/',\n avgBlockTime: 12,\n },\n};\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: ChainName): ChainConfig {\n const config = CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * List all supported chains\n */\nexport function listChains(): ChainName[] {\n return Object.keys(CHAINS) as ChainName[];\n}\n\n/**\n * Get chain config by chainId\n */\nexport function getChainById(chainId: number): ChainConfig | undefined {\n return Object.values(CHAINS).find(c => c.chainId === chainId);\n}\n\n/**\n * ERC20 ABI (minimal, only required methods)\n */\nexport const ERC20_ABI = [\n 'function balanceOf(address owner) view returns (uint256)',\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function approve(address spender, uint256 amount) returns (bool)',\n 'function allowance(address owner, address spender) view returns (uint256)',\n 'function decimals() view returns (uint8)',\n 'function symbol() view returns (string)',\n 'function name() view returns (string)',\n 'function nonces(address owner) view returns (uint256)',\n 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n 'event Approval(address indexed owner, address indexed spender, uint256 value)',\n];\n\nexport type { ChainConfig, ChainName };\n"],"mappings":"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","names":[]}
@@ -0,0 +1,296 @@
1
+ // src/client/index.ts
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { Wallet } from "ethers";
6
+
7
+ // src/chains/index.ts
8
+ var CHAINS = {
9
+ // ============ Mainnet ============
10
+ base: {
11
+ name: "Base",
12
+ chainId: 8453,
13
+ rpc: "https://mainnet.base.org",
14
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
15
+ explorer: "https://basescan.org/address/",
16
+ explorerTx: "https://basescan.org/tx/",
17
+ avgBlockTime: 2
18
+ },
19
+ polygon: {
20
+ name: "Polygon",
21
+ chainId: 137,
22
+ rpc: "https://polygon-rpc.com",
23
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
24
+ explorer: "https://polygonscan.com/address/",
25
+ explorerTx: "https://polygonscan.com/tx/",
26
+ avgBlockTime: 2
27
+ },
28
+ ethereum: {
29
+ name: "Ethereum",
30
+ chainId: 1,
31
+ rpc: "https://eth.llamarpc.com",
32
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
33
+ explorer: "https://etherscan.io/address/",
34
+ explorerTx: "https://etherscan.io/tx/",
35
+ avgBlockTime: 12
36
+ },
37
+ // ============ Testnet ============
38
+ base_sepolia: {
39
+ name: "Base Sepolia",
40
+ chainId: 84532,
41
+ rpc: "https://sepolia.base.org",
42
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
43
+ explorer: "https://sepolia.basescan.org/address/",
44
+ explorerTx: "https://sepolia.basescan.org/tx/",
45
+ avgBlockTime: 2
46
+ },
47
+ sepolia: {
48
+ name: "Sepolia",
49
+ chainId: 11155111,
50
+ rpc: "https://rpc.sepolia.org",
51
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
52
+ explorer: "https://sepolia.etherscan.io/address/",
53
+ explorerTx: "https://sepolia.etherscan.io/tx/",
54
+ avgBlockTime: 12
55
+ }
56
+ };
57
+ function getChain(name) {
58
+ const config = CHAINS[name];
59
+ if (!config) {
60
+ throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
61
+ }
62
+ return config;
63
+ }
64
+
65
+ // src/client/index.ts
66
+ var DEFAULT_CONFIG = {
67
+ chain: "base",
68
+ limits: {
69
+ maxPerTx: 100,
70
+ maxPerDay: 1e3
71
+ }
72
+ };
73
+ var MoltsPayClient = class {
74
+ configDir;
75
+ config;
76
+ walletData = null;
77
+ wallet = null;
78
+ todaySpending = 0;
79
+ lastSpendingReset = 0;
80
+ constructor(options = {}) {
81
+ this.configDir = options.configDir || join(homedir(), ".moltspay");
82
+ this.config = this.loadConfig();
83
+ this.walletData = this.loadWallet();
84
+ if (this.walletData) {
85
+ this.wallet = new Wallet(this.walletData.privateKey);
86
+ }
87
+ }
88
+ /**
89
+ * Check if client is initialized (has wallet)
90
+ */
91
+ get isInitialized() {
92
+ return this.wallet !== null;
93
+ }
94
+ /**
95
+ * Get wallet address
96
+ */
97
+ get address() {
98
+ return this.wallet?.address || null;
99
+ }
100
+ /**
101
+ * Get current config
102
+ */
103
+ getConfig() {
104
+ return { ...this.config };
105
+ }
106
+ /**
107
+ * Update config
108
+ */
109
+ updateConfig(updates) {
110
+ if (updates.maxPerTx !== void 0) {
111
+ this.config.limits.maxPerTx = updates.maxPerTx;
112
+ }
113
+ if (updates.maxPerDay !== void 0) {
114
+ this.config.limits.maxPerDay = updates.maxPerDay;
115
+ }
116
+ this.saveConfig();
117
+ }
118
+ /**
119
+ * Get services from a provider
120
+ */
121
+ async getServices(serverUrl) {
122
+ const res = await fetch(`${serverUrl}/services`);
123
+ if (!res.ok) {
124
+ throw new Error(`Failed to get services: ${res.statusText}`);
125
+ }
126
+ return res.json();
127
+ }
128
+ /**
129
+ * Pay for a service and get the result
130
+ */
131
+ async pay(serverUrl, service, params) {
132
+ if (!this.wallet) {
133
+ throw new Error("Client not initialized. Run: npx moltspay init");
134
+ }
135
+ const payRes = await fetch(`${serverUrl}/pay`, {
136
+ method: "POST",
137
+ headers: { "Content-Type": "application/json" },
138
+ body: JSON.stringify({ service, params })
139
+ });
140
+ if (payRes.status !== 402) {
141
+ const err = await payRes.json();
142
+ throw new Error(err.error || "Unexpected response");
143
+ }
144
+ const paymentReq = await payRes.json();
145
+ const { payment } = paymentReq;
146
+ this.checkLimits(payment.amount);
147
+ console.log(`[MoltsPay] Paying $${payment.amount} ${payment.currency} to ${payment.wallet}`);
148
+ const txHash = await this.executePayment(payment);
149
+ console.log(`[MoltsPay] Payment tx: ${txHash}`);
150
+ const verifyRes = await fetch(`${serverUrl}/verify`, {
151
+ method: "POST",
152
+ headers: { "Content-Type": "application/json" },
153
+ body: JSON.stringify({
154
+ chargeId: payment.chargeId,
155
+ txHash
156
+ })
157
+ });
158
+ if (!verifyRes.ok) {
159
+ const err = await verifyRes.json();
160
+ throw new Error(err.error || "Verification failed");
161
+ }
162
+ const result = await verifyRes.json();
163
+ this.recordSpending(payment.amount);
164
+ return result.result;
165
+ }
166
+ /**
167
+ * Check spending limits
168
+ */
169
+ checkLimits(amount) {
170
+ if (amount > this.config.limits.maxPerTx) {
171
+ throw new Error(
172
+ `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`
173
+ );
174
+ }
175
+ const today = (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0);
176
+ if (today > this.lastSpendingReset) {
177
+ this.todaySpending = 0;
178
+ this.lastSpendingReset = today;
179
+ }
180
+ if (this.todaySpending + amount > this.config.limits.maxPerDay) {
181
+ throw new Error(
182
+ `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`
183
+ );
184
+ }
185
+ }
186
+ /**
187
+ * Record spending
188
+ */
189
+ recordSpending(amount) {
190
+ this.todaySpending += amount;
191
+ }
192
+ /**
193
+ * Execute payment on-chain
194
+ */
195
+ async executePayment(payment) {
196
+ let chain;
197
+ try {
198
+ chain = getChain(payment.chain);
199
+ } catch {
200
+ throw new Error(`Unknown chain: ${payment.chain}`);
201
+ }
202
+ const { ethers } = await import("ethers");
203
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
204
+ const signer = new ethers.Wallet(this.walletData.privateKey, provider);
205
+ const usdcAddress = chain.usdc;
206
+ const usdcAbi = [
207
+ "function transfer(address to, uint256 amount) returns (bool)",
208
+ "function balanceOf(address account) view returns (uint256)"
209
+ ];
210
+ const usdc = new ethers.Contract(usdcAddress, usdcAbi, signer);
211
+ const amountInUnits = ethers.parseUnits(payment.amount.toString(), 6);
212
+ const balance = await usdc.balanceOf(this.wallet.address);
213
+ if (balance < amountInUnits) {
214
+ throw new Error(
215
+ `Insufficient USDC balance: ${ethers.formatUnits(balance, 6)} < ${payment.amount}`
216
+ );
217
+ }
218
+ const tx = await usdc.transfer(payment.wallet, amountInUnits);
219
+ const receipt = await tx.wait();
220
+ return receipt.hash;
221
+ }
222
+ // --- Config & Wallet Management ---
223
+ loadConfig() {
224
+ const configPath = join(this.configDir, "config.json");
225
+ if (existsSync(configPath)) {
226
+ const content = readFileSync(configPath, "utf-8");
227
+ return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
228
+ }
229
+ return { ...DEFAULT_CONFIG };
230
+ }
231
+ saveConfig() {
232
+ mkdirSync(this.configDir, { recursive: true });
233
+ const configPath = join(this.configDir, "config.json");
234
+ writeFileSync(configPath, JSON.stringify(this.config, null, 2));
235
+ }
236
+ loadWallet() {
237
+ const walletPath = join(this.configDir, "wallet.json");
238
+ if (existsSync(walletPath)) {
239
+ const content = readFileSync(walletPath, "utf-8");
240
+ return JSON.parse(content);
241
+ }
242
+ return null;
243
+ }
244
+ /**
245
+ * Initialize a new wallet (called by CLI)
246
+ */
247
+ static init(configDir, options) {
248
+ mkdirSync(configDir, { recursive: true });
249
+ const wallet = Wallet.createRandom();
250
+ const walletData = {
251
+ address: wallet.address,
252
+ privateKey: wallet.privateKey,
253
+ createdAt: Date.now()
254
+ };
255
+ const walletPath = join(configDir, "wallet.json");
256
+ writeFileSync(walletPath, JSON.stringify(walletData, null, 2));
257
+ const config = {
258
+ chain: options.chain,
259
+ limits: {
260
+ maxPerTx: options.maxPerTx,
261
+ maxPerDay: options.maxPerDay
262
+ }
263
+ };
264
+ const configPath = join(configDir, "config.json");
265
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
266
+ return { address: wallet.address, configDir };
267
+ }
268
+ /**
269
+ * Get wallet balance
270
+ */
271
+ async getBalance() {
272
+ if (!this.wallet) {
273
+ throw new Error("Client not initialized");
274
+ }
275
+ let chain;
276
+ try {
277
+ chain = getChain(this.config.chain);
278
+ } catch {
279
+ throw new Error(`Unknown chain: ${this.config.chain}`);
280
+ }
281
+ const { ethers } = await import("ethers");
282
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
283
+ const nativeBalance = await provider.getBalance(this.wallet.address);
284
+ const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
285
+ const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);
286
+ const usdcBalance = await usdc.balanceOf(this.wallet.address);
287
+ return {
288
+ usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),
289
+ native: parseFloat(ethers.formatEther(nativeBalance))
290
+ };
291
+ }
292
+ };
293
+ export {
294
+ MoltsPayClient
295
+ };
296
+ //# sourceMappingURL=index.mjs.map