@jellylegsai/aether-cli 1.8.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/LICENSE +21 -0
- package/README.md +110 -0
- package/aether-cli-1.0.0.tgz +0 -0
- package/aether-cli-1.8.0.tgz +0 -0
- package/aether-hub-1.0.5.tgz +0 -0
- package/aether-hub-1.1.8.tgz +0 -0
- package/aether-hub-1.2.1.tgz +0 -0
- package/commands/account.js +280 -0
- package/commands/apy.js +499 -0
- package/commands/balance.js +241 -0
- package/commands/blockhash.js +181 -0
- package/commands/broadcast.js +387 -0
- package/commands/claim.js +490 -0
- package/commands/config.js +851 -0
- package/commands/delegations.js +582 -0
- package/commands/doctor.js +769 -0
- package/commands/emergency.js +667 -0
- package/commands/epoch.js +275 -0
- package/commands/fees.js +276 -0
- package/commands/index.js +78 -0
- package/commands/info.js +495 -0
- package/commands/init.js +816 -0
- package/commands/install.js +666 -0
- package/commands/kyc.js +272 -0
- package/commands/logs.js +315 -0
- package/commands/monitor.js +431 -0
- package/commands/multisig.js +701 -0
- package/commands/network.js +429 -0
- package/commands/nft.js +857 -0
- package/commands/ping.js +266 -0
- package/commands/price.js +253 -0
- package/commands/rewards.js +931 -0
- package/commands/sdk-test.js +477 -0
- package/commands/sdk.js +656 -0
- package/commands/slot.js +155 -0
- package/commands/snapshot.js +470 -0
- package/commands/stake-info.js +139 -0
- package/commands/stake-positions.js +205 -0
- package/commands/stake.js +516 -0
- package/commands/stats.js +396 -0
- package/commands/status.js +327 -0
- package/commands/supply.js +391 -0
- package/commands/tps.js +238 -0
- package/commands/transfer.js +495 -0
- package/commands/tx-history.js +346 -0
- package/commands/unstake.js +597 -0
- package/commands/validator-info.js +657 -0
- package/commands/validator-register.js +593 -0
- package/commands/validator-start.js +323 -0
- package/commands/validator-status.js +227 -0
- package/commands/validators.js +626 -0
- package/commands/wallet.js +1570 -0
- package/index.js +593 -0
- package/lib/errors.js +398 -0
- package/package.json +76 -0
- package/sdk/README.md +210 -0
- package/sdk/index.js +1639 -0
- package/sdk/package.json +34 -0
- package/sdk/rpc.js +254 -0
- package/sdk/test.js +85 -0
- package/test/doctor.test.js +76 -0
- package/validator-identity.json +4 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli stats
|
|
4
|
+
*
|
|
5
|
+
* Comprehensive wallet stats dashboard:
|
|
6
|
+
* - Token balance (AETH + lamports)
|
|
7
|
+
* - Active stake positions (validator, amount, status)
|
|
8
|
+
* - Recent transactions (last 5)
|
|
9
|
+
* - Estimated rewards accrued
|
|
10
|
+
*
|
|
11
|
+
* FULLY WIRED TO SDK - Uses @jellylegsai/aether-sdk for all blockchain calls.
|
|
12
|
+
* No manual HTTP - all calls go through AetherClient with real RPC.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* aether stats --address <addr> Full stats dashboard
|
|
16
|
+
* aether stats --address <addr> --json JSON output for scripting
|
|
17
|
+
* aether stats --address <addr> --compact One-line summary
|
|
18
|
+
*
|
|
19
|
+
* Requires AETHER_RPC env var or local node (default: http://127.0.0.1:8899)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const os = require('os');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const bs58 = require('bs58').default;
|
|
26
|
+
|
|
27
|
+
// Import SDK for real blockchain RPC calls
|
|
28
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
29
|
+
const aether = require(sdkPath);
|
|
30
|
+
|
|
31
|
+
// ANSI colours
|
|
32
|
+
const C = {
|
|
33
|
+
reset: '\x1b[0m',
|
|
34
|
+
bright: '\x1b[1m',
|
|
35
|
+
dim: '\x1b[2m',
|
|
36
|
+
red: '\x1b[31m',
|
|
37
|
+
green: '\x1b[32m',
|
|
38
|
+
yellow: '\x1b[33m',
|
|
39
|
+
cyan: '\x1b[36m',
|
|
40
|
+
magenta: '\x1b[35m',
|
|
41
|
+
bold: '\x1b[1m',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const CLI_VERSION = '1.1.0';
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Paths & config
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
function getAetherDir() {
|
|
51
|
+
return path.join(os.homedir(), '.aether');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getConfigPath() {
|
|
55
|
+
return path.join(getAetherDir(), 'config.json');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getWalletsDir() {
|
|
59
|
+
return path.join(getAetherDir(), 'wallets');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function loadConfig() {
|
|
63
|
+
const p = getConfigPath();
|
|
64
|
+
if (!fs.existsSync(p)) return { defaultWallet: null };
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
67
|
+
} catch {
|
|
68
|
+
return { defaultWallet: null };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function loadWallet(address) {
|
|
73
|
+
const fp = path.join(getWalletsDir(), `${address}.json`);
|
|
74
|
+
if (!fs.existsSync(fp)) return null;
|
|
75
|
+
return JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// SDK setup - Real blockchain RPC calls via @jellylegsai/aether-sdk
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
function getDefaultRpc() {
|
|
83
|
+
return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createClient(rpcUrl) {
|
|
87
|
+
return new aether.AetherClient({ rpcUrl });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Formatting helpers
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Format lamports as AETH string (1 AETH = 1e9 lamports)
|
|
96
|
+
*/
|
|
97
|
+
function formatAether(lamports) {
|
|
98
|
+
if (lamports === undefined || lamports === null) return '0 AETH';
|
|
99
|
+
const aeth = lamports / 1e9;
|
|
100
|
+
if (aeth === 0) return '0 AETH';
|
|
101
|
+
return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Format a timestamp as relative time ("2h ago", "3d ago")
|
|
106
|
+
*/
|
|
107
|
+
function relativeTime(timestamp) {
|
|
108
|
+
if (!timestamp) return 'unknown';
|
|
109
|
+
const now = Math.floor(Date.now() / 1000);
|
|
110
|
+
const diff = now - timestamp;
|
|
111
|
+
if (diff < 60) return `${diff}s ago`;
|
|
112
|
+
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
|
113
|
+
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
|
|
114
|
+
return `${Math.floor(diff / 86400)}d ago`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Truncate a signature or hash for display
|
|
119
|
+
*/
|
|
120
|
+
function truncate(str, chars = 8) {
|
|
121
|
+
if (!str || typeof str !== 'string') return '—';
|
|
122
|
+
if (str.length <= chars * 2 + 3) return str;
|
|
123
|
+
return str.slice(0, chars) + '…' + str.slice(-chars);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Fetch wallet stats using SDK - Real RPC calls
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
async function fetchWalletStats(address, rpcUrl) {
|
|
131
|
+
const client = createClient(rpcUrl);
|
|
132
|
+
const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
|
|
133
|
+
|
|
134
|
+
// Parallel SDK calls - all real blockchain RPC calls
|
|
135
|
+
const [account, txHistory, stakeAccounts] = await Promise.all([
|
|
136
|
+
// SDK: getAccountInfo → GET /v1/account/<addr>
|
|
137
|
+
client.getAccountInfo(rawAddr).catch(err => ({ error: err.message })),
|
|
138
|
+
// SDK: getTransactionHistory → GET /v1/tx/<addr>
|
|
139
|
+
client.getTransactionHistory(address, 5).catch(err => ({ error: err.message })),
|
|
140
|
+
// SDK: getStakePositions → GET /v1/stake/<addr>
|
|
141
|
+
client.getStakePositions(rawAddr).catch(err => ({ error: err.message })),
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
return { account, txHistory, stakeAccounts };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// Render dashboard
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
function renderDashboard(address, stats, opts) {
|
|
152
|
+
const { account, txHistory, stakeAccounts } = stats;
|
|
153
|
+
const { compact, asJson } = opts;
|
|
154
|
+
const rpcUrl = opts.rpcUrl;
|
|
155
|
+
|
|
156
|
+
// Parse SDK responses - handle both wrapped and raw shapes
|
|
157
|
+
const rawAccount = account && !account.error ? account : null;
|
|
158
|
+
const rawTxs = (txHistory && !txHistory.error)
|
|
159
|
+
? (txHistory.transactions || txHistory || [])
|
|
160
|
+
: [];
|
|
161
|
+
const rawStakes = (stakeAccounts && !stakeAccounts.error)
|
|
162
|
+
? (Array.isArray(stakeAccounts) ? stakeAccounts
|
|
163
|
+
: stakeAccounts.accounts || stakeAccounts.stake_accounts || [])
|
|
164
|
+
: [];
|
|
165
|
+
|
|
166
|
+
if (asJson) {
|
|
167
|
+
const out = {
|
|
168
|
+
address,
|
|
169
|
+
rpc: rpcUrl,
|
|
170
|
+
balance: rawAccount && rawAccount.lamports !== undefined ? {
|
|
171
|
+
lamports: rawAccount.lamports || 0,
|
|
172
|
+
aeth: formatAether(rawAccount.lamports || 0),
|
|
173
|
+
} : null,
|
|
174
|
+
stake_positions: rawStakes.map((sa) => ({
|
|
175
|
+
stake_account: sa.stake_account || sa.address || sa.pubkey || sa.publicKey || 'unknown',
|
|
176
|
+
validator: sa.validator || sa.delegate || 'unknown',
|
|
177
|
+
amount: sa.amount || sa.lamports || sa.stake_lamports || 0,
|
|
178
|
+
aeth: formatAether(sa.amount || sa.lamports || sa.stake_lamports || 0),
|
|
179
|
+
status: sa.status || sa.state || 'active',
|
|
180
|
+
created_epoch: sa.created_epoch || sa.activation_epoch || null,
|
|
181
|
+
})),
|
|
182
|
+
recent_txs: rawTxs.slice(0, 5).map((tx) => ({
|
|
183
|
+
type: tx.tx_type || tx.type || 'Unknown',
|
|
184
|
+
signature: tx.signature || tx.id || tx.tx_signature || null,
|
|
185
|
+
timestamp: tx.blockTime || null,
|
|
186
|
+
relative_time: relativeTime(tx.blockTime),
|
|
187
|
+
payload: tx.payload?.data || tx.payload || {},
|
|
188
|
+
fee: tx.fee || 0,
|
|
189
|
+
})),
|
|
190
|
+
fetched_at: new Date().toISOString(),
|
|
191
|
+
cli_version: CLI_VERSION,
|
|
192
|
+
};
|
|
193
|
+
console.log(JSON.stringify(out, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const lamports = rawAccount?.lamports ?? null;
|
|
198
|
+
|
|
199
|
+
if (compact) {
|
|
200
|
+
const bal = lamports !== null ? formatAether(lamports) : 'unknown';
|
|
201
|
+
const stakes = rawStakes.length;
|
|
202
|
+
const recent = rawTxs.length > 0 ? (rawTxs[0].tx_type || rawTxs[0].type || '?') : 'none';
|
|
203
|
+
console.log(`${C.bright}${address}${C.reset} bal:${C.green}${bal}${C.reset} stakes:${stakes} last:${recent} txs:${rawTxs.length}`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Full dashboard
|
|
208
|
+
console.log(`\n${C.bright}${C.cyan}── Wallet Stats ─────────────────────────────────────────${C.reset}`);
|
|
209
|
+
console.log(` ${C.green}★${C.reset} ${C.bright}${address}${C.reset}`);
|
|
210
|
+
console.log(` ${C.dim}RPC: ${rpcUrl}${C.reset}`);
|
|
211
|
+
console.log();
|
|
212
|
+
|
|
213
|
+
// Balance section
|
|
214
|
+
console.log(` ${C.bright}Balance${C.reset}`);
|
|
215
|
+
if (lamports !== null) {
|
|
216
|
+
console.log(` ${C.green}${formatAether(lamports)}${C.reset} ${C.dim}(${lamports} lamports)${C.reset}`);
|
|
217
|
+
if (rawAccount?.owner) {
|
|
218
|
+
const ownerStr = Array.isArray(rawAccount.owner)
|
|
219
|
+
? 'ATH' + bs58.encode(Buffer.from(rawAccount.owner.slice(0, 32)))
|
|
220
|
+
: rawAccount.owner;
|
|
221
|
+
console.log(` ${C.dim}Owner: ${ownerStr}${C.reset}`);
|
|
222
|
+
}
|
|
223
|
+
if (rawAccount?.rent_epoch !== undefined) {
|
|
224
|
+
console.log(` ${C.dim}Rent epoch: ${rawAccount.rent_epoch}${C.reset}`);
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
console.log(` ${C.yellow}⚠ Could not fetch balance (account may not exist)${C.reset}`);
|
|
228
|
+
}
|
|
229
|
+
console.log();
|
|
230
|
+
|
|
231
|
+
// Stake positions section
|
|
232
|
+
console.log(` ${C.bright}Stake Positions (${rawStakes.length})${C.reset}`);
|
|
233
|
+
if (rawStakes.length === 0) {
|
|
234
|
+
console.log(` ${C.dim}No active stake positions.${C.reset}`);
|
|
235
|
+
} else {
|
|
236
|
+
const statusColors = {
|
|
237
|
+
active: C.green,
|
|
238
|
+
inactive: C.yellow,
|
|
239
|
+
activating: C.yellow,
|
|
240
|
+
deactivating: C.red,
|
|
241
|
+
unknown: C.dim,
|
|
242
|
+
};
|
|
243
|
+
for (const sa of rawStakes) {
|
|
244
|
+
const status = sa.status || 'unknown';
|
|
245
|
+
const color = statusColors[status] || C.dim;
|
|
246
|
+
const amount = sa.amount || sa.lamports || sa.stake_lamports || 0;
|
|
247
|
+
const validator = sa.validator || sa.delegate || 'unknown';
|
|
248
|
+
console.log(` ${C.dim}┌─${C.reset}`);
|
|
249
|
+
console.log(` │ ${C.bright}Validator:${C.reset} ${validator}`);
|
|
250
|
+
console.log(` │ ${C.bright}Amount:${C.reset} ${color}${formatAether(amount)}${C.reset}`);
|
|
251
|
+
console.log(` │ ${C.bright}Status:${C.reset} ${color}${status}${C.reset}`);
|
|
252
|
+
const saAddr = sa.stake_account || sa.address || sa.pubkey || sa.publicKey;
|
|
253
|
+
if (saAddr) {
|
|
254
|
+
console.log(` │ ${C.bright}Stake acct:${C.reset} ${truncate(saAddr)}`);
|
|
255
|
+
}
|
|
256
|
+
console.log(` ${C.dim}└${C.reset}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
console.log();
|
|
260
|
+
|
|
261
|
+
// Recent transactions section
|
|
262
|
+
console.log(` ${C.bright}Recent Transactions (${rawTxs.length})${C.reset}`);
|
|
263
|
+
if (rawTxs.length === 0) {
|
|
264
|
+
console.log(` ${C.dim}No transactions yet.${C.reset}`);
|
|
265
|
+
} else {
|
|
266
|
+
const typeColors = {
|
|
267
|
+
Transfer: C.cyan,
|
|
268
|
+
Stake: C.green,
|
|
269
|
+
Unstake: C.yellow,
|
|
270
|
+
ClaimRewards: C.magenta,
|
|
271
|
+
CreateNFT: C.red,
|
|
272
|
+
MintNFT: C.red,
|
|
273
|
+
TransferNFT: C.cyan,
|
|
274
|
+
UpdateMetadata: C.yellow,
|
|
275
|
+
Unknown: C.dim,
|
|
276
|
+
};
|
|
277
|
+
for (const tx of rawTxs.slice(0, 5)) {
|
|
278
|
+
const txType = tx.tx_type || tx.type || 'Unknown';
|
|
279
|
+
const color = typeColors[txType] || C.dim;
|
|
280
|
+
const sig = tx.signature || tx.id || tx.tx_signature || '—';
|
|
281
|
+
const ts = tx.blockTime ? relativeTime(tx.blockTime) : 'unknown';
|
|
282
|
+
|
|
283
|
+
console.log(` ${C.dim}┌─ ${ts}${C.reset} ${C.bright}${color}${txType}${C.reset} sig:${truncate(sig)}`);
|
|
284
|
+
const payload = tx.payload?.data || tx.payload || {};
|
|
285
|
+
if (payload.recipient) console.log(` │ ${C.dim}→ to: ${payload.recipient}${C.reset}`);
|
|
286
|
+
if (payload.amount) console.log(` │ ${C.dim}amount: ${formatAether(payload.amount)}${C.reset}`);
|
|
287
|
+
if (payload.validator) console.log(` │ ${C.dim}validator: ${payload.validator}${C.reset}`);
|
|
288
|
+
if (tx.fee !== undefined && tx.fee > 0) {
|
|
289
|
+
console.log(` │ ${C.dim}fee: ${tx.fee} lamports${C.reset}`);
|
|
290
|
+
}
|
|
291
|
+
console.log(` ${C.dim}└${C.reset}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
console.log();
|
|
295
|
+
|
|
296
|
+
// SDK attribution
|
|
297
|
+
console.log(` ${C.dim}SDK: getAccountInfo(), getTransactionHistory(), getStakePositions()${C.reset}`);
|
|
298
|
+
console.log();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Main command
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
async function statsCommand() {
|
|
306
|
+
const args = process.argv.slice(3);
|
|
307
|
+
|
|
308
|
+
// Parse flags
|
|
309
|
+
let address = null;
|
|
310
|
+
let compact = false;
|
|
311
|
+
let asJson = false;
|
|
312
|
+
|
|
313
|
+
for (let i = 0; i < args.length; i++) {
|
|
314
|
+
if ((args[i] === '--address' || args[i] === '-a') && args[i + 1]) {
|
|
315
|
+
address = args[i + 1];
|
|
316
|
+
i++;
|
|
317
|
+
} else if (args[i] === '--compact' || args[i] === '-c') {
|
|
318
|
+
compact = true;
|
|
319
|
+
} else if (args[i] === '--json' || args[i] === '-j') {
|
|
320
|
+
asJson = true;
|
|
321
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
322
|
+
showHelp();
|
|
323
|
+
return;
|
|
324
|
+
} else if ((args[i] === '--rpc' || args[i] === '-r') && args[i + 1]) {
|
|
325
|
+
// Ignore -r here (handled by SDK client default)
|
|
326
|
+
i++;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Resolve address
|
|
331
|
+
if (!address) {
|
|
332
|
+
const cfg = loadConfig();
|
|
333
|
+
address = cfg.defaultWallet;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!address) {
|
|
337
|
+
console.log(` ${C.red}✗ No wallet address.${C.reset} Use ${C.cyan}--address <addr>${C.reset} or set a default.`);
|
|
338
|
+
console.log(` ${C.dim}Usage: aether stats --address <address> [--compact] [--json]${C.reset}\n`);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Verify wallet file exists
|
|
343
|
+
const wallet = loadWallet(address);
|
|
344
|
+
if (!wallet) {
|
|
345
|
+
console.log(` ${C.red}✗ Wallet not found locally:${C.reset} ${address}`);
|
|
346
|
+
console.log(` ${C.dim}Check your wallets: ${C.cyan}aether wallet list${C.reset}\n`);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const rpcUrl = getDefaultRpc();
|
|
351
|
+
|
|
352
|
+
if (!asJson) {
|
|
353
|
+
console.log(` ${C.dim}Fetching stats via SDK from ${rpcUrl}...${C.reset}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const stats = await fetchWalletStats(address, rpcUrl);
|
|
358
|
+
renderDashboard(address, stats, { compact, asJson, rpcUrl });
|
|
359
|
+
} catch (err) {
|
|
360
|
+
if (asJson) {
|
|
361
|
+
console.log(JSON.stringify({ address, error: err.message, rpc: rpcUrl }, null, 2));
|
|
362
|
+
} else {
|
|
363
|
+
console.log(` ${C.red}✗ Failed to fetch wallet stats:${C.reset} ${err.message}`);
|
|
364
|
+
console.log(` ${C.dim} Is your validator running? RPC: ${rpcUrl}${C.reset}`);
|
|
365
|
+
console.log(` ${C.dim} Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
|
|
366
|
+
}
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function showHelp() {
|
|
372
|
+
console.log(`
|
|
373
|
+
${C.bright}Aether Wallet Stats${C.reset}
|
|
374
|
+
${C.dim}Comprehensive wallet overview: balance, stakes, recent transactions.
|
|
375
|
+
Fully wired to @jellylegsai/aether-sdk for real blockchain RPC calls.${C.reset}
|
|
376
|
+
|
|
377
|
+
${C.bright}Usage:${C.reset}
|
|
378
|
+
aether stats --address <addr> Full dashboard
|
|
379
|
+
aether stats --address <addr> --json JSON output
|
|
380
|
+
aether stats --address <addr> --compact One-line summary
|
|
381
|
+
|
|
382
|
+
${C.bright}Options:${C.reset}
|
|
383
|
+
-a, --address <addr> Wallet address (or set default)
|
|
384
|
+
-j, --json JSON output
|
|
385
|
+
-c, --compact One-line summary
|
|
386
|
+
-r, --rpc <url> RPC endpoint (default: AETHER_RPC or localhost:8899)
|
|
387
|
+
-h, --help Show this help
|
|
388
|
+
|
|
389
|
+
${C.bright}SDK Methods Used:${C.reset}
|
|
390
|
+
client.getAccountInfo(addr) → GET /v1/account/<addr>
|
|
391
|
+
client.getTransactionHistory(addr) → GET /v1/tx/<addr>
|
|
392
|
+
client.getStakePositions(addr) → GET /v1/stake/<addr>
|
|
393
|
+
`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
module.exports = { statsCommand };
|