moltlaunch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # moltlaunch
2
+
3
+ CLI for AI agents to launch tokens on Base via [Flaunch](https://flaunch.gg). One command — generates a wallet, uploads to IPFS, deploys on-chain. Zero gas required.
4
+
5
+ ## Install & Launch
6
+
7
+ ```bash
8
+ npx moltlaunch --name "My Token" --symbol "MYTKN" --description "A cool token" --image ./logo.png
9
+ ```
10
+
11
+ That's it. No wallet setup, no gas, no signing. Flaunch handles the on-chain deployment.
12
+
13
+ First run creates a wallet at `~/.moltlaunch/wallet.json` — the private key is shown once on creation.
14
+
15
+ ### JSON output (for agents)
16
+
17
+ ```bash
18
+ npx moltlaunch --name "My Token" --symbol "MYTKN" --description "..." --image ./logo.png --json
19
+ ```
20
+
21
+ Returns:
22
+ ```json
23
+ {
24
+ "success": true,
25
+ "tokenAddress": "0x...",
26
+ "transactionHash": "0x...",
27
+ "name": "My Token",
28
+ "symbol": "MYTKN",
29
+ "network": "Base",
30
+ "explorer": "https://basescan.org/token/0x...",
31
+ "flaunch": "https://flaunch.gg/base/coin/0x...",
32
+ "wallet": "0x..."
33
+ }
34
+ ```
35
+
36
+ ## Commands
37
+
38
+ | Command | Description |
39
+ |---------|-------------|
40
+ | `moltlaunch` | Launch a token (default command) |
41
+ | `moltlaunch wallet` | Show wallet address and balance |
42
+ | `moltlaunch wallet --show-key` | Show wallet with private key |
43
+ | `moltlaunch status` | List all launched tokens |
44
+ | `moltlaunch claim` | Claim accumulated fees |
45
+
46
+ All commands support `--json` for structured output.
47
+
48
+ ## State Diagram
49
+
50
+ ```
51
+ npx moltlaunch --name "X" --symbol "X" --image ./logo.png
52
+
53
+ ├─ 1. Load/create wallet (~/.moltlaunch/wallet.json)
54
+
55
+ ├─ 2. Upload image to IPFS
56
+ │ POST web2-api.flaunch.gg/api/v1/upload-image
57
+ │ → returns ipfsHash
58
+
59
+ ├─ 3. Submit gasless launch
60
+ │ POST web2-api.flaunch.gg/api/v1/base/launch-memecoin
61
+ │ body: { name, symbol, description, imageIpfs, creatorAddress, revenueManagerAddress }
62
+ │ → returns jobId
63
+
64
+ ├─ 4. Poll for deployment (2s intervals, 120s timeout)
65
+ │ GET web2-api.flaunch.gg/api/v1/launch-status/{jobId}
66
+ │ states: waiting → active → completed
67
+ │ → returns tokenAddress, transactionHash
68
+
69
+ ├─ 5. Save record to ~/.moltlaunch/launches.json
70
+
71
+ └─ 6. Output result (human-readable or --json)
72
+ ```
73
+
74
+ ## Fee Architecture
75
+
76
+ Every token launched through moltlaunch has our Revenue Manager attached. Flaunch uses a **waterfall fee model** — each tier takes a percentage of what remains, not of the total.
77
+
78
+ ```
79
+ Trade executes on Uniswap V4 (Base)
80
+
81
+ ├─ Swap Fee (1% base, up to 50% during high volume)
82
+ │ │
83
+ │ ├─ Referrer Fee (5% of swap fee)
84
+ │ │ └─ Only if a referrer address was passed in the swap
85
+ │ │
86
+ │ ├─ Protocol Fee (10% of remainder after referrer) ◄── OUR REVENUE
87
+ │ │ └─ Sent to PositionManager escrow for 0x160e...
88
+ │ │
89
+ │ ├─ Creator Fee (80% of remainder after protocol) ◄── AGENT'S REVENUE
90
+ │ │ └─ Sent to PositionManager escrow for agent's creatorAddress
91
+ │ │
92
+ │ └─ BidWall (100% of remainder)
93
+ │ └─ Automated buybacks / token liquidity
94
+ ```
95
+
96
+ ### Concrete numbers per 1 ETH trade (no referrer, 1% swap fee)
97
+
98
+ | Tier | Rate | Amount | Recipient |
99
+ |------|------|--------|-----------|
100
+ | Swap fee | 1% of trade | 0.01 ETH | (distributed below) |
101
+ | Referrer | 5% of 0.01 | 0 (no referrer) | — |
102
+ | **Protocol (us)** | **10% of 0.01** | **0.001 ETH** | **RM recipient** |
103
+ | Creator | 80% of 0.009 | 0.0072 ETH | creatorAddress |
104
+ | BidWall | 100% of rest | 0.0018 ETH | Token liquidity |
105
+
106
+ The swap fee is dynamic (`DynamicFeeCalculatorV2`): 1% baseline, scales linearly with volume up to 50%, decays over a 1-hour rolling window. Tokens trade heaviest at launch — that's when fees are highest.
107
+
108
+ ### Claiming fees
109
+
110
+ Fees accumulate in escrow on the Flaunch **PositionManager** contract, not on the Revenue Manager itself. The RM only tells the fee distributor where to route protocol fees.
111
+
112
+ ```
113
+ moltlaunch claim
114
+
115
+ ├─ Check balance: PositionManager.balances(walletAddress)
116
+
117
+ ├─ If > 0: PositionManager.withdrawFees(walletAddress, true)
118
+ │ └─ unwrap=true converts flETH → native ETH
119
+
120
+ └─ Requires ETH for gas (claim is an on-chain transaction)
121
+ ```
122
+
123
+ ## Key Contracts (Base Mainnet)
124
+
125
+ | Contract | Address | Role |
126
+ |----------|---------|------|
127
+ | Revenue Manager | `0x3Bc08524d9DaaDEC9d1Af87818d809611F0fD669` | Our RM — 10% protocol fee, recipient `0x160e...` |
128
+ | PositionManager | `0x51Bba15255406Cfe7099a42183302640ba7dAFDC` | Fee escrow — `balances()`, `withdrawFees()` |
129
+ | Flaunch | `0x6A53F8b799bE11a2A3264eF0bfF183dCB12d9571` | Token factory |
130
+
131
+ ## API Endpoints
132
+
133
+ | Endpoint | Method | Purpose |
134
+ |----------|--------|---------|
135
+ | `/api/v1/upload-image` | POST | Upload image to IPFS |
136
+ | `/api/v1/{network}/launch-memecoin` | POST | Gasless token launch |
137
+ | `/api/v1/launch-status/{jobId}` | GET | Poll launch status |
138
+ | `/api/v1/{network}/create-revenue-manager` | POST | Create new RM (one-time) |
139
+
140
+ Base URL: `https://web2-api.flaunch.gg`
141
+
142
+ ## Exit Codes
143
+
144
+ | Code | Meaning |
145
+ |------|---------|
146
+ | 0 | Success |
147
+ | 1 | General error |
148
+ | 2 | No wallet found |
149
+ | 3 | Image upload failed |
150
+ | 4 | Token launch failed |
151
+ | 5 | Launch timed out |
152
+ | 6 | No gas (claim only) |
153
+
154
+ ## Agent Integration
155
+
156
+ ```python
157
+ import subprocess, json
158
+
159
+ result = subprocess.run(
160
+ ["npx", "moltlaunch", "--name", "AgentCoin", "--symbol", "AGT",
161
+ "--description", "Launched by AI", "--image", "./logo.png", "--json"],
162
+ capture_output=True, text=True
163
+ )
164
+
165
+ if result.returncode == 0:
166
+ data = json.loads(result.stdout)
167
+ token_address = data["tokenAddress"]
168
+ flaunch_url = data["flaunch"]
169
+ ```
170
+
171
+ ## Project Structure
172
+
173
+ ```
174
+ src/
175
+ ├── index.ts # CLI entry point (commander)
176
+ ├── types.ts # Shared interfaces (wallet, launch, API responses)
177
+ ├── commands/
178
+ │ ├── launch.ts # Main flow: wallet → upload → launch → poll → save
179
+ │ ├── claim.ts # Withdraw fees from PositionManager escrow
180
+ │ ├── status.ts # List launched tokens from local history
181
+ │ └── wallet.ts # Show wallet address, balance, private key
182
+ └── lib/
183
+ ├── config.ts # Constants: RM address, PM address, API base, chains
184
+ ├── flaunch-api.ts # Flaunch Web2 API client (upload, launch, poll)
185
+ ├── wallet.ts # Generate/load/save wallet and launch records
186
+ ├── output.ts # JSON vs human-readable formatting
187
+ └── errors.ts # Typed errors with exit codes
188
+ ```
189
+
190
+ ## Development
191
+
192
+ ```bash
193
+ npm install
194
+ npm run build
195
+ npx . # test locally
196
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/launch.ts
7
+ import { resolve } from "path";
8
+ import { access as access2 } from "fs/promises";
9
+
10
+ // src/lib/wallet.ts
11
+ import { ethers } from "ethers";
12
+ import { readFile, writeFile, mkdir, chmod, access } from "fs/promises";
13
+ import { join } from "path";
14
+ import { homedir } from "os";
15
+
16
+ // src/lib/config.ts
17
+ var REVENUE_MANAGER_ADDRESS = "0x3Bc08524d9DaaDEC9d1Af87818d809611F0fD669";
18
+ var POSITION_MANAGER_ADDRESS = "0x51Bba15255406Cfe7099a42183302640ba7dAFDC";
19
+ var FLAUNCH_API_BASE = "https://web2-api.flaunch.gg";
20
+ var CHAIN = {
21
+ mainnet: {
22
+ id: 8453,
23
+ name: "Base",
24
+ network: "base",
25
+ rpcUrl: "https://mainnet.base.org",
26
+ explorer: "https://basescan.org",
27
+ flaunchUrl: "https://flaunch.gg/base"
28
+ },
29
+ testnet: {
30
+ id: 84532,
31
+ name: "Base Sepolia",
32
+ network: "base-sepolia",
33
+ rpcUrl: "https://sepolia.base.org",
34
+ explorer: "https://sepolia.basescan.org",
35
+ flaunchUrl: "https://flaunch.gg/base-sepolia"
36
+ }
37
+ };
38
+ var WALLET_DIR = ".moltlaunch";
39
+ var WALLET_FILE = "wallet.json";
40
+ var LAUNCHES_FILE = "launches.json";
41
+ var MAX_IMAGE_SIZE_BYTES = 5 * 1024 * 1024;
42
+ var POLL_INTERVAL_MS = 2e3;
43
+ var POLL_TIMEOUT_MS = 12e4;
44
+
45
+ // src/lib/wallet.ts
46
+ function getWalletDir() {
47
+ return join(homedir(), WALLET_DIR);
48
+ }
49
+ function getWalletPath() {
50
+ return join(getWalletDir(), WALLET_FILE);
51
+ }
52
+ function getLaunchesPath() {
53
+ return join(getWalletDir(), LAUNCHES_FILE);
54
+ }
55
+ async function fileExists(path) {
56
+ try {
57
+ await access(path);
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+ async function loadWallet() {
64
+ const path = getWalletPath();
65
+ if (!await fileExists(path)) return null;
66
+ const raw = await readFile(path, "utf-8");
67
+ return JSON.parse(raw);
68
+ }
69
+ async function createWallet() {
70
+ const wallet2 = ethers.Wallet.createRandom();
71
+ const data = {
72
+ address: wallet2.address,
73
+ privateKey: wallet2.privateKey,
74
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
75
+ };
76
+ const dir = getWalletDir();
77
+ await mkdir(dir, { recursive: true });
78
+ const path = getWalletPath();
79
+ await writeFile(path, JSON.stringify(data, null, 2), { mode: 384 });
80
+ await chmod(path, 384);
81
+ return data;
82
+ }
83
+ async function loadOrCreateWallet() {
84
+ const existing = await loadWallet();
85
+ if (existing) return { wallet: existing, isNew: false };
86
+ const wallet2 = await createWallet();
87
+ return { wallet: wallet2, isNew: true };
88
+ }
89
+ async function getWalletBalance(address, network) {
90
+ const chain = network === "testnet" ? CHAIN.testnet : CHAIN.mainnet;
91
+ const provider = new ethers.JsonRpcProvider(chain.rpcUrl);
92
+ const balance = await provider.getBalance(address);
93
+ return ethers.formatEther(balance);
94
+ }
95
+ async function getSigner(privateKey, network) {
96
+ const chain = network === "testnet" ? CHAIN.testnet : CHAIN.mainnet;
97
+ const provider = new ethers.JsonRpcProvider(chain.rpcUrl);
98
+ return new ethers.Wallet(privateKey, provider);
99
+ }
100
+ async function saveLaunchRecord(record) {
101
+ const path = getLaunchesPath();
102
+ let records = [];
103
+ if (await fileExists(path)) {
104
+ const raw = await readFile(path, "utf-8");
105
+ records = JSON.parse(raw);
106
+ }
107
+ records.push(record);
108
+ const dir = getWalletDir();
109
+ await mkdir(dir, { recursive: true });
110
+ await writeFile(path, JSON.stringify(records, null, 2), { mode: 384 });
111
+ }
112
+ async function loadLaunchRecords() {
113
+ const path = getLaunchesPath();
114
+ if (!await fileExists(path)) return [];
115
+ const raw = await readFile(path, "utf-8");
116
+ return JSON.parse(raw);
117
+ }
118
+
119
+ // src/lib/flaunch-api.ts
120
+ import { readFile as readFile2, stat } from "fs/promises";
121
+
122
+ // src/lib/errors.ts
123
+ var EXIT_CODES = {
124
+ SUCCESS: 0,
125
+ GENERAL: 1,
126
+ NO_WALLET: 2,
127
+ UPLOAD_FAIL: 3,
128
+ LAUNCH_FAIL: 4,
129
+ TIMEOUT: 5,
130
+ NO_GAS: 6
131
+ };
132
+ var MoltlaunchError = class extends Error {
133
+ constructor(message, exitCode) {
134
+ super(message);
135
+ this.exitCode = exitCode;
136
+ this.name = "MoltlaunchError";
137
+ }
138
+ };
139
+ var NoWalletError = class extends MoltlaunchError {
140
+ constructor() {
141
+ super("No wallet found. Run `moltlaunch` to create one.", EXIT_CODES.NO_WALLET);
142
+ }
143
+ };
144
+ var UploadError = class extends MoltlaunchError {
145
+ constructor(detail) {
146
+ super(`Image upload failed: ${detail}`, EXIT_CODES.UPLOAD_FAIL);
147
+ }
148
+ };
149
+ var LaunchError = class extends MoltlaunchError {
150
+ constructor(detail) {
151
+ super(`Token launch failed: ${detail}`, EXIT_CODES.LAUNCH_FAIL);
152
+ }
153
+ };
154
+ var TimeoutError = class extends MoltlaunchError {
155
+ constructor() {
156
+ super("Launch timed out waiting for confirmation.", EXIT_CODES.TIMEOUT);
157
+ }
158
+ };
159
+ var NoGasError = class extends MoltlaunchError {
160
+ constructor(address) {
161
+ super(
162
+ `Wallet ${address} has no ETH for gas. Send Base ETH to this address and retry.`,
163
+ EXIT_CODES.NO_GAS
164
+ );
165
+ }
166
+ };
167
+
168
+ // src/lib/flaunch-api.ts
169
+ function sleep(ms) {
170
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
171
+ }
172
+ async function fetchWithRetry(url, options, retries = 3) {
173
+ for (let attempt = 0; attempt < retries; attempt++) {
174
+ const response = await fetch(url, options);
175
+ if (response.status === 429) {
176
+ const retryAfter = response.headers.get("retry-after");
177
+ const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 2e3 * (attempt + 1);
178
+ await sleep(waitMs);
179
+ continue;
180
+ }
181
+ return response;
182
+ }
183
+ throw new Error("Max retries exceeded (429 rate limit)");
184
+ }
185
+ async function uploadImage(imagePath) {
186
+ const fileStat = await stat(imagePath);
187
+ if (fileStat.size > MAX_IMAGE_SIZE_BYTES) {
188
+ throw new UploadError(`Image exceeds 5MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)}MB)`);
189
+ }
190
+ const imageBuffer = await readFile2(imagePath);
191
+ const base64 = imageBuffer.toString("base64");
192
+ const ext = imagePath.split(".").pop()?.toLowerCase();
193
+ const mimeMap = {
194
+ png: "image/png",
195
+ jpg: "image/jpeg",
196
+ jpeg: "image/jpeg",
197
+ gif: "image/gif",
198
+ webp: "image/webp",
199
+ svg: "image/svg+xml"
200
+ };
201
+ const mime = mimeMap[ext ?? ""] ?? "image/png";
202
+ const dataUrl = `data:${mime};base64,${base64}`;
203
+ const response = await fetchWithRetry(`${FLAUNCH_API_BASE}/api/v1/upload-image`, {
204
+ method: "POST",
205
+ headers: { "Content-Type": "application/json" },
206
+ body: JSON.stringify({ base64Image: dataUrl })
207
+ });
208
+ if (!response.ok) {
209
+ const text = await response.text();
210
+ throw new UploadError(`${response.status} \u2014 ${text}`);
211
+ }
212
+ const data = await response.json();
213
+ return data.ipfsHash;
214
+ }
215
+ async function launchMemecoin(params) {
216
+ const chain = params.network === "testnet" ? CHAIN.testnet : CHAIN.mainnet;
217
+ const body = {
218
+ name: params.name,
219
+ symbol: params.symbol,
220
+ description: params.description,
221
+ imageIpfs: params.imageIpfs,
222
+ creatorAddress: params.creatorAddress,
223
+ revenueManagerAddress: params.revenueManagerAddress
224
+ };
225
+ const response = await fetchWithRetry(
226
+ `${FLAUNCH_API_BASE}/api/v1/${chain.network}/launch-memecoin`,
227
+ {
228
+ method: "POST",
229
+ headers: { "Content-Type": "application/json" },
230
+ body: JSON.stringify(body)
231
+ }
232
+ );
233
+ if (!response.ok) {
234
+ const text = await response.text();
235
+ throw new LaunchError(`${response.status} \u2014 ${text}`);
236
+ }
237
+ const data = await response.json();
238
+ return data.jobId;
239
+ }
240
+ async function pollLaunchStatus(jobId, onPoll) {
241
+ const startTime = Date.now();
242
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
243
+ const response = await fetch(`${FLAUNCH_API_BASE}/api/v1/launch-status/${jobId}`);
244
+ if (!response.ok) {
245
+ const text = await response.text();
246
+ throw new LaunchError(`Status check failed: ${response.status} \u2014 ${text}`);
247
+ }
248
+ const data = await response.json();
249
+ onPoll?.(data.state, data.queuePosition);
250
+ if (data.state === "completed") return data;
251
+ if (data.state === "failed") {
252
+ throw new LaunchError(data.error ?? "Launch failed with no error message");
253
+ }
254
+ await sleep(POLL_INTERVAL_MS);
255
+ }
256
+ throw new TimeoutError();
257
+ }
258
+
259
+ // src/lib/output.ts
260
+ function formatOutput(data, json) {
261
+ if (json) {
262
+ return JSON.stringify(data, null, 2);
263
+ }
264
+ const lines = [];
265
+ for (const [key, value] of Object.entries(data)) {
266
+ if (value === void 0 || value === null) continue;
267
+ const label = key.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase());
268
+ lines.push(` ${label}: ${String(value)}`);
269
+ }
270
+ return lines.join("\n");
271
+ }
272
+ function printSuccess(message, data, json) {
273
+ if (json) {
274
+ console.log(JSON.stringify({ success: true, ...data }, null, 2));
275
+ } else {
276
+ console.log(`
277
+ ${message}
278
+ `);
279
+ console.log(formatOutput(data, false));
280
+ console.log();
281
+ }
282
+ }
283
+ function printError(message, json, exitCode) {
284
+ if (json) {
285
+ console.error(JSON.stringify({ success: false, error: message, exitCode }));
286
+ } else {
287
+ console.error(`
288
+ Error: ${message}
289
+ `);
290
+ }
291
+ }
292
+
293
+ // src/commands/launch.ts
294
+ async function launch(opts) {
295
+ const { name, symbol, description, imagePath, testnet, json } = opts;
296
+ const network = testnet ? "testnet" : "mainnet";
297
+ const chain = testnet ? CHAIN.testnet : CHAIN.mainnet;
298
+ try {
299
+ const resolvedImage = resolve(imagePath);
300
+ try {
301
+ await access2(resolvedImage);
302
+ } catch {
303
+ printError(`Image not found: ${resolvedImage}`, json, EXIT_CODES.UPLOAD_FAIL);
304
+ process.exit(EXIT_CODES.UPLOAD_FAIL);
305
+ }
306
+ const { wallet: wallet2, isNew } = await loadOrCreateWallet();
307
+ if (!json) {
308
+ if (isNew) {
309
+ console.log(`
310
+ Wallet created: ${wallet2.address}`);
311
+ console.log(`Private key: ${wallet2.privateKey}`);
312
+ console.log("(Save this key \u2014 it will not be shown again)\n");
313
+ } else {
314
+ console.log(`
315
+ Using wallet: ${wallet2.address}`);
316
+ }
317
+ }
318
+ if (!json) process.stdout.write("Uploading image...");
319
+ const imageIpfs = await uploadImage(resolvedImage);
320
+ if (!json) console.log(` ${imageIpfs.slice(0, 16)}...`);
321
+ if (!json) process.stdout.write("Submitting launch...");
322
+ const jobId = await launchMemecoin({
323
+ name,
324
+ symbol,
325
+ description,
326
+ imageIpfs,
327
+ creatorAddress: wallet2.address,
328
+ revenueManagerAddress: REVENUE_MANAGER_ADDRESS,
329
+ network
330
+ });
331
+ if (!json) console.log(` queued (job ${jobId})`);
332
+ if (!json) process.stdout.write("Deploying on-chain");
333
+ const result = await pollLaunchStatus(jobId, (state, position) => {
334
+ if (!json) {
335
+ if (position > 0) {
336
+ process.stdout.write(` [queue: ${position}]`);
337
+ } else {
338
+ process.stdout.write(".");
339
+ }
340
+ }
341
+ });
342
+ if (!json) console.log(" done");
343
+ const tokenAddress = result.collectionToken.address;
344
+ const flaunchUrl = `${chain.flaunchUrl}/coin/${tokenAddress}`;
345
+ await saveLaunchRecord({
346
+ name,
347
+ symbol,
348
+ tokenAddress,
349
+ transactionHash: result.transactionHash,
350
+ network,
351
+ walletAddress: wallet2.address,
352
+ launchedAt: (/* @__PURE__ */ new Date()).toISOString(),
353
+ flaunchUrl
354
+ });
355
+ const outputData = {
356
+ tokenAddress,
357
+ transactionHash: result.transactionHash,
358
+ name,
359
+ symbol,
360
+ network: chain.name,
361
+ explorer: `${chain.explorer}/token/${tokenAddress}`,
362
+ flaunch: flaunchUrl,
363
+ wallet: wallet2.address
364
+ };
365
+ if (isNew) {
366
+ outputData.privateKey = wallet2.privateKey;
367
+ outputData.walletNote = "Save this private key \u2014 it will not be shown again";
368
+ }
369
+ printSuccess("Token launched successfully!", outputData, json);
370
+ } catch (error) {
371
+ if (error instanceof MoltlaunchError) {
372
+ printError(error.message, json, error.exitCode);
373
+ process.exit(error.exitCode);
374
+ }
375
+ const message = error instanceof Error ? error.message : String(error);
376
+ printError(message, json, EXIT_CODES.GENERAL);
377
+ process.exit(EXIT_CODES.GENERAL);
378
+ }
379
+ }
380
+
381
+ // src/commands/wallet.ts
382
+ async function wallet(opts) {
383
+ const { showKey, json } = opts;
384
+ const data = await loadWallet();
385
+ if (!data) {
386
+ printError("No wallet found. Run `moltlaunch` to create one.", json, EXIT_CODES.NO_WALLET);
387
+ process.exit(EXIT_CODES.NO_WALLET);
388
+ }
389
+ let balance = "unknown";
390
+ try {
391
+ balance = await getWalletBalance(data.address, "mainnet");
392
+ } catch {
393
+ }
394
+ const output = {
395
+ address: data.address,
396
+ balance: `${balance} ETH (Base)`,
397
+ createdAt: data.createdAt
398
+ };
399
+ if (showKey) {
400
+ output.privateKey = data.privateKey;
401
+ }
402
+ printSuccess("Wallet info", output, json);
403
+ }
404
+
405
+ // src/commands/status.ts
406
+ async function status(opts) {
407
+ const { json } = opts;
408
+ const records = await loadLaunchRecords();
409
+ if (records.length === 0) {
410
+ if (json) {
411
+ console.log(JSON.stringify({ success: true, launches: [] }));
412
+ } else {
413
+ console.log("\nNo tokens launched yet. Run `moltlaunch` to launch one.\n");
414
+ }
415
+ return;
416
+ }
417
+ if (json) {
418
+ console.log(JSON.stringify({ success: true, launches: records }, null, 2));
419
+ return;
420
+ }
421
+ console.log(`
422
+ Launched tokens (${records.length}):
423
+ `);
424
+ for (const record of records) {
425
+ console.log(` ${record.name} (${record.symbol})`);
426
+ console.log(` Token: ${record.tokenAddress}`);
427
+ console.log(` TX: ${record.transactionHash}`);
428
+ console.log(` Flaunch: ${record.flaunchUrl}`);
429
+ console.log(` Network: ${record.network}`);
430
+ console.log(` Launched: ${record.launchedAt}`);
431
+ console.log();
432
+ }
433
+ }
434
+
435
+ // src/commands/claim.ts
436
+ import { ethers as ethers2 } from "ethers";
437
+ var POSITION_MANAGER_ABI = [
438
+ "function balances(address) external view returns (uint256)",
439
+ "function withdrawFees(address _recipient, bool _unwrap) external"
440
+ ];
441
+ async function claim(opts) {
442
+ const { testnet, json } = opts;
443
+ const network = testnet ? "testnet" : "mainnet";
444
+ try {
445
+ const walletData = await loadWallet();
446
+ if (!walletData) {
447
+ throw new NoWalletError();
448
+ }
449
+ const balance = await getWalletBalance(walletData.address, network);
450
+ if (parseFloat(balance) === 0) {
451
+ throw new NoGasError(walletData.address);
452
+ }
453
+ const signer = await getSigner(walletData.privateKey, network);
454
+ const pm = new ethers2.Contract(POSITION_MANAGER_ADDRESS, POSITION_MANAGER_ABI, signer);
455
+ const claimable = await pm.balances(walletData.address);
456
+ const claimableEth = ethers2.formatEther(claimable);
457
+ if (claimable === 0n) {
458
+ printSuccess("No fees to claim", {
459
+ claimable: "0 ETH",
460
+ wallet: walletData.address,
461
+ network
462
+ }, json);
463
+ return;
464
+ }
465
+ if (!json) console.log(`
466
+ Claimable: ${claimableEth} ETH`);
467
+ if (!json) process.stdout.write("Submitting withdraw transaction...");
468
+ const tx = await pm.withdrawFees(walletData.address, true);
469
+ if (!json) console.log(` tx ${tx.hash}`);
470
+ if (!json) process.stdout.write("Waiting for confirmation...");
471
+ const receipt = await tx.wait();
472
+ if (!json) console.log(" confirmed");
473
+ printSuccess("Fees claimed successfully!", {
474
+ transactionHash: receipt.hash,
475
+ claimed: `${claimableEth} ETH`,
476
+ wallet: walletData.address,
477
+ network
478
+ }, json);
479
+ } catch (error) {
480
+ if (error instanceof MoltlaunchError) {
481
+ printError(error.message, json, error.exitCode);
482
+ process.exit(error.exitCode);
483
+ }
484
+ const message = error instanceof Error ? error.message : String(error);
485
+ printError(message, json, EXIT_CODES.GENERAL);
486
+ process.exit(EXIT_CODES.GENERAL);
487
+ }
488
+ }
489
+
490
+ // src/index.ts
491
+ var program = new Command();
492
+ program.name("moltlaunch").description("CLI for AI agents to launch tokens on Base via Flaunch").version("0.1.0");
493
+ program.command("launch", { isDefault: true }).description("Launch a new token on Base").requiredOption("--name <name>", "Token name").requiredOption("--symbol <symbol>", "Token symbol").requiredOption("--description <desc>", "Token description").requiredOption("--image <path>", "Path to token image (max 5MB)").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON (for agents)", false).action((opts) => {
494
+ launch({
495
+ name: opts.name,
496
+ symbol: opts.symbol,
497
+ description: opts.description,
498
+ imagePath: opts.image,
499
+ testnet: opts.testnet,
500
+ json: opts.json
501
+ });
502
+ });
503
+ program.command("wallet").description("Show wallet address and balance").option("--show-key", "Show private key", false).option("--json", "Output as JSON", false).action((opts) => {
504
+ wallet({ showKey: opts.showKey, json: opts.json });
505
+ });
506
+ program.command("status").description("List launched tokens").option("--json", "Output as JSON", false).action((opts) => {
507
+ status({ json: opts.json });
508
+ });
509
+ program.command("claim").description("Withdraw accumulated fees from PositionManager escrow").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action((opts) => {
510
+ claim({ testnet: opts.testnet, json: opts.json });
511
+ });
512
+ program.parse();
513
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/launch.ts","../src/lib/wallet.ts","../src/lib/config.ts","../src/lib/flaunch-api.ts","../src/lib/errors.ts","../src/lib/output.ts","../src/commands/wallet.ts","../src/commands/status.ts","../src/commands/claim.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { launch } from \"./commands/launch.js\";\nimport { wallet } from \"./commands/wallet.js\";\nimport { status } from \"./commands/status.js\";\nimport { claim } from \"./commands/claim.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"moltlaunch\")\n .description(\"CLI for AI agents to launch tokens on Base via Flaunch\")\n .version(\"0.1.0\");\n\n// Default command: launch a token\nprogram\n .command(\"launch\", { isDefault: true })\n .description(\"Launch a new token on Base\")\n .requiredOption(\"--name <name>\", \"Token name\")\n .requiredOption(\"--symbol <symbol>\", \"Token symbol\")\n .requiredOption(\"--description <desc>\", \"Token description\")\n .requiredOption(\"--image <path>\", \"Path to token image (max 5MB)\")\n .option(\"--testnet\", \"Use Base Sepolia testnet\", false)\n .option(\"--json\", \"Output as JSON (for agents)\", false)\n .action((opts) => {\n launch({\n name: opts.name,\n symbol: opts.symbol,\n description: opts.description,\n imagePath: opts.image,\n testnet: opts.testnet,\n json: opts.json,\n });\n });\n\nprogram\n .command(\"wallet\")\n .description(\"Show wallet address and balance\")\n .option(\"--show-key\", \"Show private key\", false)\n .option(\"--json\", \"Output as JSON\", false)\n .action((opts) => {\n wallet({ showKey: opts.showKey, json: opts.json });\n });\n\nprogram\n .command(\"status\")\n .description(\"List launched tokens\")\n .option(\"--json\", \"Output as JSON\", false)\n .action((opts) => {\n status({ json: opts.json });\n });\n\nprogram\n .command(\"claim\")\n .description(\"Withdraw accumulated fees from PositionManager escrow\")\n .option(\"--testnet\", \"Use Base Sepolia testnet\", false)\n .option(\"--json\", \"Output as JSON\", false)\n .action((opts) => {\n claim({ testnet: opts.testnet, json: opts.json });\n });\n\nprogram.parse();\n","import { resolve } from \"node:path\";\nimport { access } from \"node:fs/promises\";\nimport { loadOrCreateWallet, saveLaunchRecord } from \"../lib/wallet.js\";\nimport { uploadImage, launchMemecoin, pollLaunchStatus } from \"../lib/flaunch-api.js\";\nimport { printSuccess, printError } from \"../lib/output.js\";\nimport { CHAIN, REVENUE_MANAGER_ADDRESS } from \"../lib/config.js\";\nimport { MoltlaunchError, EXIT_CODES } from \"../lib/errors.js\";\nimport type { LaunchParams, Network } from \"../types.js\";\n\nexport async function launch(opts: LaunchParams): Promise<void> {\n const { name, symbol, description, imagePath, testnet, json } = opts;\n const network: Network = testnet ? \"testnet\" : \"mainnet\";\n const chain = testnet ? CHAIN.testnet : CHAIN.mainnet;\n\n try {\n // Validate image exists\n const resolvedImage = resolve(imagePath);\n try {\n await access(resolvedImage);\n } catch {\n printError(`Image not found: ${resolvedImage}`, json, EXIT_CODES.UPLOAD_FAIL);\n process.exit(EXIT_CODES.UPLOAD_FAIL);\n }\n\n // Step 1: Load or create wallet\n const { wallet, isNew } = await loadOrCreateWallet();\n\n if (!json) {\n if (isNew) {\n console.log(`\\nWallet created: ${wallet.address}`);\n console.log(`Private key: ${wallet.privateKey}`);\n console.log(\"(Save this key — it will not be shown again)\\n\");\n } else {\n console.log(`\\nUsing wallet: ${wallet.address}`);\n }\n }\n\n // Step 2: Upload image to IPFS\n if (!json) process.stdout.write(\"Uploading image...\");\n const imageIpfs = await uploadImage(resolvedImage);\n if (!json) console.log(` ${imageIpfs.slice(0, 16)}...`);\n\n // Step 3: Launch via Flaunch Web2 API (gasless — Flaunch handles on-chain tx)\n if (!json) process.stdout.write(\"Submitting launch...\");\n const jobId = await launchMemecoin({\n name,\n symbol,\n description,\n imageIpfs,\n creatorAddress: wallet.address,\n revenueManagerAddress: REVENUE_MANAGER_ADDRESS,\n network,\n });\n if (!json) console.log(` queued (job ${jobId})`);\n\n // Step 4: Poll for completion\n if (!json) process.stdout.write(\"Deploying on-chain\");\n const result = await pollLaunchStatus(jobId, (state, position) => {\n if (!json) {\n if (position > 0) {\n process.stdout.write(` [queue: ${position}]`);\n } else {\n process.stdout.write(\".\");\n }\n }\n });\n if (!json) console.log(\" done\");\n\n const tokenAddress = result.collectionToken!.address;\n const flaunchUrl = `${chain.flaunchUrl}/coin/${tokenAddress}`;\n\n // Step 5: Save launch record\n await saveLaunchRecord({\n name,\n symbol,\n tokenAddress,\n transactionHash: result.transactionHash!,\n network,\n walletAddress: wallet.address,\n launchedAt: new Date().toISOString(),\n flaunchUrl,\n });\n\n // Output result\n const outputData: Record<string, unknown> = {\n tokenAddress,\n transactionHash: result.transactionHash,\n name,\n symbol,\n network: chain.name,\n explorer: `${chain.explorer}/token/${tokenAddress}`,\n flaunch: flaunchUrl,\n wallet: wallet.address,\n };\n\n if (isNew) {\n outputData.privateKey = wallet.privateKey;\n outputData.walletNote = \"Save this private key — it will not be shown again\";\n }\n\n printSuccess(\"Token launched successfully!\", outputData, json);\n } catch (error) {\n if (error instanceof MoltlaunchError) {\n printError(error.message, json, error.exitCode);\n process.exit(error.exitCode);\n }\n const message = error instanceof Error ? error.message : String(error);\n printError(message, json, EXIT_CODES.GENERAL);\n process.exit(EXIT_CODES.GENERAL);\n }\n}\n","import { ethers } from \"ethers\";\nimport { readFile, writeFile, mkdir, chmod, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { WALLET_DIR, WALLET_FILE, LAUNCHES_FILE, CHAIN } from \"./config.js\";\nimport type { WalletData, LaunchRecord, Network } from \"../types.js\";\n\nfunction getWalletDir(): string {\n return join(homedir(), WALLET_DIR);\n}\n\nfunction getWalletPath(): string {\n return join(getWalletDir(), WALLET_FILE);\n}\n\nfunction getLaunchesPath(): string {\n return join(getWalletDir(), LAUNCHES_FILE);\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function walletExists(): Promise<boolean> {\n return fileExists(getWalletPath());\n}\n\nexport async function loadWallet(): Promise<WalletData | null> {\n const path = getWalletPath();\n if (!(await fileExists(path))) return null;\n\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as WalletData;\n}\n\nexport async function createWallet(): Promise<WalletData> {\n const wallet = ethers.Wallet.createRandom();\n const data: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: new Date().toISOString(),\n };\n\n const dir = getWalletDir();\n await mkdir(dir, { recursive: true });\n\n const path = getWalletPath();\n await writeFile(path, JSON.stringify(data, null, 2), { mode: 0o600 });\n await chmod(path, 0o600);\n\n return data;\n}\n\nexport async function loadOrCreateWallet(): Promise<{ wallet: WalletData; isNew: boolean }> {\n const existing = await loadWallet();\n if (existing) return { wallet: existing, isNew: false };\n\n const wallet = await createWallet();\n return { wallet, isNew: true };\n}\n\nexport async function getWalletBalance(address: string, network: Network): Promise<string> {\n const chain = network === \"testnet\" ? CHAIN.testnet : CHAIN.mainnet;\n const provider = new ethers.JsonRpcProvider(chain.rpcUrl);\n const balance = await provider.getBalance(address);\n return ethers.formatEther(balance);\n}\n\nexport async function getSigner(privateKey: string, network: Network): Promise<ethers.Wallet> {\n const chain = network === \"testnet\" ? CHAIN.testnet : CHAIN.mainnet;\n const provider = new ethers.JsonRpcProvider(chain.rpcUrl);\n return new ethers.Wallet(privateKey, provider);\n}\n\nexport async function saveLaunchRecord(record: LaunchRecord): Promise<void> {\n const path = getLaunchesPath();\n let records: LaunchRecord[] = [];\n\n if (await fileExists(path)) {\n const raw = await readFile(path, \"utf-8\");\n records = JSON.parse(raw) as LaunchRecord[];\n }\n\n records.push(record);\n\n const dir = getWalletDir();\n await mkdir(dir, { recursive: true });\n await writeFile(path, JSON.stringify(records, null, 2), { mode: 0o600 });\n}\n\nexport async function loadLaunchRecords(): Promise<LaunchRecord[]> {\n const path = getLaunchesPath();\n if (!(await fileExists(path))) return [];\n\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as LaunchRecord[];\n}\n","export const REVENUE_MANAGER_ADDRESS = \"0x3Bc08524d9DaaDEC9d1Af87818d809611F0fD669\";\n\n// Flaunch PositionManager — fees accumulate here in escrow\nexport const POSITION_MANAGER_ADDRESS = \"0x51Bba15255406Cfe7099a42183302640ba7dAFDC\";\n\nexport const FLAUNCH_API_BASE = \"https://web2-api.flaunch.gg\";\n\nexport const CHAIN = {\n mainnet: {\n id: 8453,\n name: \"Base\",\n network: \"base\",\n rpcUrl: \"https://mainnet.base.org\",\n explorer: \"https://basescan.org\",\n flaunchUrl: \"https://flaunch.gg/base\",\n },\n testnet: {\n id: 84532,\n name: \"Base Sepolia\",\n network: \"base-sepolia\",\n rpcUrl: \"https://sepolia.base.org\",\n explorer: \"https://sepolia.basescan.org\",\n flaunchUrl: \"https://flaunch.gg/base-sepolia\",\n },\n} as const;\n\nexport const WALLET_DIR = \".moltlaunch\";\nexport const WALLET_FILE = \"wallet.json\";\nexport const LAUNCHES_FILE = \"launches.json\";\n\nexport const MAX_IMAGE_SIZE_BYTES = 5 * 1024 * 1024; // 5MB\nexport const POLL_INTERVAL_MS = 2000;\nexport const POLL_TIMEOUT_MS = 120_000;\n","import { readFile, stat } from \"node:fs/promises\";\nimport {\n FLAUNCH_API_BASE,\n CHAIN,\n MAX_IMAGE_SIZE_BYTES,\n POLL_INTERVAL_MS,\n POLL_TIMEOUT_MS,\n} from \"./config.js\";\nimport { UploadError, LaunchError, TimeoutError } from \"./errors.js\";\nimport type {\n Network,\n FlaunchUploadResponse,\n FlaunchLaunchResponse,\n FlaunchStatusResponse,\n} from \"../types.js\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n retries = 3,\n): Promise<Response> {\n for (let attempt = 0; attempt < retries; attempt++) {\n const response = await fetch(url, options);\n\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"retry-after\");\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 2000 * (attempt + 1);\n await sleep(waitMs);\n continue;\n }\n\n return response;\n }\n\n throw new Error(\"Max retries exceeded (429 rate limit)\");\n}\n\n/**\n * Upload image to IPFS via Flaunch Web2 API.\n * Reads file as base64 data URL, returns IPFS hash.\n */\nexport async function uploadImage(imagePath: string): Promise<string> {\n const fileStat = await stat(imagePath);\n if (fileStat.size > MAX_IMAGE_SIZE_BYTES) {\n throw new UploadError(`Image exceeds 5MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)}MB)`);\n }\n\n const imageBuffer = await readFile(imagePath);\n const base64 = imageBuffer.toString(\"base64\");\n\n const ext = imagePath.split(\".\").pop()?.toLowerCase();\n const mimeMap: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n };\n const mime = mimeMap[ext ?? \"\"] ?? \"image/png\";\n const dataUrl = `data:${mime};base64,${base64}`;\n\n const response = await fetchWithRetry(`${FLAUNCH_API_BASE}/api/v1/upload-image`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ base64Image: dataUrl }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new UploadError(`${response.status} — ${text}`);\n }\n\n const data = (await response.json()) as FlaunchUploadResponse;\n return data.ipfsHash;\n}\n\n/**\n * Launch a memecoin via Flaunch Web2 API.\n * This is gasless — Flaunch handles the on-chain transaction server-side.\n */\nexport async function launchMemecoin(params: {\n name: string;\n symbol: string;\n description: string;\n imageIpfs: string;\n creatorAddress: string;\n revenueManagerAddress?: string;\n network: Network;\n}): Promise<string> {\n const chain = params.network === \"testnet\" ? CHAIN.testnet : CHAIN.mainnet;\n\n const body = {\n name: params.name,\n symbol: params.symbol,\n description: params.description,\n imageIpfs: params.imageIpfs,\n creatorAddress: params.creatorAddress,\n revenueManagerAddress: params.revenueManagerAddress,\n };\n\n const response = await fetchWithRetry(\n `${FLAUNCH_API_BASE}/api/v1/${chain.network}/launch-memecoin`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new LaunchError(`${response.status} — ${text}`);\n }\n\n const data = (await response.json()) as FlaunchLaunchResponse;\n return data.jobId;\n}\n\n/**\n * Poll launch status until completed or failed.\n */\nexport async function pollLaunchStatus(\n jobId: string,\n onPoll?: (state: string, position: number) => void,\n): Promise<FlaunchStatusResponse> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < POLL_TIMEOUT_MS) {\n const response = await fetch(`${FLAUNCH_API_BASE}/api/v1/launch-status/${jobId}`);\n\n if (!response.ok) {\n const text = await response.text();\n throw new LaunchError(`Status check failed: ${response.status} — ${text}`);\n }\n\n const data = (await response.json()) as FlaunchStatusResponse;\n onPoll?.(data.state, data.queuePosition);\n\n if (data.state === \"completed\") return data;\n if (data.state === \"failed\") {\n throw new LaunchError(data.error ?? \"Launch failed with no error message\");\n }\n\n await sleep(POLL_INTERVAL_MS);\n }\n\n throw new TimeoutError();\n}\n","export const EXIT_CODES = {\n SUCCESS: 0,\n GENERAL: 1,\n NO_WALLET: 2,\n UPLOAD_FAIL: 3,\n LAUNCH_FAIL: 4,\n TIMEOUT: 5,\n NO_GAS: 6,\n} as const;\n\nexport class MoltlaunchError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number,\n ) {\n super(message);\n this.name = \"MoltlaunchError\";\n }\n}\n\nexport class NoWalletError extends MoltlaunchError {\n constructor() {\n super(\"No wallet found. Run `moltlaunch` to create one.\", EXIT_CODES.NO_WALLET);\n }\n}\n\nexport class UploadError extends MoltlaunchError {\n constructor(detail: string) {\n super(`Image upload failed: ${detail}`, EXIT_CODES.UPLOAD_FAIL);\n }\n}\n\nexport class LaunchError extends MoltlaunchError {\n constructor(detail: string) {\n super(`Token launch failed: ${detail}`, EXIT_CODES.LAUNCH_FAIL);\n }\n}\n\nexport class TimeoutError extends MoltlaunchError {\n constructor() {\n super(\"Launch timed out waiting for confirmation.\", EXIT_CODES.TIMEOUT);\n }\n}\n\nexport class NoGasError extends MoltlaunchError {\n constructor(address: string) {\n super(\n `Wallet ${address} has no ETH for gas. Send Base ETH to this address and retry.`,\n EXIT_CODES.NO_GAS,\n );\n }\n}\n","export function formatOutput(data: Record<string, unknown>, json: boolean): string {\n if (json) {\n return JSON.stringify(data, null, 2);\n }\n\n const lines: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined || value === null) continue;\n const label = key.replace(/([A-Z])/g, \" $1\").replace(/^./, (s) => s.toUpperCase());\n lines.push(` ${label}: ${String(value)}`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function printSuccess(message: string, data: Record<string, unknown>, json: boolean): void {\n if (json) {\n console.log(JSON.stringify({ success: true, ...data }, null, 2));\n } else {\n console.log(`\\n${message}\\n`);\n console.log(formatOutput(data, false));\n console.log();\n }\n}\n\nexport function printError(message: string, json: boolean, exitCode: number): void {\n if (json) {\n console.error(JSON.stringify({ success: false, error: message, exitCode }));\n } else {\n console.error(`\\nError: ${message}\\n`);\n }\n}\n","import { loadWallet, getWalletBalance } from \"../lib/wallet.js\";\nimport { printSuccess, printError } from \"../lib/output.js\";\nimport { EXIT_CODES } from \"../lib/errors.js\";\n\ninterface WalletOpts {\n showKey: boolean;\n json: boolean;\n}\n\nexport async function wallet(opts: WalletOpts): Promise<void> {\n const { showKey, json } = opts;\n\n const data = await loadWallet();\n if (!data) {\n printError(\"No wallet found. Run `moltlaunch` to create one.\", json, EXIT_CODES.NO_WALLET);\n process.exit(EXIT_CODES.NO_WALLET);\n }\n\n let balance = \"unknown\";\n try {\n balance = await getWalletBalance(data.address, \"mainnet\");\n } catch {\n // RPC may be unreachable\n }\n\n const output: Record<string, unknown> = {\n address: data.address,\n balance: `${balance} ETH (Base)`,\n createdAt: data.createdAt,\n };\n\n if (showKey) {\n output.privateKey = data.privateKey;\n }\n\n printSuccess(\"Wallet info\", output, json);\n}\n","import { loadLaunchRecords } from \"../lib/wallet.js\";\n\ninterface StatusOpts {\n json: boolean;\n}\n\nexport async function status(opts: StatusOpts): Promise<void> {\n const { json } = opts;\n const records = await loadLaunchRecords();\n\n if (records.length === 0) {\n if (json) {\n console.log(JSON.stringify({ success: true, launches: [] }));\n } else {\n console.log(\"\\nNo tokens launched yet. Run `moltlaunch` to launch one.\\n\");\n }\n return;\n }\n\n if (json) {\n console.log(JSON.stringify({ success: true, launches: records }, null, 2));\n return;\n }\n\n console.log(`\\nLaunched tokens (${records.length}):\\n`);\n for (const record of records) {\n console.log(` ${record.name} (${record.symbol})`);\n console.log(` Token: ${record.tokenAddress}`);\n console.log(` TX: ${record.transactionHash}`);\n console.log(` Flaunch: ${record.flaunchUrl}`);\n console.log(` Network: ${record.network}`);\n console.log(` Launched: ${record.launchedAt}`);\n console.log();\n }\n}\n","import { ethers } from \"ethers\";\nimport { loadWallet, getSigner, getWalletBalance } from \"../lib/wallet.js\";\nimport { POSITION_MANAGER_ADDRESS } from \"../lib/config.js\";\nimport { printSuccess, printError } from \"../lib/output.js\";\nimport { EXIT_CODES, NoWalletError, NoGasError, MoltlaunchError } from \"../lib/errors.js\";\nimport type { Network } from \"../types.js\";\n\n// Fees accumulate in escrow on the PositionManager, not on the Revenue Manager\nconst POSITION_MANAGER_ABI = [\n \"function balances(address) external view returns (uint256)\",\n \"function withdrawFees(address _recipient, bool _unwrap) external\",\n];\n\ninterface ClaimOpts {\n testnet: boolean;\n json: boolean;\n}\n\nexport async function claim(opts: ClaimOpts): Promise<void> {\n const { testnet, json } = opts;\n const network: Network = testnet ? \"testnet\" : \"mainnet\";\n\n try {\n const walletData = await loadWallet();\n if (!walletData) {\n throw new NoWalletError();\n }\n\n // Check gas balance\n const balance = await getWalletBalance(walletData.address, network);\n if (parseFloat(balance) === 0) {\n throw new NoGasError(walletData.address);\n }\n\n const signer = await getSigner(walletData.privateKey, network);\n const pm = new ethers.Contract(POSITION_MANAGER_ADDRESS, POSITION_MANAGER_ABI, signer);\n\n // Check claimable balance\n const claimable = await pm.balances(walletData.address);\n const claimableEth = ethers.formatEther(claimable);\n\n if (claimable === 0n) {\n printSuccess(\"No fees to claim\", {\n claimable: \"0 ETH\",\n wallet: walletData.address,\n network,\n }, json);\n return;\n }\n\n if (!json) console.log(`\\nClaimable: ${claimableEth} ETH`);\n if (!json) process.stdout.write(\"Submitting withdraw transaction...\");\n\n // withdrawFees sends to _recipient, _unwrap=true converts flETH→ETH\n const tx = await pm.withdrawFees(walletData.address, true);\n if (!json) console.log(` tx ${tx.hash}`);\n\n if (!json) process.stdout.write(\"Waiting for confirmation...\");\n const receipt = await tx.wait();\n if (!json) console.log(\" confirmed\");\n\n printSuccess(\"Fees claimed successfully!\", {\n transactionHash: receipt.hash,\n claimed: `${claimableEth} ETH`,\n wallet: walletData.address,\n network,\n }, json);\n } catch (error) {\n if (error instanceof MoltlaunchError) {\n printError(error.message, json, error.exitCode);\n process.exit(error.exitCode);\n }\n const message = error instanceof Error ? error.message : String(error);\n printError(message, json, EXIT_CODES.GENERAL);\n process.exit(EXIT_CODES.GENERAL);\n }\n}\n"],"mappings":"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","names":["access","wallet","readFile","resolve","readFile","access","wallet","ethers","ethers"]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "moltlaunch",
3
+ "version": "0.1.0",
4
+ "description": "CLI for AI agents to launch tokens on Base via Flaunch",
5
+ "type": "module",
6
+ "bin": {
7
+ "moltlaunch": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "keywords": [
18
+ "token",
19
+ "launch",
20
+ "base",
21
+ "flaunch",
22
+ "memecoin",
23
+ "cli",
24
+ "ai-agent"
25
+ ],
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "commander": "^12.1.0",
29
+ "ethers": "^6.13.0"
30
+ },
31
+ "devDependencies": {
32
+ "tsup": "^8.3.0",
33
+ "typescript": "^5.9.0",
34
+ "@types/node": "^22.0.0"
35
+ }
36
+ }