@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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/aether-cli-1.0.0.tgz +0 -0
  4. package/aether-cli-1.8.0.tgz +0 -0
  5. package/aether-hub-1.0.5.tgz +0 -0
  6. package/aether-hub-1.1.8.tgz +0 -0
  7. package/aether-hub-1.2.1.tgz +0 -0
  8. package/commands/account.js +280 -0
  9. package/commands/apy.js +499 -0
  10. package/commands/balance.js +241 -0
  11. package/commands/blockhash.js +181 -0
  12. package/commands/broadcast.js +387 -0
  13. package/commands/claim.js +490 -0
  14. package/commands/config.js +851 -0
  15. package/commands/delegations.js +582 -0
  16. package/commands/doctor.js +769 -0
  17. package/commands/emergency.js +667 -0
  18. package/commands/epoch.js +275 -0
  19. package/commands/fees.js +276 -0
  20. package/commands/index.js +78 -0
  21. package/commands/info.js +495 -0
  22. package/commands/init.js +816 -0
  23. package/commands/install.js +666 -0
  24. package/commands/kyc.js +272 -0
  25. package/commands/logs.js +315 -0
  26. package/commands/monitor.js +431 -0
  27. package/commands/multisig.js +701 -0
  28. package/commands/network.js +429 -0
  29. package/commands/nft.js +857 -0
  30. package/commands/ping.js +266 -0
  31. package/commands/price.js +253 -0
  32. package/commands/rewards.js +931 -0
  33. package/commands/sdk-test.js +477 -0
  34. package/commands/sdk.js +656 -0
  35. package/commands/slot.js +155 -0
  36. package/commands/snapshot.js +470 -0
  37. package/commands/stake-info.js +139 -0
  38. package/commands/stake-positions.js +205 -0
  39. package/commands/stake.js +516 -0
  40. package/commands/stats.js +396 -0
  41. package/commands/status.js +327 -0
  42. package/commands/supply.js +391 -0
  43. package/commands/tps.js +238 -0
  44. package/commands/transfer.js +495 -0
  45. package/commands/tx-history.js +346 -0
  46. package/commands/unstake.js +597 -0
  47. package/commands/validator-info.js +657 -0
  48. package/commands/validator-register.js +593 -0
  49. package/commands/validator-start.js +323 -0
  50. package/commands/validator-status.js +227 -0
  51. package/commands/validators.js +626 -0
  52. package/commands/wallet.js +1570 -0
  53. package/index.js +593 -0
  54. package/lib/errors.js +398 -0
  55. package/package.json +76 -0
  56. package/sdk/README.md +210 -0
  57. package/sdk/index.js +1639 -0
  58. package/sdk/package.json +34 -0
  59. package/sdk/rpc.js +254 -0
  60. package/sdk/test.js +85 -0
  61. package/test/doctor.test.js +76 -0
  62. package/validator-identity.json +4 -0
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli tx-history
4
+ *
5
+ * Fetch and display transaction history for an Aether wallet address.
6
+ * Shows recent transactions with type, amount, timestamp, fee, and status.
7
+ *
8
+ * Usage:
9
+ * aether tx history --address <addr> [--limit <n>] [--json] [--rpc <url>]
10
+ * aether history --address <addr> [--limit <n>] [--json]
11
+ *
12
+ * Examples:
13
+ * aether tx history --address ATHxxx
14
+ * aether tx history --address ATHxxx --limit 50 --json
15
+ * aether history --address ATHxxx --rpc https://rpc.aether.io
16
+ */
17
+
18
+ const path = require('path');
19
+
20
+ // Import SDK for real blockchain RPC calls
21
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
22
+ const aether = require(sdkPath);
23
+
24
+ // ANSI colours
25
+ const C = {
26
+ reset: '\x1b[0m',
27
+ bright: '\x1b[1m',
28
+ dim: '\x1b[2m',
29
+ red: '\x1b[31m',
30
+ green: '\x1b[32m',
31
+ yellow: '\x1b[33m',
32
+ cyan: '\x1b[36m',
33
+ magenta: '\x1b[35m',
34
+ };
35
+
36
+ const CLI_VERSION = '1.0.0';
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // SDK helpers - Real blockchain RPC calls via Aether SDK
40
+ // ---------------------------------------------------------------------------
41
+
42
+ function getDefaultRpc() {
43
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
44
+ }
45
+
46
+ function createClient(rpcUrl) {
47
+ return new aether.AetherClient({ rpcUrl });
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Argument parsing
52
+ // ---------------------------------------------------------------------------
53
+
54
+ function parseArgs() {
55
+ const args = process.argv.slice(2);
56
+ const result = { address: null, limit: 20, json: false, rpc: null };
57
+
58
+ for (let i = 0; i < args.length; i++) {
59
+ const arg = args[i];
60
+ if ((arg === '--address' || arg === '-a') && args[i + 1] && !args[i + 1].startsWith('-')) {
61
+ result.address = args[i + 1];
62
+ i++;
63
+ } else if ((arg === '--limit' || arg === '-l') && args[i + 1] && !args[i + 1].startsWith('-')) {
64
+ result.limit = parseInt(args[i + 1], 10);
65
+ i++;
66
+ } else if (arg === '--json' || arg === '--json-output') {
67
+ result.json = true;
68
+ } else if (arg === '--rpc' && args[i + 1] && !args[i + 1].startsWith('-')) {
69
+ result.rpc = args[i + 1];
70
+ i++;
71
+ } else if (arg === '--help' || arg === '-h') {
72
+ result.help = true;
73
+ }
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Formatting helpers
81
+ // ---------------------------------------------------------------------------
82
+
83
+ function formatAether(lamports) {
84
+ if (lamports == null) return '—';
85
+ const aeth = lamports / 1e9;
86
+ if (aeth === 0) return '0 AETH';
87
+ return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
88
+ }
89
+
90
+ function formatTimestamp(slot, blockTime) {
91
+ if (!blockTime) return '—';
92
+ try {
93
+ const d = new Date(blockTime * 1000);
94
+ return d.toISOString().replace('T', ' ').slice(0, 19);
95
+ } catch {
96
+ return String(blockTime);
97
+ }
98
+ }
99
+
100
+ function shortAddress(addr) {
101
+ if (!addr) return '—';
102
+ if (addr.length <= 16) return addr;
103
+ return addr.slice(0, 8) + '…' + addr.slice(-6);
104
+ }
105
+
106
+ function formatTxType(type) {
107
+ const t = (type || 'unknown').toLowerCase();
108
+ if (t.includes('transfer') || t.includes('send') || t.includes('payment')) return { label: 'TRANSFER', color: C.cyan };
109
+ if (t.includes('stake') || t.includes('delegate')) return { label: 'STAKE', color: C.green };
110
+ if (t.includes('unstake') || t.includes('deactivate')) return { label: 'UNSTAKE', color: C.yellow };
111
+ if (t.includes('reward') || t.includes('mint')) return { label: 'REWARD', color: C.magenta };
112
+ if (t.includes('vote')) return { label: 'VOTE', color: C.bright };
113
+ if (t.includes('create') || t.includes('initialize')) return { label: 'CREATE', color: C.bright };
114
+ if (t.includes('burn')) return { label: 'BURN', color: C.red };
115
+ return { label: type ? type.toUpperCase().slice(0, 10) : 'TX', color: C.dim };
116
+ }
117
+
118
+ function formatStatus(status) {
119
+ if (!status) return { label: '—', color: C.dim };
120
+ const s = status.toLowerCase();
121
+ if (s === 'success' || s === 'finalized' || s === 'confirmed') {
122
+ return { label: '✓ OK', color: C.green };
123
+ }
124
+ if (s === 'pending' || s === 'processing') {
125
+ return { label: '⏳', color: C.yellow };
126
+ }
127
+ if (s === 'failed') {
128
+ return { label: '✗ FAIL', color: C.red };
129
+ }
130
+ return { label: status.slice(0, 8), color: C.dim };
131
+ }
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // Parse a transaction result into a normalized display object
135
+ // ---------------------------------------------------------------------------
136
+
137
+ function parseTransaction(txResult) {
138
+ const blockTime = txResult.blockTime;
139
+ const slot = txResult.slot;
140
+ const status = txResult.status || 'confirmed';
141
+
142
+ let txType = txResult.tx_type || txResult.type || 'unknown';
143
+ let amount = 0;
144
+ let fee = txResult.fee || 0;
145
+ let fromAddr = txResult.signer || null;
146
+ let toAddr = null;
147
+ let memo = txResult.memo || null;
148
+
149
+ // Parse payload for details
150
+ if (txResult.payload) {
151
+ const payload = txResult.payload;
152
+ if (payload.amount !== undefined) {
153
+ amount = Number(payload.amount);
154
+ }
155
+ if (payload.recipient) {
156
+ toAddr = payload.recipient;
157
+ }
158
+ if (payload.validator) {
159
+ toAddr = payload.validator;
160
+ txType = 'stake';
161
+ }
162
+ if (payload.stake_account) {
163
+ fromAddr = payload.stake_account;
164
+ txType = 'unstake';
165
+ }
166
+ }
167
+
168
+ return {
169
+ signature: txResult.signature,
170
+ slot,
171
+ blockTime,
172
+ status,
173
+ type: txType,
174
+ amount,
175
+ fee,
176
+ from: fromAddr,
177
+ to: toAddr,
178
+ memo,
179
+ };
180
+ }
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // Display
184
+ // ---------------------------------------------------------------------------
185
+
186
+ function displayTxTable(txs) {
187
+ // Header
188
+ console.log(
189
+ `\n${C.bright}${C.cyan} ╔══════════════════════════════════════════════════════════════════════════════════════════╗${C.reset}\n` +
190
+ ` ║${C.bright} ${C.cyan}Transaction History${C.reset}${C.bright} ║${C.reset}\n` +
191
+ ` ${C.cyan}╚══════════════════════════════════════════════════════════════════════════════════════════${C.reset}`
192
+ );
193
+
194
+ if (txs.length === 0) {
195
+ console.log(`\n ${C.yellow}No transactions found for this address.${C.reset}\n`);
196
+ return;
197
+ }
198
+
199
+ console.log(
200
+ ` ${C.dim}┌────┬──────────────────────┬──────────┬───────────────┬────────────┬────────┬───────────┐${C.reset}\n` +
201
+ ` ${C.dim}│ # │ ${C.reset}Timestamp ${C.dim}│ ${C.reset}Type ${C.dim}│ ${C.reset}Amount ${C.dim}│ ${C.reset}From ${C.dim}│ ${C.reset}To ${C.dim}│ ${C.reset}Status ${C.dim}│${C.reset}`
202
+ );
203
+
204
+ for (let i = 0; i < txs.length; i++) {
205
+ const tx = txs[i];
206
+ const num = String(i + 1).padStart(3);
207
+ const time = formatTimestamp(tx.blockTime).slice(0, 19).padEnd(19);
208
+ const typeInfo = formatTxType(tx.type);
209
+ const type = typeInfo.label.padEnd(10);
210
+ const amt = tx.amount > 0 ? formatAether(tx.amount).padEnd(13) : '—'.padEnd(13);
211
+ const from = shortAddress(tx.from || '').padEnd(11);
212
+ const to = shortAddress(tx.to || '').padEnd(11);
213
+ const statusInfo = formatStatus(tx.status);
214
+ const status = statusInfo.label;
215
+
216
+ const bgAlt = (i % 2 === 0) ? '' : C.dim;
217
+ const reset = C.reset + bgAlt;
218
+
219
+ console.log(
220
+ ` ${bgAlt}${C.dim}├────┼──────────────────────┼──────────┼───────────────┼────────────┼────────┼───────────┤${reset}`
221
+ );
222
+ console.log(
223
+ ` ${bgAlt}${C.dim}│${reset} ${num} ${bgAlt}${C.dim}│ ${reset}${time} ${bgAlt}${C.dim}│ ${reset}${typeInfo.color}${type}${reset} ${bgAlt}${C.dim}│ ${reset}${amt} ${bgAlt}${C.dim}│ ${reset}${from} ${bgAlt}${C.dim}│ ${reset}${to} ${bgAlt}${C.dim}│ ${reset}${statusInfo.color}${status}${reset}${bgAlt} │${reset}`
224
+ );
225
+ }
226
+
227
+ console.log(
228
+ ` ${C.dim}└────┴──────────────────────┴──────────┴───────────────┴────────────┴────────┴───────────┘${C.reset}`
229
+ );
230
+
231
+ // Summary
232
+ const totalVolume = txs.reduce((sum, tx) => sum + tx.amount, 0);
233
+ const successCount = txs.filter(tx => tx.status !== 'failed').length;
234
+ console.log(`\n ${C.dim} ${txs.length} transactions · Total volume: ${C.reset}${C.bright}${formatAether(totalVolume)}${C.reset} ${C.dim}· Success: ${C.reset}${C.green}${successCount}/${txs.length}${C.reset} ${C.dim}· Failed: ${C.reset}${C.red}${(txs.length - successCount)}${C.reset}\n`);
235
+ }
236
+
237
+ function displayJson(txs, meta) {
238
+ console.log(JSON.stringify({
239
+ address: meta.address,
240
+ rpc: meta.rpc,
241
+ limit: meta.limit,
242
+ transaction_count: txs.length,
243
+ total_volume_lamports: txs.reduce((sum, tx) => sum + tx.amount, 0),
244
+ transactions: txs.map(tx => ({
245
+ signature: tx.signature,
246
+ slot: tx.slot,
247
+ timestamp: tx.blockTime ? new Date(tx.blockTime * 1000).toISOString() : null,
248
+ type: tx.type,
249
+ amount_lamports: tx.amount,
250
+ amount_aeth: (tx.amount / 1e9).toFixed(9),
251
+ fee_lamports: tx.fee,
252
+ from: tx.from,
253
+ to: tx.to,
254
+ memo: tx.memo,
255
+ status: tx.status,
256
+ })),
257
+ }, null, 2));
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Main
262
+ // ---------------------------------------------------------------------------
263
+
264
+ async function main() {
265
+ const opts = parseArgs();
266
+ // Shift args if this module was called via index.js (extra argument in argv)
267
+ // Detect by checking if first non-option arg after 'tx' is the command name
268
+ const args = process.argv.slice(2);
269
+ const txIdx = args.indexOf('tx');
270
+ const historyIdx = args.indexOf('history');
271
+ if (txIdx !== -1 && historyIdx !== -1) {
272
+ // Already parsed correctly above - no action needed
273
+ }
274
+
275
+ if (opts.help) {
276
+ console.log(`
277
+ ${C.bright}${C.cyan}tx-history${C.reset} — Fetch and display transaction history for an Aether address
278
+
279
+ ${C.bright}USAGE${C.reset}
280
+ aether tx history --address <addr> [--limit <n>] [--json] [--rpc <url>]
281
+ aether history --address <addr> [--limit <n>] [--json]
282
+
283
+ ${C.bright}OPTIONS${C.reset}
284
+ --address <addr> Aether wallet address (ATH...)
285
+ --limit <n> Max transactions to fetch (default: 20, max: 100)
286
+ --json Output raw JSON for scripting
287
+ --rpc <url> RPC endpoint (default: AETHER_RPC or http://127.0.0.1:8899)
288
+ --help Show this help
289
+
290
+ ${C.bright}EXAMPLES${C.reset}
291
+ aether tx history --address ATH3abc... --limit 50
292
+ aether tx history --address ATH3abc... --json
293
+ aether history --address ATH3abc... --rpc https://mainnet.aether.io
294
+ `);
295
+ return;
296
+ }
297
+
298
+ if (!opts.address) {
299
+ console.log(` ${C.red}✗ Missing --address${C.reset}\n`);
300
+ console.log(` Usage: aether tx history --address <addr> [--limit <n>] [--json]\n`);
301
+ process.exit(1);
302
+ }
303
+
304
+ const rpcUrl = opts.rpc || getDefaultRpc();
305
+ const limit = Math.min(Math.max(1, opts.limit || 20), 100);
306
+
307
+ if (!opts.json) {
308
+ console.log(`\n${C.bright}${C.cyan} Tx History${C.reset} · ${C.dim}address:${C.reset} ${opts.address} ${C.dim}· ${C.dim}limit:${C.reset} ${limit} ${C.dim}· ${C.dim}rpc:${C.reset} ${rpcUrl}\n`);
309
+ }
310
+
311
+ try {
312
+ // Use SDK for real blockchain RPC calls
313
+ const client = createClient(rpcUrl);
314
+ const history = await client.getTransactionHistory(opts.address, limit);
315
+
316
+ const txs = (history.transactions || []).map(tx => parseTransaction(tx));
317
+
318
+ if (opts.json) {
319
+ displayJson(txs, { address: opts.address, rpc: rpcUrl, limit });
320
+ } else {
321
+ displayTxTable(txs);
322
+ }
323
+
324
+ } catch (err) {
325
+ if (opts.json) {
326
+ console.log(JSON.stringify({
327
+ error: err.message,
328
+ address: opts.address,
329
+ rpc: rpcUrl,
330
+ }, null, 2));
331
+ } else {
332
+ console.log(`\n ${C.red}✗ Failed to fetch transaction history:${C.reset} ${err.message}\n`);
333
+ console.log(` ${C.dim} Is your validator running? RPC: ${rpcUrl}${C.reset}`);
334
+ console.log(` ${C.dim} Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
335
+ }
336
+ process.exit(1);
337
+ }
338
+ }
339
+
340
+ // Export for CLI integration
341
+ module.exports = { txHistoryCommand: main };
342
+
343
+ // Run if called directly
344
+ if (require.main === module) {
345
+ main();
346
+ }