aether-hub 1.2.7 → 1.3.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.
- package/commands/account.js +280 -280
- package/commands/apy.js +480 -480
- package/commands/balance.js +276 -276
- package/commands/claim.js +43 -77
- package/commands/delegations.js +412 -462
- package/commands/emergency.js +607 -657
- package/commands/epoch.js +275 -275
- package/commands/fees.js +276 -276
- package/commands/info.js +495 -495
- package/commands/monitor.js +431 -431
- package/commands/multisig.js +685 -726
- package/commands/network.js +429 -429
- package/commands/ping.js +266 -266
- package/commands/rewards.js +479 -866
- package/commands/sdk-test.js +477 -477
- package/commands/sdk.js +537 -537
- package/commands/slot.js +155 -0
- package/commands/snapshot.js +509 -509
- package/commands/stake-positions.js +205 -220
- package/commands/stats.js +418 -418
- package/commands/status.js +326 -326
- package/commands/supply.js +3 -1
- package/commands/tps.js +238 -238
- package/commands/tx-history.js +52 -168
- package/commands/wallet.js +3 -0
- package/index.js +42 -43
- package/package.json +3 -1
- package/sdk/README.md +210 -0
- package/sdk/index.js +1013 -0
- package/sdk/package.json +33 -0
- package/sdk/rpc.js +108 -0
- package/sdk/test.js +85 -0
- package/theme.js +211 -0
package/commands/fees.js
CHANGED
|
@@ -1,276 +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
|
-
}
|
|
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
|
+
}
|