@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,387 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli broadcast
4
+ *
5
+ * Broadcast a signed transaction to the Aether network.
6
+ * Fully wired to @jellylegsai/aether-sdk — uses AetherClient for all RPC calls.
7
+ *
8
+ * Accepts a base58-encoded transaction signature or a raw JSON payload.
9
+ * Useful for submitting offline-constructed transactions.
10
+ *
11
+ * Usage:
12
+ * aether broadcast --tx <signature> Broadcast by tx signature
13
+ * aether broadcast --json <payload> Broadcast raw JSON tx payload
14
+ * aether broadcast --file <path> Read tx from a JSON file
15
+ * aether broadcast --rpc <url> Use a specific RPC endpoint
16
+ * aether broadcast --wait Wait for confirmation (max 60s)
17
+ * aether broadcast --json JSON output for scripting
18
+ *
19
+ * SDK Methods Used:
20
+ * - client.sendTransaction(tx) → POST /v1/transaction
21
+ * - client.getSlot() → GET /v1/slot
22
+ * - client.getTransaction(signature) → GET /v1/transaction/<sig>
23
+ *
24
+ * Examples:
25
+ * aether broadcast --tx 5abcdef... # Submit pre-signed tx
26
+ * aether broadcast --json '{"type":"Transfer",...}'
27
+ * aether broadcast --file ./unsigned_tx.json
28
+ * aether broadcast --tx <sig> --wait --json
29
+ */
30
+
31
+ const fs = require('fs');
32
+ const path = require('path');
33
+
34
+ // ANSI colours
35
+ const C = {
36
+ reset: '\x1b[0m',
37
+ bright: '\x1b[1m',
38
+ dim: '\x1b[2m',
39
+ red: '\x1b[31m',
40
+ green: '\x1b[32m',
41
+ yellow: '\x1b[33m',
42
+ cyan: '\x1b[36m',
43
+ };
44
+
45
+ const CLI_VERSION = '1.1.0';
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // SDK Import - uses @jellylegsai/aether-sdk for ALL blockchain RPC calls
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
52
+ const aether = require(sdkPath);
53
+
54
+ function getDefaultRpc() {
55
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
56
+ }
57
+
58
+ function createClient(rpcUrl) {
59
+ return new aether.AetherClient({ rpcUrl });
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Argument parsing
64
+ // ---------------------------------------------------------------------------
65
+
66
+ function parseArgs() {
67
+ const args = process.argv.slice(2);
68
+ const opts = {
69
+ rpc: getDefaultRpc(),
70
+ signature: null,
71
+ jsonPayload: null,
72
+ filePath: null,
73
+ asJson: false,
74
+ wait: false,
75
+ waitTimeoutMs: 60000,
76
+ };
77
+
78
+ for (let i = 0; i < args.length; i++) {
79
+ if (args[i] === '--tx' || args[i] === '-t') {
80
+ opts.signature = args[++i];
81
+ } else if (args[i] === '--json' || args[i] === '-j') {
82
+ opts.jsonPayload = args[++i];
83
+ } else if (args[i] === '--file' || args[i] === '-f') {
84
+ opts.filePath = args[++i];
85
+ } else if (args[i] === '--rpc' || args[i] === '-r') {
86
+ opts.rpc = args[++i];
87
+ } else if (args[i] === '--wait' || args[i] === '-w') {
88
+ opts.wait = true;
89
+ } else if (args[i] === '--json-output') {
90
+ // Backward-compatible alias (used in old code)
91
+ opts.asJson = true;
92
+ } else if (args[i] === '--json') {
93
+ opts.asJson = true;
94
+ } else if (args[i] === '--help' || args[i] === '-h') {
95
+ showHelp();
96
+ process.exit(0);
97
+ }
98
+ }
99
+
100
+ return opts;
101
+ }
102
+
103
+ function showHelp() {
104
+ console.log(`
105
+ ${C.bright}${C.cyan}aether-cli broadcast${C.reset} - Broadcast a Signed Transaction
106
+
107
+ ${C.bright}Usage:${C.reset}
108
+ aether-cli broadcast --tx <signature> Broadcast by base58 signature
109
+ aether-cli broadcast --json <payload> Broadcast inline JSON payload
110
+ aether-cli broadcast --file <path> Read tx from a JSON file
111
+ aether-cli broadcast --rpc <url> Override default RPC
112
+ aether-cli broadcast --wait Poll for confirmation (max 60s)
113
+ aether-cli broadcast --json JSON output for scripting
114
+
115
+ ${C.bright}SDK Methods Used:${C.reset}
116
+ client.sendTransaction(tx) → POST /v1/transaction
117
+ client.getTransaction(sig) → GET /v1/transaction/<sig>
118
+ client.getSlot() → GET /v1/slot
119
+
120
+ ${C.bright}Examples:${C.reset}
121
+ aether-cli broadcast --tx 5abcdef123456... # Submit by signature
122
+ aether-cli broadcast --json '{"type":"Transfer","data":{...}}'
123
+ aether-cli broadcast --file ./my_tx.json
124
+ aether-cli broadcast --tx <sig> --wait --json # Broadcast and wait for confirm
125
+ `.trim());
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Validate transaction payload
130
+ // ---------------------------------------------------------------------------
131
+
132
+ function validateTxPayload(tx) {
133
+ const errors = [];
134
+
135
+ if (!tx) {
136
+ errors.push('Transaction payload is null or empty');
137
+ return errors;
138
+ }
139
+
140
+ // Must have signer
141
+ if (!tx.signer && !tx.from && !tx.pubkey) {
142
+ errors.push('Missing signer field (signer | from | pubkey)');
143
+ }
144
+
145
+ // Must have tx_type or type
146
+ if (!tx.tx_type && !tx.type) {
147
+ errors.push('Missing tx_type or type field');
148
+ }
149
+
150
+ // Must have payload
151
+ if (!tx.payload && !tx.data) {
152
+ errors.push('Missing payload or data field');
153
+ }
154
+
155
+ return errors;
156
+ }
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // Wait for transaction confirmation via SDK
160
+ // Polls getTransaction() until the signature appears on-chain or times out.
161
+ // ---------------------------------------------------------------------------
162
+
163
+ async function waitForConfirmation(client, signature, timeoutMs = 60000, pollIntervalMs = 2000) {
164
+ const start = Date.now();
165
+ let lastResult = null;
166
+
167
+ while (Date.now() - start < timeoutMs) {
168
+ try {
169
+ // SDK call: getTransaction → GET /v1/transaction/<signature>
170
+ const result = await client.getTransaction(signature);
171
+ lastResult = result;
172
+
173
+ // If result has blockTime or slot, tx is confirmed
174
+ if (result && (result.blockTime !== undefined || result.slot !== undefined)) {
175
+ return { confirmed: true, result, waitedMs: Date.now() - start };
176
+ }
177
+ } catch (err) {
178
+ // Transaction not yet visible — expected during confirmation
179
+ lastResult = { error: err.message };
180
+ }
181
+
182
+ await new Promise(r => setTimeout(r, pollIntervalMs));
183
+ }
184
+
185
+ return { confirmed: false, result: lastResult, waitedMs: Date.now() - start };
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // Broadcast logic - uses SDK for all RPC calls
190
+ // ---------------------------------------------------------------------------
191
+
192
+ async function broadcast({ rpc, signature, jsonPayload, filePath, asJson, wait }) {
193
+ const client = createClient(rpc);
194
+
195
+ // Build the tx object from inputs (priority: signature > file > inline JSON)
196
+ let tx = null;
197
+
198
+ if (signature) {
199
+ // Signature-only broadcast: the SDK's sendTransaction accepts { signature }
200
+ tx = { signature };
201
+ } else if (filePath) {
202
+ // Read from file
203
+ const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
204
+ if (!fs.existsSync(absPath)) {
205
+ throw new Error(`File not found: ${absPath}`);
206
+ }
207
+ try {
208
+ tx = JSON.parse(fs.readFileSync(absPath, 'utf8'));
209
+ } catch {
210
+ throw new Error(`Invalid JSON in file: ${absPath}`);
211
+ }
212
+ } else if (jsonPayload) {
213
+ try {
214
+ tx = JSON.parse(jsonPayload);
215
+ } catch {
216
+ throw new Error('Invalid JSON payload provided with --json');
217
+ }
218
+ } else {
219
+ throw new Error('No transaction provided. Use --tx, --json, or --file');
220
+ }
221
+
222
+ // Validate the transaction has required fields
223
+ if (!signature) {
224
+ const validationErrors = validateTxPayload(tx);
225
+ if (validationErrors.length > 0) {
226
+ throw new Error('Invalid transaction payload:\n ' + validationErrors.join('\n '));
227
+ }
228
+ }
229
+
230
+ if (!asJson) {
231
+ console.log(`\n${C.bright}${C.cyan}── Broadcast Transaction ─────────────────────────────────────${C.reset}`);
232
+ console.log(` ${C.dim}SDK: AetherClient → ${rpc}${C.reset}`);
233
+ if (signature) {
234
+ console.log(` ${C.dim}Signature:${C.reset} ${C.cyan}${signature}${C.reset}`);
235
+ } else {
236
+ const txType = tx.tx_type || tx.type || 'Unknown';
237
+ const signer = tx.signer || tx.from || tx.pubkey || 'unknown';
238
+ console.log(` ${C.dim}Type:${C.reset} ${C.cyan}${txType}${C.reset}`);
239
+ console.log(` ${C.dim}Signer:${C.reset} ${C.cyan}${signer}${C.reset}`);
240
+ }
241
+ console.log();
242
+ }
243
+
244
+ // Submit the transaction via SDK
245
+ let result;
246
+ let latencyMs;
247
+ let submittedSig = signature || tx.signature || null;
248
+
249
+ try {
250
+ const start = Date.now();
251
+ // SDK call: sendTransaction → POST /v1/transaction
252
+ result = await client.sendTransaction(tx);
253
+ latencyMs = Date.now() - start;
254
+
255
+ // Capture returned signature if different from input
256
+ if (result && result.signature && !submittedSig) {
257
+ submittedSig = result.signature;
258
+ }
259
+ } catch (err) {
260
+ if (asJson) {
261
+ console.log(JSON.stringify({
262
+ success: false,
263
+ error: err.message,
264
+ rpc,
265
+ cli_version: CLI_VERSION,
266
+ timestamp: new Date().toISOString(),
267
+ }, null, 2));
268
+ } else {
269
+ console.log(` ${C.red}✗ SDK error:${C.reset} ${err.message}`);
270
+ console.log(` ${C.dim} RPC: ${rpc}${C.reset}`);
271
+ console.log(` ${C.dim} SDK Method: client.sendTransaction() → POST /v1/transaction${C.reset}`);
272
+ }
273
+ process.exit(1);
274
+ }
275
+
276
+ const success = result && !result.error && result.accepted !== false;
277
+
278
+ if (asJson) {
279
+ console.log(JSON.stringify({
280
+ success,
281
+ accepted: result?.accepted ?? null,
282
+ signature: result?.signature ?? submittedSig ?? null,
283
+ slot: result?.slot ?? null,
284
+ error: result?.error ?? null,
285
+ rpc,
286
+ sdk_method: 'client.sendTransaction()',
287
+ rpc_endpoint: 'POST /v1/transaction',
288
+ latency_ms: latencyMs,
289
+ cli_version: CLI_VERSION,
290
+ timestamp: new Date().toISOString(),
291
+ }, null, 2));
292
+
293
+ if (wait && submittedSig && success) {
294
+ process.stdout.write(JSON.stringify({ confirming: true, signature: submittedSig }) + '\n');
295
+ const confirmResult = await waitForConfirmation(client, submittedSig, 60000);
296
+ console.log(JSON.stringify({
297
+ confirmed: confirmResult.confirmed,
298
+ signature: submittedSig,
299
+ confirm_waited_ms: confirmResult.waitedMs,
300
+ slot: confirmResult.result?.slot ?? null,
301
+ blocktime: confirmResult.result?.blockTime ?? null,
302
+ }, null, 2));
303
+ }
304
+ return;
305
+ }
306
+
307
+ if (success) {
308
+ const sig = result?.signature ?? submittedSig ?? 'unknown';
309
+ console.log(`${C.green}✓ Transaction accepted!${C.reset}`);
310
+ console.log(` ${C.green}★${C.reset} ${C.bright}Signature:${C.reset} ${sig}`);
311
+ if (result?.slot) {
312
+ console.log(` ${C.dim} Slot:${C.reset} ${result.slot}`);
313
+ }
314
+ console.log(` ${C.dim} Latency:${C.reset} ${latencyMs}ms`);
315
+ console.log(` ${C.dim} SDK Method:${C.reset} client.sendTransaction() → POST /v1/transaction`);
316
+ console.log(` ${C.dim} RPC:${C.reset} ${rpc}`);
317
+ console.log();
318
+
319
+ // Wait for confirmation if requested
320
+ if (wait && submittedSig) {
321
+ console.log(` ${C.dim}Waiting for confirmation...${C.reset}`);
322
+ const confirmResult = await waitForConfirmation(client, submittedSig, 60000);
323
+
324
+ if (confirmResult.confirmed) {
325
+ console.log(` ${C.green}✓ Confirmed!${C.reset}`);
326
+ console.log(` ${C.dim} Waited:${C.reset} ${confirmResult.waitedMs}ms`);
327
+ if (confirmResult.result?.slot) {
328
+ console.log(` ${C.dim} Slot:${C.reset} ${confirmResult.result.slot}`);
329
+ }
330
+ if (confirmResult.result?.blockTime) {
331
+ const confirmedAt = new Date(confirmResult.result.blockTime * 1000).toISOString();
332
+ console.log(` ${C.dim} Block time:${C.reset} ${confirmedAt}`);
333
+ }
334
+ } else {
335
+ console.log(` ${C.yellow}⚠ Transaction submitted but not yet confirmed${C.reset}`);
336
+ console.log(` ${C.dim} Signature:${C.reset} ${sig}`);
337
+ console.log(` ${C.dim} Check manually: aether tx ${sig}${C.reset}`);
338
+ }
339
+ console.log();
340
+ }
341
+ } else {
342
+ const errMsg = result?.error || 'Transaction rejected by network';
343
+ console.log(`${C.red}✗ Transaction rejected${C.reset}`);
344
+ if (result?.error) {
345
+ console.log(` ${C.red} Error:${C.reset} ${result.error}`);
346
+ }
347
+ if (result?.logs) {
348
+ console.log(` ${C.dim} Logs:${C.reset}`);
349
+ for (const log of result.logs) {
350
+ console.log(` ${C.dim}${log}${C.reset}`);
351
+ }
352
+ }
353
+ console.log(` ${C.dim} Latency:${C.reset} ${latencyMs}ms`);
354
+ console.log(` ${C.dim} RPC:${C.reset} ${rpc}`);
355
+ console.log();
356
+ process.exit(1);
357
+ }
358
+ }
359
+
360
+ // ---------------------------------------------------------------------------
361
+ // Main
362
+ // ---------------------------------------------------------------------------
363
+
364
+ async function main() {
365
+ const opts = parseArgs();
366
+
367
+ try {
368
+ await broadcast(opts);
369
+ } catch (err) {
370
+ if (opts.asJson) {
371
+ console.log(JSON.stringify({
372
+ success: false,
373
+ error: err.message,
374
+ rpc: opts.rpc,
375
+ cli_version: CLI_VERSION,
376
+ timestamp: new Date().toISOString(),
377
+ }, null, 2));
378
+ } else {
379
+ console.log(`\n ${C.red}✗ ${err.message}${C.reset}\n`);
380
+ }
381
+ process.exit(1);
382
+ }
383
+ }
384
+
385
+ main();
386
+
387
+ module.exports = { broadcastCommand: main };