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.
@@ -634,7 +634,13 @@ async function main() {
634
634
  console.log();
635
635
  }
636
636
 
637
- main().catch(err => {
638
- console.error(`\n${C.red}✗ Validator info failed:${C.reset} ${err.message}\n`);
639
- process.exit(1);
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 http = require('http');
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
- * Make an RPC call to the validator
28
+ * Create SDK client
23
29
  */
24
- function rpcCall(url, method, params = []) {
25
- return new Promise((resolve, reject) => {
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 [slot, blockHeight, transactionCount, epochInfoResult, blockProdResult] = await Promise.all([
187
- rpcCall(options.rpcUrl, 'getSlot').catch(e => ({ error: e.message })),
188
- rpcCall(options.rpcUrl, 'getBlockHeight').catch(e => ({ error: e.message })),
189
- rpcCall(options.rpcUrl, 'getTransactionCount').catch(e => ({ error: e.message })),
190
- rpcCall(options.rpcUrl, 'getEpochInfo').catch(e => ({})),
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 slot === 'object' && slot.error) {
157
+ if (typeof slotResult === 'object' && slotResult.error) {
195
158
  if (options.json) {
196
- console.log(JSON.stringify({ error: slot.error }, null, 2));
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}${slot.error}${colors.reset}`);
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 slot === 'number' ? slot : 0;
210
- status.blockHeight = typeof blockHeight === 'number' ? blockHeight : status.slot;
211
- status.transactionCount = typeof transactionCount === 'number' ? transactionCount : 0;
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 (blockProdResult && typeof blockProdResult === 'object') {
221
- blockProduction = blockProdResult;
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, rpcCall };
222
+ module.exports = { validatorStatus };
264
223
 
265
224
  // Run if called directly
266
225
  if (require.main === module) {
@@ -13,10 +13,15 @@
13
13
  * aether validators list --sort stake Sort by stake (default: score)
14
14
  *
15
15
  * Requires AETHER_RPC env var (default: http://127.0.0.1:8899)
16
+ *
17
+ * SDK wired to: GET /v1/validators, GET /v1/epoch, GET /v1/supply
16
18
  */
17
19
 
18
- const http = require('http');
19
- const https = require('https');
20
+ const path = require('path');
21
+
22
+ // Import SDK for real blockchain RPC calls
23
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
24
+ const aether = require(sdkPath);
20
25
 
21
26
  // ANSI colours
22
27
  const C = {
@@ -31,56 +36,12 @@ const C = {
31
36
  magenta: '\x1b[35m',
32
37
  };
33
38
 
34
- const DEFAULT_RPC = process.env.AETHER_RPC || 'http://127.0.0.1:8899';
35
-
36
- // ---------------------------------------------------------------------------
37
- // HTTP helpers
38
- // ---------------------------------------------------------------------------
39
-
40
- function httpRequest(rpcUrl, path) {
41
- return new Promise((resolve, reject) => {
42
- const url = new URL(path, rpcUrl);
43
- const lib = url.protocol === 'https:' ? https : http;
44
- const req = lib.request({
45
- hostname: url.hostname,
46
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
47
- path: url.pathname + url.search,
48
- method: 'GET',
49
- timeout: 8000,
50
- headers: { 'Content-Type': 'application/json' },
51
- }, (res) => {
52
- let data = '';
53
- res.on('data', (chunk) => data += chunk);
54
- res.on('end', () => { try { resolve(JSON.parse(data)); } catch { resolve({ raw: data }); } });
55
- });
56
- req.on('error', reject);
57
- req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
58
- req.end();
59
- });
39
+ function getDefaultRpc() {
40
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
60
41
  }
61
42
 
62
- function httpPost(rpcUrl, path, body) {
63
- return new Promise((resolve, reject) => {
64
- const url = new URL(path, rpcUrl);
65
- const lib = url.protocol === 'https:' ? https : http;
66
- const bodyStr = JSON.stringify(body);
67
- const req = lib.request({
68
- hostname: url.hostname,
69
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
70
- path: url.pathname + url.search,
71
- method: 'POST',
72
- timeout: 8000,
73
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(bodyStr) },
74
- }, (res) => {
75
- let data = '';
76
- res.on('data', (chunk) => data += chunk);
77
- res.on('end', () => { try { resolve(JSON.parse(data)); } catch { resolve(data); } });
78
- });
79
- req.on('error', reject);
80
- req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
81
- req.write(bodyStr);
82
- req.end();
83
- });
43
+ function createClient(rpcUrl) {
44
+ return new aether.AetherClient({ rpcUrl });
84
45
  }
85
46
 
86
47
  // ---------------------------------------------------------------------------
@@ -90,7 +51,7 @@ function httpPost(rpcUrl, path, body) {
90
51
  function parseArgs() {
91
52
  const args = process.argv.slice(3); // [node, index.js, validators, list, ...]
92
53
  const opts = {
93
- rpc: DEFAULT_RPC,
54
+ rpc: getDefaultRpc(),
94
55
  subcmd: 'list',
95
56
  tier: null,
96
57
  asJson: false,
@@ -137,6 +98,7 @@ function parseArgs() {
137
98
  }
138
99
 
139
100
  function showHelp() {
101
+ const defaultRpc = getDefaultRpc();
140
102
  console.log(`
141
103
  ${C.bright}${C.cyan}aether-cli validators${C.reset} - List and inspect Aether validators
142
104
 
@@ -148,17 +110,22 @@ ${C.bright}Options (list):${C.reset}
148
110
  -t, --tier <type> Filter by tier: full, lite, observer
149
111
  -s, --sort <field> Sort by: stake, score, apy, uptime, name (default: score)
150
112
  -l, --limit <n> Max validators to show (default: 100, max: 500)
151
- -r, --rpc <url> RPC endpoint (default: ${DEFAULT_RPC} or $AETHER_RPC)
113
+ -r, --rpc <url> RPC endpoint (default: ${defaultRpc} or $AETHER_RPC)
152
114
  -j, --json Output raw JSON (for scripting)
153
115
  -h, --help Show this help message
154
116
 
155
117
  ${C.bright}Options (rank):${C.reset}
156
118
  -t, --tier <type> Filter by tier: full, lite, observer
157
119
  -l, --limit <n> Max validators to show (default: 50, max: 200)
158
- -r, --rpc <url> RPC endpoint (default: ${DEFAULT_RPC} or $AETHER_RPC)
120
+ -r, --rpc <url> RPC endpoint (default: ${defaultRpc} or $AETHER_RPC)
159
121
  -j, --json Output raw JSON (for scripting)
160
122
  -h, --help Show this help message
161
123
 
124
+ ${C.bright}SDK Methods Used:${C.reset}
125
+ client.getValidators() → GET /v1/validators
126
+ client.getEpochInfo() → GET /v1/epoch
127
+ client.getSupply() → GET /v1/supply
128
+
162
129
  ${C.bright}Examples:${C.reset}
163
130
  aether validators list # All validators, sorted by score
164
131
  aether validators list --tier full # Full validators only
@@ -173,46 +140,40 @@ ${C.bright}Examples:${C.reset}
173
140
  }
174
141
 
175
142
  // ---------------------------------------------------------------------------
176
- // Data fetchers
143
+ // Data fetchers using SDK - Real blockchain RPC calls
177
144
  // ---------------------------------------------------------------------------
178
145
 
179
- /** Fetch all validators from the network */
146
+ /** Fetch all validators from the network using SDK */
180
147
  async function fetchValidators(rpc) {
181
148
  try {
182
- // Try /v1/validators first (standard Aether RPC endpoint)
183
- const res = await httpRequest(rpc, '/v1/validators');
184
- if (res && !res.error) {
185
- if (Array.isArray(res)) return res;
186
- if (res.validators && Array.isArray(res.validators)) return res.validators;
187
- if (res.accounts && Array.isArray(res.accounts)) return res.accounts;
188
- }
189
- // Fallback: POST to a validators query endpoint
190
- const res2 = await httpPost(rpc, '/v1/validators', {});
191
- if (res2 && !res2.error) {
192
- if (Array.isArray(res2)) return res2;
193
- if (res2.validators && Array.isArray(res2.validators)) return res2.validators;
194
- }
195
- return [];
149
+ const client = createClient(rpc);
150
+ // SDK getValidators() GET /v1/validators
151
+ const result = await client.getValidators();
152
+ return Array.isArray(result) ? result : [];
196
153
  } catch {
197
154
  return [];
198
155
  }
199
156
  }
200
157
 
201
- /** Fetch epoch info for APY calculations */
158
+ /** Fetch epoch info for APY calculations using SDK */
202
159
  async function fetchEpochInfo(rpc) {
203
160
  try {
204
- const res = await httpRequest(rpc, '/v1/epoch-info');
205
- return res;
161
+ const client = createClient(rpc);
162
+ // SDK getEpochInfo() → GET /v1/epoch
163
+ const result = await client.getEpochInfo();
164
+ return result || null;
206
165
  } catch {
207
166
  return null;
208
167
  }
209
168
  }
210
169
 
211
- /** Fetch network-wide stake totals for APY estimation */
170
+ /** Fetch network-wide stake totals for APY estimation using SDK */
212
171
  async function fetchSupply(rpc) {
213
172
  try {
214
- const res = await httpRequest(rpc, '/v1/supply');
215
- return res;
173
+ const client = createClient(rpc);
174
+ // SDK getSupply() → GET /v1/supply
175
+ const result = await client.getSupply();
176
+ return result || null;
216
177
  } catch {
217
178
  return null;
218
179
  }
@@ -541,37 +541,11 @@ async function connectWallet(rl) {
541
541
 
542
542
  // ---------------------------------------------------------------------------
543
543
  // BALANCE
544
- // Query chain RPC GET /v1/account/<addr> for real AETH balance
544
+ // Query chain RPC GET /v1/account/<addr> for real AETH balance using SDK
545
545
  // ---------------------------------------------------------------------------
546
546
 
547
547
  function getDefaultRpc() {
548
- return process.env.AETHER_RPC || 'http://127.0.0.1:8899';
549
- }
550
-
551
- /**
552
- * Make HTTP GET request to the RPC endpoint
553
- */
554
- function httpRequest(rpcUrl, path) {
555
- return new Promise((resolve, reject) => {
556
- const url = new URL(path, rpcUrl);
557
- const lib = url.protocol === 'https:' ? require('https') : require('http');
558
- const req = lib.request({
559
- hostname: url.hostname,
560
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
561
- path: url.pathname + url.search,
562
- method: 'GET',
563
- headers: { 'Content-Type': 'application/json' },
564
- }, (res) => {
565
- let data = '';
566
- res.on('data', (chunk) => data += chunk);
567
- res.on('end', () => {
568
- try { resolve(JSON.parse(data)); }
569
- catch { resolve(data); }
570
- });
571
- });
572
- req.on('error', reject);
573
- req.end();
574
- });
548
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
575
549
  }
576
550
 
577
551
  /**
@@ -612,9 +586,11 @@ async function balanceWallet(rl) {
612
586
  console.log();
613
587
 
614
588
  try {
589
+ // Use SDK for real blockchain RPC call
590
+ const client = new aether.AetherClient({ rpcUrl });
615
591
  // Strip ATH prefix if present for API call
616
592
  const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
617
- const account = await httpRequest(rpcUrl, `/v1/account/${rawAddr}`);
593
+ const account = await client.getAccountInfo(rawAddr);
618
594
 
619
595
  if (!account || account.error) {
620
596
  console.log(` ${C.yellow}⚠ Account not found on chain or RPC error.${C.reset}`);
package/index.js CHANGED
@@ -30,6 +30,12 @@ const { broadcastCommand } = require('./commands/broadcast');
30
30
  const { apyCommand } = require('./commands/apy');
31
31
  const { statsCommand } = require('./commands/stats');
32
32
  const { txHistoryCommand } = require('./commands/tx-history');
33
+ const { feesCommand } = require('./commands/fees');
34
+ const { tpsCommand } = require('./commands/tps');
35
+ const { blockhashCommand } = require('./commands/blockhash');
36
+ const { sdkTestCommand } = require('./commands/sdk-test');
37
+ const { balanceCommand } = require('./commands/balance');
38
+ const { transferCommand } = require('./commands/transfer');
33
39
  const readline = require('readline');
34
40
 
35
41
  // CLI version
@@ -204,6 +210,13 @@ const COMMANDS = {
204
210
  stakePositionsCommand();
205
211
  },
206
212
  },
213
+ 'stake-info': {
214
+ description: 'Get staking info for an address via real chain RPC — aether stake-info <address>',
215
+ handler: () => {
216
+ const { stakeInfoCommand } = require('./commands/stake-info');
217
+ stakeInfoCommand();
218
+ },
219
+ },
207
220
  unstake: {
208
221
  description: 'Unstake AETH — deactivate a stake account — aether unstake --account <stakeAcct> [--amount <aeth>]',
209
222
  handler: () => {
@@ -227,11 +240,7 @@ const COMMANDS = {
227
240
  transfer: {
228
241
  description: 'Transfer AETH to another address — aether transfer --to <addr> --amount <aeth>',
229
242
  handler: () => {
230
- const { walletCommand } = require('./commands/wallet');
231
- const originalArgv = process.argv;
232
- process.argv = [...originalArgv.slice(0, 2), 'wallet', 'transfer', ...originalArgv.slice(3)];
233
- walletCommand();
234
- process.argv = originalArgv;
243
+ transferCommand();
235
244
  },
236
245
  },
237
246
  tx: {
@@ -246,6 +255,20 @@ const COMMANDS = {
246
255
  txHistoryCommand();
247
256
  },
248
257
  },
258
+ blockhash: {
259
+ description: 'Get the latest blockhash from the chain (required for signing TXs) — aether blockhash [--json] [--watch]',
260
+ handler: () => {
261
+ const { blockhashCommand } = require('./commands/blockhash');
262
+ blockhashCommand();
263
+ },
264
+ },
265
+ balance: {
266
+ description: 'Query account balance — aether balance [address] [--json] [--lamports] [--rpc <url>]',
267
+ handler: () => {
268
+ const { balanceCommand } = require('./commands/balance');
269
+ balanceCommand();
270
+ },
271
+ },
249
272
  network: {
250
273
  description: 'Aether network status — slot, block height, peers, TPS, epoch info',
251
274
  handler: () => {
@@ -394,6 +417,31 @@ const COMMANDS = {
394
417
  emergencyCommand();
395
418
  },
396
419
  },
420
+ fees: {
421
+ description: 'Network fee estimates — aether fees [--json] [--verbose] [--rpc <url>]',
422
+ handler: () => {
423
+ feesCommand();
424
+ },
425
+ },
426
+ tps: {
427
+ description: 'Transactions per second monitor — aether tps [--monitor] [--interval 2] [--json]',
428
+ handler: () => {
429
+ tpsCommand();
430
+ },
431
+ },
432
+ claim: {
433
+ description: 'Claim accumulated staking rewards — aether claim --address <addr> [--json] [--dry-run]',
434
+ handler: () => {
435
+ const { claimCommand } = require('./commands/claim');
436
+ claimCommand();
437
+ },
438
+ },
439
+ 'sdk-test': {
440
+ description: 'Test SDK with real RPC calls — aether sdk-test [--rpc <url>] [--quick] [--json]',
441
+ handler: () => {
442
+ sdkTestCommand();
443
+ },
444
+ },
397
445
  help: {
398
446
  description: 'Show this help message',
399
447
  handler: showHelp,
@@ -448,7 +496,7 @@ function parseArgs() {
448
496
  const args = process.argv.slice(2);
449
497
 
450
498
  // Handle version flag
451
- if (args.includes('--version') || args.includes('-v')) {
499
+ if (args.includes('--version') || args.includes('-v') || args.includes('-V')) {
452
500
  return 'version';
453
501
  }
454
502
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aether-hub",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "AeTHer Validator CLI — tiered validators (Full/Lite/Observer), system checks, onboarding, and node management",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -18,8 +18,6 @@
18
18
  "test": "node test/doctor.test.js",
19
19
  "start": "node index.js",
20
20
  "doctor": "node index.js doctor",
21
- "price": "node index.js price",
22
- "build": "node -e \"require('./commands/price'); require('./commands/account'); require('./commands/emergency')\" && echo \"Build OK\"",
23
21
  "postinstall": "node -e \"console.log('\\n\\n [Aether] aether-hub installed!\\n\\n Run: aether start\\n Docs: https://github.com/jelly-legs-ai/Jelly-legs-unsteady-workshop\\n')\""
24
22
  },
25
23
  "keywords": [