@clawdvault/cli 0.1.5 → 0.3.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 +0 -48
- package/dist/index.js +338 -409
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -160,54 +160,6 @@ Examples:
|
|
|
160
160
|
clawdvault trade quote -m TOKEN_MINT -t buy -a 0.1
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
### `clawdvault stream`
|
|
164
|
-
|
|
165
|
-
Real-time data streaming (live trades, prices, chat).
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
# Stream live trades
|
|
169
|
-
clawdvault stream trades [options]
|
|
170
|
-
-m, --mint <address> Token mint address (required)
|
|
171
|
-
--json Output as JSON (one object per line)
|
|
172
|
-
--append Append mode (simple log format)
|
|
173
|
-
|
|
174
|
-
# Stream token price updates
|
|
175
|
-
clawdvault stream token [options]
|
|
176
|
-
-m, --mint <address> Token mint address (required)
|
|
177
|
-
--json Output as JSON
|
|
178
|
-
|
|
179
|
-
# Stream chat messages
|
|
180
|
-
clawdvault stream chat [options]
|
|
181
|
-
-m, --mint <address> Token mint address (required)
|
|
182
|
-
--json Output as JSON
|
|
183
|
-
|
|
184
|
-
Examples:
|
|
185
|
-
# Watch trades in real-time (table mode)
|
|
186
|
-
clawdvault stream trades -m TOKEN_MINT
|
|
187
|
-
|
|
188
|
-
# Watch trades for scripting (JSON output)
|
|
189
|
-
clawdvault stream trades -m TOKEN_MINT --json
|
|
190
|
-
|
|
191
|
-
# Log trades to file
|
|
192
|
-
clawdvault stream trades -m TOKEN_MINT --append >> trades.log
|
|
193
|
-
|
|
194
|
-
# Monitor price changes
|
|
195
|
-
clawdvault stream token -m TOKEN_MINT
|
|
196
|
-
|
|
197
|
-
# Watch chat
|
|
198
|
-
clawdvault stream chat -m TOKEN_MINT
|
|
199
|
-
|
|
200
|
-
# Pipe to jq for filtering
|
|
201
|
-
clawdvault stream trades -m TOKEN_MINT --json | jq 'select(.type == "buy")'
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
**Features:**
|
|
205
|
-
- Auto-reconnect on connection loss
|
|
206
|
-
- Graceful shutdown with Ctrl+C
|
|
207
|
-
- Multiple output modes: table (default), append, JSON
|
|
208
|
-
- Table mode clears and updates in-place
|
|
209
|
-
- JSON mode outputs one object per line (great for piping)
|
|
210
|
-
|
|
211
163
|
### `clawdvault wallet`
|
|
212
164
|
|
|
213
165
|
Wallet management and info.
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,44 @@ var os = __toESM(require("os"));
|
|
|
43
43
|
function getConfigDir() {
|
|
44
44
|
return path.join(os.homedir(), ".clawdvault");
|
|
45
45
|
}
|
|
46
|
+
function getAuthConfigPath() {
|
|
47
|
+
return path.join(getConfigDir(), "auth.json");
|
|
48
|
+
}
|
|
49
|
+
function loadAuthConfig() {
|
|
50
|
+
const configPath = getAuthConfigPath();
|
|
51
|
+
if (!fs.existsSync(configPath)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const data = fs.readFileSync(configPath, "utf-8");
|
|
56
|
+
const config = JSON.parse(data);
|
|
57
|
+
if (config.expiresAt) {
|
|
58
|
+
const expiresAt = new Date(config.expiresAt);
|
|
59
|
+
if (expiresAt <= /* @__PURE__ */ new Date()) {
|
|
60
|
+
fs.unlinkSync(configPath);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return config;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function saveAuthConfig(config) {
|
|
70
|
+
const configDir = getConfigDir();
|
|
71
|
+
const configPath = getAuthConfigPath();
|
|
72
|
+
if (!fs.existsSync(configDir)) {
|
|
73
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
76
|
+
fs.chmodSync(configPath, 384);
|
|
77
|
+
}
|
|
78
|
+
function clearAuthConfig() {
|
|
79
|
+
const configPath = getAuthConfigPath();
|
|
80
|
+
if (fs.existsSync(configPath)) {
|
|
81
|
+
fs.unlinkSync(configPath);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
46
84
|
function getWalletPath() {
|
|
47
85
|
if (process.env.CLAWDVAULT_WALLET) {
|
|
48
86
|
return process.env.CLAWDVAULT_WALLET;
|
|
@@ -81,13 +119,27 @@ function getBaseUrl() {
|
|
|
81
119
|
function createClientWithWallet(walletPath) {
|
|
82
120
|
const signer = loadSigner(walletPath);
|
|
83
121
|
const baseUrl = getBaseUrl();
|
|
84
|
-
const
|
|
122
|
+
const authConfig = loadAuthConfig();
|
|
123
|
+
const sessionToken = authConfig?.sessionToken;
|
|
124
|
+
const client = (0, import_sdk.createClient)({
|
|
125
|
+
signer: signer || void 0,
|
|
126
|
+
baseUrl,
|
|
127
|
+
sessionToken
|
|
128
|
+
});
|
|
85
129
|
return {
|
|
86
130
|
client,
|
|
87
131
|
signer,
|
|
88
132
|
walletAddress: signer?.publicKey.toBase58() ?? null
|
|
89
133
|
};
|
|
90
134
|
}
|
|
135
|
+
function requireAuth() {
|
|
136
|
+
const authConfig = loadAuthConfig();
|
|
137
|
+
if (!authConfig?.sessionToken) {
|
|
138
|
+
error("Not logged in. Run: clawdvault wallet login");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
return authConfig;
|
|
142
|
+
}
|
|
91
143
|
function createReadOnlyClient() {
|
|
92
144
|
const baseUrl = getBaseUrl();
|
|
93
145
|
return (0, import_sdk.createClient)({ baseUrl });
|
|
@@ -178,7 +230,7 @@ tokensCommand.command("list").description("List all tokens").option("-s, --sort
|
|
|
178
230
|
return;
|
|
179
231
|
}
|
|
180
232
|
console.log(import_chalk2.default.bold(`
|
|
181
|
-
\u{1F4CA} Tokens (Page ${result.page}, ${result.tokens
|
|
233
|
+
\u{1F4CA} Tokens (Page ${result.page}, ${result.tokens?.length ?? 0} of ${result.total ?? 0})
|
|
182
234
|
`));
|
|
183
235
|
const table = new import_cli_table3.default({
|
|
184
236
|
head: [
|
|
@@ -192,16 +244,16 @@ tokensCommand.command("list").description("List all tokens").option("-s, --sort
|
|
|
192
244
|
],
|
|
193
245
|
style: { head: [], border: [] }
|
|
194
246
|
});
|
|
195
|
-
for (const token of result.tokens) {
|
|
247
|
+
for (const token of result.tokens ?? []) {
|
|
196
248
|
const status = token.graduated ? import_chalk2.default.green("\u{1F393} Graduated") : import_chalk2.default.yellow("\u{1F4C8} Bonding");
|
|
197
249
|
table.push([
|
|
198
|
-
import_chalk2.default.bold(token.symbol),
|
|
199
|
-
token.name,
|
|
200
|
-
formatSol(token.price_sol),
|
|
201
|
-
formatSol(token.market_cap_sol),
|
|
250
|
+
import_chalk2.default.bold(token.symbol ?? "???"),
|
|
251
|
+
token.name ?? "Unknown",
|
|
252
|
+
formatSol(token.price_sol ?? 0),
|
|
253
|
+
formatSol(token.market_cap_sol ?? 0),
|
|
202
254
|
token.volume_24h ? formatSol(token.volume_24h) : "-",
|
|
203
255
|
status,
|
|
204
|
-
shortenAddress(token.mint)
|
|
256
|
+
shortenAddress(token.mint ?? "")
|
|
205
257
|
]);
|
|
206
258
|
}
|
|
207
259
|
console.log(table.toString());
|
|
@@ -223,7 +275,6 @@ var import_commander2 = require("commander");
|
|
|
223
275
|
var import_chalk3 = __toESM(require("chalk"));
|
|
224
276
|
var import_cli_table32 = __toESM(require("cli-table3"));
|
|
225
277
|
var fs2 = __toESM(require("fs"));
|
|
226
|
-
var import_sdk3 = require("@clawdvault/sdk");
|
|
227
278
|
var tokenCommand = new import_commander2.Command("token").description("Token operations");
|
|
228
279
|
tokenCommand.command("get <mint>").description("Get token details").option("--json", "Output as JSON").action(async (mint, options) => {
|
|
229
280
|
const spin = spinner("Fetching token...").start();
|
|
@@ -235,18 +286,23 @@ tokenCommand.command("get <mint>").description("Get token details").option("--js
|
|
|
235
286
|
console.log(JSON.stringify(result, null, 2));
|
|
236
287
|
return;
|
|
237
288
|
}
|
|
238
|
-
const
|
|
289
|
+
const token = result.token;
|
|
290
|
+
const trades = result.trades ?? [];
|
|
291
|
+
if (!token) {
|
|
292
|
+
console.log(import_chalk3.default.yellow("Token not found"));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
239
295
|
console.log(import_chalk3.default.bold(`
|
|
240
|
-
\u{1FA99} ${token.name} (${token.symbol})
|
|
296
|
+
\u{1FA99} ${token.name ?? "Unknown"} (${token.symbol ?? "???"})
|
|
241
297
|
`));
|
|
242
298
|
const table = new import_cli_table32.default({
|
|
243
299
|
style: { head: [], border: [] }
|
|
244
300
|
});
|
|
245
301
|
table.push(
|
|
246
|
-
{ [import_chalk3.default.cyan("Mint")]: token.mint },
|
|
247
|
-
{ [import_chalk3.default.cyan("Creator")]: shortenAddress(token.creator) },
|
|
248
|
-
{ [import_chalk3.default.cyan("Price")]: formatSol(token.price_sol) },
|
|
249
|
-
{ [import_chalk3.default.cyan("Market Cap")]: formatSol(token.market_cap_sol) },
|
|
302
|
+
{ [import_chalk3.default.cyan("Mint")]: token.mint ?? "" },
|
|
303
|
+
{ [import_chalk3.default.cyan("Creator")]: shortenAddress(token.creator ?? "") },
|
|
304
|
+
{ [import_chalk3.default.cyan("Price")]: formatSol(token.price_sol ?? 0) },
|
|
305
|
+
{ [import_chalk3.default.cyan("Market Cap")]: formatSol(token.market_cap_sol ?? 0) },
|
|
250
306
|
{ [import_chalk3.default.cyan("Status")]: token.graduated ? "\u{1F393} Graduated" : "\u{1F4C8} Bonding Curve" }
|
|
251
307
|
);
|
|
252
308
|
if (token.description) {
|
|
@@ -280,10 +336,10 @@ tokenCommand.command("get <mint>").description("Get token details").option("--js
|
|
|
280
336
|
const typeStr = trade.type === "buy" ? import_chalk3.default.green("BUY") : import_chalk3.default.red("SELL");
|
|
281
337
|
tradesTable.push([
|
|
282
338
|
typeStr,
|
|
283
|
-
formatSol(trade.sol_amount),
|
|
284
|
-
formatTokens(trade.token_amount),
|
|
285
|
-
shortenAddress(trade.trader),
|
|
286
|
-
new Date(trade.created_at).toLocaleString()
|
|
339
|
+
formatSol(trade.sol_amount ?? 0),
|
|
340
|
+
formatTokens(trade.token_amount ?? 0),
|
|
341
|
+
shortenAddress(trade.trader ?? ""),
|
|
342
|
+
new Date(trade.created_at ?? Date.now()).toLocaleString()
|
|
287
343
|
]);
|
|
288
344
|
}
|
|
289
345
|
console.log(tradesTable.toString());
|
|
@@ -327,13 +383,13 @@ tokenCommand.command("create").description("Create a new token").requiredOption(
|
|
|
327
383
|
createSpin.stop();
|
|
328
384
|
success(`Token created successfully!`);
|
|
329
385
|
console.log();
|
|
330
|
-
info(`Name: ${result.token
|
|
331
|
-
info(`Symbol: ${result.token
|
|
332
|
-
info(`Mint: ${result.mint}`);
|
|
333
|
-
info(`Signature: ${result.signature}`);
|
|
386
|
+
info(`Name: ${result.token?.name ?? "Unknown"}`);
|
|
387
|
+
info(`Symbol: ${result.token?.symbol ?? "???"}`);
|
|
388
|
+
info(`Mint: ${result.mint ?? ""}`);
|
|
389
|
+
info(`Signature: ${result.signature ?? ""}`);
|
|
334
390
|
console.log();
|
|
335
|
-
info(`Explorer: ${link(result.explorer)}`);
|
|
336
|
-
info(`View: ${link(`https://clawdvault.com/${result.mint}`)}`);
|
|
391
|
+
info(`Explorer: ${link(result.explorer ?? "")}`);
|
|
392
|
+
info(`View: ${link(`https://clawdvault.com/${result.mint ?? ""}`)}`);
|
|
337
393
|
console.log();
|
|
338
394
|
} catch (err) {
|
|
339
395
|
createSpin.stop();
|
|
@@ -353,17 +409,21 @@ tokenCommand.command("stats <mint>").description("Get on-chain stats").option("-
|
|
|
353
409
|
console.log(import_chalk3.default.bold(`
|
|
354
410
|
\u{1F4C8} On-Chain Stats
|
|
355
411
|
`));
|
|
356
|
-
const
|
|
412
|
+
const onChain = result.onChain;
|
|
413
|
+
if (!onChain) {
|
|
414
|
+
console.log(import_chalk3.default.yellow("No on-chain data available"));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
357
417
|
const table = new import_cli_table32.default({
|
|
358
418
|
style: { head: [], border: [] }
|
|
359
419
|
});
|
|
360
420
|
table.push(
|
|
361
|
-
{ [import_chalk3.default.cyan("Price")]: formatSol(onChain.price) },
|
|
362
|
-
{ [import_chalk3.default.cyan("Market Cap")]: formatSol(onChain.marketCap) },
|
|
363
|
-
{ [import_chalk3.default.cyan("Total Supply")]: formatTokens(onChain.totalSupply) },
|
|
364
|
-
{ [import_chalk3.default.cyan("Circulating")]: formatTokens(onChain.circulatingSupply) },
|
|
365
|
-
{ [import_chalk3.default.cyan("Curve Balance")]: formatTokens(onChain.bondingCurveBalance) },
|
|
366
|
-
{ [import_chalk3.default.cyan("Curve SOL")]: formatSol(onChain.bondingCurveSol) },
|
|
421
|
+
{ [import_chalk3.default.cyan("Price")]: formatSol(onChain.price ?? 0) },
|
|
422
|
+
{ [import_chalk3.default.cyan("Market Cap")]: formatSol(onChain.marketCap ?? 0) },
|
|
423
|
+
{ [import_chalk3.default.cyan("Total Supply")]: formatTokens(onChain.totalSupply ?? 0) },
|
|
424
|
+
{ [import_chalk3.default.cyan("Circulating")]: formatTokens(onChain.circulatingSupply ?? 0) },
|
|
425
|
+
{ [import_chalk3.default.cyan("Curve Balance")]: formatTokens(onChain.bondingCurveBalance ?? 0) },
|
|
426
|
+
{ [import_chalk3.default.cyan("Curve SOL")]: formatSol(onChain.bondingCurveSol ?? 0) },
|
|
367
427
|
{ [import_chalk3.default.cyan("Status")]: onChain.graduated ? "\u{1F393} Graduated" : "\u{1F4C8} Bonding" }
|
|
368
428
|
);
|
|
369
429
|
console.log(table.toString());
|
|
@@ -396,12 +456,12 @@ tokenCommand.command("holders <mint>").description("Get top token holders").opti
|
|
|
396
456
|
],
|
|
397
457
|
style: { head: [], border: [] }
|
|
398
458
|
});
|
|
399
|
-
result.holders.forEach((holder, i) => {
|
|
459
|
+
(result.holders ?? []).forEach((holder, i) => {
|
|
400
460
|
table.push([
|
|
401
461
|
i + 1,
|
|
402
|
-
shortenAddress(holder.address),
|
|
403
|
-
formatTokens(holder.balance),
|
|
404
|
-
`${holder.percentage.toFixed(2)}%`,
|
|
462
|
+
shortenAddress(holder.address ?? ""),
|
|
463
|
+
formatTokens(holder.balance ?? 0),
|
|
464
|
+
`${(holder.percentage ?? 0).toFixed(2)}%`,
|
|
405
465
|
holder.label || "-"
|
|
406
466
|
]);
|
|
407
467
|
});
|
|
@@ -412,86 +472,11 @@ tokenCommand.command("holders <mint>").description("Get top token holders").opti
|
|
|
412
472
|
handleError(err);
|
|
413
473
|
}
|
|
414
474
|
});
|
|
415
|
-
tokenCommand.command("watch <mint>").description("Watch token price in real-time").option("--json", "Output as JSON").action(async (mint, options) => {
|
|
416
|
-
const baseUrl = process.env.CLAWDVAULT_API_URL || "https://clawdvault.com/api";
|
|
417
|
-
console.log(import_chalk3.default.bold(`
|
|
418
|
-
\u{1F4C8} Watching ${shortenAddress(mint)}
|
|
419
|
-
`));
|
|
420
|
-
info(`Connecting to ${baseUrl}...`);
|
|
421
|
-
console.log(import_chalk3.default.dim("Press Ctrl+C to stop\n"));
|
|
422
|
-
const streaming = (0, import_sdk3.createStreaming)(baseUrl);
|
|
423
|
-
const conn = streaming.streamToken(mint);
|
|
424
|
-
const client = createReadOnlyClient();
|
|
425
|
-
let solPrice = 0;
|
|
426
|
-
try {
|
|
427
|
-
const { price } = await client.getSolPrice();
|
|
428
|
-
solPrice = price;
|
|
429
|
-
} catch {
|
|
430
|
-
}
|
|
431
|
-
let tokenInfo = {};
|
|
432
|
-
let lastUpdate = null;
|
|
433
|
-
const displayUpdate = (update) => {
|
|
434
|
-
if (options.json) {
|
|
435
|
-
console.log(JSON.stringify({ ...update, ...tokenInfo }));
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
console.clear();
|
|
439
|
-
console.log(import_chalk3.default.bold(`
|
|
440
|
-
\u{1F4C8} ${tokenInfo.name || "Token"} (${tokenInfo.symbol || shortenAddress(mint)})
|
|
441
|
-
`));
|
|
442
|
-
const table = new import_cli_table32.default({
|
|
443
|
-
style: { head: [], border: [] }
|
|
444
|
-
});
|
|
445
|
-
const priceUsd = solPrice > 0 ? formatUsd(update.price_sol * solPrice) : "-";
|
|
446
|
-
const mcapUsd = solPrice > 0 ? formatUsd(update.market_cap_sol * solPrice) : "-";
|
|
447
|
-
table.push(
|
|
448
|
-
{ [import_chalk3.default.cyan("Price (SOL)")]: formatSol(update.price_sol) },
|
|
449
|
-
{ [import_chalk3.default.cyan("Price (USD)")]: priceUsd },
|
|
450
|
-
{ [import_chalk3.default.cyan("Market Cap (SOL)")]: formatSol(update.market_cap_sol) },
|
|
451
|
-
{ [import_chalk3.default.cyan("Market Cap (USD)")]: mcapUsd },
|
|
452
|
-
{ [import_chalk3.default.cyan("Bonding Curve SOL")]: formatSol(update.real_sol_reserves) },
|
|
453
|
-
{ [import_chalk3.default.cyan("Status")]: update.graduated ? import_chalk3.default.green("\u2713 Graduated") : import_chalk3.default.yellow("Bonding Curve") }
|
|
454
|
-
);
|
|
455
|
-
console.log(table.toString());
|
|
456
|
-
if (lastUpdate && lastUpdate.price_sol !== update.price_sol) {
|
|
457
|
-
const change = (update.price_sol - lastUpdate.price_sol) / lastUpdate.price_sol * 100;
|
|
458
|
-
const changeStr = change >= 0 ? import_chalk3.default.green(`+${change.toFixed(2)}%`) : import_chalk3.default.red(`${change.toFixed(2)}%`);
|
|
459
|
-
console.log(`
|
|
460
|
-
${import_chalk3.default.dim("Last change:")} ${changeStr}`);
|
|
461
|
-
}
|
|
462
|
-
console.log(import_chalk3.default.dim("\nLast update: " + new Date(update.timestamp).toLocaleTimeString()));
|
|
463
|
-
console.log(import_chalk3.default.dim("Press Ctrl+C to stop"));
|
|
464
|
-
lastUpdate = update;
|
|
465
|
-
};
|
|
466
|
-
conn.onConnect(() => success("Connected to stream"));
|
|
467
|
-
conn.onDisconnect(() => warn("Disconnected - reconnecting..."));
|
|
468
|
-
conn.on("connected", (data) => {
|
|
469
|
-
tokenInfo = { name: data.name, symbol: data.symbol };
|
|
470
|
-
displayUpdate(data);
|
|
471
|
-
});
|
|
472
|
-
conn.on("update", displayUpdate);
|
|
473
|
-
conn.on("trade", (trade) => {
|
|
474
|
-
if (lastUpdate) {
|
|
475
|
-
lastUpdate.price_sol = trade.price_sol;
|
|
476
|
-
displayUpdate(lastUpdate);
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
|
-
conn.connect();
|
|
480
|
-
process.on("SIGINT", () => {
|
|
481
|
-
console.log("\n");
|
|
482
|
-
info("Disconnecting...");
|
|
483
|
-
streaming.disconnectAll();
|
|
484
|
-
process.exit(0);
|
|
485
|
-
});
|
|
486
|
-
await new Promise(() => {
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
475
|
|
|
490
476
|
// src/commands/trade.ts
|
|
491
477
|
var import_commander3 = require("commander");
|
|
492
478
|
var import_chalk4 = __toESM(require("chalk"));
|
|
493
479
|
var import_cli_table33 = __toESM(require("cli-table3"));
|
|
494
|
-
var import_sdk4 = require("@clawdvault/sdk");
|
|
495
480
|
var tradeCommand = new import_commander3.Command("trade").description("Trading operations");
|
|
496
481
|
tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint <address>", "Token mint address").requiredOption("-a, --sol <amount>", "Amount of SOL to spend").option("-s, --slippage <percent>", "Slippage tolerance (default: 1%)", "1").option("-w, --wallet <path>", "Wallet file path").option("--simulate", "Only simulate, don't execute").action(async (options) => {
|
|
497
482
|
const { client, signer, walletAddress } = createClientWithWallet(options.wallet);
|
|
@@ -510,12 +495,13 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
|
|
|
510
495
|
const table = new import_cli_table33.default({
|
|
511
496
|
style: { head: [], border: [] }
|
|
512
497
|
});
|
|
498
|
+
const priceImpact = quote.price_impact ?? 0;
|
|
513
499
|
table.push(
|
|
514
500
|
{ [import_chalk4.default.cyan("Input")]: `${solAmount} SOL` },
|
|
515
|
-
{ [import_chalk4.default.cyan("Output")]: `~${formatTokens(quote.output)} tokens` },
|
|
516
|
-
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
|
|
517
|
-
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
|
|
518
|
-
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(
|
|
501
|
+
{ [import_chalk4.default.cyan("Output")]: `~${formatTokens(quote.output ?? 0)} tokens` },
|
|
502
|
+
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
|
|
503
|
+
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
|
|
504
|
+
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(priceImpact) },
|
|
519
505
|
{ [import_chalk4.default.cyan("Slippage")]: `${(slippage * 100).toFixed(1)}%` }
|
|
520
506
|
);
|
|
521
507
|
console.log(table.toString());
|
|
@@ -523,8 +509,8 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
|
|
|
523
509
|
info("Simulation only - no transaction executed");
|
|
524
510
|
return;
|
|
525
511
|
}
|
|
526
|
-
if (
|
|
527
|
-
warn(`High price impact: ${(
|
|
512
|
+
if (priceImpact > 0.05) {
|
|
513
|
+
warn(`High price impact: ${(priceImpact * 100).toFixed(2)}%`);
|
|
528
514
|
}
|
|
529
515
|
console.log();
|
|
530
516
|
const tradeSpin = spinner("Executing trade...").start();
|
|
@@ -540,10 +526,10 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
|
|
|
540
526
|
success("Trade executed successfully!");
|
|
541
527
|
console.log();
|
|
542
528
|
info(`Signature: ${result.signature}`);
|
|
543
|
-
info(`SOL spent: ${formatSol(result.trade
|
|
544
|
-
info(`Tokens received: ${formatTokens(result.trade
|
|
529
|
+
info(`SOL spent: ${formatSol(result.trade?.solAmount ?? 0)}`);
|
|
530
|
+
info(`Tokens received: ${formatTokens(result.trade?.tokenAmount ?? 0)}`);
|
|
545
531
|
console.log();
|
|
546
|
-
info(`Explorer: ${link(result.explorer)}`);
|
|
532
|
+
info(`Explorer: ${link(result.explorer ?? "")}`);
|
|
547
533
|
console.log();
|
|
548
534
|
} catch (err) {
|
|
549
535
|
quoteSpin.stop();
|
|
@@ -559,7 +545,8 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
|
|
|
559
545
|
const slippage = parseFloat(options.slippage) / 100;
|
|
560
546
|
const balanceSpin = spinner("Checking balance...").start();
|
|
561
547
|
try {
|
|
562
|
-
const { balance } = await client.getMyBalance(options.mint);
|
|
548
|
+
const { balance: rawBalance } = await client.getMyBalance(options.mint);
|
|
549
|
+
const balance = rawBalance ?? 0;
|
|
563
550
|
balanceSpin.stop();
|
|
564
551
|
if (balance <= 0) {
|
|
565
552
|
throw new Error("No tokens to sell");
|
|
@@ -586,12 +573,13 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
|
|
|
586
573
|
const table = new import_cli_table33.default({
|
|
587
574
|
style: { head: [], border: [] }
|
|
588
575
|
});
|
|
576
|
+
const sellPriceImpact = quote.price_impact ?? 0;
|
|
589
577
|
table.push(
|
|
590
578
|
{ [import_chalk4.default.cyan("Input")]: `${formatTokens(tokenAmount)} tokens` },
|
|
591
|
-
{ [import_chalk4.default.cyan("Output")]: `~${formatSol(quote.output)}` },
|
|
592
|
-
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
|
|
593
|
-
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
|
|
594
|
-
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(-
|
|
579
|
+
{ [import_chalk4.default.cyan("Output")]: `~${formatSol(quote.output ?? 0)}` },
|
|
580
|
+
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
|
|
581
|
+
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
|
|
582
|
+
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(-sellPriceImpact) },
|
|
595
583
|
{ [import_chalk4.default.cyan("Slippage")]: `${(slippage * 100).toFixed(1)}%` }
|
|
596
584
|
);
|
|
597
585
|
console.log(table.toString());
|
|
@@ -599,8 +587,8 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
|
|
|
599
587
|
info("Simulation only - no transaction executed");
|
|
600
588
|
return;
|
|
601
589
|
}
|
|
602
|
-
if (
|
|
603
|
-
warn(`High price impact: ${(
|
|
590
|
+
if (sellPriceImpact > 0.05) {
|
|
591
|
+
warn(`High price impact: ${(sellPriceImpact * 100).toFixed(2)}%`);
|
|
604
592
|
}
|
|
605
593
|
console.log();
|
|
606
594
|
const tradeSpin = spinner("Executing trade...").start();
|
|
@@ -616,10 +604,10 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
|
|
|
616
604
|
success("Trade executed successfully!");
|
|
617
605
|
console.log();
|
|
618
606
|
info(`Signature: ${result.signature}`);
|
|
619
|
-
info(`Tokens sold: ${formatTokens(result.trade
|
|
620
|
-
info(`SOL received: ${formatSol(result.trade
|
|
607
|
+
info(`Tokens sold: ${formatTokens(result.trade?.tokenAmount ?? 0)}`);
|
|
608
|
+
info(`SOL received: ${formatSol(result.trade?.solAmount ?? 0)}`);
|
|
621
609
|
console.log();
|
|
622
|
-
info(`Explorer: ${link(result.explorer)}`);
|
|
610
|
+
info(`Explorer: ${link(result.explorer ?? "")}`);
|
|
623
611
|
console.log();
|
|
624
612
|
} catch (err) {
|
|
625
613
|
balanceSpin.stop();
|
|
@@ -647,12 +635,13 @@ tradeCommand.command("quote").description("Get a price quote without executing")
|
|
|
647
635
|
const table = new import_cli_table33.default({
|
|
648
636
|
style: { head: [], border: [] }
|
|
649
637
|
});
|
|
638
|
+
const quotePriceImpact = quote.price_impact ?? 0;
|
|
650
639
|
table.push(
|
|
651
640
|
{ [import_chalk4.default.cyan("Input")]: isBuy ? `${options.amount} SOL` : `${formatTokens(parseFloat(options.amount))} tokens` },
|
|
652
|
-
{ [import_chalk4.default.cyan("Output")]: isBuy ? `~${formatTokens(quote.output)} tokens` : `~${formatSol(quote.output)}` },
|
|
653
|
-
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
|
|
654
|
-
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
|
|
655
|
-
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(isBuy ?
|
|
641
|
+
{ [import_chalk4.default.cyan("Output")]: isBuy ? `~${formatTokens(quote.output ?? 0)} tokens` : `~${formatSol(quote.output ?? 0)}` },
|
|
642
|
+
{ [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
|
|
643
|
+
{ [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
|
|
644
|
+
{ [import_chalk4.default.cyan("Price Impact")]: formatPercent(isBuy ? quotePriceImpact : -quotePriceImpact) }
|
|
656
645
|
);
|
|
657
646
|
console.log(table.toString());
|
|
658
647
|
console.log();
|
|
@@ -688,15 +677,15 @@ tradeCommand.command("history").description("Get trade history for a token").req
|
|
|
688
677
|
],
|
|
689
678
|
style: { head: [], border: [] }
|
|
690
679
|
});
|
|
691
|
-
for (const trade of result.trades) {
|
|
680
|
+
for (const trade of result.trades ?? []) {
|
|
692
681
|
const typeStr = trade.type === "buy" ? import_chalk4.default.green("BUY") : import_chalk4.default.red("SELL");
|
|
693
682
|
table.push([
|
|
694
683
|
typeStr,
|
|
695
|
-
formatSol(trade.sol_amount),
|
|
696
|
-
formatTokens(trade.token_amount),
|
|
697
|
-
formatSol(trade.
|
|
698
|
-
shortenAddress(trade.trader),
|
|
699
|
-
new Date(trade.created_at).toLocaleString()
|
|
684
|
+
formatSol(trade.sol_amount ?? 0),
|
|
685
|
+
formatTokens(trade.token_amount ?? 0),
|
|
686
|
+
formatSol(trade.price ?? 0),
|
|
687
|
+
shortenAddress(trade.trader ?? ""),
|
|
688
|
+
new Date(trade.created_at ?? Date.now()).toLocaleString()
|
|
700
689
|
]);
|
|
701
690
|
}
|
|
702
691
|
console.log(table.toString());
|
|
@@ -706,78 +695,6 @@ tradeCommand.command("history").description("Get trade history for a token").req
|
|
|
706
695
|
handleError(err);
|
|
707
696
|
}
|
|
708
697
|
});
|
|
709
|
-
tradeCommand.command("stream").description("Stream trades in real-time").requiredOption("-m, --mint <address>", "Token mint address").option("--json", "Output as JSON (one object per line)").option("--append", "Append mode (simple log format)").action(async (options) => {
|
|
710
|
-
const baseUrl = process.env.CLAWDVAULT_API_URL || "https://clawdvault.com/api";
|
|
711
|
-
console.log(import_chalk4.default.bold(`
|
|
712
|
-
\u{1F4E1} Streaming trades for ${shortenAddress(options.mint)}
|
|
713
|
-
`));
|
|
714
|
-
info(`Connecting to ${baseUrl}...`);
|
|
715
|
-
console.log(import_chalk4.default.dim("Press Ctrl+C to stop\n"));
|
|
716
|
-
const streaming = (0, import_sdk4.createStreaming)(baseUrl);
|
|
717
|
-
const conn = streaming.streamTrades(options.mint);
|
|
718
|
-
const trades = [];
|
|
719
|
-
const MAX_DISPLAY = 20;
|
|
720
|
-
conn.onConnect(() => {
|
|
721
|
-
success("Connected to stream");
|
|
722
|
-
console.log();
|
|
723
|
-
});
|
|
724
|
-
conn.onDisconnect(() => warn("Disconnected - reconnecting..."));
|
|
725
|
-
conn.on("trade", (trade) => {
|
|
726
|
-
if (options.json) {
|
|
727
|
-
console.log(JSON.stringify(trade));
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
if (options.append) {
|
|
731
|
-
const typeStr = trade.type === "buy" ? import_chalk4.default.green("BUY ") : import_chalk4.default.red("SELL");
|
|
732
|
-
const time = new Date(trade.created_at).toLocaleTimeString();
|
|
733
|
-
console.log(
|
|
734
|
-
`${import_chalk4.default.dim(time)} ${typeStr} ${formatSol(trade.sol_amount).padEnd(15)} ${formatTokens(trade.token_amount).padEnd(12)} @ ${formatSol(trade.price_sol).padEnd(18)} ${import_chalk4.default.dim(shortenAddress(trade.trader))}`
|
|
735
|
-
);
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
trades.unshift(trade);
|
|
739
|
-
if (trades.length > MAX_DISPLAY) {
|
|
740
|
-
trades.pop();
|
|
741
|
-
}
|
|
742
|
-
console.clear();
|
|
743
|
-
console.log(import_chalk4.default.bold(`
|
|
744
|
-
\u{1F4E1} Live Trades - ${shortenAddress(options.mint)}
|
|
745
|
-
`));
|
|
746
|
-
const table = new import_cli_table33.default({
|
|
747
|
-
head: [
|
|
748
|
-
import_chalk4.default.cyan("Type"),
|
|
749
|
-
import_chalk4.default.cyan("SOL"),
|
|
750
|
-
import_chalk4.default.cyan("Tokens"),
|
|
751
|
-
import_chalk4.default.cyan("Price"),
|
|
752
|
-
import_chalk4.default.cyan("Trader"),
|
|
753
|
-
import_chalk4.default.cyan("Time")
|
|
754
|
-
],
|
|
755
|
-
style: { head: [], border: [] }
|
|
756
|
-
});
|
|
757
|
-
for (const t of trades) {
|
|
758
|
-
const typeStr = t.type === "buy" ? import_chalk4.default.green("BUY") : import_chalk4.default.red("SELL");
|
|
759
|
-
table.push([
|
|
760
|
-
typeStr,
|
|
761
|
-
formatSol(t.sol_amount),
|
|
762
|
-
formatTokens(t.token_amount),
|
|
763
|
-
formatSol(t.price_sol),
|
|
764
|
-
shortenAddress(t.trader),
|
|
765
|
-
new Date(t.created_at).toLocaleTimeString()
|
|
766
|
-
]);
|
|
767
|
-
}
|
|
768
|
-
console.log(table.toString());
|
|
769
|
-
console.log(import_chalk4.default.dim("\nPress Ctrl+C to stop"));
|
|
770
|
-
});
|
|
771
|
-
conn.connect();
|
|
772
|
-
process.on("SIGINT", () => {
|
|
773
|
-
console.log("\n");
|
|
774
|
-
info("Disconnecting...");
|
|
775
|
-
streaming.disconnectAll();
|
|
776
|
-
process.exit(0);
|
|
777
|
-
});
|
|
778
|
-
await new Promise(() => {
|
|
779
|
-
});
|
|
780
|
-
});
|
|
781
698
|
|
|
782
699
|
// src/commands/wallet.ts
|
|
783
700
|
var import_commander4 = require("commander");
|
|
@@ -811,7 +728,7 @@ walletCommand.command("info").description("Show wallet information").option("-w,
|
|
|
811
728
|
handleError(err);
|
|
812
729
|
}
|
|
813
730
|
});
|
|
814
|
-
walletCommand.command("init").description("Generate a new wallet").option("-o, --output <path>", "Output file path (default: ~/.clawdvault/wallet.json)").option("--force", "Overwrite existing wallet").action(async (options) => {
|
|
731
|
+
walletCommand.command("init").description("Generate a new wallet").option("-o, --output <path>", "Output file path (default: ~/.clawdvault/wallet.json)").option("--force", "Overwrite existing wallet").option("--mnemonic", "Generate wallet with recoverable seed phrase (can import into Phantom)").action(async (options) => {
|
|
815
732
|
try {
|
|
816
733
|
const configDir = getConfigDir();
|
|
817
734
|
const outputPath = options.output || path2.join(configDir, "wallet.json");
|
|
@@ -823,16 +740,36 @@ walletCommand.command("init").description("Generate a new wallet").option("-o, -
|
|
|
823
740
|
if (!fs3.existsSync(configDir)) {
|
|
824
741
|
fs3.mkdirSync(configDir, { recursive: true });
|
|
825
742
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
743
|
+
let keypair;
|
|
744
|
+
let mnemonic;
|
|
745
|
+
if (options.mnemonic) {
|
|
746
|
+
const bip39 = await import("bip39");
|
|
747
|
+
mnemonic = bip39.generateMnemonic(256);
|
|
748
|
+
const seed = await bip39.mnemonicToSeed(mnemonic);
|
|
749
|
+
const secretKey = seed.slice(0, 32);
|
|
750
|
+
keypair = import_web3.Keypair.fromSeed(new Uint8Array(secretKey));
|
|
751
|
+
} else {
|
|
752
|
+
keypair = import_web3.Keypair.generate();
|
|
753
|
+
}
|
|
754
|
+
const secretKeyArray = Array.from(keypair.secretKey);
|
|
755
|
+
fs3.writeFileSync(outputPath, JSON.stringify(secretKeyArray));
|
|
829
756
|
fs3.chmodSync(outputPath, 384);
|
|
830
757
|
success("Wallet created successfully!");
|
|
831
758
|
console.log();
|
|
832
759
|
info(`Address: ${keypair.publicKey.toBase58()}`);
|
|
833
760
|
info(`Saved to: ${outputPath}`);
|
|
834
|
-
|
|
835
|
-
|
|
761
|
+
if (mnemonic) {
|
|
762
|
+
console.log();
|
|
763
|
+
console.log(import_chalk5.default.yellow("\u{1F4DD} SEED PHRASE (Write this down!):"));
|
|
764
|
+
console.log(import_chalk5.default.white(mnemonic));
|
|
765
|
+
console.log();
|
|
766
|
+
warn("\u26A0\uFE0F IMPORTANT: This seed phrase can recover your wallet in Phantom!");
|
|
767
|
+
warn(" Write it down and store it safely. Anyone with this phrase can steal your funds!");
|
|
768
|
+
} else {
|
|
769
|
+
console.log();
|
|
770
|
+
warn("\u26A0\uFE0F IMPORTANT: Back up your wallet file! Loss of this file means loss of funds.");
|
|
771
|
+
info(" Use --mnemonic if you want a recoverable seed phrase for Phantom import.");
|
|
772
|
+
}
|
|
836
773
|
console.log();
|
|
837
774
|
} catch (err) {
|
|
838
775
|
handleError(err);
|
|
@@ -860,7 +797,7 @@ walletCommand.command("balance").description("Get wallet balance for a token").r
|
|
|
860
797
|
table.push(
|
|
861
798
|
{ [import_chalk5.default.cyan("Wallet")]: result.wallet },
|
|
862
799
|
{ [import_chalk5.default.cyan("Token")]: result.mint },
|
|
863
|
-
{ [import_chalk5.default.cyan("Balance")]: formatTokens(result.balance) }
|
|
800
|
+
{ [import_chalk5.default.cyan("Balance")]: formatTokens(result.balance ?? 0) }
|
|
864
801
|
);
|
|
865
802
|
console.log(table.toString());
|
|
866
803
|
console.log();
|
|
@@ -925,7 +862,7 @@ walletCommand.command("sol-price").description("Get current SOL/USD price").opti
|
|
|
925
862
|
return;
|
|
926
863
|
}
|
|
927
864
|
console.log(import_chalk5.default.bold(`
|
|
928
|
-
\u{1F4B2} SOL Price: $${result.price.toFixed(2)}
|
|
865
|
+
\u{1F4B2} SOL Price: $${(result.price ?? 0).toFixed(2)}
|
|
929
866
|
`));
|
|
930
867
|
if (result.cached) {
|
|
931
868
|
info(`Cached ${result.age}s ago from ${result.source}`);
|
|
@@ -955,7 +892,7 @@ walletCommand.command("sol-balance").description("Get SOL balance with USD value
|
|
|
955
892
|
let usdValue = 0;
|
|
956
893
|
try {
|
|
957
894
|
const priceData = await client.getSolPrice();
|
|
958
|
-
solPrice = priceData.price;
|
|
895
|
+
solPrice = priceData.price ?? 0;
|
|
959
896
|
usdValue = balanceSol * solPrice;
|
|
960
897
|
} catch {
|
|
961
898
|
}
|
|
@@ -1154,214 +1091,206 @@ walletCommand.command("airdrop").description("Request SOL from devnet faucet (de
|
|
|
1154
1091
|
handleError(err);
|
|
1155
1092
|
}
|
|
1156
1093
|
});
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
\u{1F4E1} Streaming trades for ${shortenAddress(options.mint)}
|
|
1171
|
-
`));
|
|
1172
|
-
info(`Connecting to ${baseUrl}...`);
|
|
1173
|
-
console.log(import_chalk6.default.dim("Press Ctrl+C to stop\n"));
|
|
1174
|
-
const streaming = (0, import_sdk5.createStreaming)(baseUrl, {
|
|
1175
|
-
autoReconnect: true,
|
|
1176
|
-
reconnectDelay: 3e3
|
|
1177
|
-
});
|
|
1178
|
-
const conn = streaming.streamTrades(options.mint);
|
|
1179
|
-
const trades = [];
|
|
1180
|
-
const MAX_DISPLAY = 20;
|
|
1181
|
-
conn.onConnect(() => {
|
|
1182
|
-
success("Connected to stream");
|
|
1183
|
-
console.log();
|
|
1184
|
-
});
|
|
1185
|
-
conn.onDisconnect(() => {
|
|
1186
|
-
warn("Disconnected - reconnecting...");
|
|
1187
|
-
});
|
|
1188
|
-
conn.onError((err) => {
|
|
1189
|
-
if (!err.message.includes("Max reconnect")) {
|
|
1190
|
-
} else {
|
|
1191
|
-
console.error(import_chalk6.default.red(`Error: ${err.message}`));
|
|
1094
|
+
walletCommand.command("login").description("Login to ClawdVault (get session token)").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
|
|
1095
|
+
const spin = spinner("Authenticating...").start();
|
|
1096
|
+
try {
|
|
1097
|
+
const signer = loadSigner(options.wallet);
|
|
1098
|
+
requireWallet(signer);
|
|
1099
|
+
const walletAddress = signer.publicKey.toBase58();
|
|
1100
|
+
const { createClient: createClient3 } = require("@clawdvault/sdk");
|
|
1101
|
+
const baseUrl = process.env.CLAWDVAULT_API_URL;
|
|
1102
|
+
const client = createClient3({ signer, baseUrl });
|
|
1103
|
+
const session = await client.createSession();
|
|
1104
|
+
if (!session.token) {
|
|
1105
|
+
spin.stop();
|
|
1106
|
+
error("Failed to get session token");
|
|
1192
1107
|
process.exit(1);
|
|
1193
1108
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1109
|
+
const expiresInMs = (session.expiresIn || 7 * 24 * 60 * 60) * 1e3;
|
|
1110
|
+
const expiresAt = new Date(Date.now() + expiresInMs).toISOString();
|
|
1111
|
+
saveAuthConfig({
|
|
1112
|
+
sessionToken: session.token,
|
|
1113
|
+
wallet: walletAddress,
|
|
1114
|
+
expiresAt
|
|
1115
|
+
});
|
|
1116
|
+
spin.stop();
|
|
1117
|
+
success("Logged in successfully!");
|
|
1118
|
+
console.log();
|
|
1119
|
+
info(`Wallet: ${walletAddress}`);
|
|
1120
|
+
info(`Session expires: ${new Date(expiresAt).toLocaleString()}`);
|
|
1121
|
+
info(`Config saved to: ${getAuthConfigPath()}`);
|
|
1122
|
+
console.log();
|
|
1123
|
+
} catch (err) {
|
|
1124
|
+
spin.stop();
|
|
1125
|
+
handleError(err);
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
walletCommand.command("logout").description("Logout from ClawdVault (clear session token)").action(async () => {
|
|
1129
|
+
try {
|
|
1130
|
+
const authConfig = loadAuthConfig();
|
|
1131
|
+
if (!authConfig) {
|
|
1132
|
+
warn("Not logged in");
|
|
1198
1133
|
return;
|
|
1199
1134
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1135
|
+
clearAuthConfig();
|
|
1136
|
+
success("Logged out successfully");
|
|
1137
|
+
console.log();
|
|
1138
|
+
} catch (err) {
|
|
1139
|
+
handleError(err);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
walletCommand.command("status").description("Show authentication status").action(async () => {
|
|
1143
|
+
try {
|
|
1144
|
+
const authConfig = loadAuthConfig();
|
|
1145
|
+
console.log(import_chalk5.default.bold("\n\u{1F510} Auth Status\n"));
|
|
1146
|
+
if (!authConfig?.sessionToken) {
|
|
1147
|
+
warn("Not logged in");
|
|
1148
|
+
info("Run: clawdvault wallet login");
|
|
1149
|
+
console.log();
|
|
1206
1150
|
return;
|
|
1207
1151
|
}
|
|
1208
|
-
|
|
1209
|
-
if (trades.length > MAX_DISPLAY) {
|
|
1210
|
-
trades.pop();
|
|
1211
|
-
}
|
|
1212
|
-
console.clear();
|
|
1213
|
-
console.log(import_chalk6.default.bold(`
|
|
1214
|
-
\u{1F4E1} Live Trades - ${shortenAddress(options.mint)}
|
|
1215
|
-
`));
|
|
1216
|
-
const table = new import_cli_table35.default({
|
|
1217
|
-
head: [
|
|
1218
|
-
import_chalk6.default.cyan("Type"),
|
|
1219
|
-
import_chalk6.default.cyan("SOL"),
|
|
1220
|
-
import_chalk6.default.cyan("Tokens"),
|
|
1221
|
-
import_chalk6.default.cyan("Price"),
|
|
1222
|
-
import_chalk6.default.cyan("Trader"),
|
|
1223
|
-
import_chalk6.default.cyan("Time")
|
|
1224
|
-
],
|
|
1152
|
+
const table = new import_cli_table34.default({
|
|
1225
1153
|
style: { head: [], border: [] }
|
|
1226
1154
|
});
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
formatTokens(t.token_amount),
|
|
1233
|
-
formatSol(t.price_sol),
|
|
1234
|
-
shortenAddress(t.trader),
|
|
1235
|
-
new Date(t.created_at).toLocaleTimeString()
|
|
1236
|
-
]);
|
|
1237
|
-
}
|
|
1155
|
+
table.push(
|
|
1156
|
+
{ [import_chalk5.default.cyan("Status")]: import_chalk5.default.green("\u2713 Logged in") },
|
|
1157
|
+
{ [import_chalk5.default.cyan("Wallet")]: authConfig.wallet || "Unknown" },
|
|
1158
|
+
{ [import_chalk5.default.cyan("Expires")]: authConfig.expiresAt ? new Date(authConfig.expiresAt).toLocaleString() : "Unknown" }
|
|
1159
|
+
);
|
|
1238
1160
|
console.log(table.toString());
|
|
1239
|
-
console.log(
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1161
|
+
console.log();
|
|
1162
|
+
const { client } = createClientWithWallet();
|
|
1163
|
+
const spin = spinner("Validating session...").start();
|
|
1164
|
+
try {
|
|
1165
|
+
const validation = await client.validateSession();
|
|
1166
|
+
spin.stop();
|
|
1167
|
+
if (validation.valid) {
|
|
1168
|
+
success("Session is valid");
|
|
1169
|
+
} else {
|
|
1170
|
+
warn("Session may be expired or invalid");
|
|
1171
|
+
info("Run: clawdvault wallet login");
|
|
1172
|
+
}
|
|
1173
|
+
} catch {
|
|
1174
|
+
spin.stop();
|
|
1175
|
+
warn("Could not validate session with server");
|
|
1176
|
+
}
|
|
1177
|
+
console.log();
|
|
1178
|
+
} catch (err) {
|
|
1179
|
+
handleError(err);
|
|
1180
|
+
}
|
|
1250
1181
|
});
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
const streaming = (0, import_sdk5.createStreaming)(baseUrl);
|
|
1259
|
-
const conn = streaming.streamToken(options.mint);
|
|
1260
|
-
const client = createReadOnlyClient();
|
|
1261
|
-
let solPrice = 0;
|
|
1182
|
+
|
|
1183
|
+
// src/commands/chat.ts
|
|
1184
|
+
var import_commander5 = require("commander");
|
|
1185
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
1186
|
+
var chatCommand = new import_commander5.Command("chat").description("Token chat operations");
|
|
1187
|
+
chatCommand.command("send").description("Send a chat message to a token").requiredOption("-m, --mint <address>", "Token mint address").argument("<message>", "Message to send").option("-w, --wallet <path>", "Wallet file path").option("--reply <id>", "Reply to message ID").option("--json", "Output as JSON").action(async (message, options) => {
|
|
1188
|
+
const spin = spinner("Sending message...").start();
|
|
1262
1189
|
try {
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1190
|
+
const auth = requireAuth();
|
|
1191
|
+
const { client, signer, walletAddress } = createClientWithWallet(options.wallet);
|
|
1192
|
+
if (!signer) {
|
|
1193
|
+
spin.stop();
|
|
1194
|
+
warn("Wallet recommended for chat (using session wallet)");
|
|
1195
|
+
}
|
|
1196
|
+
const result = await client.sendChat({
|
|
1197
|
+
mint: options.mint,
|
|
1198
|
+
message,
|
|
1199
|
+
replyTo: options.reply
|
|
1200
|
+
});
|
|
1201
|
+
spin.stop();
|
|
1202
|
+
if (options.json) {
|
|
1203
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (result.success) {
|
|
1207
|
+
success("Message sent!");
|
|
1208
|
+
console.log();
|
|
1209
|
+
const msg = result.message;
|
|
1210
|
+
if (msg) {
|
|
1211
|
+
info(`ID: ${msg.id}`);
|
|
1212
|
+
if (msg.created_at) {
|
|
1213
|
+
info(`Time: ${new Date(msg.created_at).toLocaleString()}`);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
error("Failed to send message");
|
|
1218
|
+
}
|
|
1219
|
+
console.log();
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
spin.stop();
|
|
1222
|
+
handleError(err);
|
|
1266
1223
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
const
|
|
1224
|
+
});
|
|
1225
|
+
chatCommand.command("history").description("Get chat history for a token").requiredOption("-m, --mint <address>", "Token mint address").option("-l, --limit <number>", "Number of messages to fetch", "20").option("--before <id>", "Get messages before this ID").option("--json", "Output as JSON").action(async (options) => {
|
|
1226
|
+
const spin = spinner("Fetching messages...").start();
|
|
1227
|
+
try {
|
|
1228
|
+
const { client } = createClientWithWallet();
|
|
1229
|
+
const result = await client.getChat({
|
|
1230
|
+
mint: options.mint,
|
|
1231
|
+
limit: parseInt(options.limit, 10),
|
|
1232
|
+
before: options.before
|
|
1233
|
+
});
|
|
1234
|
+
spin.stop();
|
|
1270
1235
|
if (options.json) {
|
|
1271
|
-
console.log(JSON.stringify(
|
|
1236
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
const messages = result.messages || [];
|
|
1240
|
+
if (messages.length === 0) {
|
|
1241
|
+
info("No messages found");
|
|
1272
1242
|
return;
|
|
1273
1243
|
}
|
|
1274
|
-
console.clear();
|
|
1275
1244
|
console.log(import_chalk6.default.bold(`
|
|
1276
|
-
\u{
|
|
1245
|
+
\u{1F4AC} Chat History - ${shortenAddress(options.mint)}
|
|
1277
1246
|
`));
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
{ [import_chalk6.default.cyan("Market Cap (SOL)")]: formatSol(update.market_cap_sol) },
|
|
1287
|
-
{ [import_chalk6.default.cyan("Market Cap (USD)")]: mcapUsd },
|
|
1288
|
-
{ [import_chalk6.default.cyan("Bonding Curve SOL")]: formatSol(update.real_sol_reserves) },
|
|
1289
|
-
{ [import_chalk6.default.cyan("Status")]: update.graduated ? import_chalk6.default.green("\u2713 Graduated") : import_chalk6.default.yellow("Bonding Curve") }
|
|
1290
|
-
);
|
|
1291
|
-
console.log(table.toString());
|
|
1292
|
-
if (lastUpdate && lastUpdate.price_sol !== update.price_sol) {
|
|
1293
|
-
const change = (update.price_sol - lastUpdate.price_sol) / lastUpdate.price_sol * 100;
|
|
1294
|
-
const changeStr = change >= 0 ? import_chalk6.default.green(`+${change.toFixed(2)}%`) : import_chalk6.default.red(`${change.toFixed(2)}%`);
|
|
1295
|
-
console.log(`
|
|
1296
|
-
${import_chalk6.default.dim("Last change:")} ${changeStr}`);
|
|
1247
|
+
const sorted = [...messages].reverse();
|
|
1248
|
+
for (const msg of sorted) {
|
|
1249
|
+
const time = msg.created_at ? new Date(msg.created_at).toLocaleTimeString() : "??:??";
|
|
1250
|
+
const sender = msg.username || (msg.wallet ? shortenAddress(msg.wallet) : "unknown");
|
|
1251
|
+
const replyPrefix = msg.reply_to ? import_chalk6.default.dim(`\u21A9 ${shortenAddress(msg.reply_to)} `) : "";
|
|
1252
|
+
console.log(
|
|
1253
|
+
`${import_chalk6.default.dim(time)} ${import_chalk6.default.cyan(sender)}: ${replyPrefix}${msg.message || ""}`
|
|
1254
|
+
);
|
|
1297
1255
|
}
|
|
1298
|
-
console.log(
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
conn.onConnect(() => {
|
|
1303
|
-
success("Connected to stream");
|
|
1304
|
-
});
|
|
1305
|
-
conn.onDisconnect(() => {
|
|
1306
|
-
warn("Disconnected - reconnecting...");
|
|
1307
|
-
});
|
|
1308
|
-
conn.on("connected", (data) => {
|
|
1309
|
-
tokenInfo = { name: data.name, symbol: data.symbol };
|
|
1310
|
-
displayUpdate(data);
|
|
1311
|
-
});
|
|
1312
|
-
conn.on("update", displayUpdate);
|
|
1313
|
-
conn.on("trade", (trade) => {
|
|
1314
|
-
if (lastUpdate) {
|
|
1315
|
-
lastUpdate.price_sol = trade.price_sol;
|
|
1316
|
-
displayUpdate(lastUpdate);
|
|
1256
|
+
console.log();
|
|
1257
|
+
info(`Showing ${messages.length} messages`);
|
|
1258
|
+
if (messages.length > 0) {
|
|
1259
|
+
info(`Use --before ${messages[messages.length - 1]?.id} for older messages`);
|
|
1317
1260
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
streaming.disconnectAll();
|
|
1324
|
-
process.exit(0);
|
|
1325
|
-
});
|
|
1326
|
-
await new Promise(() => {
|
|
1327
|
-
});
|
|
1261
|
+
console.log();
|
|
1262
|
+
} catch (err) {
|
|
1263
|
+
spin.stop();
|
|
1264
|
+
handleError(err);
|
|
1265
|
+
}
|
|
1328
1266
|
});
|
|
1329
|
-
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
const conn = streaming.streamChat(options.mint);
|
|
1338
|
-
conn.onConnect(() => {
|
|
1339
|
-
success("Connected to stream");
|
|
1267
|
+
chatCommand.command("react").description("Add reaction to a message").requiredOption("-i, --id <messageId>", "Message ID").requiredOption("-e, --emoji <emoji>", "Emoji to react with").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
|
|
1268
|
+
const spin = spinner("Adding reaction...").start();
|
|
1269
|
+
try {
|
|
1270
|
+
requireAuth();
|
|
1271
|
+
const { client } = createClientWithWallet(options.wallet);
|
|
1272
|
+
await client.addReaction(options.id, options.emoji);
|
|
1273
|
+
spin.stop();
|
|
1274
|
+
success(`Reacted with ${options.emoji}`);
|
|
1340
1275
|
console.log();
|
|
1341
|
-
})
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
);
|
|
1355
|
-
})
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
info("Disconnecting...");
|
|
1360
|
-
streaming.disconnectAll();
|
|
1361
|
-
process.exit(0);
|
|
1362
|
-
});
|
|
1363
|
-
await new Promise(() => {
|
|
1364
|
-
});
|
|
1276
|
+
} catch (err) {
|
|
1277
|
+
spin.stop();
|
|
1278
|
+
handleError(err);
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
chatCommand.command("unreact").description("Remove reaction from a message").requiredOption("-i, --id <messageId>", "Message ID").requiredOption("-e, --emoji <emoji>", "Emoji to remove").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
|
|
1282
|
+
const spin = spinner("Removing reaction...").start();
|
|
1283
|
+
try {
|
|
1284
|
+
requireAuth();
|
|
1285
|
+
const { client } = createClientWithWallet(options.wallet);
|
|
1286
|
+
await client.removeReaction(options.id, options.emoji);
|
|
1287
|
+
spin.stop();
|
|
1288
|
+
success(`Removed ${options.emoji} reaction`);
|
|
1289
|
+
console.log();
|
|
1290
|
+
} catch (err) {
|
|
1291
|
+
spin.stop();
|
|
1292
|
+
handleError(err);
|
|
1293
|
+
}
|
|
1365
1294
|
});
|
|
1366
1295
|
|
|
1367
1296
|
// src/index.ts
|
|
@@ -1371,7 +1300,7 @@ program.addCommand(tokensCommand);
|
|
|
1371
1300
|
program.addCommand(tokenCommand);
|
|
1372
1301
|
program.addCommand(tradeCommand);
|
|
1373
1302
|
program.addCommand(walletCommand);
|
|
1374
|
-
program.addCommand(
|
|
1303
|
+
program.addCommand(chatCommand);
|
|
1375
1304
|
program.hook("preAction", () => {
|
|
1376
1305
|
});
|
|
1377
1306
|
program.configureOutput({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawdvault/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for ClawdVault - Solana token launchpad",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"build": "tsup src/index.ts --format cjs --clean --shims",
|
|
13
|
+
"build": "tsc --noEmit && tsup src/index.ts --format cjs --clean --shims",
|
|
14
14
|
"dev": "tsup src/index.ts --format cjs --watch --shims",
|
|
15
15
|
"clean": "rm -rf dist"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@clawdvault/sdk": "^0.
|
|
18
|
+
"@clawdvault/sdk": "^0.3.0",
|
|
19
19
|
"@solana/spl-token": "^0.4.0",
|
|
20
20
|
"@solana/web3.js": "^1.91.0",
|
|
21
|
+
"bip39": "^3.1.0",
|
|
21
22
|
"bs58": "^5.0.0",
|
|
22
23
|
"chalk": "^4.1.2",
|
|
23
24
|
"commander": "^12.0.0",
|