aether-hub 1.2.6 → 1.2.8
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 -304
- package/commands/apy.js +480 -480
- 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/delegations.js +45 -95
- package/commands/emergency.js +667 -657
- package/commands/epoch.js +275 -357
- package/commands/fees.js +276 -0
- package/commands/info.js +495 -536
- package/commands/monitor.js +431 -431
- package/commands/multisig.js +726 -726
- package/commands/network.js +429 -503
- package/commands/ping.js +266 -320
- package/commands/price.js +253 -253
- package/commands/sdk-test.js +477 -0
- package/commands/sdk.js +537 -537
- package/commands/slot.js +155 -0
- package/commands/snapshot.js +509 -509
- package/commands/stake-info.js +139 -0
- package/commands/stake-positions.js +31 -46
- package/commands/stats.js +418 -418
- package/commands/status.js +326 -370
- package/commands/supply.js +37 -83
- package/commands/tps.js +238 -0
- package/commands/transfer.js +495 -0
- package/commands/tx-history.js +65 -227
- 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 +8 -29
- package/index.js +65 -8
- package/package.json +1 -3
package/commands/tx-history.js
CHANGED
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
* aether history --address ATHxxx --rpc https://rpc.aether.io
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// Import SDK for real blockchain RPC calls
|
|
21
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
22
|
+
const aether = require(sdkPath);
|
|
20
23
|
|
|
21
24
|
// ANSI colours
|
|
22
25
|
const C = {
|
|
@@ -33,67 +36,15 @@ const C = {
|
|
|
33
36
|
const CLI_VERSION = '1.0.0';
|
|
34
37
|
|
|
35
38
|
// ---------------------------------------------------------------------------
|
|
36
|
-
//
|
|
39
|
+
// SDK helpers - Real blockchain RPC calls via Aether SDK
|
|
37
40
|
// ---------------------------------------------------------------------------
|
|
38
41
|
|
|
39
42
|
function getDefaultRpc() {
|
|
40
|
-
return process.env.AETHER_RPC || 'http://127.0.0.1:8899';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
// HTTP helpers
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
|
|
47
|
-
function httpRequest(rpcUrl, path, timeoutMs = 10000) {
|
|
48
|
-
return new Promise((resolve, reject) => {
|
|
49
|
-
const url = new URL(path, rpcUrl);
|
|
50
|
-
const lib = url.protocol === 'https:' ? https : http;
|
|
51
|
-
const req = lib.request({
|
|
52
|
-
hostname: url.hostname,
|
|
53
|
-
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
54
|
-
path: url.pathname + url.search,
|
|
55
|
-
method: 'GET',
|
|
56
|
-
timeout: timeoutMs,
|
|
57
|
-
headers: { 'Content-Type': 'application/json' },
|
|
58
|
-
}, (res) => {
|
|
59
|
-
let data = '';
|
|
60
|
-
res.on('data', (chunk) => data += chunk);
|
|
61
|
-
res.on('end', () => {
|
|
62
|
-
try { resolve(JSON.parse(data)); }
|
|
63
|
-
catch { resolve({ raw: data }); }
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
req.on('error', reject);
|
|
67
|
-
req.on('timeout', () => { req.destroy(); reject(new Error(`Request timeout after ${timeoutMs}ms`)); });
|
|
68
|
-
req.end();
|
|
69
|
-
});
|
|
43
|
+
return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
|
|
70
44
|
}
|
|
71
45
|
|
|
72
|
-
function
|
|
73
|
-
return new
|
|
74
|
-
const url = new URL(path, rpcUrl);
|
|
75
|
-
const lib = url.protocol === 'https:' ? https : http;
|
|
76
|
-
const bodyStr = JSON.stringify(body);
|
|
77
|
-
const req = lib.request({
|
|
78
|
-
hostname: url.hostname,
|
|
79
|
-
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
80
|
-
path: url.pathname + url.search,
|
|
81
|
-
method: 'POST',
|
|
82
|
-
timeout: timeoutMs,
|
|
83
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(bodyStr) },
|
|
84
|
-
}, (res) => {
|
|
85
|
-
let data = '';
|
|
86
|
-
res.on('data', (chunk) => data += chunk);
|
|
87
|
-
res.on('end', () => {
|
|
88
|
-
try { resolve(JSON.parse(data)); }
|
|
89
|
-
catch { resolve({ raw: data }); }
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
req.on('error', reject);
|
|
93
|
-
req.on('timeout', () => { req.destroy(); reject(new Error(`Request timeout after ${timeoutMs}ms`)); });
|
|
94
|
-
req.write(bodyStr);
|
|
95
|
-
req.end();
|
|
96
|
-
});
|
|
46
|
+
function createClient(rpcUrl) {
|
|
47
|
+
return new aether.AetherClient({ rpcUrl });
|
|
97
48
|
}
|
|
98
49
|
|
|
99
50
|
// ---------------------------------------------------------------------------
|
|
@@ -180,141 +131,42 @@ function formatStatus(status) {
|
|
|
180
131
|
}
|
|
181
132
|
|
|
182
133
|
// ---------------------------------------------------------------------------
|
|
183
|
-
//
|
|
134
|
+
// Parse a transaction result into a normalized display object
|
|
184
135
|
// ---------------------------------------------------------------------------
|
|
185
136
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const body = {
|
|
191
|
-
jsonrpc: '2.0',
|
|
192
|
-
id: 1,
|
|
193
|
-
method: 'getSignaturesForAddress',
|
|
194
|
-
params: [
|
|
195
|
-
address,
|
|
196
|
-
{ limit },
|
|
197
|
-
],
|
|
198
|
-
};
|
|
199
|
-
return httpPost(rpcUrl, '/', body);
|
|
200
|
-
}
|
|
137
|
+
function parseTransaction(txResult) {
|
|
138
|
+
const blockTime = txResult.blockTime;
|
|
139
|
+
const slot = txResult.slot;
|
|
140
|
+
const status = txResult.status || 'confirmed';
|
|
201
141
|
|
|
202
|
-
|
|
203
|
-
* Fetch a specific confirmed transaction by signature.
|
|
204
|
-
*/
|
|
205
|
-
async function fetchTx(rpcUrl, signature) {
|
|
206
|
-
const body = {
|
|
207
|
-
jsonrpc: '2.0',
|
|
208
|
-
id: 1,
|
|
209
|
-
method: 'getTransaction',
|
|
210
|
-
params: [
|
|
211
|
-
signature,
|
|
212
|
-
{ encoding: 'jsonParsed', maxSupportedTransactionVersion: 0 },
|
|
213
|
-
],
|
|
214
|
-
};
|
|
215
|
-
return httpPost(rpcUrl, '/', body);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Parse a transaction result into a normalized display object.
|
|
220
|
-
*/
|
|
221
|
-
function parseTransaction(txResult, sigInfo) {
|
|
222
|
-
const blockTime = sigInfo.blockTime || txResult.blockTime;
|
|
223
|
-
const slot = sigInfo.slot;
|
|
224
|
-
const status = sigInfo.err ? 'failed' : (sigInfo.confirmationStatus || 'confirmed');
|
|
225
|
-
|
|
226
|
-
let txType = 'unknown';
|
|
142
|
+
let txType = txResult.tx_type || txResult.type || 'unknown';
|
|
227
143
|
let amount = 0;
|
|
228
|
-
let fee = txResult.
|
|
229
|
-
let fromAddr = null;
|
|
144
|
+
let fee = txResult.fee || 0;
|
|
145
|
+
let fromAddr = txResult.signer || null;
|
|
230
146
|
let toAddr = null;
|
|
231
|
-
let memo = null;
|
|
147
|
+
let memo = txResult.memo || null;
|
|
232
148
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const info = ix.parsed.info;
|
|
250
|
-
fromAddr = info.from || info.funder;
|
|
251
|
-
toAddr = info.validator;
|
|
252
|
-
amount = info.lamports || info.amount || 0;
|
|
253
|
-
} else if (ix.parsed && ix.parsed.type === 'withdrawStake') {
|
|
254
|
-
txType = 'unstake';
|
|
255
|
-
const info = ix.parsed.info;
|
|
256
|
-
toAddr = info.destination || info.withdrawer;
|
|
257
|
-
amount = info.lamports || info.amount || 0;
|
|
258
|
-
} else if (ix.parsed && ix.parsed.type === 'vote') {
|
|
259
|
-
txType = 'vote';
|
|
260
|
-
} else if (ix.parsed && ix.parsed.type === 'initialize') {
|
|
261
|
-
txType = 'initialize';
|
|
262
|
-
} else if (ix.parsed && ix.parsed.type === 'createAccount') {
|
|
263
|
-
txType = 'create';
|
|
264
|
-
} else if (ix.parsed && ix.parsed.type === 'approve') {
|
|
265
|
-
txType = 'stake';
|
|
266
|
-
const info = ix.parsed.info || {};
|
|
267
|
-
fromAddr = info.from || info.owner;
|
|
268
|
-
toAddr = info.stake;
|
|
269
|
-
amount = info.amount || info.lamports || 0;
|
|
270
|
-
} else if (ix.parsed && ix.parsed.type === 'delegate') {
|
|
271
|
-
txType = 'stake';
|
|
272
|
-
const info = ix.parsed.info || {};
|
|
273
|
-
fromAddr = info.stake || info.from;
|
|
274
|
-
toAddr = info.validator;
|
|
275
|
-
amount = info.lamports || 0;
|
|
276
|
-
} else if (ix.parsed && ix.parsed.type === 'withdraw') {
|
|
277
|
-
txType = 'unstake';
|
|
278
|
-
const info = ix.parsed.info || {};
|
|
279
|
-
toAddr = info.destination;
|
|
280
|
-
amount = info.lamports || 0;
|
|
281
|
-
}
|
|
282
|
-
// Check memo
|
|
283
|
-
if (ix.memo) memo = ix.memo;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Fallback: try legacy instructions if no parsed instructions
|
|
287
|
-
if (!instructions.length || instructions.every(ix => !ix.parsed)) {
|
|
288
|
-
for (const ix of instructions) {
|
|
289
|
-
if (ix.data === 'AAAA' || ix.data === '2ugJ4ELK3wW9qNXH' || !ix.data) {
|
|
290
|
-
txType = 'transfer';
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Compute fee
|
|
296
|
-
if (txResult.meta) {
|
|
297
|
-
fee = txResult.meta.fee || 0;
|
|
298
|
-
if (txResult.meta.postBalances && txResult.meta.preBalances) {
|
|
299
|
-
// Try to detect native transfer from balance changes
|
|
300
|
-
for (let i = 0; i < txResult.meta.postBalances.length; i++) {
|
|
301
|
-
const diff = txResult.meta.postBalances[i] - txResult.meta.preBalances[i];
|
|
302
|
-
if (diff < 0) {
|
|
303
|
-
amount = Math.abs(diff);
|
|
304
|
-
if (!fromAddr) fromAddr = msg.accountKeys?.[i];
|
|
305
|
-
} else if (diff > 0 && amount === 0) {
|
|
306
|
-
if (!toAddr) toAddr = msg.accountKeys?.[i];
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
149
|
+
// Parse payload for details
|
|
150
|
+
if (txResult.payload) {
|
|
151
|
+
const payload = txResult.payload;
|
|
152
|
+
if (payload.amount !== undefined) {
|
|
153
|
+
amount = Number(payload.amount);
|
|
154
|
+
}
|
|
155
|
+
if (payload.recipient) {
|
|
156
|
+
toAddr = payload.recipient;
|
|
157
|
+
}
|
|
158
|
+
if (payload.validator) {
|
|
159
|
+
toAddr = payload.validator;
|
|
160
|
+
txType = 'stake';
|
|
161
|
+
}
|
|
162
|
+
if (payload.stake_account) {
|
|
163
|
+
fromAddr = payload.stake_account;
|
|
164
|
+
txType = 'unstake';
|
|
311
165
|
}
|
|
312
|
-
} catch (e) {
|
|
313
|
-
// Parsing failed — use defaults
|
|
314
166
|
}
|
|
315
167
|
|
|
316
168
|
return {
|
|
317
|
-
signature:
|
|
169
|
+
signature: txResult.signature,
|
|
318
170
|
slot,
|
|
319
171
|
blockTime,
|
|
320
172
|
status,
|
|
@@ -411,6 +263,14 @@ function displayJson(txs, meta) {
|
|
|
411
263
|
|
|
412
264
|
async function main() {
|
|
413
265
|
const opts = parseArgs();
|
|
266
|
+
// Shift args if this module was called via index.js (extra argument in argv)
|
|
267
|
+
// Detect by checking if first non-option arg after 'tx' is the command name
|
|
268
|
+
const args = process.argv.slice(2);
|
|
269
|
+
const txIdx = args.indexOf('tx');
|
|
270
|
+
const historyIdx = args.indexOf('history');
|
|
271
|
+
if (txIdx !== -1 && historyIdx !== -1) {
|
|
272
|
+
// Already parsed correctly above - no action needed
|
|
273
|
+
}
|
|
414
274
|
|
|
415
275
|
if (opts.help) {
|
|
416
276
|
console.log(`
|
|
@@ -449,46 +309,11 @@ ${C.bright}EXAMPLES${C.reset}
|
|
|
449
309
|
}
|
|
450
310
|
|
|
451
311
|
try {
|
|
452
|
-
//
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const signatures = Array.isArray(sigsResult.result) ? sigsResult.result : [];
|
|
460
|
-
|
|
461
|
-
if (signatures.length === 0) {
|
|
462
|
-
if (!opts.json) {
|
|
463
|
-
displayTxTable([]);
|
|
464
|
-
} else {
|
|
465
|
-
displayJson([], { address: opts.address, rpc: rpcUrl, limit });
|
|
466
|
-
}
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Step 2: Fetch each transaction in parallel (up to 10 at a time)
|
|
471
|
-
const txResults = [];
|
|
472
|
-
const BATCH = 10;
|
|
473
|
-
|
|
474
|
-
for (let i = 0; i < signatures.length; i += BATCH) {
|
|
475
|
-
const batch = signatures.slice(i, i + BATCH);
|
|
476
|
-
const batchPromises = batch.map(sig => fetchTx(rpcUrl, sig.signature).catch(err => ({ error: err.message })));
|
|
477
|
-
const batchResults = await Promise.all(batchPromises);
|
|
478
|
-
txResults.push(...batchResults);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Step 3: Parse and normalize
|
|
482
|
-
const txs = txResults
|
|
483
|
-
.map((res, idx) => {
|
|
484
|
-
if (res.error) return null;
|
|
485
|
-
try {
|
|
486
|
-
return parseTransaction(res.result || {}, signatures[idx] || {});
|
|
487
|
-
} catch {
|
|
488
|
-
return null;
|
|
489
|
-
}
|
|
490
|
-
})
|
|
491
|
-
.filter(Boolean);
|
|
312
|
+
// Use SDK for real blockchain RPC calls
|
|
313
|
+
const client = createClient(rpcUrl);
|
|
314
|
+
const history = await client.getTransactionHistory(opts.address, limit);
|
|
315
|
+
|
|
316
|
+
const txs = (history.transactions || []).map(tx => parseTransaction(tx));
|
|
492
317
|
|
|
493
318
|
if (opts.json) {
|
|
494
319
|
displayJson(txs, { address: opts.address, rpc: rpcUrl, limit });
|
|
@@ -497,12 +322,25 @@ ${C.bright}EXAMPLES${C.reset}
|
|
|
497
322
|
}
|
|
498
323
|
|
|
499
324
|
} catch (err) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
325
|
+
if (opts.json) {
|
|
326
|
+
console.log(JSON.stringify({
|
|
327
|
+
error: err.message,
|
|
328
|
+
address: opts.address,
|
|
329
|
+
rpc: rpcUrl,
|
|
330
|
+
}, null, 2));
|
|
331
|
+
} else {
|
|
332
|
+
console.log(`\n ${C.red}✗ Failed to fetch transaction history:${C.reset} ${err.message}\n`);
|
|
333
|
+
console.log(` ${C.dim} Is your validator running? RPC: ${rpcUrl}${C.reset}`);
|
|
334
|
+
console.log(` ${C.dim} Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
|
|
503
335
|
}
|
|
504
336
|
process.exit(1);
|
|
505
337
|
}
|
|
506
338
|
}
|
|
507
339
|
|
|
508
|
-
|
|
340
|
+
// Export for CLI integration
|
|
341
|
+
module.exports = { txHistoryCommand: main };
|
|
342
|
+
|
|
343
|
+
// Run if called directly
|
|
344
|
+
if (require.main === module) {
|
|
345
|
+
main();
|
|
346
|
+
}
|
|
@@ -634,7 +634,13 @@ async function main() {
|
|
|
634
634
|
console.log();
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
637
|
+
// Export for module use
|
|
638
|
+
module.exports = { validatorInfo: main };
|
|
639
|
+
|
|
640
|
+
// Only run if called directly (not when required as module)
|
|
641
|
+
if (require.main === module) {
|
|
642
|
+
main().catch(err => {
|
|
643
|
+
console.error(`\n${C.red}✗ Validator info failed:${C.reset} ${err.message}\n`);
|
|
644
|
+
process.exit(1);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
@@ -254,7 +254,7 @@ function startValidatorProcess({ type, path: binaryPath, inPath }, options) {
|
|
|
254
254
|
if (options.testnet) {
|
|
255
255
|
validatorArgs.push('--testnet');
|
|
256
256
|
}
|
|
257
|
-
validatorArgs.push('--tier', options.tier);
|
|
257
|
+
validatorArgs.push('--tier', options.tier.toLowerCase());
|
|
258
258
|
validatorArgs.push('--rpc-addr', options.rpcAddr);
|
|
259
259
|
validatorArgs.push('--p2p-addr', options.p2pAddr);
|
|
260
260
|
if (options.identity) {
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Queries the validator's RPC endpoint and displays status information.
|
|
5
5
|
* Shows slot height, peer count, block production, and epoch info.
|
|
6
|
+
*
|
|
7
|
+
* Uses @jellylegsai/aether-sdk for real blockchain RPC calls.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
const
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
// Import SDK for real blockchain RPC calls
|
|
13
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
14
|
+
const aether = require(sdkPath);
|
|
9
15
|
|
|
10
16
|
// ANSI colors
|
|
11
17
|
const colors = {
|
|
@@ -19,54 +25,10 @@ const colors = {
|
|
|
19
25
|
};
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
|
-
*
|
|
28
|
+
* Create SDK client
|
|
23
29
|
*/
|
|
24
|
-
function
|
|
25
|
-
return new
|
|
26
|
-
const urlObj = new URL(url);
|
|
27
|
-
|
|
28
|
-
const postData = JSON.stringify({
|
|
29
|
-
jsonrpc: '2.0',
|
|
30
|
-
id: 1,
|
|
31
|
-
method,
|
|
32
|
-
params,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const options = {
|
|
36
|
-
hostname: urlObj.hostname,
|
|
37
|
-
port: urlObj.port || 8899,
|
|
38
|
-
path: '/',
|
|
39
|
-
method: 'POST',
|
|
40
|
-
headers: {
|
|
41
|
-
'Content-Type': 'application/json',
|
|
42
|
-
'Content-Length': Buffer.byteLength(postData),
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const req = http.request(options, (res) => {
|
|
47
|
-
let data = '';
|
|
48
|
-
res.on('data', (chunk) => data += chunk);
|
|
49
|
-
res.on('end', () => {
|
|
50
|
-
try {
|
|
51
|
-
const json = JSON.parse(data);
|
|
52
|
-
if (json.error) {
|
|
53
|
-
reject(new Error(json.error.message || JSON.stringify(json.error)));
|
|
54
|
-
} else {
|
|
55
|
-
resolve(json.result);
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
reject(new Error(`Invalid JSON response: ${data}`));
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
req.on('error', (e) => {
|
|
64
|
-
reject(new Error(`Connection failed: ${e.message}`));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
req.write(postData);
|
|
68
|
-
req.end();
|
|
69
|
-
});
|
|
30
|
+
function createClient(rpcUrl) {
|
|
31
|
+
return new aether.AetherClient({ rpcUrl });
|
|
70
32
|
}
|
|
71
33
|
|
|
72
34
|
/**
|
|
@@ -181,24 +143,25 @@ async function validatorStatus() {
|
|
|
181
143
|
let epochInfo = {};
|
|
182
144
|
let blockProduction = {};
|
|
183
145
|
|
|
146
|
+
const client = createClient(options.rpcUrl);
|
|
147
|
+
|
|
184
148
|
try {
|
|
185
|
-
// Make parallel RPC calls
|
|
186
|
-
const [
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
options.details ? rpcCall(options.rpcUrl, 'getBlockProduction').catch(e => ({})) : Promise.resolve({}),
|
|
149
|
+
// Make parallel RPC calls using SDK
|
|
150
|
+
const [slotResult, blockHeightResult, epochInfoResult, peersResult] = await Promise.all([
|
|
151
|
+
client.getSlot().catch(e => ({ error: e.message })),
|
|
152
|
+
client.getBlockHeight().catch(e => ({ error: e.message })),
|
|
153
|
+
client.getEpochInfo().catch(e => ({})),
|
|
154
|
+
client.getClusterPeers().catch(e => ([])),
|
|
192
155
|
]);
|
|
193
156
|
|
|
194
|
-
if (typeof
|
|
157
|
+
if (typeof slotResult === 'object' && slotResult.error) {
|
|
195
158
|
if (options.json) {
|
|
196
|
-
console.log(JSON.stringify({ error:
|
|
159
|
+
console.log(JSON.stringify({ error: slotResult.error }, null, 2));
|
|
197
160
|
process.exit(1);
|
|
198
161
|
}
|
|
199
162
|
console.log();
|
|
200
163
|
console.log(` ${colors.red}❌ Cannot connect to validator${colors.reset}`);
|
|
201
|
-
console.log(` ${colors.yellow}${
|
|
164
|
+
console.log(` ${colors.yellow}${slotResult.error}${colors.reset}`);
|
|
202
165
|
console.log();
|
|
203
166
|
console.log(` ${colors.bright}Start the validator first:${colors.reset}`);
|
|
204
167
|
console.log(` ${colors.cyan}aether-cli validator start${colors.reset}`);
|
|
@@ -206,26 +169,22 @@ async function validatorStatus() {
|
|
|
206
169
|
process.exit(1);
|
|
207
170
|
}
|
|
208
171
|
|
|
209
|
-
status.slot = typeof
|
|
210
|
-
status.blockHeight = typeof
|
|
211
|
-
status.transactionCount =
|
|
172
|
+
status.slot = typeof slotResult === 'number' ? slotResult : (slotResult.slot || 0);
|
|
173
|
+
status.blockHeight = typeof blockHeightResult === 'number' ? blockHeightResult : status.slot;
|
|
174
|
+
status.transactionCount = 0; // Transaction count not available via SDK
|
|
175
|
+
status.peerCount = Array.isArray(peersResult) ? peersResult.length : 0;
|
|
212
176
|
|
|
213
177
|
if (epochInfoResult && typeof epochInfoResult === 'object') {
|
|
214
178
|
epochInfo = epochInfoResult;
|
|
215
179
|
status.epoch = epochInfo.epoch || 0;
|
|
216
|
-
epochInfo.slotIndex = epochInfo.slotIndex || 0;
|
|
217
|
-
epochInfo.slotsInEpoch = epochInfo.slotsInEpoch || 432000;
|
|
180
|
+
epochInfo.slotIndex = epochInfo.slotIndex || epochInfo.slot_index || 0;
|
|
181
|
+
epochInfo.slotsInEpoch = epochInfo.slotsInEpoch || epochInfo.slots_in_epoch || 432000;
|
|
218
182
|
}
|
|
219
183
|
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// Get peer count
|
|
225
|
-
try {
|
|
226
|
-
status.peerCount = await rpcCall(options.rpcUrl, 'getPeerCount') || 0;
|
|
227
|
-
} catch (e) {
|
|
228
|
-
status.peerCount = 0;
|
|
184
|
+
if (options.details) {
|
|
185
|
+
try {
|
|
186
|
+
blockProduction = await client.getSlotProduction();
|
|
187
|
+
} catch { /* Block production not available */ }
|
|
229
188
|
}
|
|
230
189
|
|
|
231
190
|
if (options.json) {
|
|
@@ -260,7 +219,7 @@ async function validatorStatus() {
|
|
|
260
219
|
}
|
|
261
220
|
|
|
262
221
|
// Export for use as module
|
|
263
|
-
module.exports = { validatorStatus
|
|
222
|
+
module.exports = { validatorStatus };
|
|
264
223
|
|
|
265
224
|
// Run if called directly
|
|
266
225
|
if (require.main === module) {
|