@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,275 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli epoch
4
+ *
5
+ * Display current epoch information including timing, schedule,
6
+ * slots per epoch, and estimated staking rewards rate.
7
+ *
8
+ * Usage:
9
+ * aether epoch Show current epoch with timing breakdown
10
+ * aether epoch --json JSON output for scripting/monitoring
11
+ * aether epoch --rpc <url> Query a specific RPC endpoint
12
+ * aether epoch --schedule Show upcoming epoch schedule
13
+ *
14
+ * Requires AETHER_RPC env var (default: http://127.0.0.1:8899)
15
+ */
16
+
17
+ const path = require('path');
18
+
19
+ // ANSI colours
20
+ const C = {
21
+ reset: '\x1b[0m',
22
+ bright: '\x1b[1m',
23
+ dim: '\x1b[2m',
24
+ red: '\x1b[31m',
25
+ green: '\x1b[32m',
26
+ yellow: '\x1b[33m',
27
+ cyan: '\x1b[36m',
28
+ magenta: '\x1b[35m',
29
+ };
30
+
31
+ const CLI_VERSION = '1.0.0';
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // SDK Import - Real blockchain RPC calls via @jellylegsai/aether-sdk
35
+ // ---------------------------------------------------------------------------
36
+
37
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
38
+ const aether = require(sdkPath);
39
+
40
+ function getDefaultRpc() {
41
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Argument parsing
46
+ // ---------------------------------------------------------------------------
47
+
48
+ function parseArgs() {
49
+ const args = process.argv.slice(3); // [node, index.js, epoch, ...]
50
+ return {
51
+ rpc: getDefaultRpc(),
52
+ asJson: args.indexOf('--json') !== -1 || args.indexOf('-j') !== -1,
53
+ showSchedule: args.indexOf('--schedule') !== -1 || args.indexOf('-s') !== -1,
54
+ rpcUrl: getDefaultRpc(),
55
+ };
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Fetch epoch info from RPC using SDK
60
+ // ---------------------------------------------------------------------------
61
+
62
+ async function fetchEpochInfo(rpc) {
63
+ // Use SDK for real blockchain RPC calls
64
+ const client = new aether.AetherClient({ rpcUrl: rpc });
65
+ try {
66
+ const epochInfo = await client.getEpochInfo();
67
+ if (epochInfo && (epochInfo.epoch !== undefined || epochInfo.current_epoch)) {
68
+ return { data: epochInfo, source: 'aether-sdk' };
69
+ }
70
+ } catch(e) {
71
+ throw new Error('Failed to fetch epoch info from RPC. Is your validator running?');
72
+ }
73
+
74
+ throw new Error('Failed to fetch epoch info from RPC. Is your validator running?');
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Format helpers
79
+ // ---------------------------------------------------------------------------
80
+
81
+ function formatAether(lamports) {
82
+ const aeth = lamports / 1e9;
83
+ if (aeth === 0) return '0 AETH';
84
+ return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
85
+ }
86
+
87
+ function formatDuration(seconds) {
88
+ if (seconds < 0) return '\u2014';
89
+ const h = Math.floor(seconds / 3600);
90
+ const m = Math.floor((seconds % 3600) / 60);
91
+ const s = Math.floor(seconds % 60);
92
+ if (h > 24) {
93
+ const d = Math.floor(h / 24);
94
+ return d + 'd ' + (h % 24) + 'h';
95
+ }
96
+ if (h > 0) return h + 'h ' + m + 'm';
97
+ if (m > 0) return m + 'm ' + s + 's';
98
+ return s + 's';
99
+ }
100
+
101
+ function fmtPct(value, decimals) {
102
+ decimals = decimals || 1;
103
+ return (value || 0).toFixed(decimals) + '%';
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Box drawing helpers
108
+ // ---------------------------------------------------------------------------
109
+
110
+ // Box drawing characters
111
+ const BOX_H = '\u2500'; // ─
112
+ const BOX_V = '\u2502'; // │
113
+ const BOX_TL = '\u256d'; // ╭
114
+ const BOX_TR = '\u256e'; // ╮
115
+ const BOX_BL = '\u2570'; // ╰
116
+ const BOX_BR = '\u256f'; // ╯
117
+ const BOX_Cross = '\u253c'; // ┼
118
+
119
+ function makeBoxLine(chars, width) {
120
+ let s = '';
121
+ for (let i = 0; i < width; i++) s += chars;
122
+ return s;
123
+ }
124
+
125
+ function makeSectionHeader(label) {
126
+ const total = 62;
127
+ const labelWithSpaces = ' ' + label + ' ';
128
+ const remaining = total - 4 - labelWithSpaces.length; // 4 for ╼ on each side
129
+ const half = Math.floor(remaining / 2);
130
+ const left = makeBoxLine('\u2550', half);
131
+ const right = makeBoxLine('\u2550', remaining - half);
132
+ return C.bright + C.cyan + '\u256e' + left + '\u2554' + labelWithSpaces + '\u2557' + right + '\u256f' + C.reset;
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Main display
137
+ // ---------------------------------------------------------------------------
138
+
139
+ async function showEpochInfo(opts) {
140
+ const rpc = opts.rpcUrl;
141
+ const { data, source } = await fetchEpochInfo(rpc);
142
+
143
+ // Normalise fields across different RPC response formats
144
+ const epoch = data.epoch ?? data.current_epoch ?? 0;
145
+ const slotIndex = data.slotIndex ?? data.current_slot ?? 0;
146
+ const slotsInEpoch = data.slotsInEpoch ?? data.slots_per_epoch ?? 8192;
147
+ const epochProgress = slotsInEpoch > 0 ? (slotIndex / slotsInEpoch) * 100 : 0;
148
+ const absoluteSlot = data.absoluteSlot ?? data.slot ?? 0;
149
+ const totalStaked = BigInt(data.totalStaked ?? data.total_staked ?? data.stake ?? 0);
150
+ const rewardsPerEpoch = BigInt(data.rewardsPerEpoch ?? data.rewards_per_epoch ?? data.rewards ?? 0);
151
+
152
+ // Estimate seconds per slot from slot data
153
+ const epochDurationSecs = data.epochDurationSecs ?? (slotsInEpoch * 0.4); // ~400ms/slot default
154
+ const secsPerSlot = epochDurationSecs / slotsInEpoch;
155
+ const secondsIntoEpoch = slotIndex * secsPerSlot;
156
+ const secondsRemaining = (slotsInEpoch - slotIndex) * secsPerSlot;
157
+
158
+ // APY estimate: rewards per epoch / total staked * epochs per year
159
+ const epochsPerYear = 365 * 24 * 3600 / epochDurationSecs;
160
+ const apyRate = totalStaked > 0n
161
+ ? (Number(rewardsPerEpoch) / Number(totalStaked)) * epochsPerYear
162
+ : 0;
163
+ const apyBps = Math.round(apyRate * 10000);
164
+
165
+ if (opts.asJson) {
166
+ const out = {
167
+ epoch: epoch,
168
+ slotIndex: slotIndex,
169
+ slotsInEpoch: slotsInEpoch,
170
+ absoluteSlot: absoluteSlot,
171
+ epochProgress: epochProgress,
172
+ secondsIntoEpoch: Math.round(secondsIntoEpoch),
173
+ secondsRemaining: Math.round(secondsRemaining),
174
+ totalStaked: totalStaked.toString(),
175
+ totalStakedFormatted: formatAether(totalStaked),
176
+ rewardsPerEpoch: rewardsPerEpoch.toString(),
177
+ rewardsPerEpochFormatted: formatAether(rewardsPerEpoch),
178
+ estimatedApyBps: apyBps,
179
+ estimatedApy: fmtPct(apyRate),
180
+ source: source,
181
+ fetchedAt: new Date().toISOString(),
182
+ };
183
+ console.log(JSON.stringify(out, null, 2));
184
+ return;
185
+ }
186
+
187
+ // ASCII art header
188
+ console.log('');
189
+ const line1 = C.bright + C.cyan + BOX_TL + makeBoxLine(BOX_H, 60) + BOX_TR + C.reset;
190
+ console.log(line1);
191
+ const line2 = C.bright + C.cyan + BOX_V + ' AeTHer Epoch ' + epoch + ' Info ' + BOX_V + C.reset;
192
+ console.log(line2);
193
+ const line3 = C.bright + C.cyan + BOX_BL + makeBoxLine(BOX_H, 60) + BOX_BR + C.reset;
194
+ console.log(line3);
195
+ console.log('');
196
+
197
+ console.log(' ' + C.dim + 'RPC: ' + rpc + C.reset);
198
+ console.log('');
199
+
200
+ // ── Epoch timing ───────────────────────────────────────────────────────
201
+ console.log(makeSectionHeader('Epoch Timing'));
202
+
203
+ const progressBars = 40;
204
+ const filled = Math.round((epochProgress / 100) * progressBars);
205
+ const empty = progressBars - filled;
206
+ const bar = C.green + '#'.repeat(filled) + C.dim + '\u2500'.repeat(empty) + C.reset;
207
+
208
+ console.log(' ' + C.dim + ' Progress: [' + bar + '] ' + C.bright + fmtPct(epochProgress) + C.reset);
209
+ console.log(' ' + C.dim + ' Slot: ' + C.reset + C.bright + slotIndex.toLocaleString() + C.reset + ' / ' + slotsInEpoch.toLocaleString() + ' slots into epoch');
210
+ console.log(' ' + C.dim + ' Abs slot: ' + C.reset + absoluteSlot.toLocaleString());
211
+ console.log(' ' + C.dim + ' Elapsed: ' + C.reset + formatDuration(Math.round(secondsIntoEpoch)));
212
+ console.log(' ' + C.dim + ' Remaining: ' + C.reset + C.yellow + formatDuration(Math.round(secondsRemaining)) + C.reset);
213
+ console.log(' ' + C.dim + ' Duration: ' + C.reset + '~' + formatDuration(Math.round(epochDurationSecs)) + ' per epoch');
214
+ console.log('');
215
+
216
+ // ── Staking rewards ─────────────────────────────────────────────────────
217
+ console.log(makeSectionHeader('Staking Rewards'));
218
+
219
+ console.log(' ' + C.dim + ' Network stake: ' + C.reset + C.bright + formatAether(totalStaked) + C.reset);
220
+ console.log(' ' + C.dim + ' Rewards/epoch: ' + C.reset + C.green + formatAether(rewardsPerEpoch) + C.reset);
221
+ console.log(' ' + C.dim + ' Estimated APY: ' + C.reset + C.green + C.bright + fmtPct(apyRate) + C.reset + ' ' + C.dim + '(~' + (apyBps / 100).toFixed(0) + ' bps)' + C.reset);
222
+ console.log('');
223
+
224
+ // ── Epoch schedule ──────────────────────────────────────────────────────
225
+ if (opts.showSchedule) {
226
+ console.log(makeSectionHeader('Upcoming Epochs'));
227
+ const startSlotNext = absoluteSlot + (slotsInEpoch - slotIndex);
228
+ for (let i = 0; i < 5; i++) {
229
+ const e = epoch + i;
230
+ const start = startSlotNext + i * slotsInEpoch;
231
+ const end = start + slotsInEpoch - 1;
232
+ const isNext = i === 0 ? ' ' + C.green + '(next)' + C.reset : '';
233
+ console.log(' ' + C.dim + ' Epoch ' + String(e).padStart(4) + ': slots ' + start.toLocaleString() + ' \u2013 ' + end.toLocaleString() + isNext + C.reset);
234
+ }
235
+ console.log('');
236
+ }
237
+
238
+ // ── Raw data ─────────────────────────────────────────────────────────────
239
+ console.log(makeSectionHeader('Raw RPC Data'));
240
+ console.log(' ' + C.dim + ' Source: ' + source + C.reset);
241
+ const rawPreview = JSON.stringify(data).substring(0, 80);
242
+ console.log(' ' + C.dim + ' ' + rawPreview + C.reset);
243
+ console.log('');
244
+
245
+ console.log(' ' + C.dim + 'Run "aether validators list" to see validator performance for epoch ' + epoch + '.' + C.reset);
246
+ console.log(' ' + C.dim + 'Run "aether rewards list --address <addr>" to check your staking rewards.' + C.reset);
247
+ console.log('');
248
+ }
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // Main entry point
252
+ // ---------------------------------------------------------------------------
253
+
254
+ async function epochCommand() {
255
+ const opts = parseArgs();
256
+ try {
257
+ await showEpochInfo(opts);
258
+ } catch (err) {
259
+ if (opts.asJson) {
260
+ console.log(JSON.stringify({ error: err.message }, null, 2));
261
+ } else {
262
+ console.log('');
263
+ console.log(' ' + C.red + '\u2514 Error: ' + C.reset + ' ' + err.message);
264
+ console.log(' ' + C.dim + 'Set a custom RPC: AETHER_RPC=https://your-rpc-url' + C.reset);
265
+ console.log('');
266
+ }
267
+ process.exit(1);
268
+ }
269
+ }
270
+
271
+ module.exports = { epochCommand };
272
+
273
+ if (require.main === module) {
274
+ epochCommand();
275
+ }
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli fees
4
+ *
5
+ * Query current network fee estimates for Aether transactions.
6
+ * Shows priority fee tiers (low, medium, high) and recent average fees.
7
+ * Uses @jellylegsai/aether-sdk for real RPC calls to /v1/fees.
8
+ *
9
+ * Usage:
10
+ * aether fees Show current fee estimates
11
+ * aether fees --json JSON output for scripting
12
+ * aether fees --verbose Show detailed fee breakdown
13
+ * aether fees --rpc <url> Custom RPC endpoint
14
+ */
15
+
16
+ const path = require('path');
17
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
18
+ const aether = require(sdkPath);
19
+
20
+ // ANSI colours
21
+ const C = {
22
+ reset: '\x1b[0m',
23
+ bright: '\x1b[1m',
24
+ dim: '\x1b[2m',
25
+ red: '\x1b[31m',
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ cyan: '\x1b[36m',
29
+ magenta: '\x1b[35m',
30
+ };
31
+
32
+ // Fee priority levels
33
+ const PRIORITY_LEVELS = {
34
+ LOW: 'low',
35
+ MEDIUM: 'medium',
36
+ HIGH: 'high',
37
+ TURBO: 'turbo',
38
+ };
39
+
40
+ /**
41
+ * Fetch fee data from Aether RPC endpoint using SDK
42
+ * Real RPC call: GET /v1/fees
43
+ */
44
+ async function fetchFromRpc(rpcUrl) {
45
+ const client = new aether.AetherClient({ rpcUrl });
46
+ try {
47
+ const fees = await client.getFees();
48
+ if (fees && fees.fee !== undefined) {
49
+ return {
50
+ baseFee: fees.baseFee || fees.fee || 5000,
51
+ prioritizationFee: fees.prioritizationFee || 0,
52
+ totalFee: fees.totalFee || fees.fee || 5000,
53
+ raw: fees,
54
+ };
55
+ }
56
+ return null;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Generate simulated fee estimates as fallback
64
+ * Used when RPC endpoint is unavailable
65
+ */
66
+ function generateSimulatedFees() {
67
+ const baseFee = 5000;
68
+ const congestionFactor = 1.0 + (Math.random() * 0.5);
69
+
70
+ return {
71
+ baseFee,
72
+ levels: {
73
+ [PRIORITY_LEVELS.LOW]: {
74
+ lamports: Math.round(baseFee * 1.0 * congestionFactor),
75
+ aeth: (baseFee * 1.0 * congestionFactor / 1e9).toFixed(9),
76
+ description: 'Economy - may take longer during congestion',
77
+ color: C.green,
78
+ },
79
+ [PRIORITY_LEVELS.MEDIUM]: {
80
+ lamports: Math.round(baseFee * 1.5 * congestionFactor),
81
+ aeth: (baseFee * 1.5 * congestionFactor / 1e9).toFixed(9),
82
+ description: 'Standard - typical confirmation time',
83
+ color: C.cyan,
84
+ },
85
+ [PRIORITY_LEVELS.HIGH]: {
86
+ lamports: Math.round(baseFee * 2.5 * congestionFactor),
87
+ aeth: (baseFee * 2.5 * congestionFactor / 1e9).toFixed(9),
88
+ description: 'Fast - priority during high congestion',
89
+ color: C.yellow,
90
+ },
91
+ [PRIORITY_LEVELS.TURBO]: {
92
+ lamports: Math.round(baseFee * 5.0 * congestionFactor),
93
+ aeth: (baseFee * 5.0 * congestionFactor / 1e9).toFixed(9),
94
+ description: 'Maximum - fastest confirmation',
95
+ color: C.magenta,
96
+ },
97
+ },
98
+ averageFee24h: Math.round(baseFee * 1.8 * congestionFactor),
99
+ medianFee24h: Math.round(baseFee * 1.5 * congestionFactor),
100
+ source: 'Aether Network (simulated)',
101
+ timestamp: new Date(),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Build fee data from RPC response
107
+ */
108
+ function buildFeeData(rpcFees) {
109
+ const baseFee = rpcFees.baseFee || 5000;
110
+ const totalFee = rpcFees.totalFee || baseFee;
111
+
112
+ return {
113
+ baseFee,
114
+ levels: {
115
+ [PRIORITY_LEVELS.LOW]: {
116
+ lamports: totalFee,
117
+ aeth: (totalFee / 1e9).toFixed(9),
118
+ description: 'Economy',
119
+ color: C.green,
120
+ },
121
+ [PRIORITY_LEVELS.MEDIUM]: {
122
+ lamports: Math.round(totalFee * 1.5),
123
+ aeth: (totalFee * 1.5 / 1e9).toFixed(9),
124
+ description: 'Standard',
125
+ color: C.cyan,
126
+ },
127
+ [PRIORITY_LEVELS.HIGH]: {
128
+ lamports: Math.round(totalFee * 2.5),
129
+ aeth: (totalFee * 2.5 / 1e9).toFixed(9),
130
+ description: 'Fast',
131
+ color: C.yellow,
132
+ },
133
+ [PRIORITY_LEVELS.TURBO]: {
134
+ lamports: Math.round(totalFee * 5),
135
+ aeth: (totalFee * 5 / 1e9).toFixed(9),
136
+ description: 'Maximum',
137
+ color: C.magenta,
138
+ },
139
+ },
140
+ averageFee24h: totalFee,
141
+ medianFee24h: totalFee,
142
+ source: 'Aether RPC',
143
+ timestamp: new Date(),
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Format lamports to human-readable string
149
+ */
150
+ function formatLamports(lamports) {
151
+ if (lamports >= 1e9) {
152
+ return `${(lamports / 1e9).toFixed(6)} AETH`;
153
+ } else if (lamports >= 1e6) {
154
+ return `${(lamports / 1e6).toFixed(3)} mAETH`;
155
+ } else if (lamports >= 1e3) {
156
+ return `${(lamports / 1e3).toFixed(1)} µAETH`;
157
+ }
158
+ return `${lamports} lamports`;
159
+ }
160
+
161
+ /**
162
+ * Format timestamp
163
+ */
164
+ function formatTime(date) {
165
+ return date.toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
166
+ }
167
+
168
+ /**
169
+ * Main fees command
170
+ */
171
+ async function feesCommand() {
172
+ const args = process.argv.slice(2);
173
+ const asJson = args.includes('--json') || args.includes('-j');
174
+ const verbose = args.includes('--verbose') || args.includes('-v');
175
+ const rpcUrl = args.includes('--rpc')
176
+ ? args[args.indexOf('--rpc') + 1]
177
+ : process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
178
+
179
+ if (!asJson) {
180
+ console.log(`\n${C.bright}${C.cyan}── Aether Network Fees ──────────────────────────────────${C.reset}\n`);
181
+ }
182
+
183
+ // Fetch real fee data from RPC using SDK
184
+ let feeData = null;
185
+ let source = 'Simulated';
186
+
187
+ try {
188
+ const rpcFees = await fetchFromRpc(rpcUrl);
189
+ if (rpcFees) {
190
+ feeData = buildFeeData(rpcFees);
191
+ source = 'Aether RPC';
192
+ }
193
+ } catch { /* RPC not available */ }
194
+
195
+ // Use simulated fees as fallback
196
+ if (!feeData) {
197
+ feeData = generateSimulatedFees();
198
+ source = 'Simulated';
199
+ }
200
+
201
+ // JSON output
202
+ if (asJson) {
203
+ const output = {
204
+ source: feeData.source,
205
+ timestamp: formatTime(feeData.timestamp),
206
+ base_fee_lamports: feeData.baseFee,
207
+ priority_levels: Object.fromEntries(
208
+ Object.entries(feeData.levels).map(([key, val]) => [
209
+ key,
210
+ { lamports: val.lamports, aeth: parseFloat(val.aeth) }
211
+ ])
212
+ ),
213
+ average_fee_24h_lamports: feeData.averageFee24h,
214
+ median_fee_24h_lamports: feeData.medianFee24h,
215
+ };
216
+ console.log(JSON.stringify(output, null, 2));
217
+ return;
218
+ }
219
+
220
+ // Human-readable output
221
+ console.log(` ${C.dim}Source:${C.reset} ${C.bright}${feeData.source}${C.reset}`);
222
+ console.log(` ${C.dim}Updated:${C.reset} ${formatTime(feeData.timestamp)}`);
223
+ console.log(` ${C.dim}Base Fee:${C.reset} ${formatLamports(feeData.baseFee)}`);
224
+ console.log();
225
+
226
+ console.log(` ${C.bright}Priority Levels:${C.reset}\n`);
227
+ console.log(` ┌─────────────┬──────────────────────┬─────────────────────────────────────┐`);
228
+ console.log(` │ ${C.bright}Level${C.reset} │ ${C.bright}Fee${C.reset} │ ${C.bright}Description${C.reset} │`);
229
+ console.log(` ├─────────────┼──────────────────────┼─────────────────────────────────────┤`);
230
+
231
+ Object.entries(feeData.levels).forEach(([level, info]) => {
232
+ const levelName = level.charAt(0) + level.slice(1).toLowerCase();
233
+ const feeStr = `${info.color}${formatLamports(info.lamports).padEnd(20)}${C.reset}`;
234
+ const descStr = info.description.padEnd(35);
235
+ console.log(` │ ${levelName.padEnd(11)} │ ${feeStr} │ ${descStr} │`);
236
+ });
237
+
238
+ console.log(` └─────────────┴──────────────────────┴─────────────────────────────────────┘`);
239
+ console.log();
240
+
241
+ // 24h statistics
242
+ console.log(` ${C.dim}24h Statistics:${C.reset}`);
243
+ console.log(` ${C.dim}Average:${C.reset} ${formatLamports(feeData.averageFee24h)}`);
244
+ console.log(` ${C.dim}Median:${C.reset} ${formatLamports(feeData.medianFee24h)}`);
245
+ console.log();
246
+
247
+ // Verbose mode - show additional details
248
+ if (verbose) {
249
+ console.log(` ${C.bright}Fee Breakdown:${C.reset}\n`);
250
+ console.log(` ${C.dim}Base Fee:${C.reset} ${formatLamports(feeData.baseFee)}`);
251
+ console.log(` ${C.dim}Priority Multiplier:${C.reset} 1.0x - 5.0x (based on urgency)`);
252
+ console.log(` ${C.dim}Network Congestion:${C.reset} ${Math.round((feeData.averageFee24h / feeData.baseFee - 1) * 100)}% above base`);
253
+ console.log();
254
+
255
+ console.log(` ${C.bright}Recommendations:${C.reset}\n`);
256
+ console.log(` • Use ${C.cyan}Standard${C.reset} for routine transactions`);
257
+ console.log(` • Use ${C.yellow}Fast${C.reset} during network congestion or for time-sensitive ops`);
258
+ console.log(` • Use ${C.magenta}Maximum${C.reset} for validator operations or large transfers`);
259
+ console.log();
260
+ }
261
+
262
+ // Usage tip
263
+ console.log(` ${C.dim}Tip: Set ${C.cyan}--priority${C.reset}${C.dim} flag when submitting transactions to choose fee level.${C.reset}`);
264
+ console.log(` ${C.dim}Example: ${C.cyan}aether transfer --to <addr> --amount 10 --priority high${C.reset}\n`);
265
+ }
266
+
267
+ // Export for use in index.js
268
+ module.exports = { feesCommand };
269
+
270
+ // Run if called directly
271
+ if (require.main === module) {
272
+ feesCommand().catch(err => {
273
+ console.error(`\n${C.red}Fees error:${C.reset} ${err.message}\n`);
274
+ process.exit(1);
275
+ });
276
+ }
@@ -0,0 +1,78 @@
1
+ // Export all command modules
2
+ const doctorCommand = require('./doctor');
3
+ const initCommand = require('./init');
4
+ const validatorStartCommand = require('./validator-start');
5
+ const validatorStatusCommand = require('./validator-status');
6
+ const validatorInfoCommand = require('./validator-info');
7
+ const validatorRegisterCommand = require('./validator-register');
8
+ const delegationsCommand = require('./delegations');
9
+ const rewardsCommand = require('./rewards');
10
+ const walletCommand = require('./wallet');
11
+ const balanceCommand = require('./balance');
12
+ const transferCommand = require('./transfer');
13
+ const txHistoryCommand = require('./tx-history');
14
+ const multisigCommand = require('./multisig');
15
+ const claimCommand = require('./claim');
16
+ const unstakeCommand = require('./unstake');
17
+ const stakeCommand = require('./stake');
18
+ const stakePositionsCommand = require('./stake-positions');
19
+ const networkCommand = require('./network');
20
+ const monitorLoop = require('./monitor');
21
+ const logsCommand = require('./logs');
22
+ const sdkCommand = require('./sdk');
23
+ const sdkTestCommand = require('./sdk-test');
24
+ const configCommand = require('./config');
25
+ const epochCommand = require('./epoch');
26
+ const supplyCommand = require('./supply');
27
+ const statusCommand = require('./status');
28
+ const validatorsListCommand = require('./validators');
29
+ const blockhashCommand = require('./blockhash');
30
+ const tpsCommand = require('./tps');
31
+ const feesCommand = require('./fees');
32
+ const apyCommand = require('./apy');
33
+ const broadcastCommand = require('./broadcast');
34
+ const accountCommand = require('./account');
35
+ const priceCommand = require('./price');
36
+ const emergencyCommand = require('./emergency');
37
+ const snapshotCommand = require('./snapshot');
38
+ const nftCommand = require('./nft');
39
+
40
+ module.exports = {
41
+ doctorCommand,
42
+ initCommand,
43
+ validatorStartCommand,
44
+ validatorStatusCommand,
45
+ validatorInfoCommand,
46
+ validatorRegisterCommand,
47
+ delegationsCommand,
48
+ rewardsCommand,
49
+ walletCommand,
50
+ balanceCommand,
51
+ transferCommand,
52
+ txHistoryCommand,
53
+ multisigCommand,
54
+ claimCommand,
55
+ unstakeCommand,
56
+ stakeCommand,
57
+ stakePositionsCommand,
58
+ networkCommand,
59
+ monitorLoop,
60
+ logsCommand,
61
+ sdkCommand,
62
+ sdkTestCommand,
63
+ configCommand,
64
+ epochCommand,
65
+ supplyCommand,
66
+ statusCommand,
67
+ validatorsListCommand,
68
+ blockhashCommand,
69
+ tpsCommand,
70
+ feesCommand,
71
+ apyCommand,
72
+ broadcastCommand,
73
+ accountCommand,
74
+ priceCommand,
75
+ emergencyCommand,
76
+ snapshotCommand,
77
+ nftCommand,
78
+ };