clawdpad 1.0.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,125 @@
1
+ # Clawdpad CLI
2
+
3
+ > One-command token launch for AI agents 🦞
4
+
5
+ ```bash
6
+ npx clawdpad launch "Doge AI" "DOGEAI" --supply 1000000000
7
+ ```
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ # Use directly with npx (no install needed)
13
+ npx clawdpad launch "MyToken" "MTK"
14
+
15
+ # Or install globally
16
+ npm install -g clawdpad
17
+ clawdpad launch "MyToken" "MTK"
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ ### Launch a Token
23
+
24
+ ```bash
25
+ # Basic launch
26
+ clawdpad launch "Token Name" "SYMBOL"
27
+
28
+ # With options
29
+ clawdpad launch "Doge AI" "DOGEAI" \
30
+ --supply 1000000000 \
31
+ --description "The smartest doge on Base" \
32
+ --website "https://dogeai.xyz" \
33
+ --twitter "@dogeai" \
34
+ --wallet 0xYourAddress
35
+
36
+ # Dry run (test without launching)
37
+ clawdpad launch "Test Token" "TEST" --dry-run
38
+ ```
39
+
40
+ ### List Recent Tokens
41
+
42
+ ```bash
43
+ clawdpad list
44
+ clawdpad list --limit 20
45
+ ```
46
+
47
+ ### Check Trust Score
48
+
49
+ ```bash
50
+ clawdpad trust 0xYourWalletAddress
51
+ ```
52
+
53
+ ### Interactive Mode
54
+
55
+ ```bash
56
+ clawdpad interactive
57
+ # or
58
+ clawdpad i
59
+ ```
60
+
61
+ ### Get Info
62
+
63
+ ```bash
64
+ clawdpad info
65
+ ```
66
+
67
+ ## Options
68
+
69
+ | Option | Description |
70
+ |--------|-------------|
71
+ | `-s, --supply <amount>` | Total supply (default: 1,000,000,000) |
72
+ | `-d, --description <text>` | Token description |
73
+ | `-w, --website <url>` | Website URL |
74
+ | `-t, --twitter <handle>` | Twitter/X handle |
75
+ | `-i, --image <url>` | Token image URL |
76
+ | `--wallet <address>` | Creator wallet (receives tokens) |
77
+ | `--dry-run` | Simulate without launching |
78
+
79
+ ## Payment
80
+
81
+ Launching a token costs **$1 USDC** via x402 protocol.
82
+
83
+ When you run the launch command:
84
+ 1. You'll receive payment instructions
85
+ 2. Send $1 USDC to the treasury on Base
86
+ 3. Re-run the command with payment proof
87
+
88
+ ## For AI Agents
89
+
90
+ Example integration:
91
+
92
+ ```javascript
93
+ import { exec } from 'child_process';
94
+
95
+ async function launchToken(name, symbol, wallet) {
96
+ return new Promise((resolve, reject) => {
97
+ exec(
98
+ `npx clawdpad launch "${name}" "${symbol}" --wallet ${wallet}`,
99
+ (error, stdout, stderr) => {
100
+ if (error) reject(error);
101
+ else resolve(stdout);
102
+ }
103
+ );
104
+ });
105
+ }
106
+
107
+ // Usage
108
+ await launchToken("My AI Token", "MYAI", "0x...");
109
+ ```
110
+
111
+ ## Environment Variables
112
+
113
+ | Variable | Description |
114
+ |----------|-------------|
115
+ | `CLAWDPAD_API_URL` | Custom API URL (default: https://www.clawdpad.ai/api) |
116
+
117
+ ## Links
118
+
119
+ - Website: https://www.clawdpad.ai
120
+ - Token: $CLAWDPAD on Base
121
+ - Twitter: @clawdpad
122
+
123
+ ## License
124
+
125
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const commander_1 = require("commander");
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const ora_1 = __importDefault(require("ora"));
43
+ const node_fetch_1 = __importDefault(require("node-fetch"));
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ const viem_1 = require("viem");
48
+ const chains_1 = require("viem/chains");
49
+ const accounts_1 = require("viem/accounts");
50
+ const API_URL = process.env.CLAWDPAD_API_URL || 'https://www.clawdpad.ai/api';
51
+ const CONFIG_DIR = path.join(os.homedir(), '.clawdpad');
52
+ const WALLET_FILE = path.join(CONFIG_DIR, 'wallet.json');
53
+ const LAUNCHES_FILE = path.join(CONFIG_DIR, 'launches.json');
54
+ // Clanker API (for token deployment)
55
+ const CLANKER_API = 'https://www.clanker.world/api/v1';
56
+ const program = new commander_1.Command();
57
+ // ASCII Art Banner
58
+ const banner = `
59
+ ${chalk_1.default.cyan(' ██████╗██╗ █████╗ ██╗ ██╗██████╗ ██████╗ █████╗ ██████╗ ')}
60
+ ${chalk_1.default.cyan(' ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗')}
61
+ ${chalk_1.default.cyan(' ██║ ██║ ███████║██║ █╗ ██║██║ ██║██████╔╝███████║██║ ██║')}
62
+ ${chalk_1.default.cyan(' ██║ ██║ ██╔══██║██║███╗██║██║ ██║██╔═══╝ ██╔══██║██║ ██║')}
63
+ ${chalk_1.default.cyan(' ╚██████╗███████╗██║ ██║╚███╔███╔╝██████╔╝██║ ██║ ██║██████╔╝')}
64
+ ${chalk_1.default.cyan(' ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ')}
65
+ ${chalk_1.default.gray(' one command. your token. live on base.')}
66
+ `;
67
+ function ensureConfigDir() {
68
+ if (!fs.existsSync(CONFIG_DIR)) {
69
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
70
+ }
71
+ }
72
+ function loadWallet() {
73
+ if (!fs.existsSync(WALLET_FILE)) {
74
+ return null;
75
+ }
76
+ try {
77
+ const data = fs.readFileSync(WALLET_FILE, 'utf-8');
78
+ return JSON.parse(data);
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ function createWallet() {
85
+ ensureConfigDir();
86
+ const privateKey = (0, accounts_1.generatePrivateKey)();
87
+ const account = (0, accounts_1.privateKeyToAccount)(privateKey);
88
+ const wallet = {
89
+ address: account.address,
90
+ privateKey: privateKey,
91
+ createdAt: new Date().toISOString(),
92
+ };
93
+ fs.writeFileSync(WALLET_FILE, JSON.stringify(wallet, null, 2), { mode: 0o600 });
94
+ return wallet;
95
+ }
96
+ function getOrCreateWallet() {
97
+ const existing = loadWallet();
98
+ if (existing) {
99
+ return { wallet: existing, isNew: false };
100
+ }
101
+ return { wallet: createWallet(), isNew: true };
102
+ }
103
+ function loadLaunches() {
104
+ if (!fs.existsSync(LAUNCHES_FILE)) {
105
+ return [];
106
+ }
107
+ try {
108
+ const data = fs.readFileSync(LAUNCHES_FILE, 'utf-8');
109
+ return JSON.parse(data);
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ function saveLaunch(launch) {
116
+ ensureConfigDir();
117
+ const launches = loadLaunches();
118
+ launches.push(launch);
119
+ fs.writeFileSync(LAUNCHES_FILE, JSON.stringify(launches, null, 2));
120
+ }
121
+ // ============ Image Upload ============
122
+ async function uploadImage(imagePath) {
123
+ // Read image file
124
+ const imageBuffer = fs.readFileSync(imagePath);
125
+ const base64 = imageBuffer.toString('base64');
126
+ const ext = path.extname(imagePath).toLowerCase();
127
+ const mimeType = ext === '.png' ? 'image/png' :
128
+ ext === '.gif' ? 'image/gif' :
129
+ ext === '.webp' ? 'image/webp' : 'image/jpeg';
130
+ // Upload to Clawdpad (which uses imgbb or similar)
131
+ const response = await (0, node_fetch_1.default)(`${API_URL}/upload`, {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json' },
134
+ body: JSON.stringify({
135
+ image: `data:${mimeType};base64,${base64}`,
136
+ name: path.basename(imagePath, ext),
137
+ }),
138
+ });
139
+ const result = await response.json();
140
+ if (!result.success || !result.url) {
141
+ throw new Error(result.error || 'Image upload failed');
142
+ }
143
+ return result.url;
144
+ }
145
+ // ============ Token Launch via Clanker ============
146
+ async function launchViaClanker(params) {
147
+ // For now, use Clawdpad API which proxies to Clanker
148
+ // This allows us to sponsor gas and handle the complexity
149
+ const response = await (0, node_fetch_1.default)(`${API_URL}/launch`, {
150
+ method: 'POST',
151
+ headers: { 'Content-Type': 'application/json' },
152
+ body: JSON.stringify({
153
+ name: params.name,
154
+ symbol: params.symbol,
155
+ description: params.description,
156
+ image: params.image,
157
+ website: params.website,
158
+ creatorWallet: params.wallet.address,
159
+ testnet: params.testnet,
160
+ }),
161
+ });
162
+ const result = await response.json();
163
+ if (!response.ok) {
164
+ throw new Error(result.error || 'Launch failed');
165
+ }
166
+ const chainName = params.testnet ? 'base-sepolia' : 'base';
167
+ const explorerBase = params.testnet ? 'https://sepolia.basescan.org' : 'https://basescan.org';
168
+ return {
169
+ tokenAddress: result.token?.address || result.tokenAddress || '0x...',
170
+ transactionHash: result.transactionHash || result.tx_hash || '0x...',
171
+ clankerUrl: `https://clanker.world/clanker/${result.token?.address || result.tokenAddress}`,
172
+ explorerUrl: `${explorerBase}/token/${result.token?.address || result.tokenAddress}`,
173
+ };
174
+ }
175
+ // ============ CLI Commands ============
176
+ program
177
+ .name('clawdpad')
178
+ .description('Launch memecoins on Base — one command, no gas, earn fees')
179
+ .version('1.0.0');
180
+ // Main launch command
181
+ program
182
+ .option('--name <name>', 'Token name')
183
+ .option('--symbol <symbol>', 'Token symbol')
184
+ .option('--description <text>', 'Token description')
185
+ .option('--image <path>', 'Path to image (PNG/JPG/GIF/WebP, max 5MB)')
186
+ .option('--website <url>', 'Website URL (e.g., Moltbook post)')
187
+ .option('--testnet', 'Use Base Sepolia testnet')
188
+ .option('--json', 'Output JSON (machine-readable)')
189
+ .action(async (options) => {
190
+ const jsonMode = options.json;
191
+ if (!jsonMode) {
192
+ console.log(banner);
193
+ }
194
+ // Check if this is a subcommand or launch
195
+ if (!options.name && !options.symbol) {
196
+ if (!jsonMode) {
197
+ program.help();
198
+ }
199
+ return;
200
+ }
201
+ // Validate required fields
202
+ if (!options.name || !options.symbol) {
203
+ const error = { success: false, error: 'Missing required fields: --name and --symbol' };
204
+ if (jsonMode) {
205
+ console.log(JSON.stringify(error));
206
+ process.exit(1);
207
+ }
208
+ console.error(chalk_1.default.red('Error: --name and --symbol are required'));
209
+ process.exit(1);
210
+ }
211
+ const spinner = jsonMode ? null : (0, ora_1.default)('Preparing launch...').start();
212
+ try {
213
+ // Get or create wallet
214
+ const { wallet, isNew } = getOrCreateWallet();
215
+ if (spinner) {
216
+ spinner.text = isNew ? 'Created new wallet...' : 'Using existing wallet...';
217
+ }
218
+ // Upload image if provided
219
+ let imageUrl = '';
220
+ if (options.image) {
221
+ if (spinner)
222
+ spinner.text = 'Uploading image...';
223
+ if (options.image.startsWith('http')) {
224
+ imageUrl = options.image;
225
+ }
226
+ else if (fs.existsSync(options.image)) {
227
+ imageUrl = await uploadImage(options.image);
228
+ }
229
+ else {
230
+ throw new Error(`Image not found: ${options.image}`);
231
+ }
232
+ }
233
+ // Launch token
234
+ if (spinner)
235
+ spinner.text = 'Launching token on Base...';
236
+ const result = await launchViaClanker({
237
+ name: options.name,
238
+ symbol: options.symbol,
239
+ description: options.description || `${options.name} - Launched via Clawdpad`,
240
+ image: imageUrl,
241
+ website: options.website,
242
+ wallet,
243
+ testnet: options.testnet,
244
+ });
245
+ // Save launch record
246
+ saveLaunch({
247
+ tokenAddress: result.tokenAddress,
248
+ name: options.name,
249
+ symbol: options.symbol,
250
+ network: options.testnet ? 'base-sepolia' : 'base',
251
+ transactionHash: result.transactionHash,
252
+ launchedAt: new Date().toISOString(),
253
+ website: options.website,
254
+ });
255
+ if (spinner)
256
+ spinner.succeed('Token launched!');
257
+ // Output
258
+ const output = {
259
+ success: true,
260
+ tokenAddress: result.tokenAddress,
261
+ transactionHash: result.transactionHash,
262
+ name: options.name,
263
+ symbol: options.symbol,
264
+ network: options.testnet ? 'Base Sepolia' : 'Base',
265
+ explorer: result.explorerUrl,
266
+ clanker: result.clankerUrl,
267
+ wallet: wallet.address,
268
+ ...(isNew ? { privateKey: wallet.privateKey } : {}),
269
+ };
270
+ if (jsonMode) {
271
+ console.log(JSON.stringify(output, null, 2));
272
+ }
273
+ else {
274
+ console.log('\n' + chalk_1.default.green('✓ Token launched successfully!'));
275
+ console.log(chalk_1.default.gray('─'.repeat(60)));
276
+ console.log(` ${chalk_1.default.gray('Name:')} ${chalk_1.default.white(options.name)}`);
277
+ console.log(` ${chalk_1.default.gray('Symbol:')} ${chalk_1.default.cyan(options.symbol)}`);
278
+ console.log(` ${chalk_1.default.gray('Network:')} ${chalk_1.default.blue(options.testnet ? 'Base Sepolia' : 'Base')}`);
279
+ console.log(` ${chalk_1.default.gray('Address:')} ${chalk_1.default.yellow(result.tokenAddress)}`);
280
+ console.log(` ${chalk_1.default.gray('Wallet:')} ${chalk_1.default.gray(wallet.address)}`);
281
+ console.log(chalk_1.default.gray('─'.repeat(60)));
282
+ console.log(`\n ${chalk_1.default.white('View on Basescan:')}`);
283
+ console.log(` ${chalk_1.default.cyan(result.explorerUrl)}`);
284
+ console.log(`\n ${chalk_1.default.white('View on Clanker:')}`);
285
+ console.log(` ${chalk_1.default.cyan(result.clankerUrl)}`);
286
+ if (isNew) {
287
+ console.log('\n' + chalk_1.default.yellow('⚠️ NEW WALLET CREATED'));
288
+ console.log(chalk_1.default.gray(' Save this private key — it won\'t be shown again:'));
289
+ console.log(` ${chalk_1.default.red(wallet.privateKey)}`);
290
+ }
291
+ console.log('\n' + chalk_1.default.gray('Earn 80% of all trading fees! Check with: clawdpad fees'));
292
+ }
293
+ }
294
+ catch (error) {
295
+ if (spinner)
296
+ spinner.fail('Launch failed');
297
+ const errorOutput = { success: false, error: error.message };
298
+ if (jsonMode) {
299
+ console.log(JSON.stringify(errorOutput));
300
+ }
301
+ else {
302
+ console.error(chalk_1.default.red(`\nError: ${error.message}`));
303
+ }
304
+ process.exit(1);
305
+ }
306
+ });
307
+ // Wallet command
308
+ program
309
+ .command('wallet')
310
+ .description('Show wallet address and balance')
311
+ .option('--json', 'Output JSON')
312
+ .action(async (options) => {
313
+ const { wallet, isNew } = getOrCreateWallet();
314
+ // Get balance
315
+ const client = (0, viem_1.createPublicClient)({
316
+ chain: chains_1.base,
317
+ transport: (0, viem_1.http)(),
318
+ });
319
+ const balance = await client.getBalance({ address: wallet.address });
320
+ const output = {
321
+ address: wallet.address,
322
+ balance: (0, viem_1.formatEther)(balance),
323
+ balanceWei: balance.toString(),
324
+ isNew,
325
+ };
326
+ if (options.json) {
327
+ console.log(JSON.stringify(output, null, 2));
328
+ }
329
+ else {
330
+ console.log(banner);
331
+ console.log(chalk_1.default.white('Wallet:'));
332
+ console.log(chalk_1.default.gray('─'.repeat(50)));
333
+ console.log(` Address: ${chalk_1.default.cyan(wallet.address)}`);
334
+ console.log(` Balance: ${chalk_1.default.green((0, viem_1.formatEther)(balance))} ETH`);
335
+ if (isNew) {
336
+ console.log(chalk_1.default.yellow('\n ⚠️ New wallet created'));
337
+ console.log(chalk_1.default.gray(` Private key saved to: ${WALLET_FILE}`));
338
+ }
339
+ console.log(chalk_1.default.gray('─'.repeat(50)));
340
+ }
341
+ });
342
+ // Status command
343
+ program
344
+ .command('status')
345
+ .description('List all launched tokens')
346
+ .option('--json', 'Output JSON')
347
+ .action(async (options) => {
348
+ const launches = loadLaunches();
349
+ if (options.json) {
350
+ console.log(JSON.stringify({ launches }, null, 2));
351
+ }
352
+ else {
353
+ console.log(banner);
354
+ console.log(chalk_1.default.white(`Launched Tokens (${launches.length}):`));
355
+ console.log(chalk_1.default.gray('─'.repeat(70)));
356
+ if (launches.length === 0) {
357
+ console.log(chalk_1.default.gray(' No tokens launched yet. Run: clawdpad --name "MyToken" --symbol "TKN"'));
358
+ }
359
+ else {
360
+ for (const launch of launches) {
361
+ console.log(` ${chalk_1.default.cyan(launch.symbol.padEnd(10))} ` +
362
+ `${chalk_1.default.white(launch.name.slice(0, 20).padEnd(22))} ` +
363
+ `${chalk_1.default.gray(launch.tokenAddress.slice(0, 16) + '...')}`);
364
+ }
365
+ }
366
+ console.log(chalk_1.default.gray('─'.repeat(70)));
367
+ }
368
+ });
369
+ // Fees command
370
+ program
371
+ .command('fees')
372
+ .description('Check claimable fees')
373
+ .option('--json', 'Output JSON')
374
+ .action(async (options) => {
375
+ const wallet = loadWallet();
376
+ if (!wallet) {
377
+ const error = { success: false, error: 'No wallet found. Launch a token first.' };
378
+ if (options.json) {
379
+ console.log(JSON.stringify(error));
380
+ }
381
+ else {
382
+ console.error(chalk_1.default.red('No wallet found. Launch a token first.'));
383
+ }
384
+ process.exit(2);
385
+ }
386
+ // Check fees via Clanker FeeLocker
387
+ // For now, return placeholder
388
+ const output = {
389
+ wallet: wallet.address,
390
+ claimableWETH: '0',
391
+ claimableTokens: '0',
392
+ canClaim: false,
393
+ hasGas: false,
394
+ hint: 'Fees accumulate when people trade your tokens',
395
+ };
396
+ if (options.json) {
397
+ console.log(JSON.stringify(output, null, 2));
398
+ }
399
+ else {
400
+ console.log(banner);
401
+ console.log(chalk_1.default.white('Claimable Fees:'));
402
+ console.log(chalk_1.default.gray('─'.repeat(50)));
403
+ console.log(` Wallet: ${chalk_1.default.gray(wallet.address)}`);
404
+ console.log(` WETH Fees: ${chalk_1.default.green(output.claimableWETH)} WETH`);
405
+ console.log(` Can Claim: ${output.canClaim ? chalk_1.default.green('Yes') : chalk_1.default.yellow('No')}`);
406
+ console.log(chalk_1.default.gray('─'.repeat(50)));
407
+ console.log(chalk_1.default.gray('\n Fees accumulate when people trade your tokens.'));
408
+ }
409
+ });
410
+ // Claim command
411
+ program
412
+ .command('claim')
413
+ .description('Withdraw accumulated fees')
414
+ .option('--json', 'Output JSON')
415
+ .action(async (options) => {
416
+ const wallet = loadWallet();
417
+ if (!wallet) {
418
+ const error = { success: false, error: 'No wallet found' };
419
+ if (options.json) {
420
+ console.log(JSON.stringify(error));
421
+ }
422
+ else {
423
+ console.error(chalk_1.default.red('No wallet found. Launch a token first.'));
424
+ }
425
+ process.exit(2);
426
+ }
427
+ // TODO: Implement actual fee claiming via Clanker FeeLocker contract
428
+ const output = {
429
+ success: false,
430
+ error: 'No fees to claim yet. Keep promoting your tokens!',
431
+ };
432
+ if (options.json) {
433
+ console.log(JSON.stringify(output));
434
+ }
435
+ else {
436
+ console.log(banner);
437
+ console.log(chalk_1.default.yellow('No fees to claim yet.'));
438
+ console.log(chalk_1.default.gray('Keep promoting your tokens! Fees accumulate from trades.'));
439
+ }
440
+ });
441
+ // Info command
442
+ program
443
+ .command('info')
444
+ .description('About Clawdpad')
445
+ .action(() => {
446
+ console.log(banner);
447
+ console.log(chalk_1.default.white('\nWhat is Clawdpad?'));
448
+ console.log(chalk_1.default.gray('─'.repeat(50)));
449
+ console.log(' Clawdpad lets AI agents launch tokens on Base');
450
+ console.log(' with one command. No wallet setup, no gas fees.');
451
+ console.log('');
452
+ console.log(chalk_1.default.white('How it works:'));
453
+ console.log(' 1. Run: clawdpad --name "X" --symbol "X"');
454
+ console.log(' 2. Token deploys on Base via Clanker');
455
+ console.log(' 3. Earn 80% of all trading fees');
456
+ console.log(' 4. Claim fees anytime: clawdpad claim');
457
+ console.log('');
458
+ console.log(chalk_1.default.white('Links:'));
459
+ console.log(` Website: ${chalk_1.default.cyan('https://www.clawdpad.ai')}`);
460
+ console.log(` Token: ${chalk_1.default.cyan('$CLAWDPAD on Base')}`);
461
+ console.log(` Twitter: ${chalk_1.default.cyan('@kk5Ai_world')}`);
462
+ console.log(chalk_1.default.gray('─'.repeat(50)));
463
+ });
464
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "clawdpad",
3
+ "version": "1.0.0",
4
+ "description": "CLI for AI agents to launch memecoins on Clawdpad",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "clawdpad": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/index.ts",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "memecoin",
16
+ "token",
17
+ "ai",
18
+ "agent",
19
+ "base",
20
+ "clawdpad",
21
+ "x402"
22
+ ],
23
+ "author": "Clawdpad",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "commander": "^11.1.0",
27
+ "chalk": "^4.1.2",
28
+ "ora": "^5.4.1",
29
+ "inquirer": "^8.2.6",
30
+ "node-fetch": "^2.7.0",
31
+ "viem": "^2.0.0",
32
+ "dotenv": "^16.3.1"
33
+ },
34
+ "devDependencies": {
35
+ "@types/inquirer": "^9.0.7",
36
+ "@types/node": "^20.10.0",
37
+ "@types/node-fetch": "^2.6.9",
38
+ "typescript": "^5.3.0",
39
+ "ts-node": "^10.9.2"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/clawdpad/clawdpad-cli"
47
+ }
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,509 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import fetch from 'node-fetch';
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+ import { createPublicClient, http, formatEther, createWalletClient } from 'viem';
11
+ import { base, baseSepolia } from 'viem/chains';
12
+ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
13
+
14
+ const API_URL = process.env.CLAWDPAD_API_URL || 'https://www.clawdpad.ai/api';
15
+ const CONFIG_DIR = path.join(os.homedir(), '.clawdpad');
16
+ const WALLET_FILE = path.join(CONFIG_DIR, 'wallet.json');
17
+ const LAUNCHES_FILE = path.join(CONFIG_DIR, 'launches.json');
18
+
19
+ // Clanker API (for token deployment)
20
+ const CLANKER_API = 'https://www.clanker.world/api/v1';
21
+
22
+ const program = new Command();
23
+
24
+ // ASCII Art Banner
25
+ const banner = `
26
+ ${chalk.cyan(' ██████╗██╗ █████╗ ██╗ ██╗██████╗ ██████╗ █████╗ ██████╗ ')}
27
+ ${chalk.cyan(' ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗')}
28
+ ${chalk.cyan(' ██║ ██║ ███████║██║ █╗ ██║██║ ██║██████╔╝███████║██║ ██║')}
29
+ ${chalk.cyan(' ██║ ██║ ██╔══██║██║███╗██║██║ ██║██╔═══╝ ██╔══██║██║ ██║')}
30
+ ${chalk.cyan(' ╚██████╗███████╗██║ ██║╚███╔███╔╝██████╔╝██║ ██║ ██║██████╔╝')}
31
+ ${chalk.cyan(' ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ')}
32
+ ${chalk.gray(' one command. your token. live on base.')}
33
+ `;
34
+
35
+ // ============ Wallet Management ============
36
+
37
+ interface WalletData {
38
+ address: string;
39
+ privateKey: string;
40
+ createdAt: string;
41
+ }
42
+
43
+ interface LaunchRecord {
44
+ tokenAddress: string;
45
+ name: string;
46
+ symbol: string;
47
+ network: string;
48
+ transactionHash: string;
49
+ launchedAt: string;
50
+ website?: string;
51
+ }
52
+
53
+ function ensureConfigDir(): void {
54
+ if (!fs.existsSync(CONFIG_DIR)) {
55
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
56
+ }
57
+ }
58
+
59
+ function loadWallet(): WalletData | null {
60
+ if (!fs.existsSync(WALLET_FILE)) {
61
+ return null;
62
+ }
63
+ try {
64
+ const data = fs.readFileSync(WALLET_FILE, 'utf-8');
65
+ return JSON.parse(data);
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ function createWallet(): WalletData {
72
+ ensureConfigDir();
73
+
74
+ const privateKey = generatePrivateKey();
75
+ const account = privateKeyToAccount(privateKey);
76
+
77
+ const wallet: WalletData = {
78
+ address: account.address,
79
+ privateKey: privateKey,
80
+ createdAt: new Date().toISOString(),
81
+ };
82
+
83
+ fs.writeFileSync(WALLET_FILE, JSON.stringify(wallet, null, 2), { mode: 0o600 });
84
+
85
+ return wallet;
86
+ }
87
+
88
+ function getOrCreateWallet(): { wallet: WalletData; isNew: boolean } {
89
+ const existing = loadWallet();
90
+ if (existing) {
91
+ return { wallet: existing, isNew: false };
92
+ }
93
+ return { wallet: createWallet(), isNew: true };
94
+ }
95
+
96
+ function loadLaunches(): LaunchRecord[] {
97
+ if (!fs.existsSync(LAUNCHES_FILE)) {
98
+ return [];
99
+ }
100
+ try {
101
+ const data = fs.readFileSync(LAUNCHES_FILE, 'utf-8');
102
+ return JSON.parse(data);
103
+ } catch {
104
+ return [];
105
+ }
106
+ }
107
+
108
+ function saveLaunch(launch: LaunchRecord): void {
109
+ ensureConfigDir();
110
+ const launches = loadLaunches();
111
+ launches.push(launch);
112
+ fs.writeFileSync(LAUNCHES_FILE, JSON.stringify(launches, null, 2));
113
+ }
114
+
115
+ // ============ Image Upload ============
116
+
117
+ async function uploadImage(imagePath: string): Promise<string> {
118
+ // Read image file
119
+ const imageBuffer = fs.readFileSync(imagePath);
120
+ const base64 = imageBuffer.toString('base64');
121
+ const ext = path.extname(imagePath).toLowerCase();
122
+ const mimeType = ext === '.png' ? 'image/png' :
123
+ ext === '.gif' ? 'image/gif' :
124
+ ext === '.webp' ? 'image/webp' : 'image/jpeg';
125
+
126
+ // Upload to Clawdpad (which uses imgbb or similar)
127
+ const response = await fetch(`${API_URL}/upload`, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/json' },
130
+ body: JSON.stringify({
131
+ image: `data:${mimeType};base64,${base64}`,
132
+ name: path.basename(imagePath, ext),
133
+ }),
134
+ });
135
+
136
+ const result = await response.json() as any;
137
+
138
+ if (!result.success || !result.url) {
139
+ throw new Error(result.error || 'Image upload failed');
140
+ }
141
+
142
+ return result.url;
143
+ }
144
+
145
+ // ============ Token Launch via Clanker ============
146
+
147
+ async function launchViaClanker(params: {
148
+ name: string;
149
+ symbol: string;
150
+ description: string;
151
+ image: string;
152
+ website?: string;
153
+ wallet: WalletData;
154
+ testnet?: boolean;
155
+ }): Promise<{
156
+ tokenAddress: string;
157
+ transactionHash: string;
158
+ clankerUrl: string;
159
+ explorerUrl: string;
160
+ }> {
161
+ // For now, use Clawdpad API which proxies to Clanker
162
+ // This allows us to sponsor gas and handle the complexity
163
+
164
+ const response = await fetch(`${API_URL}/launch`, {
165
+ method: 'POST',
166
+ headers: { 'Content-Type': 'application/json' },
167
+ body: JSON.stringify({
168
+ name: params.name,
169
+ symbol: params.symbol,
170
+ description: params.description,
171
+ image: params.image,
172
+ website: params.website,
173
+ creatorWallet: params.wallet.address,
174
+ testnet: params.testnet,
175
+ }),
176
+ });
177
+
178
+ const result = await response.json() as any;
179
+
180
+ if (!response.ok) {
181
+ throw new Error(result.error || 'Launch failed');
182
+ }
183
+
184
+ const chainName = params.testnet ? 'base-sepolia' : 'base';
185
+ const explorerBase = params.testnet ? 'https://sepolia.basescan.org' : 'https://basescan.org';
186
+
187
+ return {
188
+ tokenAddress: result.token?.address || result.tokenAddress || '0x...',
189
+ transactionHash: result.transactionHash || result.tx_hash || '0x...',
190
+ clankerUrl: `https://clanker.world/clanker/${result.token?.address || result.tokenAddress}`,
191
+ explorerUrl: `${explorerBase}/token/${result.token?.address || result.tokenAddress}`,
192
+ };
193
+ }
194
+
195
+ // ============ CLI Commands ============
196
+
197
+ program
198
+ .name('clawdpad')
199
+ .description('Launch memecoins on Base — one command, no gas, earn fees')
200
+ .version('1.0.0');
201
+
202
+ // Main launch command
203
+ program
204
+ .option('--name <name>', 'Token name')
205
+ .option('--symbol <symbol>', 'Token symbol')
206
+ .option('--description <text>', 'Token description')
207
+ .option('--image <path>', 'Path to image (PNG/JPG/GIF/WebP, max 5MB)')
208
+ .option('--website <url>', 'Website URL (e.g., Moltbook post)')
209
+ .option('--testnet', 'Use Base Sepolia testnet')
210
+ .option('--json', 'Output JSON (machine-readable)')
211
+ .action(async (options) => {
212
+ const jsonMode = options.json;
213
+
214
+ if (!jsonMode) {
215
+ console.log(banner);
216
+ }
217
+
218
+ // Check if this is a subcommand or launch
219
+ if (!options.name && !options.symbol) {
220
+ if (!jsonMode) {
221
+ program.help();
222
+ }
223
+ return;
224
+ }
225
+
226
+ // Validate required fields
227
+ if (!options.name || !options.symbol) {
228
+ const error = { success: false, error: 'Missing required fields: --name and --symbol' };
229
+ if (jsonMode) {
230
+ console.log(JSON.stringify(error));
231
+ process.exit(1);
232
+ }
233
+ console.error(chalk.red('Error: --name and --symbol are required'));
234
+ process.exit(1);
235
+ }
236
+
237
+ const spinner = jsonMode ? null : ora('Preparing launch...').start();
238
+
239
+ try {
240
+ // Get or create wallet
241
+ const { wallet, isNew } = getOrCreateWallet();
242
+
243
+ if (spinner) {
244
+ spinner.text = isNew ? 'Created new wallet...' : 'Using existing wallet...';
245
+ }
246
+
247
+ // Upload image if provided
248
+ let imageUrl = '';
249
+ if (options.image) {
250
+ if (spinner) spinner.text = 'Uploading image...';
251
+
252
+ if (options.image.startsWith('http')) {
253
+ imageUrl = options.image;
254
+ } else if (fs.existsSync(options.image)) {
255
+ imageUrl = await uploadImage(options.image);
256
+ } else {
257
+ throw new Error(`Image not found: ${options.image}`);
258
+ }
259
+ }
260
+
261
+ // Launch token
262
+ if (spinner) spinner.text = 'Launching token on Base...';
263
+
264
+ const result = await launchViaClanker({
265
+ name: options.name,
266
+ symbol: options.symbol,
267
+ description: options.description || `${options.name} - Launched via Clawdpad`,
268
+ image: imageUrl,
269
+ website: options.website,
270
+ wallet,
271
+ testnet: options.testnet,
272
+ });
273
+
274
+ // Save launch record
275
+ saveLaunch({
276
+ tokenAddress: result.tokenAddress,
277
+ name: options.name,
278
+ symbol: options.symbol,
279
+ network: options.testnet ? 'base-sepolia' : 'base',
280
+ transactionHash: result.transactionHash,
281
+ launchedAt: new Date().toISOString(),
282
+ website: options.website,
283
+ });
284
+
285
+ if (spinner) spinner.succeed('Token launched!');
286
+
287
+ // Output
288
+ const output = {
289
+ success: true,
290
+ tokenAddress: result.tokenAddress,
291
+ transactionHash: result.transactionHash,
292
+ name: options.name,
293
+ symbol: options.symbol,
294
+ network: options.testnet ? 'Base Sepolia' : 'Base',
295
+ explorer: result.explorerUrl,
296
+ clanker: result.clankerUrl,
297
+ wallet: wallet.address,
298
+ ...(isNew ? { privateKey: wallet.privateKey } : {}),
299
+ };
300
+
301
+ if (jsonMode) {
302
+ console.log(JSON.stringify(output, null, 2));
303
+ } else {
304
+ console.log('\n' + chalk.green('✓ Token launched successfully!'));
305
+ console.log(chalk.gray('─'.repeat(60)));
306
+ console.log(` ${chalk.gray('Name:')} ${chalk.white(options.name)}`);
307
+ console.log(` ${chalk.gray('Symbol:')} ${chalk.cyan(options.symbol)}`);
308
+ console.log(` ${chalk.gray('Network:')} ${chalk.blue(options.testnet ? 'Base Sepolia' : 'Base')}`);
309
+ console.log(` ${chalk.gray('Address:')} ${chalk.yellow(result.tokenAddress)}`);
310
+ console.log(` ${chalk.gray('Wallet:')} ${chalk.gray(wallet.address)}`);
311
+ console.log(chalk.gray('─'.repeat(60)));
312
+ console.log(`\n ${chalk.white('View on Basescan:')}`);
313
+ console.log(` ${chalk.cyan(result.explorerUrl)}`);
314
+ console.log(`\n ${chalk.white('View on Clanker:')}`);
315
+ console.log(` ${chalk.cyan(result.clankerUrl)}`);
316
+
317
+ if (isNew) {
318
+ console.log('\n' + chalk.yellow('⚠️ NEW WALLET CREATED'));
319
+ console.log(chalk.gray(' Save this private key — it won\'t be shown again:'));
320
+ console.log(` ${chalk.red(wallet.privateKey)}`);
321
+ }
322
+
323
+ console.log('\n' + chalk.gray('Earn 80% of all trading fees! Check with: clawdpad fees'));
324
+ }
325
+
326
+ } catch (error: any) {
327
+ if (spinner) spinner.fail('Launch failed');
328
+
329
+ const errorOutput = { success: false, error: error.message };
330
+
331
+ if (jsonMode) {
332
+ console.log(JSON.stringify(errorOutput));
333
+ } else {
334
+ console.error(chalk.red(`\nError: ${error.message}`));
335
+ }
336
+ process.exit(1);
337
+ }
338
+ });
339
+
340
+ // Wallet command
341
+ program
342
+ .command('wallet')
343
+ .description('Show wallet address and balance')
344
+ .option('--json', 'Output JSON')
345
+ .action(async (options) => {
346
+ const { wallet, isNew } = getOrCreateWallet();
347
+
348
+ // Get balance
349
+ const client = createPublicClient({
350
+ chain: base,
351
+ transport: http(),
352
+ });
353
+
354
+ const balance = await client.getBalance({ address: wallet.address as `0x${string}` });
355
+
356
+ const output = {
357
+ address: wallet.address,
358
+ balance: formatEther(balance),
359
+ balanceWei: balance.toString(),
360
+ isNew,
361
+ };
362
+
363
+ if (options.json) {
364
+ console.log(JSON.stringify(output, null, 2));
365
+ } else {
366
+ console.log(banner);
367
+ console.log(chalk.white('Wallet:'));
368
+ console.log(chalk.gray('─'.repeat(50)));
369
+ console.log(` Address: ${chalk.cyan(wallet.address)}`);
370
+ console.log(` Balance: ${chalk.green(formatEther(balance))} ETH`);
371
+ if (isNew) {
372
+ console.log(chalk.yellow('\n ⚠️ New wallet created'));
373
+ console.log(chalk.gray(` Private key saved to: ${WALLET_FILE}`));
374
+ }
375
+ console.log(chalk.gray('─'.repeat(50)));
376
+ }
377
+ });
378
+
379
+ // Status command
380
+ program
381
+ .command('status')
382
+ .description('List all launched tokens')
383
+ .option('--json', 'Output JSON')
384
+ .action(async (options) => {
385
+ const launches = loadLaunches();
386
+
387
+ if (options.json) {
388
+ console.log(JSON.stringify({ launches }, null, 2));
389
+ } else {
390
+ console.log(banner);
391
+ console.log(chalk.white(`Launched Tokens (${launches.length}):`));
392
+ console.log(chalk.gray('─'.repeat(70)));
393
+
394
+ if (launches.length === 0) {
395
+ console.log(chalk.gray(' No tokens launched yet. Run: clawdpad --name "MyToken" --symbol "TKN"'));
396
+ } else {
397
+ for (const launch of launches) {
398
+ console.log(
399
+ ` ${chalk.cyan(launch.symbol.padEnd(10))} ` +
400
+ `${chalk.white(launch.name.slice(0, 20).padEnd(22))} ` +
401
+ `${chalk.gray(launch.tokenAddress.slice(0, 16) + '...')}`
402
+ );
403
+ }
404
+ }
405
+ console.log(chalk.gray('─'.repeat(70)));
406
+ }
407
+ });
408
+
409
+ // Fees command
410
+ program
411
+ .command('fees')
412
+ .description('Check claimable fees')
413
+ .option('--json', 'Output JSON')
414
+ .action(async (options) => {
415
+ const wallet = loadWallet();
416
+
417
+ if (!wallet) {
418
+ const error = { success: false, error: 'No wallet found. Launch a token first.' };
419
+ if (options.json) {
420
+ console.log(JSON.stringify(error));
421
+ } else {
422
+ console.error(chalk.red('No wallet found. Launch a token first.'));
423
+ }
424
+ process.exit(2);
425
+ }
426
+
427
+ // Check fees via Clanker FeeLocker
428
+ // For now, return placeholder
429
+ const output = {
430
+ wallet: wallet.address,
431
+ claimableWETH: '0',
432
+ claimableTokens: '0',
433
+ canClaim: false,
434
+ hasGas: false,
435
+ hint: 'Fees accumulate when people trade your tokens',
436
+ };
437
+
438
+ if (options.json) {
439
+ console.log(JSON.stringify(output, null, 2));
440
+ } else {
441
+ console.log(banner);
442
+ console.log(chalk.white('Claimable Fees:'));
443
+ console.log(chalk.gray('─'.repeat(50)));
444
+ console.log(` Wallet: ${chalk.gray(wallet.address)}`);
445
+ console.log(` WETH Fees: ${chalk.green(output.claimableWETH)} WETH`);
446
+ console.log(` Can Claim: ${output.canClaim ? chalk.green('Yes') : chalk.yellow('No')}`);
447
+ console.log(chalk.gray('─'.repeat(50)));
448
+ console.log(chalk.gray('\n Fees accumulate when people trade your tokens.'));
449
+ }
450
+ });
451
+
452
+ // Claim command
453
+ program
454
+ .command('claim')
455
+ .description('Withdraw accumulated fees')
456
+ .option('--json', 'Output JSON')
457
+ .action(async (options) => {
458
+ const wallet = loadWallet();
459
+
460
+ if (!wallet) {
461
+ const error = { success: false, error: 'No wallet found' };
462
+ if (options.json) {
463
+ console.log(JSON.stringify(error));
464
+ } else {
465
+ console.error(chalk.red('No wallet found. Launch a token first.'));
466
+ }
467
+ process.exit(2);
468
+ }
469
+
470
+ // TODO: Implement actual fee claiming via Clanker FeeLocker contract
471
+ const output = {
472
+ success: false,
473
+ error: 'No fees to claim yet. Keep promoting your tokens!',
474
+ };
475
+
476
+ if (options.json) {
477
+ console.log(JSON.stringify(output));
478
+ } else {
479
+ console.log(banner);
480
+ console.log(chalk.yellow('No fees to claim yet.'));
481
+ console.log(chalk.gray('Keep promoting your tokens! Fees accumulate from trades.'));
482
+ }
483
+ });
484
+
485
+ // Info command
486
+ program
487
+ .command('info')
488
+ .description('About Clawdpad')
489
+ .action(() => {
490
+ console.log(banner);
491
+ console.log(chalk.white('\nWhat is Clawdpad?'));
492
+ console.log(chalk.gray('─'.repeat(50)));
493
+ console.log(' Clawdpad lets AI agents launch tokens on Base');
494
+ console.log(' with one command. No wallet setup, no gas fees.');
495
+ console.log('');
496
+ console.log(chalk.white('How it works:'));
497
+ console.log(' 1. Run: clawdpad --name "X" --symbol "X"');
498
+ console.log(' 2. Token deploys on Base via Clanker');
499
+ console.log(' 3. Earn 80% of all trading fees');
500
+ console.log(' 4. Claim fees anytime: clawdpad claim');
501
+ console.log('');
502
+ console.log(chalk.white('Links:'));
503
+ console.log(` Website: ${chalk.cyan('https://www.clawdpad.ai')}`);
504
+ console.log(` Token: ${chalk.cyan('$CLAWDPAD on Base')}`);
505
+ console.log(` Twitter: ${chalk.cyan('@kk5Ai_world')}`);
506
+ console.log(chalk.gray('─'.repeat(50)));
507
+ });
508
+
509
+ program.parse();
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "lib": ["ES2022", "DOM"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }