@jellylegsai/aether-cli 1.9.1 → 2.0.1
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/IMPLEMENTATION_REPORT.md +319 -0
- package/commands/blockheight.js +230 -0
- package/commands/call.js +981 -0
- package/commands/claim.js +98 -72
- package/commands/deploy.js +959 -0
- package/commands/index.js +88 -86
- package/commands/init.js +33 -49
- package/commands/network-diagnostics.js +706 -0
- package/commands/network.js +412 -429
- package/commands/rewards.js +311 -266
- package/commands/sdk.js +791 -656
- package/commands/slot.js +3 -11
- package/commands/stake.js +581 -516
- package/commands/supply.js +483 -391
- package/commands/token-accounts.js +275 -0
- package/commands/transfer.js +3 -11
- package/commands/unstake.js +3 -11
- package/commands/validator-start.js +681 -323
- package/commands/validator.js +959 -0
- package/commands/validators.js +623 -626
- package/commands/version.js +240 -0
- package/commands/wallet.js +17 -24
- package/cycle-report-issue-116.txt +165 -0
- package/index.js +501 -595
- package/lib/ui.js +623 -0
- package/package.json +83 -76
- package/sdk/index.d.ts +546 -0
- package/sdk/index.js +130 -0
- package/sdk/package.json +2 -1
package/commands/validators.js
CHANGED
|
@@ -1,626 +1,623 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* aether-cli validators
|
|
4
|
-
*
|
|
5
|
-
* List
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* aether validators list
|
|
10
|
-
* aether validators
|
|
11
|
-
* aether validators
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
console.log(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
console.log(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
: '
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
opts
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
if (require.main === module) {
|
|
625
|
-
main();
|
|
626
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli validators
|
|
4
|
+
*
|
|
5
|
+
* List and manage validators on the Aether network.
|
|
6
|
+
* Fully wired to @jellylegsai/aether-sdk for real blockchain RPC calls.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* aether validators list [--tier full|lite|observer] [--json]
|
|
10
|
+
* aether validators info <address> [--json]
|
|
11
|
+
* aether validators top [--limit 10] [--json]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const readline = require('readline');
|
|
18
|
+
|
|
19
|
+
// Import SDK for real blockchain RPC calls
|
|
20
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
21
|
+
const aether = require(sdkPath);
|
|
22
|
+
|
|
23
|
+
// Import UI framework for consistent branding
|
|
24
|
+
const { BRANDING, C, indicators, drawBox, drawTable,
|
|
25
|
+
success, error, warning, info, code, highlight, value,
|
|
26
|
+
startSpinner, stopSpinner, progressBar, progressBarColored,
|
|
27
|
+
formatHelp } = require('../lib/ui');
|
|
28
|
+
|
|
29
|
+
const CLI_VERSION = '2.0.0';
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// SDK Setup
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
function getDefaultRpc() {
|
|
36
|
+
return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createClient(rpcUrl) {
|
|
40
|
+
return new aether.AetherClient({ rpcUrl });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Config Helpers
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
function getAetherDir() {
|
|
48
|
+
return path.join(os.homedir(), '.aether');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getConfigPath() {
|
|
52
|
+
return path.join(getAetherDir(), 'config.json');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function loadConfig() {
|
|
56
|
+
if (!fs.existsSync(getConfigPath())) {
|
|
57
|
+
return { defaultWallet: null };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(fs.readFileSync(getConfigPath(), 'utf8'));
|
|
61
|
+
} catch {
|
|
62
|
+
return { defaultWallet: null };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Format Helpers
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
function formatAether(lamports) {
|
|
71
|
+
if (!lamports || lamports === '0') return '0 AETH';
|
|
72
|
+
const aeth = Number(lamports) / 1e9;
|
|
73
|
+
if (aeth >= 1_000_000) {
|
|
74
|
+
return (aeth / 1_000_000).toFixed(2) + 'M AETH';
|
|
75
|
+
}
|
|
76
|
+
if (aeth >= 1_000) {
|
|
77
|
+
return (aeth / 1_000).toFixed(2) + 'K AETH';
|
|
78
|
+
}
|
|
79
|
+
return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function formatNumber(n) {
|
|
83
|
+
if (n === null || n === undefined) return 'N/A';
|
|
84
|
+
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function shortAddress(addr) {
|
|
88
|
+
if (!addr || addr.length < 16) return addr || 'unknown';
|
|
89
|
+
return addr.slice(0, 8) + '...' + addr.slice(-8);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatPercent(val) {
|
|
93
|
+
if (val === undefined || val === null) return 'N/A';
|
|
94
|
+
const pct = Number(val);
|
|
95
|
+
if (isNaN(pct)) return 'N/A';
|
|
96
|
+
return pct.toFixed(2) + '%';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function formatAPY(apy) {
|
|
100
|
+
if (!apy && apy !== 0) return `${C.dim}N/A${C.reset}`;
|
|
101
|
+
const val = Number(apy);
|
|
102
|
+
if (isNaN(val)) return `${C.dim}N/A${C.reset}`;
|
|
103
|
+
if (val >= 10) return `${C.green}${val.toFixed(2)}%${C.reset}`;
|
|
104
|
+
if (val >= 5) return `${C.yellow}${val.toFixed(2)}%${C.reset}`;
|
|
105
|
+
return `${C.cyan}${val.toFixed(2)}%${C.reset}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Fetch Validators via SDK
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
async function fetchValidators(rpcUrl) {
|
|
113
|
+
const client = createClient(rpcUrl);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const validators = await client.getValidators();
|
|
117
|
+
if (!Array.isArray(validators)) return [];
|
|
118
|
+
|
|
119
|
+
return validators.map(v => ({
|
|
120
|
+
address: v.vote_account || v.pubkey || v.address || v.identity || 'unknown',
|
|
121
|
+
identity: v.identity || v.node_pubkey || v.address,
|
|
122
|
+
stake: v.stake_lamports || v.activated_stake || v.stake || 0,
|
|
123
|
+
commission: v.commission || v.commission_bps || 0,
|
|
124
|
+
apy: v.apy || v.return_rate || v.estimated_apy || 0,
|
|
125
|
+
name: v.name || v.moniker || v.identity_name || 'Unknown',
|
|
126
|
+
tier: v.tier || 'unknown',
|
|
127
|
+
active: v.active !== false && v.delinquent !== true,
|
|
128
|
+
delinquent: v.delinquent === true,
|
|
129
|
+
skipRate: v.skip_rate || 0,
|
|
130
|
+
uptime: v.uptime || 0,
|
|
131
|
+
lastVote: v.last_vote || v.last_vote_slot || 0,
|
|
132
|
+
version: v.version || 'unknown',
|
|
133
|
+
website: v.website || null,
|
|
134
|
+
details: v.details || null,
|
|
135
|
+
}));
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function fetchValidatorInfo(rpcUrl, address) {
|
|
142
|
+
const validators = await fetchValidators(rpcUrl);
|
|
143
|
+
const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
|
|
144
|
+
return validators.find(v =>
|
|
145
|
+
v.address === address ||
|
|
146
|
+
v.address === rawAddr ||
|
|
147
|
+
v.identity === address ||
|
|
148
|
+
v.identity === rawAddr
|
|
149
|
+
) || null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ============================================================================
|
|
153
|
+
// Validator Status Indicator
|
|
154
|
+
// ============================================================================
|
|
155
|
+
|
|
156
|
+
function getStatusIndicator(validator) {
|
|
157
|
+
if (validator.delinquent) {
|
|
158
|
+
return `${C.red}● DELINQUENT${C.reset}`;
|
|
159
|
+
}
|
|
160
|
+
if (validator.active) {
|
|
161
|
+
return `${C.green}● ACTIVE${C.reset}`;
|
|
162
|
+
}
|
|
163
|
+
return `${C.yellow}● INACTIVE${C.reset}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getTierBadge(tier) {
|
|
167
|
+
const tiers = {
|
|
168
|
+
full: `${C.magenta}[FULL]${C.reset}`,
|
|
169
|
+
lite: `${C.cyan}[LITE]${C.reset}`,
|
|
170
|
+
observer: `${C.dim}[OBSERVER]${C.reset}`,
|
|
171
|
+
};
|
|
172
|
+
return tiers[tier?.toLowerCase()] || `${C.dim}[${tier?.toUpperCase() || 'UNKNOWN'}]${C.reset}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// List Command
|
|
177
|
+
// ============================================================================
|
|
178
|
+
|
|
179
|
+
async function listCommand(opts) {
|
|
180
|
+
if (!opts.json) {
|
|
181
|
+
console.log(BRANDING.header(CLI_VERSION));
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(drawBox(
|
|
184
|
+
`${C.bright}VALIDATORS${C.reset} — Active validators on the Aether network`,
|
|
185
|
+
{ style: 'double', title: 'NETWORK', titleColor: C.cyan }
|
|
186
|
+
));
|
|
187
|
+
console.log();
|
|
188
|
+
startSpinner('Fetching validators from ' + shortAddress(opts.rpc));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const validators = await fetchValidators(opts.rpc);
|
|
192
|
+
|
|
193
|
+
if (!opts.json) {
|
|
194
|
+
stopSpinner(true, `Found ${validators.length} validators`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Filter by tier if specified
|
|
198
|
+
let filtered = validators;
|
|
199
|
+
if (opts.tier && opts.tier !== 'all') {
|
|
200
|
+
filtered = validators.filter(v => v.tier?.toLowerCase() === opts.tier.toLowerCase());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Sort by stake (descending)
|
|
204
|
+
filtered.sort((a, b) => b.stake - a.stake);
|
|
205
|
+
|
|
206
|
+
if (filtered.length === 0) {
|
|
207
|
+
if (opts.json) {
|
|
208
|
+
console.log(JSON.stringify({
|
|
209
|
+
success: false,
|
|
210
|
+
error: 'No validators found',
|
|
211
|
+
rpc: opts.rpc,
|
|
212
|
+
tier_filter: opts.tier,
|
|
213
|
+
}, null, 2));
|
|
214
|
+
} else {
|
|
215
|
+
console.log(`\n ${warning('No validators found.')}`);
|
|
216
|
+
if (opts.tier) {
|
|
217
|
+
console.log(` ${C.dim}Tier filter: ${opts.tier}${C.reset}`);
|
|
218
|
+
}
|
|
219
|
+
console.log(` ${C.dim}Check your RPC endpoint: ${opts.rpc}${C.reset}\n`);
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Calculate stats
|
|
225
|
+
const totalStake = filtered.reduce((sum, v) => sum + Number(v.stake), 0);
|
|
226
|
+
const activeCount = filtered.filter(v => v.active && !v.delinquent).length;
|
|
227
|
+
const delinquentCount = filtered.filter(v => v.delinquent).length;
|
|
228
|
+
const avgAPY = filtered.length > 0
|
|
229
|
+
? filtered.reduce((sum, v) => sum + Number(v.apy || 0), 0) / filtered.length
|
|
230
|
+
: 0;
|
|
231
|
+
|
|
232
|
+
if (opts.json) {
|
|
233
|
+
console.log(JSON.stringify({
|
|
234
|
+
success: true,
|
|
235
|
+
count: filtered.length,
|
|
236
|
+
total_validators: validators.length,
|
|
237
|
+
tier_filter: opts.tier || 'all',
|
|
238
|
+
stats: {
|
|
239
|
+
total_stake_lamports: totalStake,
|
|
240
|
+
total_stake_aeth: totalStake / 1e9,
|
|
241
|
+
active_count: activeCount,
|
|
242
|
+
delinquent_count: delinquentCount,
|
|
243
|
+
average_apy: avgAPY,
|
|
244
|
+
},
|
|
245
|
+
validators: filtered.map((v, i) => ({
|
|
246
|
+
rank: i + 1,
|
|
247
|
+
address: v.address,
|
|
248
|
+
name: v.name,
|
|
249
|
+
tier: v.tier,
|
|
250
|
+
stake_lamports: v.stake,
|
|
251
|
+
stake_aeth: v.stake / 1e9,
|
|
252
|
+
commission: v.commission,
|
|
253
|
+
apy: v.apy,
|
|
254
|
+
active: v.active,
|
|
255
|
+
delinquent: v.delinquent,
|
|
256
|
+
skip_rate: v.skipRate,
|
|
257
|
+
uptime: v.uptime,
|
|
258
|
+
})),
|
|
259
|
+
rpc: opts.rpc,
|
|
260
|
+
timestamp: new Date().toISOString(),
|
|
261
|
+
}, null, 2));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Pretty output
|
|
266
|
+
console.log();
|
|
267
|
+
console.log(drawBox(
|
|
268
|
+
`${C.dim}Network Stats${C.reset}\n\n` +
|
|
269
|
+
` ${C.cyan}Total Validators:${C.reset} ${C.bright}${formatNumber(filtered.length)}${C.reset}\n` +
|
|
270
|
+
` ${C.green}Active:${C.reset} ${C.bright}${formatNumber(activeCount)}${C.reset} ` +
|
|
271
|
+
`${C.red}Delinquent:${C.reset} ${C.bright}${formatNumber(delinquentCount)}${C.reset}\n` +
|
|
272
|
+
` ${C.yellow}Total Stake:${C.reset} ${C.bright}${formatAether(totalStake)}${C.reset}\n` +
|
|
273
|
+
` ${C.magenta}Avg APY:${C.reset} ${formatAPY(avgAPY)}`,
|
|
274
|
+
{ style: 'single', borderColor: C.dim }
|
|
275
|
+
));
|
|
276
|
+
console.log();
|
|
277
|
+
|
|
278
|
+
// Build table
|
|
279
|
+
const headers = ['#', 'Status', 'Name', 'Address', 'Tier', 'Stake', 'APY', 'Comm'];
|
|
280
|
+
const rows = filtered.slice(0, opts.limit).map((v, i) => {
|
|
281
|
+
const status = v.delinquent ? indicators.error : (v.active ? indicators.success : indicators.warning);
|
|
282
|
+
const name = (v.name || 'Unknown').slice(0, 16).padEnd(16);
|
|
283
|
+
const addr = shortAddress(v.address);
|
|
284
|
+
const tier = getTierBadge(v.tier);
|
|
285
|
+
const stake = formatAether(v.stake);
|
|
286
|
+
const apy = formatAPY(v.apy);
|
|
287
|
+
const comm = formatPercent(v.commission / 100); // Assuming basis points
|
|
288
|
+
return [`${i + 1}`, status, name, addr, tier, stake, apy, comm];
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
console.log(drawTable(headers, rows, {
|
|
292
|
+
borderStyle: 'single',
|
|
293
|
+
headerColor: C.cyan + C.bright,
|
|
294
|
+
borderColor: C.dim,
|
|
295
|
+
}));
|
|
296
|
+
|
|
297
|
+
if (filtered.length > opts.limit) {
|
|
298
|
+
console.log(`\n ${C.dim}... and ${filtered.length - opts.limit} more validators${C.reset}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log(`\n ${C.dim}To stake:${C.reset} ${code('aether stake --validator <address> --amount <aeth>')}`);
|
|
302
|
+
console.log(` ${C.dim}For details:${C.reset} ${code('aether validators info <address>')}`);
|
|
303
|
+
console.log();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ============================================================================
|
|
307
|
+
// Info Command
|
|
308
|
+
// ============================================================================
|
|
309
|
+
|
|
310
|
+
async function infoCommand(opts) {
|
|
311
|
+
if (!opts.address) {
|
|
312
|
+
if (!opts.json) {
|
|
313
|
+
console.log(`\n ${error('Validator address required.')}`);
|
|
314
|
+
console.log(` ${C.dim}Usage: aether validators info <address>${C.reset}\n`);
|
|
315
|
+
} else {
|
|
316
|
+
console.log(JSON.stringify({ error: 'Validator address required' }, null, 2));
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!opts.json) {
|
|
322
|
+
console.log(BRANDING.header(CLI_VERSION));
|
|
323
|
+
console.log();
|
|
324
|
+
startSpinner('Fetching validator info');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const validator = await fetchValidatorInfo(opts.rpc, opts.address);
|
|
328
|
+
|
|
329
|
+
if (!opts.json) {
|
|
330
|
+
stopSpinner(!!validator, validator ? 'Validator found' : 'Validator not found');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!validator) {
|
|
334
|
+
if (opts.json) {
|
|
335
|
+
console.log(JSON.stringify({
|
|
336
|
+
success: false,
|
|
337
|
+
error: 'Validator not found',
|
|
338
|
+
address: opts.address,
|
|
339
|
+
rpc: opts.rpc,
|
|
340
|
+
}, null, 2));
|
|
341
|
+
} else {
|
|
342
|
+
console.log(`\n ${error('Validator not found:')} ${opts.address}`);
|
|
343
|
+
console.log(` ${C.dim}Check the address and try again.${C.reset}\n`);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (opts.json) {
|
|
349
|
+
console.log(JSON.stringify({
|
|
350
|
+
success: true,
|
|
351
|
+
validator: {
|
|
352
|
+
address: validator.address,
|
|
353
|
+
identity: validator.identity,
|
|
354
|
+
name: validator.name,
|
|
355
|
+
tier: validator.tier,
|
|
356
|
+
stake_lamports: validator.stake,
|
|
357
|
+
stake_aeth: validator.stake / 1e9,
|
|
358
|
+
commission: validator.commission,
|
|
359
|
+
apy: validator.apy,
|
|
360
|
+
active: validator.active,
|
|
361
|
+
delinquent: validator.delinquent,
|
|
362
|
+
skip_rate: validator.skipRate,
|
|
363
|
+
uptime: validator.uptime,
|
|
364
|
+
last_vote: validator.lastVote,
|
|
365
|
+
version: validator.version,
|
|
366
|
+
website: validator.website,
|
|
367
|
+
details: validator.details,
|
|
368
|
+
},
|
|
369
|
+
rpc: opts.rpc,
|
|
370
|
+
timestamp: new Date().toISOString(),
|
|
371
|
+
}, null, 2));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Pretty output
|
|
376
|
+
const statusLine = getStatusIndicator(validator);
|
|
377
|
+
const uptimeBar = validator.uptime
|
|
378
|
+
? progressBarColored(validator.uptime, 100, 30)
|
|
379
|
+
: `${C.dim}N/A${C.reset}`;
|
|
380
|
+
|
|
381
|
+
console.log();
|
|
382
|
+
console.log(drawBox(
|
|
383
|
+
`${C.bright}VALIDATOR DETAILS${C.reset}\n\n` +
|
|
384
|
+
` ${C.cyan}Status:${C.reset} ${statusLine}\n` +
|
|
385
|
+
` ${C.cyan}Name:${C.reset} ${C.bright}${validator.name || 'Unknown'}${C.reset}\n` +
|
|
386
|
+
` ${C.cyan}Tier:${C.reset} ${getTierBadge(validator.tier)}\n\n` +
|
|
387
|
+
` ${C.cyan}Address:${C.reset} ${C.bright}${validator.address}${C.reset}\n` +
|
|
388
|
+
` ${C.cyan}Identity:${C.reset} ${validator.identity || 'N/A'}\n\n` +
|
|
389
|
+
` ${C.cyan}Stake:${C.reset} ${C.bright}${formatAether(validator.stake)}${C.reset}\n` +
|
|
390
|
+
` ${C.cyan}APY:${C.reset} ${formatAPY(validator.apy)}\n` +
|
|
391
|
+
` ${C.cyan}Commission:${C.reset} ${formatPercent(validator.commission / 100)}\n\n` +
|
|
392
|
+
` ${C.cyan}Uptime:${C.reset} ${uptimeBar}\n` +
|
|
393
|
+
` ${C.cyan}Skip Rate:${C.reset} ${formatPercent(validator.skipRate)}\n` +
|
|
394
|
+
` ${C.cyan}Version:${C.reset} ${validator.version || 'unknown'}`,
|
|
395
|
+
{ style: 'double', title: validator.name?.toUpperCase() || 'VALIDATOR', titleColor: C.cyan }
|
|
396
|
+
));
|
|
397
|
+
|
|
398
|
+
if (validator.website) {
|
|
399
|
+
console.log(`\n ${C.dim}Website:${C.reset} ${C.blue}${validator.website}${C.reset}`);
|
|
400
|
+
}
|
|
401
|
+
if (validator.details) {
|
|
402
|
+
console.log(`\n ${C.dim}Details:${C.reset} ${validator.details}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
console.log(`\n ${C.dim}To stake:${C.reset} ${code(`aether stake --validator ${validator.address} --amount <aeth>`)}`);
|
|
406
|
+
console.log();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ============================================================================
|
|
410
|
+
// Top Command
|
|
411
|
+
// ============================================================================
|
|
412
|
+
|
|
413
|
+
async function topCommand(opts) {
|
|
414
|
+
if (!opts.json) {
|
|
415
|
+
console.log(BRANDING.header(CLI_VERSION));
|
|
416
|
+
console.log();
|
|
417
|
+
console.log(drawBox(
|
|
418
|
+
`${C.bright}TOP VALIDATORS${C.reset} — Highest stake validators`,
|
|
419
|
+
{ style: 'double', title: 'LEADERBOARD', titleColor: C.yellow }
|
|
420
|
+
));
|
|
421
|
+
console.log();
|
|
422
|
+
startSpinner('Fetching top validators');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const validators = await fetchValidators(opts.rpc);
|
|
426
|
+
|
|
427
|
+
// Sort by stake and take top N
|
|
428
|
+
validators.sort((a, b) => b.stake - a.stake);
|
|
429
|
+
const topValidators = validators.slice(0, opts.limit);
|
|
430
|
+
|
|
431
|
+
if (!opts.json) {
|
|
432
|
+
stopSpinner(topValidators.length > 0, `Found ${topValidators.length} validators`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (topValidators.length === 0) {
|
|
436
|
+
if (opts.json) {
|
|
437
|
+
console.log(JSON.stringify({ error: 'No validators found' }, null, 2));
|
|
438
|
+
} else {
|
|
439
|
+
console.log(`\n ${warning('No validators found.')}\n`);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Calculate total stake for percentages
|
|
445
|
+
const totalNetworkStake = validators.reduce((sum, v) => sum + Number(v.stake), 0);
|
|
446
|
+
|
|
447
|
+
if (opts.json) {
|
|
448
|
+
console.log(JSON.stringify({
|
|
449
|
+
success: true,
|
|
450
|
+
top_count: topValidators.length,
|
|
451
|
+
total_network_stake: totalNetworkStake,
|
|
452
|
+
validators: topValidators.map((v, i) => ({
|
|
453
|
+
rank: i + 1,
|
|
454
|
+
address: v.address,
|
|
455
|
+
name: v.name,
|
|
456
|
+
stake_lamports: v.stake,
|
|
457
|
+
stake_aeth: v.stake / 1e9,
|
|
458
|
+
stake_percentage: totalNetworkStake > 0 ? (v.stake / totalNetworkStake * 100).toFixed(2) : 0,
|
|
459
|
+
apy: v.apy,
|
|
460
|
+
commission: v.commission,
|
|
461
|
+
active: v.active,
|
|
462
|
+
})),
|
|
463
|
+
rpc: opts.rpc,
|
|
464
|
+
timestamp: new Date().toISOString(),
|
|
465
|
+
}, null, 2));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
console.log();
|
|
470
|
+
console.log(` ${C.dim}Network:${C.reset} ${C.bright}${formatAether(totalNetworkStake)}${C.reset} total stake`);
|
|
471
|
+
console.log(` ${C.dim}Showing top ${opts.limit} validators${C.reset}\n`);
|
|
472
|
+
|
|
473
|
+
// Build leaderboard table
|
|
474
|
+
const headers = ['Rank', 'Validator', 'Stake', '% Network', 'APY'];
|
|
475
|
+
const rows = topValidators.map((v, i) => {
|
|
476
|
+
const rank = i + 1;
|
|
477
|
+
const rankIcon = rank === 1 ? `${C.yellow}🥇${C.reset}` :
|
|
478
|
+
rank === 2 ? `${C.dim}🥈${C.reset}` :
|
|
479
|
+
rank === 3 ? `${C.brightYellow}🥉${C.reset}` :
|
|
480
|
+
`${C.dim}${rank}${C.reset}`;
|
|
481
|
+
const name = (v.name || shortAddress(v.address)).slice(0, 20);
|
|
482
|
+
const stake = formatAether(v.stake);
|
|
483
|
+
const pct = totalNetworkStake > 0 ? (v.stake / totalNetworkStake * 100).toFixed(2) + '%' : '0%';
|
|
484
|
+
const apy = formatAPY(v.apy);
|
|
485
|
+
return [rankIcon, name, stake, pct, apy];
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
console.log(drawTable(headers, rows, {
|
|
489
|
+
borderStyle: 'single',
|
|
490
|
+
headerColor: C.yellow + C.bright,
|
|
491
|
+
borderColor: C.dim,
|
|
492
|
+
}));
|
|
493
|
+
|
|
494
|
+
console.log();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ============================================================================
|
|
498
|
+
// Show Help
|
|
499
|
+
// ============================================================================
|
|
500
|
+
|
|
501
|
+
function showHelp() {
|
|
502
|
+
console.log(BRANDING.header(CLI_VERSION));
|
|
503
|
+
|
|
504
|
+
console.log(`\n ${C.bright}AETHER VALIDATORS${C.reset} — Network validator management\n`);
|
|
505
|
+
|
|
506
|
+
console.log(` ${C.cyan}◆ LIST${C.reset} ${code('aether validators list [--tier <tier>] [--json]')}`);
|
|
507
|
+
console.log(` ${C.dim}Show all validators, optionally filtered by tier${C.reset}`);
|
|
508
|
+
|
|
509
|
+
console.log(`\n ${C.cyan}◆ INFO${C.reset} ${code('aether validators info <address> [--json]')}`);
|
|
510
|
+
console.log(` ${C.dim}Detailed information about a specific validator${C.reset}`);
|
|
511
|
+
|
|
512
|
+
console.log(`\n ${C.cyan}◆ TOP${C.reset} ${code('aether validators top [--limit 10] [--json]')}`);
|
|
513
|
+
console.log(` ${C.dim}Show top validators by stake amount${C.reset}`);
|
|
514
|
+
|
|
515
|
+
console.log(`\n ${C.bright}OPTIONS${C.reset}`);
|
|
516
|
+
console.log(` ${code('--tier <tier>')} Filter by tier: full, lite, observer`);
|
|
517
|
+
console.log(` ${code('--limit <n>')} Show top N validators (default: 15)`);
|
|
518
|
+
console.log(` ${code('--rpc <url>')} Custom RPC endpoint`);
|
|
519
|
+
console.log(` ${code('--json')} Output JSON for scripting`);
|
|
520
|
+
console.log(` ${code('--help')} Show this help message`);
|
|
521
|
+
|
|
522
|
+
console.log(`\n ${C.bright}EXAMPLES${C.reset}`);
|
|
523
|
+
console.log(` ${C.dim}$${C.reset} ${code('aether validators list')}`);
|
|
524
|
+
console.log(` ${C.dim}$${C.reset} ${code('aether validators list --tier full --json')}`);
|
|
525
|
+
console.log(` ${C.dim}$${C.reset} ${code('aether validators info ATHxxx...')}`);
|
|
526
|
+
console.log(` ${C.dim}$${C.reset} ${code('aether validators top --limit 20')}`);
|
|
527
|
+
|
|
528
|
+
console.log(`\n ${C.bright}SDK METHODS${C.reset}`);
|
|
529
|
+
console.log(` ${C.dim}client.getValidators() → GET /v1/validators${C.reset}`);
|
|
530
|
+
console.log();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ============================================================================
|
|
534
|
+
// CLI Args Parser
|
|
535
|
+
// ============================================================================
|
|
536
|
+
|
|
537
|
+
function parseArgs() {
|
|
538
|
+
const rawArgs = process.argv.slice(3);
|
|
539
|
+
|
|
540
|
+
// Determine subcommand - first arg if not a flag
|
|
541
|
+
let subcmd = 'list';
|
|
542
|
+
let allArgs = rawArgs;
|
|
543
|
+
|
|
544
|
+
if (rawArgs.length > 0 && !rawArgs[0].startsWith('-')) {
|
|
545
|
+
subcmd = rawArgs[0];
|
|
546
|
+
allArgs = rawArgs.slice(1);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const opts = {
|
|
550
|
+
subcmd,
|
|
551
|
+
rpc: getDefaultRpc(),
|
|
552
|
+
json: false,
|
|
553
|
+
tier: null,
|
|
554
|
+
limit: 15,
|
|
555
|
+
address: null,
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// Parse flags
|
|
559
|
+
for (let i = 0; i < allArgs.length; i++) {
|
|
560
|
+
const arg = allArgs[i];
|
|
561
|
+
if (arg === '--rpc' || arg === '-r') {
|
|
562
|
+
opts.rpc = allArgs[++i];
|
|
563
|
+
} else if (arg === '--json' || arg === '-j') {
|
|
564
|
+
opts.json = true;
|
|
565
|
+
} else if (arg === '--tier' || arg === '-t') {
|
|
566
|
+
opts.tier = allArgs[++i];
|
|
567
|
+
} else if (arg === '--limit' || arg === '-l') {
|
|
568
|
+
const val = parseInt(allArgs[++i], 10);
|
|
569
|
+
if (!isNaN(val) && val > 0) opts.limit = val;
|
|
570
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
571
|
+
opts.subcmd = 'help';
|
|
572
|
+
} else if (!arg.startsWith('-') && subcmd === 'info') {
|
|
573
|
+
// For info command, non-flag arg is the address
|
|
574
|
+
opts.address = arg;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// For info command, also check if address was passed directly
|
|
579
|
+
if (subcmd === 'info' && !opts.address) {
|
|
580
|
+
const firstArg = allArgs.find(a => !a.startsWith('-'));
|
|
581
|
+
if (firstArg) opts.address = firstArg;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return opts;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// ============================================================================
|
|
588
|
+
// Main Entry Point
|
|
589
|
+
// ============================================================================
|
|
590
|
+
|
|
591
|
+
async function validatorsCommand() {
|
|
592
|
+
const opts = parseArgs();
|
|
593
|
+
|
|
594
|
+
switch (opts.subcmd) {
|
|
595
|
+
case 'list':
|
|
596
|
+
await listCommand(opts);
|
|
597
|
+
break;
|
|
598
|
+
case 'info':
|
|
599
|
+
await infoCommand(opts);
|
|
600
|
+
break;
|
|
601
|
+
case 'top':
|
|
602
|
+
await topCommand(opts);
|
|
603
|
+
break;
|
|
604
|
+
case 'help':
|
|
605
|
+
case '--help':
|
|
606
|
+
case '-h':
|
|
607
|
+
showHelp();
|
|
608
|
+
break;
|
|
609
|
+
default:
|
|
610
|
+
console.log(`\n ${error('Unknown subcommand:')} ${opts.subcmd}`);
|
|
611
|
+
console.log(` ${C.dim}Run 'aether validators --help' for usage.${C.reset}\n`);
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
module.exports = { validatorsCommand };
|
|
617
|
+
|
|
618
|
+
if (require.main === module) {
|
|
619
|
+
validatorsCommand().catch(err => {
|
|
620
|
+
console.error(`\n${error('Validators command failed:')} ${err.message}\n`);
|
|
621
|
+
process.exit(1);
|
|
622
|
+
});
|
|
623
|
+
}
|