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.
@@ -1,220 +1,205 @@
1
- #!/usr/bin/env node
2
- /**
3
- * aether-cli stake-positions
4
- *
5
- * Query and display current stake positions/delegations for a wallet.
6
- * Shows validator, amount, status, and accumulated rewards.
7
- *
8
- * Usage:
9
- * aether stake-positions --address <addr> [--json]
10
- * aether wallet stake-positions --address <addr> [--json]
11
- *
12
- * Examples:
13
- * aether stake-positions --address ATHxxx
14
- * aether wallet stake-positions --address ATHxxx --json
15
- */
16
-
17
- const https = require('https');
18
- const http = require('http');
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
- const CLI_VERSION = '1.0.0';
33
-
34
- // ---------------------------------------------------------------------------
35
- // Config
36
- // ---------------------------------------------------------------------------
37
-
38
- function getDefaultRpc() {
39
- return process.env.AETHER_RPC || 'http://127.0.0.1:8899';
40
- }
41
-
42
- // ---------------------------------------------------------------------------
43
- // HTTP helpers
44
- // ---------------------------------------------------------------------------
45
-
46
- function httpRequest(rpcUrl, pathStr, timeoutMs = 8000) {
47
- return new Promise((resolve, reject) => {
48
- const url = new URL(pathStr, rpcUrl);
49
- const lib = url.protocol === 'https:' ? https : http;
50
- const req = lib.request({
51
- hostname: url.hostname,
52
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
53
- path: url.pathname + url.search,
54
- method: 'GET',
55
- headers: { 'Content-Type': 'application/json' },
56
- }, (res) => {
57
- let data = '';
58
- res.on('data', (chunk) => data += chunk);
59
- res.on('end', () => {
60
- try { resolve(JSON.parse(data)); }
61
- catch { resolve(data); }
62
- });
63
- });
64
- req.on('error', reject);
65
- req.setTimeout(timeoutMs, () => {
66
- req.destroy();
67
- reject(new Error(`Request timeout after ${timeoutMs}ms`));
68
- });
69
- req.end();
70
- });
71
- }
72
-
73
- // ---------------------------------------------------------------------------
74
- // Argument parsing
75
- // ---------------------------------------------------------------------------
76
-
77
- function parseArgs() {
78
- const args = process.argv.slice(2);
79
- const result = { address: null, json: false };
80
-
81
- for (let i = 0; i < args.length; i++) {
82
- if ((args[i] === '--address' || args[i] === '-a') && args[i + 1]) {
83
- result.address = args[i + 1];
84
- i++;
85
- } else if (args[i] === '--json' || args[i] === '--json-output') {
86
- result.json = true;
87
- } else if (args[i] === '--rpc' && args[i + 1]) {
88
- result.rpc = args[i + 1];
89
- i++;
90
- } else if (args[i] === '--help' || args[i] === '-h') {
91
- result.help = true;
92
- }
93
- }
94
-
95
- return result;
96
- }
97
-
98
- // ---------------------------------------------------------------------------
99
- // Balance formatting
100
- // ---------------------------------------------------------------------------
101
-
102
- function formatAether(lamports) {
103
- const aeth = (lamports || 0) / 1e9;
104
- return aeth.toLocaleString(undefined, { minimumFractionDigits: 4, maximumFractionDigits: 4 }) + ' AETH';
105
- }
106
-
107
- // ---------------------------------------------------------------------------
108
- // Main
109
- // ---------------------------------------------------------------------------
110
-
111
- async function stakePositionsCommand() {
112
- const opts = parseArgs();
113
-
114
- if (opts.help) {
115
- console.log(`
116
- ${C.bright}${C.cyan}stake-positions${C.reset} — Query active stake delegations for a wallet
117
-
118
- ${C.bright}USAGE${C.reset}
119
- aether stake-positions --address <addr> [--json] [--rpc <url>]
120
-
121
- ${C.bright}OPTIONS${C.reset}
122
- --address <addr> Wallet address (ATH...)
123
- --json Output raw JSON
124
- --rpc <url> RPC endpoint (default: AETHER_RPC or localhost:8899)
125
- --help Show this help
126
-
127
- ${C.bright}EXAMPLES${C.reset}
128
- aether stake-positions --address ATH3abc...
129
- aether stake-positions --address ATH3abc... --json
130
- `);
131
- return;
132
- }
133
-
134
- if (!opts.address) {
135
- console.log(` ${C.red}✗ Missing --address${C.reset}\n`);
136
- console.log(` Usage: aether stake-positions --address <addr> [--json]\n`);
137
- return;
138
- }
139
-
140
- const rpcUrl = opts.rpc || getDefaultRpc();
141
- const address = opts.address;
142
- const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
143
-
144
- if (!opts.json) {
145
- console.log(`\n${C.bright}${C.cyan}── Stake Positions ──────────────────────────────────────${C.reset}\n`);
146
- console.log(` ${C.dim}Wallet:${C.reset} ${address}`);
147
- console.log(` ${C.dim}RPC: ${C.reset} ${rpcUrl}\n`);
148
- }
149
-
150
- try {
151
- // Fetch stake accounts
152
- const res = await httpRequest(rpcUrl, `/v1/stake?address=${encodeURIComponent(rawAddr)}`);
153
-
154
- let stakeAccounts = [];
155
- if (Array.isArray(res)) {
156
- stakeAccounts = res;
157
- } else if (res && typeof res === 'object') {
158
- stakeAccounts = res.accounts || res.stake_accounts || res.data || [];
159
- }
160
-
161
- if (opts.json) {
162
- const totalLamports = stakeAccounts.reduce((sum, acc) => sum + (acc.stake_lamports || acc.lamports || 0), 0);
163
- console.log(JSON.stringify({
164
- wallet_address: address,
165
- stake_accounts: stakeAccounts.map(acc => ({
166
- stake_account: acc.pubkey || acc.publicKey || acc.account || 'unknown',
167
- validator: acc.validator || acc.delegate || acc.validator_address || 'unknown',
168
- stake_lamports: acc.stake_lamports || acc.lamports || 0,
169
- stake_aeth: ((acc.stake_lamports || acc.lamports || 0) / 1e9).toFixed(4),
170
- status: acc.status || acc.state || 'active',
171
- updated_epoch: acc.epoch || acc.last_update_epoch || null,
172
- })),
173
- total_staked_lamports: totalLamports,
174
- total_staked_aeth: (totalLamports / 1e9).toFixed(4),
175
- count: stakeAccounts.length,
176
- }, null, 2));
177
- return;
178
- }
179
-
180
- if (!stakeAccounts || stakeAccounts.length === 0) {
181
- console.log(` ${C.yellow}? No active stake positions found.${C.reset}`);
182
- console.log(` ${C.dim} Stake AETH with: ${C.cyan}aether stake --validator <addr> --amount <aeth>${C.reset}\n`);
183
- return;
184
- }
185
-
186
- let totalStaked = 0;
187
- console.log(` ${C.bright}Stake Positions (${stakeAccounts.length})${C.reset}\n`);
188
-
189
- for (const acc of stakeAccounts) {
190
- const stakeAcct = acc.pubkey || acc.publicKey || acc.account || 'unknown';
191
- const validator = acc.validator || acc.delegate || acc.validator_address || 'unknown';
192
- const lamports = acc.stake_lamports || acc.lamports || 0;
193
- const status = (acc.status || acc.state || 'active').toLowerCase();
194
- const epoch = acc.epoch || acc.last_update_epoch || null;
195
-
196
- totalStaked += lamports;
197
-
198
- const statusColor = status === 'active' ? C.green : status === 'deactivating' ? C.yellow : C.dim;
199
- const shortAcct = stakeAcct.length > 20 ? stakeAcct.slice(0, 8) + '.' + stakeAcct.slice(-8) : stakeAcct;
200
- const shortVal = validator.length > 20 ? validator.slice(0, 8) + '.' + validator.slice(-8) : validator;
201
- const aeth = (lamports / 1e9).toFixed(4);
202
-
203
- console.log(` ${C.dim}┌─${C.bright}${statusColor} ${status.toUpperCase()}${C.reset}`);
204
- console.log(` │ ${C.dim}Stake acct:${C.reset} ${shortAcct}`);
205
- console.log(` │ ${C.dim}Validator:${C.reset} ${shortVal}`);
206
- console.log(` │ ${C.dim}Staked:${C.reset} ${C.bright}${aeth} AETH${C.reset} (${lamports.toLocaleString()} lamports)`);
207
- if (epoch) console.log(` │ ${C.dim}Epoch:${C.reset} ${C.bright}#${epoch}${C.reset}`);
208
- console.log(` ${C.dim}└${C.reset}\n`);
209
- }
210
-
211
- console.log(` ${C.dim}────────────────────────────────────────${C.reset}`);
212
- console.log(` ${C.bright}Total Staked:${C.reset} ${C.green}${formatAether(totalStaked)}${C.reset}\n`);
213
-
214
- } catch (err) {
215
- console.log(` ${C.red}? Failed to fetch stake positions:${C.reset} ${err.message}\n`);
216
- process.exit(1);
217
- }
218
- }
219
-
220
- stakePositionsCommand();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli stake-positions
4
+ *
5
+ * Query and display current stake positions/delegations for a wallet.
6
+ * Shows validator, amount, status, and accumulated rewards.
7
+ *
8
+ * Usage:
9
+ * aether stake-positions --address <addr> [--json]
10
+ * aether wallet stake-positions --address <addr> [--json]
11
+ *
12
+ * Examples:
13
+ * aether stake-positions --address ATHxxx
14
+ * aether wallet stake-positions --address ATHxxx --json
15
+ *
16
+ * SDK wired to: GET /v1/slot, GET /v1/stake/<address>, GET /v1/account/<addr>
17
+ */
18
+
19
+ const path = require('path');
20
+
21
+ // Import SDK — all network calls go through @jellylegsai/aether-sdk
22
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
23
+ const aether = require(sdkPath);
24
+
25
+ // ANSI colours
26
+ const C = {
27
+ reset: '\x1b[0m',
28
+ bright: '\x1b[1m',
29
+ dim: '\x1b[2m',
30
+ red: '\x1b[31m',
31
+ green: '\x1b[32m',
32
+ yellow: '\x1b[33m',
33
+ cyan: '\x1b[36m',
34
+ magenta: '\x1b[35m',
35
+ };
36
+
37
+ const CLI_VERSION = '1.0.1';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Config
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function getDefaultRpc() {
44
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
45
+ }
46
+
47
+ function createClient(rpcUrl) {
48
+ return new aether.AetherClient({ rpcUrl });
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Argument parsing
53
+ // ---------------------------------------------------------------------------
54
+
55
+ function parseArgs() {
56
+ const args = process.argv.slice(2);
57
+ const result = { address: null, json: false };
58
+
59
+ for (let i = 0; i < args.length; i++) {
60
+ if ((args[i] === '--address' || args[i] === '-a') && args[i + 1]) {
61
+ result.address = args[++i];
62
+ } else if (args[i] === '--json' || args[i] === '--json-output') {
63
+ result.json = true;
64
+ } else if (args[i] === '--rpc' && args[i + 1]) {
65
+ result.rpc = args[++i];
66
+ } else if (args[i] === '--help' || args[i] === '-h') {
67
+ result.help = true;
68
+ }
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Balance formatting
76
+ // ---------------------------------------------------------------------------
77
+
78
+ function formatAether(lamports) {
79
+ const aeth = (lamports || 0) / 1e9;
80
+ return aeth.toLocaleString(undefined, { minimumFractionDigits: 4, maximumFractionDigits: 4 }) + ' AETH';
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Main
85
+ // ---------------------------------------------------------------------------
86
+
87
+ async function stakePositionsCommand() {
88
+ const opts = parseArgs();
89
+
90
+ if (opts.help) {
91
+ console.log(`
92
+ ${C.bright}${C.cyan}stake-positions${C.reset} — Query active stake delegations for a wallet
93
+
94
+ ${C.bright}USAGE${C.reset}
95
+ aether stake-positions --address <addr> [--json] [--rpc <url>]
96
+
97
+ ${C.bright}OPTIONS${C.reset}
98
+ --address <addr> Wallet address (ATH...)
99
+ --json Output raw JSON
100
+ --rpc <url> RPC endpoint (default: AETHER_RPC or localhost:8899)
101
+ --help Show this help
102
+
103
+ ${C.bright}SDK METHODS USED${C.reset}
104
+ client.getSlot() GET /v1/slot
105
+ client.getStakePositions() → GET /v1/stake/<address>
106
+ client.getAccountInfo() → GET /v1/account/<addr>
107
+
108
+ ${C.bright}EXAMPLES${C.reset}
109
+ aether stake-positions --address ATH3abc...
110
+ aether stake-positions --address ATH3abc... --json
111
+ `);
112
+ return;
113
+ }
114
+
115
+ if (!opts.address) {
116
+ console.log(` ${C.red}✗ Missing --address${C.reset}\n`);
117
+ console.log(` Usage: aether stake-positions --address <addr> [--json]\n`);
118
+ return;
119
+ }
120
+
121
+ const rpcUrl = opts.rpc || getDefaultRpc();
122
+ const client = createClient(rpcUrl);
123
+ const address = opts.address;
124
+ const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
125
+
126
+ if (!opts.json) {
127
+ console.log(`\n${C.bright}${C.cyan}── Stake Positions ──────────────────────────────────────${C.reset}\n`);
128
+ console.log(` ${C.dim}Wallet:${C.reset} ${address}`);
129
+ console.log(` ${C.dim}RPC: ${C.reset} ${rpcUrl}\n`);
130
+ }
131
+
132
+ try {
133
+ // Verify chain connectivity via SDK (real RPC call)
134
+ const slot = await client.getSlot().catch(() => null);
135
+
136
+ // Fetch stake positions via SDK (real RPC call to GET /v1/stake/<address>)
137
+ const stakeAccounts = await client.getStakePositions(rawAddr);
138
+
139
+ if (opts.json) {
140
+ const totalLamports = stakeAccounts.reduce((sum, acc) => sum + (acc.stake_lamports || acc.lamports || 0), 0);
141
+ console.log(JSON.stringify({
142
+ wallet_address: address,
143
+ slot,
144
+ stake_accounts: stakeAccounts.map(acc => ({
145
+ stake_account: acc.pubkey || acc.publicKey || acc.account || 'unknown',
146
+ validator: acc.validator || acc.delegate || acc.validator_address || 'unknown',
147
+ stake_lamports: acc.stake_lamports || acc.lamports || 0,
148
+ stake_aeth: ((acc.stake_lamports || acc.lamports || 0) / 1e9).toFixed(4),
149
+ status: acc.status || acc.state || 'active',
150
+ updated_epoch: acc.epoch || acc.last_update_epoch || null,
151
+ })),
152
+ total_staked_lamports: totalLamports,
153
+ total_staked_aeth: (totalLamports / 1e9).toFixed(4),
154
+ count: stakeAccounts.length,
155
+ rpc: rpcUrl,
156
+ cli_version: CLI_VERSION,
157
+ fetched_at: new Date().toISOString(),
158
+ }, null, 2));
159
+ return;
160
+ }
161
+
162
+ if (!stakeAccounts || stakeAccounts.length === 0) {
163
+ console.log(` ${C.yellow}? No active stake positions found.${C.reset}`);
164
+ console.log(` ${C.dim} Stake AETH with: ${C.cyan}aether stake --validator <addr> --amount <aeth>${C.reset}\n`);
165
+ return;
166
+ }
167
+
168
+ let totalStaked = 0;
169
+ console.log(` ${C.bright}Stake Positions (${stakeAccounts.length})${C.reset}\n`);
170
+
171
+ for (const acc of stakeAccounts) {
172
+ const stakeAcct = acc.pubkey || acc.publicKey || acc.account || 'unknown';
173
+ const validator = acc.validator || acc.delegate || acc.validator_address || 'unknown';
174
+ const lamports = acc.stake_lamports || acc.lamports || 0;
175
+ const status = (acc.status || acc.state || 'active').toLowerCase();
176
+ const epoch = acc.epoch || acc.last_update_epoch || null;
177
+
178
+ totalStaked += lamports;
179
+
180
+ const statusColor = status === 'active' ? C.green : status === 'deactivating' ? C.yellow : C.dim;
181
+ const shortAcct = stakeAcct.length > 20 ? stakeAcct.slice(0, 8) + '.' + stakeAcct.slice(-8) : stakeAcct;
182
+ const shortVal = validator.length > 20 ? validator.slice(0, 8) + '.' + validator.slice(-8) : validator;
183
+ const aeth = (lamports / 1e9).toFixed(4);
184
+
185
+ console.log(` ${C.dim}┌─${C.bright}${statusColor} ${status.toUpperCase()}${C.reset}`);
186
+ console.log(` │ ${C.dim}Stake acct:${C.reset} ${shortAcct}`);
187
+ console.log(` ${C.dim}Validator:${C.reset} ${shortVal}`);
188
+ console.log(` │ ${C.dim}Staked:${C.reset} ${C.bright}${aeth} AETH${C.reset} (${lamports.toLocaleString()} lamports)`);
189
+ if (epoch) console.log(` │ ${C.dim}Epoch:${C.reset} ${C.bright}#${epoch}${C.reset}`);
190
+ console.log(` ${C.dim}└${C.reset}\n`);
191
+ }
192
+
193
+ console.log(` ${C.dim}────────────────────────────────────────${C.reset}`);
194
+ console.log(` ${C.bright}Total Staked:${C.reset} ${C.green}${formatAether(totalStaked)}${C.reset}\n`);
195
+
196
+ } catch (err) {
197
+ console.log(` ${C.red}? Failed to fetch stake positions:${C.reset} ${err.message}\n`);
198
+ console.log(` ${C.dim} Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
199
+ process.exit(1);
200
+ }
201
+ }
202
+
203
+ stakePositionsCommand();
204
+
205
+ module.exports = { stakePositionsCommand };