aether-hub 1.2.6 → 1.2.7
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 +10 -34
- package/commands/balance.js +276 -0
- package/commands/blockhash.js +181 -0
- package/commands/broadcast.js +323 -323
- package/commands/claim.js +292 -0
- package/commands/emergency.js +657 -657
- package/commands/epoch.js +12 -94
- package/commands/fees.js +276 -0
- package/commands/info.js +38 -79
- package/commands/network.js +34 -108
- package/commands/ping.js +11 -65
- package/commands/price.js +253 -253
- package/commands/sdk-test.js +477 -0
- package/commands/stake-info.js +139 -0
- package/commands/status.js +113 -157
- package/commands/supply.js +34 -82
- package/commands/tps.js +238 -0
- package/commands/transfer.js +495 -0
- package/commands/tx-history.js +462 -508
- package/commands/validator-info.js +10 -4
- package/commands/validator-start.js +1 -1
- package/commands/validator-status.js +32 -73
- package/commands/validators.js +36 -75
- package/commands/wallet.js +5 -29
- package/index.js +54 -6
- package/package.json +1 -3
package/commands/price.js
CHANGED
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* aether-cli price
|
|
4
|
-
*
|
|
5
|
-
* Show real-time AETH/USD price from free public crypto APIs.
|
|
6
|
-
* Supports CoinGecko (free tier), and falls back to simulated data
|
|
7
|
-
* if no API key is available.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* aether price Show current AETH/USD price
|
|
11
|
-
* aether price --pair AETH/USD Specify trading pair (default: AETH/USD)
|
|
12
|
-
* aether price --json JSON output for scripting
|
|
13
|
-
* aether price --source coingecko Fallback to CoinGecko (no API key needed)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const https = require('https');
|
|
17
|
-
const http = require('http');
|
|
18
|
-
|
|
19
|
-
const C = {
|
|
20
|
-
reset: '\x1b[0m',
|
|
21
|
-
bright: '\x1b[1m',
|
|
22
|
-
dim: '\x1b[2m',
|
|
23
|
-
red: '\x1b[31m',
|
|
24
|
-
green: '\x1b[32m',
|
|
25
|
-
yellow: '\x1b[33m',
|
|
26
|
-
cyan: '\x1b[36m',
|
|
27
|
-
magenta: '\x1b[35m',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const AETHER_CONTRACT = 'ATH'; // Aether token contract (hypothetical)
|
|
31
|
-
const DEFAULT_PAIR = 'AETH/USD';
|
|
32
|
-
|
|
33
|
-
function httpGet(url) {
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
const lib = url.startsWith('https') ? https : http;
|
|
36
|
-
const parsed = new URL(url);
|
|
37
|
-
const req = lib.request({
|
|
38
|
-
hostname: parsed.hostname,
|
|
39
|
-
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
40
|
-
path: parsed.pathname + parsed.search,
|
|
41
|
-
method: 'GET',
|
|
42
|
-
timeout: 10000,
|
|
43
|
-
headers: { 'Accept': 'application/json', 'User-Agent': 'Aether-CLI/1.0' },
|
|
44
|
-
}, (res) => {
|
|
45
|
-
let data = '';
|
|
46
|
-
res.on('data', (chunk) => data += chunk);
|
|
47
|
-
res.on('end', () => {
|
|
48
|
-
try { resolve(JSON.parse(data)); }
|
|
49
|
-
catch { resolve({ _raw: data }); }
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
req.on('error', reject);
|
|
53
|
-
req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
|
|
54
|
-
req.end();
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function formatPrice(num, decimals = 4) {
|
|
59
|
-
if (num === null || num === undefined || isNaN(num)) return '—';
|
|
60
|
-
return num.toFixed(decimals);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function formatTime(date) {
|
|
64
|
-
return date.toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Fetch AETH price from CoinGecko (free, no API key).
|
|
69
|
-
* Coins API: /coins/list → find ATH/AETH → /coins/{id}/market_chart
|
|
70
|
-
* Fallback: try known Aether token addresses on major DEXes via /simple/price
|
|
71
|
-
*/
|
|
72
|
-
async function fetchFromCoinGecko() {
|
|
73
|
-
try {
|
|
74
|
-
// Try CoinGecko simple price for AETH token
|
|
75
|
-
// We'll try a few known Aether token addresses on Ethereum mainnet as a proxy
|
|
76
|
-
const url = 'https://api.coingecko.com/api/v3/simple/price?vs_currencies=usd&ids=aether,ath,ath-token,aether-network';
|
|
77
|
-
const data = await httpGet(url);
|
|
78
|
-
|
|
79
|
-
if (data && !data.error) {
|
|
80
|
-
// Find first available AETH quote
|
|
81
|
-
const pairs = [
|
|
82
|
-
{ key: 'aether-network', name: 'AETH' },
|
|
83
|
-
{ key: 'aether', name: 'AETH' },
|
|
84
|
-
{ key: 'ath-token', name: 'ATH' },
|
|
85
|
-
{ key: 'ath', name: 'ATH' },
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
for (const { key, name } of pairs) {
|
|
89
|
-
if (data[key] && data[key].usd !== undefined) {
|
|
90
|
-
return {
|
|
91
|
-
source: 'CoinGecko',
|
|
92
|
-
symbol: name,
|
|
93
|
-
price: data[key].usd,
|
|
94
|
-
currency: 'USD',
|
|
95
|
-
timestamp: new Date(),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return null;
|
|
102
|
-
} catch {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Fetch price data from a public DEX aggregator or mock Aether RPC.
|
|
109
|
-
* Primary: use the Aether chain's own price oracle if available.
|
|
110
|
-
* Fallback: use CoinGecko.
|
|
111
|
-
*/
|
|
112
|
-
async function fetchAetherPrice() {
|
|
113
|
-
// Try Aether chain's built-in price oracle (if validator is running)
|
|
114
|
-
const rpcUrl = process.env.AETHER_RPC || 'http://127.0.0.1:8899';
|
|
115
|
-
try {
|
|
116
|
-
const res = await httpGet(`${rpcUrl}/v1/price`);
|
|
117
|
-
if (res && !res.error && res.price !== undefined) {
|
|
118
|
-
return {
|
|
119
|
-
source: 'Aether Oracle',
|
|
120
|
-
symbol: 'AETH',
|
|
121
|
-
price: parseFloat(res.price),
|
|
122
|
-
currency: res.currency || 'USD',
|
|
123
|
-
timestamp: new Date(),
|
|
124
|
-
change_24h: res.change_24h || null,
|
|
125
|
-
volume_24h: res.volume_24h || null,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
} catch { /* chain oracle not available */ }
|
|
129
|
-
|
|
130
|
-
// Try CoinGecko
|
|
131
|
-
const cg = await fetchFromCoinGecko();
|
|
132
|
-
if (cg) return cg;
|
|
133
|
-
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Fetch 24h price change using CoinGecko market chart.
|
|
139
|
-
*/
|
|
140
|
-
async function fetchPriceChange24h(symbol) {
|
|
141
|
-
try {
|
|
142
|
-
const idMap = {
|
|
143
|
-
'AETH': 'aether-network',
|
|
144
|
-
'ATH': 'ath-token',
|
|
145
|
-
};
|
|
146
|
-
const id = idMap[symbol] || 'aether-network';
|
|
147
|
-
const url = `https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=1`;
|
|
148
|
-
const data = await httpGet(url);
|
|
149
|
-
|
|
150
|
-
if (data && data.prices && data.prices.length >= 2) {
|
|
151
|
-
const latest = data.prices[data.prices.length - 1][1];
|
|
152
|
-
const yesterday = data.prices[0][1];
|
|
153
|
-
const change = ((latest - yesterday) / yesterday) * 100;
|
|
154
|
-
const volume = data.total_volumes ? data.total_volumes[data.total_volumes.length - 1][1] : null;
|
|
155
|
-
return {
|
|
156
|
-
change_24h: change,
|
|
157
|
-
volume_24h: volume,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
} catch { /* ignore */ }
|
|
161
|
-
return { change_24h: null, volume_24h: null };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function priceCommand() {
|
|
165
|
-
const args = process.argv.slice(2);
|
|
166
|
-
const asJson = args.includes('--json') || args.includes('-j');
|
|
167
|
-
const pair = args.includes('--pair')
|
|
168
|
-
? args[args.indexOf('--pair') + 1] || DEFAULT_PAIR
|
|
169
|
-
: DEFAULT_PAIR;
|
|
170
|
-
const source = args.includes('--source') ? args[args.indexOf('--source') + 1] : null;
|
|
171
|
-
|
|
172
|
-
// Parse pair
|
|
173
|
-
const [fromSymbol, toSymbol = 'USD'] = pair.split('/');
|
|
174
|
-
const symbol = fromSymbol.toUpperCase();
|
|
175
|
-
|
|
176
|
-
console.log(`\n${C.bright}${C.cyan}── Aether Price ───────────────────────────────────────${C.reset}\n`);
|
|
177
|
-
|
|
178
|
-
let priceData;
|
|
179
|
-
if (source === 'coingecko') {
|
|
180
|
-
priceData = await fetchFromCoinGecko();
|
|
181
|
-
} else {
|
|
182
|
-
priceData = await fetchAetherPrice();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!priceData) {
|
|
186
|
-
if (asJson) {
|
|
187
|
-
console.log(JSON.stringify({ error: 'Price data unavailable', symbol, pair }, null, 2));
|
|
188
|
-
} else {
|
|
189
|
-
console.log(` ${C.yellow}⚠ Price data temporarily unavailable.${C.reset}`);
|
|
190
|
-
console.log(` ${C.dim}Make sure your validator is running or check network connectivity.${C.reset}`);
|
|
191
|
-
console.log(` ${C.dim}Set AETHER_RPC env var to your validator's RPC address.${C.reset}`);
|
|
192
|
-
console.log(` ${C.dim}Fallback: aether price --source coingecko${C.reset}\n`);
|
|
193
|
-
}
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Fetch 24h change if available
|
|
198
|
-
const changeData = await fetchPriceChange24h(symbol);
|
|
199
|
-
const priceInfo = { ...priceData, ...changeData };
|
|
200
|
-
|
|
201
|
-
if (asJson) {
|
|
202
|
-
console.log(JSON.stringify({
|
|
203
|
-
symbol: priceInfo.symbol,
|
|
204
|
-
pair: `${symbol}/USD`,
|
|
205
|
-
price_usd: priceInfo.price,
|
|
206
|
-
change_24h_pct: priceInfo.change_24h !== null ? parseFloat(priceInfo.change_24h.toFixed(4)) : null,
|
|
207
|
-
volume_24h_usd: priceInfo.volume_24h,
|
|
208
|
-
source: priceInfo.source,
|
|
209
|
-
timestamp: formatTime(priceInfo.timestamp),
|
|
210
|
-
}, null, 2));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Human-readable output
|
|
215
|
-
const change = priceInfo.change_24h;
|
|
216
|
-
const changeColor = change === null ? C.dim : change >= 0 ? C.green : C.red;
|
|
217
|
-
const changeStr = change !== null
|
|
218
|
-
? `${change >= 0 ? '+' : ''}${change.toFixed(2)}%`
|
|
219
|
-
: '—';
|
|
220
|
-
|
|
221
|
-
const arrow = change === null ? ' ' : change >= 0 ? '▲' : '▼';
|
|
222
|
-
const volumeStr = priceInfo.volume_24h !== null
|
|
223
|
-
? `$${(priceInfo.volume_24h / 1e6).toFixed(2)}M`
|
|
224
|
-
: null;
|
|
225
|
-
|
|
226
|
-
console.log(` ${C.dim}Pair:${C.reset} ${C.bright}${symbol}/USD${C.reset}`);
|
|
227
|
-
console.log(` ${C.dim}Source:${C.reset} ${C.bright}${priceInfo.source}${C.reset}`);
|
|
228
|
-
console.log(` ${C.dim}Updated:${C.reset} ${formatTime(priceInfo.timestamp)}`);
|
|
229
|
-
console.log();
|
|
230
|
-
console.log(` ${C.bright}${C.green}$${formatPrice(priceInfo.price)}${C.reset} ${C.dim}USD${C.reset}`);
|
|
231
|
-
console.log(` ${C.dim}24h change: ${changeColor}${arrow} ${changeStr}${C.reset}`);
|
|
232
|
-
if (volumeStr) {
|
|
233
|
-
console.log(` ${C.dim}24h volume: ${volumeStr}${C.reset}`);
|
|
234
|
-
}
|
|
235
|
-
console.log();
|
|
236
|
-
|
|
237
|
-
// ASCII box
|
|
238
|
-
const barLen = 40;
|
|
239
|
-
const fillLen = change !== null ? Math.min(barLen, Math.round(Math.abs(change) / 2)) : 0;
|
|
240
|
-
const barColor = change !== null && change < 0 ? C.red : C.green;
|
|
241
|
-
const bar = barColor + '█'.repeat(fillLen) + C.dim + '░'.repeat(barLen - fillLen) + C.reset;
|
|
242
|
-
console.log(` ${C.dim}[${bar}]${C.reset}`);
|
|
243
|
-
console.log();
|
|
244
|
-
console.log(` ${C.dim}Run with ${C.cyan}--json${C.reset}${C.dim} for scripted integrations.${C.reset}`);
|
|
245
|
-
console.log(` ${C.dim}Refreshes on each call — set up a cron job for live monitoring.${C.reset}\n`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
priceCommand().catch(err => {
|
|
249
|
-
console.error(`\n${C.red}Price error:${C.reset}`, err.message, '\n');
|
|
250
|
-
process.exit(1);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
module.exports = { priceCommand };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli price
|
|
4
|
+
*
|
|
5
|
+
* Show real-time AETH/USD price from free public crypto APIs.
|
|
6
|
+
* Supports CoinGecko (free tier), and falls back to simulated data
|
|
7
|
+
* if no API key is available.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* aether price Show current AETH/USD price
|
|
11
|
+
* aether price --pair AETH/USD Specify trading pair (default: AETH/USD)
|
|
12
|
+
* aether price --json JSON output for scripting
|
|
13
|
+
* aether price --source coingecko Fallback to CoinGecko (no API key needed)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const http = require('http');
|
|
18
|
+
|
|
19
|
+
const C = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
bright: '\x1b[1m',
|
|
22
|
+
dim: '\x1b[2m',
|
|
23
|
+
red: '\x1b[31m',
|
|
24
|
+
green: '\x1b[32m',
|
|
25
|
+
yellow: '\x1b[33m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
magenta: '\x1b[35m',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const AETHER_CONTRACT = 'ATH'; // Aether token contract (hypothetical)
|
|
31
|
+
const DEFAULT_PAIR = 'AETH/USD';
|
|
32
|
+
|
|
33
|
+
function httpGet(url) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const lib = url.startsWith('https') ? https : http;
|
|
36
|
+
const parsed = new URL(url);
|
|
37
|
+
const req = lib.request({
|
|
38
|
+
hostname: parsed.hostname,
|
|
39
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
40
|
+
path: parsed.pathname + parsed.search,
|
|
41
|
+
method: 'GET',
|
|
42
|
+
timeout: 10000,
|
|
43
|
+
headers: { 'Accept': 'application/json', 'User-Agent': 'Aether-CLI/1.0' },
|
|
44
|
+
}, (res) => {
|
|
45
|
+
let data = '';
|
|
46
|
+
res.on('data', (chunk) => data += chunk);
|
|
47
|
+
res.on('end', () => {
|
|
48
|
+
try { resolve(JSON.parse(data)); }
|
|
49
|
+
catch { resolve({ _raw: data }); }
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
req.on('error', reject);
|
|
53
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
|
|
54
|
+
req.end();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatPrice(num, decimals = 4) {
|
|
59
|
+
if (num === null || num === undefined || isNaN(num)) return '—';
|
|
60
|
+
return num.toFixed(decimals);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatTime(date) {
|
|
64
|
+
return date.toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Fetch AETH price from CoinGecko (free, no API key).
|
|
69
|
+
* Coins API: /coins/list → find ATH/AETH → /coins/{id}/market_chart
|
|
70
|
+
* Fallback: try known Aether token addresses on major DEXes via /simple/price
|
|
71
|
+
*/
|
|
72
|
+
async function fetchFromCoinGecko() {
|
|
73
|
+
try {
|
|
74
|
+
// Try CoinGecko simple price for AETH token
|
|
75
|
+
// We'll try a few known Aether token addresses on Ethereum mainnet as a proxy
|
|
76
|
+
const url = 'https://api.coingecko.com/api/v3/simple/price?vs_currencies=usd&ids=aether,ath,ath-token,aether-network';
|
|
77
|
+
const data = await httpGet(url);
|
|
78
|
+
|
|
79
|
+
if (data && !data.error) {
|
|
80
|
+
// Find first available AETH quote
|
|
81
|
+
const pairs = [
|
|
82
|
+
{ key: 'aether-network', name: 'AETH' },
|
|
83
|
+
{ key: 'aether', name: 'AETH' },
|
|
84
|
+
{ key: 'ath-token', name: 'ATH' },
|
|
85
|
+
{ key: 'ath', name: 'ATH' },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const { key, name } of pairs) {
|
|
89
|
+
if (data[key] && data[key].usd !== undefined) {
|
|
90
|
+
return {
|
|
91
|
+
source: 'CoinGecko',
|
|
92
|
+
symbol: name,
|
|
93
|
+
price: data[key].usd,
|
|
94
|
+
currency: 'USD',
|
|
95
|
+
timestamp: new Date(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fetch price data from a public DEX aggregator or mock Aether RPC.
|
|
109
|
+
* Primary: use the Aether chain's own price oracle if available.
|
|
110
|
+
* Fallback: use CoinGecko.
|
|
111
|
+
*/
|
|
112
|
+
async function fetchAetherPrice() {
|
|
113
|
+
// Try Aether chain's built-in price oracle (if validator is running)
|
|
114
|
+
const rpcUrl = process.env.AETHER_RPC || 'http://127.0.0.1:8899';
|
|
115
|
+
try {
|
|
116
|
+
const res = await httpGet(`${rpcUrl}/v1/price`);
|
|
117
|
+
if (res && !res.error && res.price !== undefined) {
|
|
118
|
+
return {
|
|
119
|
+
source: 'Aether Oracle',
|
|
120
|
+
symbol: 'AETH',
|
|
121
|
+
price: parseFloat(res.price),
|
|
122
|
+
currency: res.currency || 'USD',
|
|
123
|
+
timestamp: new Date(),
|
|
124
|
+
change_24h: res.change_24h || null,
|
|
125
|
+
volume_24h: res.volume_24h || null,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
} catch { /* chain oracle not available */ }
|
|
129
|
+
|
|
130
|
+
// Try CoinGecko
|
|
131
|
+
const cg = await fetchFromCoinGecko();
|
|
132
|
+
if (cg) return cg;
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Fetch 24h price change using CoinGecko market chart.
|
|
139
|
+
*/
|
|
140
|
+
async function fetchPriceChange24h(symbol) {
|
|
141
|
+
try {
|
|
142
|
+
const idMap = {
|
|
143
|
+
'AETH': 'aether-network',
|
|
144
|
+
'ATH': 'ath-token',
|
|
145
|
+
};
|
|
146
|
+
const id = idMap[symbol] || 'aether-network';
|
|
147
|
+
const url = `https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=1`;
|
|
148
|
+
const data = await httpGet(url);
|
|
149
|
+
|
|
150
|
+
if (data && data.prices && data.prices.length >= 2) {
|
|
151
|
+
const latest = data.prices[data.prices.length - 1][1];
|
|
152
|
+
const yesterday = data.prices[0][1];
|
|
153
|
+
const change = ((latest - yesterday) / yesterday) * 100;
|
|
154
|
+
const volume = data.total_volumes ? data.total_volumes[data.total_volumes.length - 1][1] : null;
|
|
155
|
+
return {
|
|
156
|
+
change_24h: change,
|
|
157
|
+
volume_24h: volume,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
} catch { /* ignore */ }
|
|
161
|
+
return { change_24h: null, volume_24h: null };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function priceCommand() {
|
|
165
|
+
const args = process.argv.slice(2);
|
|
166
|
+
const asJson = args.includes('--json') || args.includes('-j');
|
|
167
|
+
const pair = args.includes('--pair')
|
|
168
|
+
? args[args.indexOf('--pair') + 1] || DEFAULT_PAIR
|
|
169
|
+
: DEFAULT_PAIR;
|
|
170
|
+
const source = args.includes('--source') ? args[args.indexOf('--source') + 1] : null;
|
|
171
|
+
|
|
172
|
+
// Parse pair
|
|
173
|
+
const [fromSymbol, toSymbol = 'USD'] = pair.split('/');
|
|
174
|
+
const symbol = fromSymbol.toUpperCase();
|
|
175
|
+
|
|
176
|
+
console.log(`\n${C.bright}${C.cyan}── Aether Price ───────────────────────────────────────${C.reset}\n`);
|
|
177
|
+
|
|
178
|
+
let priceData;
|
|
179
|
+
if (source === 'coingecko') {
|
|
180
|
+
priceData = await fetchFromCoinGecko();
|
|
181
|
+
} else {
|
|
182
|
+
priceData = await fetchAetherPrice();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!priceData) {
|
|
186
|
+
if (asJson) {
|
|
187
|
+
console.log(JSON.stringify({ error: 'Price data unavailable', symbol, pair }, null, 2));
|
|
188
|
+
} else {
|
|
189
|
+
console.log(` ${C.yellow}⚠ Price data temporarily unavailable.${C.reset}`);
|
|
190
|
+
console.log(` ${C.dim}Make sure your validator is running or check network connectivity.${C.reset}`);
|
|
191
|
+
console.log(` ${C.dim}Set AETHER_RPC env var to your validator's RPC address.${C.reset}`);
|
|
192
|
+
console.log(` ${C.dim}Fallback: aether price --source coingecko${C.reset}\n`);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fetch 24h change if available
|
|
198
|
+
const changeData = await fetchPriceChange24h(symbol);
|
|
199
|
+
const priceInfo = { ...priceData, ...changeData };
|
|
200
|
+
|
|
201
|
+
if (asJson) {
|
|
202
|
+
console.log(JSON.stringify({
|
|
203
|
+
symbol: priceInfo.symbol,
|
|
204
|
+
pair: `${symbol}/USD`,
|
|
205
|
+
price_usd: priceInfo.price,
|
|
206
|
+
change_24h_pct: priceInfo.change_24h !== null ? parseFloat(priceInfo.change_24h.toFixed(4)) : null,
|
|
207
|
+
volume_24h_usd: priceInfo.volume_24h,
|
|
208
|
+
source: priceInfo.source,
|
|
209
|
+
timestamp: formatTime(priceInfo.timestamp),
|
|
210
|
+
}, null, 2));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Human-readable output
|
|
215
|
+
const change = priceInfo.change_24h;
|
|
216
|
+
const changeColor = change === null ? C.dim : change >= 0 ? C.green : C.red;
|
|
217
|
+
const changeStr = change !== null
|
|
218
|
+
? `${change >= 0 ? '+' : ''}${change.toFixed(2)}%`
|
|
219
|
+
: '—';
|
|
220
|
+
|
|
221
|
+
const arrow = change === null ? ' ' : change >= 0 ? '▲' : '▼';
|
|
222
|
+
const volumeStr = priceInfo.volume_24h !== null
|
|
223
|
+
? `$${(priceInfo.volume_24h / 1e6).toFixed(2)}M`
|
|
224
|
+
: null;
|
|
225
|
+
|
|
226
|
+
console.log(` ${C.dim}Pair:${C.reset} ${C.bright}${symbol}/USD${C.reset}`);
|
|
227
|
+
console.log(` ${C.dim}Source:${C.reset} ${C.bright}${priceInfo.source}${C.reset}`);
|
|
228
|
+
console.log(` ${C.dim}Updated:${C.reset} ${formatTime(priceInfo.timestamp)}`);
|
|
229
|
+
console.log();
|
|
230
|
+
console.log(` ${C.bright}${C.green}$${formatPrice(priceInfo.price)}${C.reset} ${C.dim}USD${C.reset}`);
|
|
231
|
+
console.log(` ${C.dim}24h change: ${changeColor}${arrow} ${changeStr}${C.reset}`);
|
|
232
|
+
if (volumeStr) {
|
|
233
|
+
console.log(` ${C.dim}24h volume: ${volumeStr}${C.reset}`);
|
|
234
|
+
}
|
|
235
|
+
console.log();
|
|
236
|
+
|
|
237
|
+
// ASCII box
|
|
238
|
+
const barLen = 40;
|
|
239
|
+
const fillLen = change !== null ? Math.min(barLen, Math.round(Math.abs(change) / 2)) : 0;
|
|
240
|
+
const barColor = change !== null && change < 0 ? C.red : C.green;
|
|
241
|
+
const bar = barColor + '█'.repeat(fillLen) + C.dim + '░'.repeat(barLen - fillLen) + C.reset;
|
|
242
|
+
console.log(` ${C.dim}[${bar}]${C.reset}`);
|
|
243
|
+
console.log();
|
|
244
|
+
console.log(` ${C.dim}Run with ${C.cyan}--json${C.reset}${C.dim} for scripted integrations.${C.reset}`);
|
|
245
|
+
console.log(` ${C.dim}Refreshes on each call — set up a cron job for live monitoring.${C.reset}\n`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
priceCommand().catch(err => {
|
|
249
|
+
console.error(`\n${C.red}Price error:${C.reset}`, err.message, '\n');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
module.exports = { priceCommand };
|