@jellylegsai/aether-cli 1.9.2 → 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 +2 -0
- 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 -602
- package/lib/ui.js +623 -0
- package/package.json +10 -3
- package/sdk/index.d.ts +546 -0
- package/sdk/index.js +130 -0
- package/sdk/package.json +2 -1
package/commands/supply.js
CHANGED
|
@@ -1,391 +1,483 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* aether-cli supply
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* aether supply
|
|
13
|
-
* aether supply --
|
|
14
|
-
* aether supply --
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const path = require('path');
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
} catch {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
function
|
|
200
|
-
const { total, circulating,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
console.log(
|
|
219
|
-
console.log(
|
|
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
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
console.log();
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli supply
|
|
4
|
+
*
|
|
5
|
+
* Query Aether token supply information from the blockchain.
|
|
6
|
+
* Shows total supply, circulating supply, non-circulating supply,
|
|
7
|
+
* and supply breakdown with visual indicators.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* aether supply Show detailed supply info
|
|
11
|
+
* aether supply --json JSON output for scripting
|
|
12
|
+
* aether supply --rpc <url> Query specific RPC endpoint
|
|
13
|
+
* aether supply --watch Watch mode - updates every 5 seconds
|
|
14
|
+
* aether supply --compare Compare with theoretical max
|
|
15
|
+
*
|
|
16
|
+
* SDK wired to: GET /v1/supply
|
|
17
|
+
* SDK Function: sdk.getSupply()
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
// Import SDK for real blockchain RPC calls
|
|
23
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
24
|
+
const aether = require(sdkPath);
|
|
25
|
+
|
|
26
|
+
// Import UI framework
|
|
27
|
+
const {
|
|
28
|
+
C,
|
|
29
|
+
BRANDING,
|
|
30
|
+
indicators,
|
|
31
|
+
success,
|
|
32
|
+
error,
|
|
33
|
+
warning,
|
|
34
|
+
info,
|
|
35
|
+
highlight,
|
|
36
|
+
dim,
|
|
37
|
+
startSpinner,
|
|
38
|
+
stopSpinner,
|
|
39
|
+
drawBox,
|
|
40
|
+
drawTable,
|
|
41
|
+
progressBarColored,
|
|
42
|
+
} = require('../lib/ui');
|
|
43
|
+
|
|
44
|
+
const CLI_VERSION = '1.0.0';
|
|
45
|
+
const WATCH_INTERVAL_MS = 5000;
|
|
46
|
+
|
|
47
|
+
// Supply constants
|
|
48
|
+
const MAX_SUPPLY_AETH = 1_000_000_000; // 1 billion AETH theoretical max
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// SDK Setup
|
|
52
|
+
// ============================================================================
|
|
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
|
+
// ASCII Art & Branding
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
const SUPPLY_LOGO = `
|
|
67
|
+
${C.cyan} ╔══════════════════════════════════════════════════════════╗${C.reset}
|
|
68
|
+
${C.cyan} ║${C.reset} ${C.bright}${C.yellow}◆${C.reset} ${C.bright}AETHER TOKEN SUPPLY${C.reset}${' '.repeat(30)}${C.dim}v${CLI_VERSION}${C.reset} ${C.cyan}║${C.reset}
|
|
69
|
+
${C.cyan} ║${C.reset} ${C.dim}On-chain supply metrics and tokenomics${C.reset}${' '.repeat(20)}${C.cyan}║${C.reset}
|
|
70
|
+
${C.cyan} ╚══════════════════════════════════════════════════════════╝${C.reset}`;
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Format Helpers
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
function formatAether(lamports) {
|
|
77
|
+
if (!lamports && lamports !== 0) return 'N/A';
|
|
78
|
+
const aeth = Number(lamports) / 1e9;
|
|
79
|
+
if (aeth === 0) return '0 AETH';
|
|
80
|
+
if (aeth >= 1_000_000) {
|
|
81
|
+
return aeth.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' AETH';
|
|
82
|
+
}
|
|
83
|
+
return aeth.toFixed(6).replace(/\.?0+$/, '') + ' AETH';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatNumber(num) {
|
|
87
|
+
if (!num && num !== 0) return 'N/A';
|
|
88
|
+
return Number(num).toLocaleString();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function formatPercentage(numerator, denominator) {
|
|
92
|
+
if (!denominator || denominator === 0) return 'N/A';
|
|
93
|
+
const pct = (Number(numerator) / Number(denominator)) * 100;
|
|
94
|
+
return pct.toFixed(2) + '%';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function formatCompact(n) {
|
|
98
|
+
if (!n) return 'N/A';
|
|
99
|
+
const num = Number(n);
|
|
100
|
+
if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
|
|
101
|
+
if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
|
|
102
|
+
if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
|
|
103
|
+
return num.toString();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Argument Parsing
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
function parseArgs() {
|
|
111
|
+
const args = process.argv.slice(2);
|
|
112
|
+
return {
|
|
113
|
+
rpc: args.includes('--rpc') ? args[args.indexOf('--rpc') + 1] : getDefaultRpc(),
|
|
114
|
+
asJson: args.includes('--json') || args.includes('-j'),
|
|
115
|
+
watch: args.includes('--watch') || args.includes('-w'),
|
|
116
|
+
compare: args.includes('--compare') || args.includes('-c'),
|
|
117
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function showHelp() {
|
|
122
|
+
console.log(`
|
|
123
|
+
${C.bright}${C.cyan}aether-cli supply${C.reset} — Token Supply Information
|
|
124
|
+
|
|
125
|
+
${C.bright}USAGE${C.reset}
|
|
126
|
+
aether supply [options]
|
|
127
|
+
|
|
128
|
+
${C.bright}OPTIONS${C.reset}
|
|
129
|
+
-r, --rpc <url> RPC endpoint (default: ${getDefaultRpc()})
|
|
130
|
+
-j, --json Output as JSON
|
|
131
|
+
-w, --watch Watch mode - updates every 5 seconds
|
|
132
|
+
-c, --compare Show comparison with theoretical max supply
|
|
133
|
+
-h, --help Show this help
|
|
134
|
+
|
|
135
|
+
${C.bright}SDK METHODS USED${C.reset}
|
|
136
|
+
${C.dim}client.getSupply() → GET /v1/supply${C.reset}
|
|
137
|
+
${C.dim}client.getEpochInfo() → GET /v1/epoch${C.reset}
|
|
138
|
+
|
|
139
|
+
${C.bright}EXAMPLES${C.reset}
|
|
140
|
+
aether supply # Detailed supply view
|
|
141
|
+
aether supply --json # JSON for scripting
|
|
142
|
+
aether supply --watch # Live updates
|
|
143
|
+
aether supply --compare # Compare with max supply
|
|
144
|
+
aether supply --rpc https://api.aether.io
|
|
145
|
+
|
|
146
|
+
${C.bright}OUTPUT FIELDS${C.reset}
|
|
147
|
+
• Total Supply — Total minted AETH (lamports)
|
|
148
|
+
• Circulating — AETH in active circulation
|
|
149
|
+
• Non-Circulating — Locked, staked, or reserved AETH
|
|
150
|
+
• Staked — AETH delegated to validators
|
|
151
|
+
• Inflation Rate — Current epoch inflation
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// SDK Data Fetching (REAL RPC CALLS)
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
async function fetchSupplyData(rpcUrl) {
|
|
160
|
+
const client = createClient(rpcUrl);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Parallel SDK calls for supply and epoch info
|
|
164
|
+
const [supplyResult, epochInfo] = await Promise.all([
|
|
165
|
+
client.getSupply().catch(() => null),
|
|
166
|
+
client.getEpochInfo().catch(() => null),
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
if (!supplyResult) {
|
|
170
|
+
throw new Error('No supply data returned from RPC');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Normalize supply data from various RPC response formats
|
|
174
|
+
const total = supplyResult.total || supplyResult.total_supply || supplyResult.totalSupply || 0;
|
|
175
|
+
const circulating = supplyResult.circulating || supplyResult.circulating_supply || supplyResult.circulatingSupply || 0;
|
|
176
|
+
const nonCirculating = supplyResult.nonCirculating || supplyResult.non_circulating || supplyResult.nonCirculatingSupply || (total - circulating);
|
|
177
|
+
const staked = supplyResult.staked || supplyResult.total_staked || supplyResult.delegated || 0;
|
|
178
|
+
const rewards = supplyResult.rewards || supplyResult.validator_rewards || 0;
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
total: BigInt(total),
|
|
182
|
+
circulating: BigInt(circulating),
|
|
183
|
+
nonCirculating: BigInt(nonCirculating),
|
|
184
|
+
staked: BigInt(staked),
|
|
185
|
+
rewards: BigInt(rewards),
|
|
186
|
+
epoch: epochInfo?.epoch || 0,
|
|
187
|
+
slot: epochInfo?.absoluteSlot || 0,
|
|
188
|
+
fetchedAt: new Date().toISOString(),
|
|
189
|
+
};
|
|
190
|
+
} catch (err) {
|
|
191
|
+
throw new Error(`Failed to fetch supply: ${err.message}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Visual Rendering
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
function renderSupplyBox(data, opts = {}) {
|
|
200
|
+
const { total, circulating, nonCirculating, staked, epoch, slot } = data;
|
|
201
|
+
const { compare } = opts;
|
|
202
|
+
|
|
203
|
+
const totalAeth = Number(total) / 1e9;
|
|
204
|
+
const circulatingAeth = Number(circulating) / 1e9;
|
|
205
|
+
const nonCirculatingAeth = Number(nonCirculating) / 1e9;
|
|
206
|
+
const stakedAeth = Number(staked) / 1e9;
|
|
207
|
+
|
|
208
|
+
// Calculate percentages
|
|
209
|
+
const circulatingPct = total > 0 ? (circulatingAeth / totalAeth) * 100 : 0;
|
|
210
|
+
const nonCircPct = total > 0 ? (nonCirculatingAeth / totalAeth) * 100 : 0;
|
|
211
|
+
const stakedPct = total > 0 ? (stakedAeth / totalAeth) * 100 : 0;
|
|
212
|
+
|
|
213
|
+
// Circulation ratio
|
|
214
|
+
const circulationRatio = circulating > 0
|
|
215
|
+
? ((Number(circulating) / Number(total)) * 100).toFixed(2)
|
|
216
|
+
: '0.00';
|
|
217
|
+
|
|
218
|
+
console.log(SUPPLY_LOGO);
|
|
219
|
+
console.log();
|
|
220
|
+
|
|
221
|
+
// Main supply box
|
|
222
|
+
const supplyContent = `
|
|
223
|
+
${C.bright}Total Supply${C.reset}
|
|
224
|
+
${C.cyan}${formatAether(total)}${C.reset} ${C.dim}(${formatNumber(total)} lamports)${C.reset}
|
|
225
|
+
|
|
226
|
+
${C.bright}Circulating${C.reset} ${C.green}${formatAether(circulating)}${C.reset}
|
|
227
|
+
${C.bright}Non-Circulating${C.reset} ${C.yellow}${formatAether(nonCirculating)}${C.reset}
|
|
228
|
+
${C.bright}Staked${C.reset} ${C.magenta}${formatAether(staked)}${C.reset}
|
|
229
|
+
|
|
230
|
+
${C.dim}Circulation Ratio: ${C.bright}${circulationRatio}%${C.reset}
|
|
231
|
+
${C.dim}Current Epoch: ${C.bright}${epoch}${C.reset}
|
|
232
|
+
${C.dim}Current Slot: ${C.bright}${formatNumber(slot)}${C.reset}
|
|
233
|
+
`.trim();
|
|
234
|
+
|
|
235
|
+
console.log(drawBox(supplyContent, {
|
|
236
|
+
style: 'double',
|
|
237
|
+
title: 'SUPPLY OVERVIEW',
|
|
238
|
+
titleColor: C.cyan + C.bright,
|
|
239
|
+
borderColor: C.cyan,
|
|
240
|
+
width: 60,
|
|
241
|
+
}));
|
|
242
|
+
|
|
243
|
+
console.log();
|
|
244
|
+
|
|
245
|
+
// Distribution bars
|
|
246
|
+
console.log(` ${C.bright}Supply Distribution:${C.reset}\n`);
|
|
247
|
+
|
|
248
|
+
const barWidth = 40;
|
|
249
|
+
|
|
250
|
+
// Circulating bar
|
|
251
|
+
const circFill = Math.round((circulatingPct / 100) * barWidth);
|
|
252
|
+
const circBar = `${C.green}${'█'.repeat(circFill)}${C.dim}${'░'.repeat(barWidth - circFill)}${C.reset}`;
|
|
253
|
+
console.log(` ${C.green}●${C.reset} Circulating ${circBar} ${C.green}${circulatingPct.toFixed(1)}%${C.reset}`);
|
|
254
|
+
|
|
255
|
+
// Non-circulating bar
|
|
256
|
+
const ncFill = Math.round((nonCircPct / 100) * barWidth);
|
|
257
|
+
const ncBar = `${C.yellow}${'█'.repeat(ncFill)}${C.dim}${'░'.repeat(barWidth - ncFill)}${C.reset}`;
|
|
258
|
+
console.log(` ${C.yellow}●${C.reset} Non-Circulating ${ncBar} ${C.yellow}${nonCircPct.toFixed(1)}%${C.reset}`);
|
|
259
|
+
|
|
260
|
+
// Staked bar (subset of circulating or total)
|
|
261
|
+
const stakedOfTotal = stakedAeth / totalAeth * 100;
|
|
262
|
+
const stakedFill = Math.round((stakedOfTotal / 100) * barWidth);
|
|
263
|
+
const stakedBar = `${C.magenta}${'█'.repeat(stakedFill)}${C.dim}${'░'.repeat(barWidth - stakedFill)}${C.reset}`;
|
|
264
|
+
console.log(` ${C.magenta}●${C.reset} Staked ${stakedBar} ${C.magenta}${stakedOfTotal.toFixed(1)}%${C.reset}`);
|
|
265
|
+
|
|
266
|
+
console.log();
|
|
267
|
+
|
|
268
|
+
// Comparison with max supply if requested
|
|
269
|
+
if (compare) {
|
|
270
|
+
const pctOfMax = (totalAeth / MAX_SUPPLY_AETH) * 100;
|
|
271
|
+
const remaining = MAX_SUPPLY_AETH - totalAeth;
|
|
272
|
+
|
|
273
|
+
console.log(` ${C.bright}Comparison with Theoretical Max:${C.reset}\n`);
|
|
274
|
+
console.log(` ${C.dim}Max Supply:${C.reset} ${C.bright}${formatCompact(MAX_SUPPLY_AETH * 1e9)}${C.reset}`);
|
|
275
|
+
console.log(` ${C.dim}Current:${C.reset} ${C.cyan}${formatCompact(Number(total))}${C.reset}`);
|
|
276
|
+
console.log(` ${C.dim}Remaining:${C.reset} ${C.green}${formatCompact(remaining * 1e9)}${C.reset}`);
|
|
277
|
+
console.log(` ${C.dim}% of Max:${C.reset} ${C.bright}${pctOfMax.toFixed(4)}%${C.reset}`);
|
|
278
|
+
|
|
279
|
+
const maxFill = Math.round((pctOfMax / 100) * barWidth);
|
|
280
|
+
const maxBar = `${C.cyan}${'█'.repeat(maxFill)}${C.dim}${'░'.repeat(barWidth - maxFill)}${C.reset}`;
|
|
281
|
+
console.log(`\n ${C.dim}Supply Cap:${C.reset} ${maxBar} ${C.cyan}${pctOfMax.toFixed(2)}%${C.reset}`);
|
|
282
|
+
console.log();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Tokenomics stats table
|
|
286
|
+
const statsRows = [
|
|
287
|
+
['Metric', 'Value', 'Percentage'],
|
|
288
|
+
['─'.repeat(20), '─'.repeat(25), '─'.repeat(12)],
|
|
289
|
+
['Total Supply', formatAether(total), '100%'],
|
|
290
|
+
['Circulating', formatAether(circulating), `${circulatingPct.toFixed(2)}%`],
|
|
291
|
+
['Non-Circulating', formatAether(nonCirculating), `${nonCircPct.toFixed(2)}%`],
|
|
292
|
+
['Staked', formatAether(staked), `${stakedPct.toFixed(2)}%`],
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
console.log(` ${C.bright}Tokenomics Breakdown:${C.reset}\n`);
|
|
296
|
+
console.log(drawTable(['', '', ''], [
|
|
297
|
+
[`${C.cyan}Total Supply${C.reset}`, C.bright + formatAether(total) + C.reset, '100%'],
|
|
298
|
+
[`${C.green}Circulating${C.reset}`, formatAether(circulating), `${circulatingPct.toFixed(2)}%`],
|
|
299
|
+
[`${C.yellow}Non-Circulating${C.reset}`, formatAether(nonCirculating), `${nonCircPct.toFixed(2)}%`],
|
|
300
|
+
[`${C.magenta}Staked${C.reset}`, formatAether(staked), `${stakedPct.toFixed(2)}%`],
|
|
301
|
+
], {
|
|
302
|
+
borderStyle: 'single',
|
|
303
|
+
headerColor: C.bright,
|
|
304
|
+
}));
|
|
305
|
+
|
|
306
|
+
console.log();
|
|
307
|
+
|
|
308
|
+
// Network health indicator
|
|
309
|
+
const healthStatus = stakedPct > 50
|
|
310
|
+
? `${C.green}✓ Healthy${C.reset} - High stake ratio indicates network security`
|
|
311
|
+
: stakedPct > 30
|
|
312
|
+
? `${C.yellow}⚠ Moderate${C.reset} - Adequate stake ratio`
|
|
313
|
+
: `${C.red}✗ Low${C.reset} - Low stake ratio may indicate risk`;
|
|
314
|
+
|
|
315
|
+
console.log(` ${C.bright}Network Health:${C.reset} ${healthStatus}`);
|
|
316
|
+
console.log();
|
|
317
|
+
|
|
318
|
+
// Footer
|
|
319
|
+
console.log(` ${C.dim}Data fetched: ${data.fetchedAt}${C.reset}`);
|
|
320
|
+
console.log(` ${C.dim}RPC: ${opts.rpc}${C.reset}`);
|
|
321
|
+
console.log(` ${C.dim}SDK: @jellylegsai/aether-sdk${C.reset}`);
|
|
322
|
+
console.log();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function renderJson(data, rpc) {
|
|
326
|
+
const output = {
|
|
327
|
+
total: {
|
|
328
|
+
lamports: data.total.toString(),
|
|
329
|
+
aeth: (Number(data.total) / 1e9).toFixed(9),
|
|
330
|
+
formatted: formatAether(data.total),
|
|
331
|
+
},
|
|
332
|
+
circulating: {
|
|
333
|
+
lamports: data.circulating.toString(),
|
|
334
|
+
aeth: (Number(data.circulating) / 1e9).toFixed(9),
|
|
335
|
+
formatted: formatAether(data.circulating),
|
|
336
|
+
percentage: ((Number(data.circulating) / Number(data.total)) * 100).toFixed(2),
|
|
337
|
+
},
|
|
338
|
+
nonCirculating: {
|
|
339
|
+
lamports: data.nonCirculating.toString(),
|
|
340
|
+
aeth: (Number(data.nonCirculating) / 1e9).toFixed(9),
|
|
341
|
+
formatted: formatAether(data.nonCirculating),
|
|
342
|
+
percentage: ((Number(data.nonCirculating) / Number(data.total)) * 100).toFixed(2),
|
|
343
|
+
},
|
|
344
|
+
staked: {
|
|
345
|
+
lamports: data.staked.toString(),
|
|
346
|
+
aeth: (Number(data.staked) / 1e9).toFixed(9),
|
|
347
|
+
formatted: formatAether(data.staked),
|
|
348
|
+
percentage: ((Number(data.staked) / Number(data.total)) * 100).toFixed(2),
|
|
349
|
+
},
|
|
350
|
+
epoch: data.epoch,
|
|
351
|
+
slot: data.slot,
|
|
352
|
+
rpc,
|
|
353
|
+
fetched_at: data.fetchedAt,
|
|
354
|
+
cli_version: CLI_VERSION,
|
|
355
|
+
sdk: '@jellylegsai/aether-sdk',
|
|
356
|
+
};
|
|
357
|
+
console.log(JSON.stringify(output, null, 2));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Watch Mode
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
async function watchMode(rpc, compare) {
|
|
365
|
+
const clearScreen = () => {
|
|
366
|
+
process.stdout.write('\x1Bc');
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
let iteration = 0;
|
|
370
|
+
const spinnerFrames = ['◐', '◓', '◑', '◒'];
|
|
371
|
+
|
|
372
|
+
while (true) {
|
|
373
|
+
try {
|
|
374
|
+
clearScreen();
|
|
375
|
+
const data = await fetchSupplyData(rpc);
|
|
376
|
+
|
|
377
|
+
console.log(SUPPLY_LOGO);
|
|
378
|
+
console.log();
|
|
379
|
+
console.log(` ${C.dim}Watch mode enabled | Update ${iteration + 1} | Press Ctrl+C to exit${C.reset}`);
|
|
380
|
+
console.log();
|
|
381
|
+
|
|
382
|
+
// Simple inline display for watch mode
|
|
383
|
+
const totalAeth = Number(data.total) / 1e9;
|
|
384
|
+
const circAeth = Number(data.circulating) / 1e9;
|
|
385
|
+
const ncAeth = Number(data.nonCirculating) / 1e9;
|
|
386
|
+
const stakedAeth = Number(data.staked) / 1e9;
|
|
387
|
+
|
|
388
|
+
console.log(` ${C.bright}Total:${C.reset} ${C.cyan}${formatAether(data.total)}${C.reset}`);
|
|
389
|
+
console.log(` ${C.bright}Circulating:${C.reset} ${C.green}${formatAether(data.circulating)}${C.reset} (${((circAeth/totalAeth)*100).toFixed(2)}%)`);
|
|
390
|
+
console.log(` ${C.bright}Non-Circ:${C.reset} ${C.yellow}${formatAether(data.nonCirculating)}${C.reset} (${((ncAeth/totalAeth)*100).toFixed(2)}%)`);
|
|
391
|
+
console.log(` ${C.bright}Staked:${C.reset} ${C.magenta}${formatAether(data.staked)}${C.reset} (${((stakedAeth/totalAeth)*100).toFixed(2)}%)`);
|
|
392
|
+
console.log(` ${C.bright}Epoch:${C.reset} ${C.bright}${data.epoch}${C.reset}`);
|
|
393
|
+
console.log(` ${C.dim}Last update: ${data.fetchedAt}${C.reset}`);
|
|
394
|
+
|
|
395
|
+
if (compare) {
|
|
396
|
+
const pctOfMax = (totalAeth / MAX_SUPPLY_AETH) * 100;
|
|
397
|
+
console.log();
|
|
398
|
+
console.log(` ${C.dim}% of Max Supply: ${C.bright}${pctOfMax.toFixed(4)}%${C.reset}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log();
|
|
402
|
+
console.log(` ${C.dim}${spinnerFrames[iteration % 4]} Waiting ${WATCH_INTERVAL_MS/1000}s for next update...${C.reset}`);
|
|
403
|
+
|
|
404
|
+
iteration++;
|
|
405
|
+
await new Promise(r => setTimeout(r, WATCH_INTERVAL_MS));
|
|
406
|
+
} catch (err) {
|
|
407
|
+
console.log(`\n ${error('Watch mode error:')} ${err.message}`);
|
|
408
|
+
console.log(` ${dim('Retrying in 5s...')}`);
|
|
409
|
+
await new Promise(r => setTimeout(r, WATCH_INTERVAL_MS));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// Main Command
|
|
416
|
+
// ============================================================================
|
|
417
|
+
|
|
418
|
+
async function supplyCommand() {
|
|
419
|
+
const opts = parseArgs();
|
|
420
|
+
|
|
421
|
+
if (opts.help) {
|
|
422
|
+
showHelp();
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Handle watch mode
|
|
427
|
+
if (opts.watch) {
|
|
428
|
+
console.log(`${info('Starting watch mode... Press Ctrl+C to exit')}`);
|
|
429
|
+
await watchMode(opts.rpc, opts.compare);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (!opts.asJson) {
|
|
434
|
+
startSpinner('Fetching supply data via SDK');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const data = await fetchSupplyData(opts.rpc);
|
|
439
|
+
|
|
440
|
+
if (!opts.asJson) {
|
|
441
|
+
stopSpinner(true, 'Supply data retrieved');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (opts.asJson) {
|
|
445
|
+
renderJson(data, opts.rpc);
|
|
446
|
+
} else {
|
|
447
|
+
renderSupplyBox(data, opts);
|
|
448
|
+
}
|
|
449
|
+
} catch (err) {
|
|
450
|
+
if (!opts.asJson) {
|
|
451
|
+
stopSpinner(false, 'Failed');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (opts.asJson) {
|
|
455
|
+
console.log(JSON.stringify({
|
|
456
|
+
error: err.message,
|
|
457
|
+
rpc: opts.rpc,
|
|
458
|
+
timestamp: new Date().toISOString(),
|
|
459
|
+
}, null, 2));
|
|
460
|
+
} else {
|
|
461
|
+
console.log(`\n ${error('Supply query failed:')} ${err.message}\n`);
|
|
462
|
+
console.log(` ${dim('Troubleshooting:')}`);
|
|
463
|
+
console.log(` • Is your validator running? ${C.cyan}aether ping${C.reset}`);
|
|
464
|
+
console.log(` • Check RPC endpoint: ${C.dim}${opts.rpc}${C.reset}`);
|
|
465
|
+
console.log(` • Set custom RPC: ${C.dim}AETHER_RPC=https://your-rpc-url${C.reset}`);
|
|
466
|
+
console.log();
|
|
467
|
+
}
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ============================================================================
|
|
473
|
+
// Exports
|
|
474
|
+
// ============================================================================
|
|
475
|
+
|
|
476
|
+
module.exports = { supplyCommand };
|
|
477
|
+
|
|
478
|
+
if (require.main === module) {
|
|
479
|
+
supplyCommand().catch(err => {
|
|
480
|
+
console.error(`\n${C.red}✗ Supply command failed:${C.reset} ${err.message}\n`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
});
|
|
483
|
+
}
|