@jellylegsai/aether-cli 1.9.1 → 2.0.1

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.
@@ -36,24 +36,39 @@ const nacl = require('tweetnacl');
36
36
  const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
37
37
  const aether = require(sdkPath);
38
38
 
39
- // ANSI colours
40
- const C = {
41
- reset: '\x1b[0m',
42
- bright: '\x1b[1m',
43
- dim: '\x1b[2m',
44
- red: '\x1b[31m',
45
- green: '\x1b[32m',
46
- yellow: '\x1b[33m',
47
- cyan: '\x1b[36m',
48
- magenta: '\x1b[35m',
49
- };
39
+ // Import UI Framework for consistent branding
40
+ const ui = require('../lib/ui');
41
+ const {
42
+ C, BRANDING, indicators,
43
+ success, error, warning, info,
44
+ code, key, value, bright, dim,
45
+ startSpinner, stopSpinner,
46
+ drawBox, drawTable,
47
+ progressBar, progressBarColored,
48
+ formatHealth
49
+ } = ui;
50
50
 
51
51
  const DERIVATION_PATH = "m/44'/7777777'/0'/0'";
52
- const CLI_VERSION = '1.1.0';
52
+ const CLI_VERSION = '2.0.0';
53
53
 
54
- // ---------------------------------------------------------------------------
54
+ // ============================================================================
55
+ // ASCII Art & Branding
56
+ // ============================================================================
57
+
58
+ // Helper to create section header
59
+ function section(title) {
60
+ return `\n${C.yellow}${C.bright}── ${title} ${C.reset}${C.yellow}${'─'.repeat(60 - title.length)}${C.reset}`;
61
+ }
62
+
63
+ const REWARDS_LOGO = `
64
+ ${C.yellow} ╭────────────────────────────────────────────────────────────╮${C.reset}
65
+ ${C.yellow} │${C.reset} ${C.bright}${C.yellow}★${C.reset} ${C.bright}STAKING REWARDS${C.reset}${' '.repeat(33)}${C.dim}v${CLI_VERSION}${C.reset} ${C.yellow}│${C.reset}
66
+ ${C.yellow} │${C.reset} ${C.dim}Track and claim your staking rewards${C.reset}${' '.repeat(18)}${C.yellow}│${C.reset}
67
+ ${C.yellow} ╰────────────────────────────────────────────────────────────╯${C.reset}`;
68
+
69
+ // ============================================================================
55
70
  // SDK Client Setup
56
- // ---------------------------------------------------------------------------
71
+ // ============================================================================
57
72
 
58
73
  function getDefaultRpc() {
59
74
  return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
@@ -63,33 +78,35 @@ function createClient(rpcUrl) {
63
78
  return new aether.AetherClient({ rpcUrl });
64
79
  }
65
80
 
66
- // ---------------------------------------------------------------------------
67
- // Paths & config
68
- // ---------------------------------------------------------------------------
81
+ // ============================================================================
82
+ // Paths & Config
83
+ // ============================================================================
69
84
 
70
85
  function getAetherDir() {
71
86
  return path.join(require('os').homedir(), '.aether');
72
87
  }
73
88
 
74
89
  function loadConfig() {
90
+ const fs = require('fs');
75
91
  const p = path.join(getAetherDir(), 'config.json');
76
- if (!require('fs').existsSync(p)) return { defaultWallet: null };
92
+ if (!fs.existsSync(p)) return { defaultWallet: null };
77
93
  try {
78
- return JSON.parse(require('fs').readFileSync(p, 'utf8'));
94
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
79
95
  } catch {
80
96
  return { defaultWallet: null };
81
97
  }
82
98
  }
83
99
 
84
100
  function loadWallet(address) {
101
+ const fs = require('fs');
85
102
  const fp = path.join(getAetherDir(), 'wallets', `${address}.json`);
86
- if (!require('fs').existsSync(fp)) return null;
87
- return JSON.parse(require('fs').readFileSync(fp, 'utf8'));
103
+ if (!fs.existsSync(fp)) return null;
104
+ return JSON.parse(fs.readFileSync(fp, 'utf8'));
88
105
  }
89
106
 
90
- // ---------------------------------------------------------------------------
91
- // Crypto helpers
92
- // ---------------------------------------------------------------------------
107
+ // ============================================================================
108
+ // Crypto Helpers
109
+ // ============================================================================
93
110
 
94
111
  function deriveKeypair(mnemonic) {
95
112
  if (!bip39.validateMnemonic(mnemonic)) throw new Error('Invalid mnemonic');
@@ -109,9 +126,9 @@ function signTransaction(tx, secretKey) {
109
126
  return bs58.encode(sig);
110
127
  }
111
128
 
112
- // ---------------------------------------------------------------------------
113
- // Format helpers
114
- // ---------------------------------------------------------------------------
129
+ // ============================================================================
130
+ // Format Helpers
131
+ // ============================================================================
115
132
 
116
133
  function formatAether(lamports) {
117
134
  if (!lamports || lamports === '0') return '0 AETH';
@@ -129,9 +146,17 @@ function shortAddress(addr) {
129
146
  return addr.slice(0, 8) + '...' + addr.slice(-8);
130
147
  }
131
148
 
132
- // ---------------------------------------------------------------------------
133
- // Rewards calculation via SDK (REAL RPC CALLS)
134
- // ---------------------------------------------------------------------------
149
+ function formatAPY(apyBps) {
150
+ if (!apyBps) return dim('—');
151
+ const pct = (apyBps / 100).toFixed(2);
152
+ if (apyBps > 500) return `${C.green}${pct}%${C.reset}`;
153
+ if (apyBps > 200) return `${C.yellow}${pct}%${C.reset}`;
154
+ return `${C.dim}${pct}%${C.reset}`;
155
+ }
156
+
157
+ // ============================================================================
158
+ // Rewards Calculation via SDK (REAL RPC CALLS)
159
+ // ============================================================================
135
160
 
136
161
  /**
137
162
  * Fetch stake positions and calculate rewards using SDK
@@ -202,7 +227,7 @@ async function fetchStakeRewards(rpcUrl, stakeAddress) {
202
227
 
203
228
  /**
204
229
  * Fetch all stake accounts for a wallet using SDK
205
- * REAL RPC CALL: GET /v1/stake/<address>
230
+ * REAL RPC: GET /v1/stake/<address>
206
231
  */
207
232
  async function fetchWalletStakeAccounts(rpcUrl, walletAddress) {
208
233
  const client = createClient(rpcUrl);
@@ -226,9 +251,9 @@ async function fetchWalletStakeAccounts(rpcUrl, walletAddress) {
226
251
  }
227
252
  }
228
253
 
229
- // ---------------------------------------------------------------------------
230
- // Rewards list command - FULLY WIRED TO SDK
231
- // ---------------------------------------------------------------------------
254
+ // ============================================================================
255
+ // Rewards List Command - FULLY WIRED TO SDK with UI Framework
256
+ // ============================================================================
232
257
 
233
258
  async function rewardsList(args) {
234
259
  const rpc = args.rpc || getDefaultRpc();
@@ -239,13 +264,13 @@ async function rewardsList(args) {
239
264
  if (!address) {
240
265
  const config = loadConfig();
241
266
  const rl = createRl();
242
- const answer = await question(rl, `\n${C.cyan}Enter wallet address (or press Enter for default): ${C.reset}`);
267
+ const answer = await question(rl, `\n${C.cyan}${indicators.arrow}${C.reset} ${bright('Enter wallet address')} ${dim('(or press Enter for default)')}: `);
243
268
  rl.close();
244
269
 
245
270
  if (!answer.trim()) {
246
271
  if (!config.defaultWallet) {
247
- console.log(`\n${C.red}✗ No default wallet and no address provided.${C.reset}`);
248
- console.log(` ${C.dim}Set a default wallet first: aether wallet default${C.reset}\n`);
272
+ console.log(`\n ${error('No default wallet and no address provided.')}`);
273
+ console.log(` ${dim('Set a default wallet:')} ${code('aether wallet default')}\n`);
249
274
  return;
250
275
  }
251
276
  address = config.defaultWallet;
@@ -260,24 +285,30 @@ async function rewardsList(args) {
260
285
  if (config.defaultWallet) address = config.defaultWallet;
261
286
  }
262
287
 
263
- console.log(`\n${C.bright}${C.cyan}╔═══════════════════════════════════════════════════════╗${C.reset}`);
264
- console.log(`${C.bright}${C.cyan}║ Staking Rewards — ${shortAddress(address)} ║${C.reset}`);
265
- console.log(`${C.bright}${C.cyan}╚═══════════════════════════════════════════════════════╝${C.reset}\n`);
266
- console.log(` ${C.dim}RPC: ${rpc}${C.reset}\n`);
288
+ if (!isJson) {
289
+ console.log(REWARDS_LOGO);
290
+ console.log();
291
+ console.log(` ${indicators.info} Wallet: ${bright(shortAddress(address))}`);
292
+ console.log(` ${indicators.info} RPC: ${dim(rpc)}\n`);
293
+ }
267
294
 
268
- // Fetch stake accounts via SDK (REAL RPC)
295
+ // Fetch stake accounts via SDK with spinner
296
+ startSpinner('Fetching stake accounts');
269
297
  const stakeAccounts = await fetchWalletStakeAccounts(rpc, address);
298
+ stopSpinner(true, `${stakeAccounts.length} stake account(s) found`);
270
299
 
271
300
  if (stakeAccounts.length === 0) {
272
- console.log(` ${C.yellow}⚠ No stake accounts found for this wallet.${C.reset}`);
273
- console.log(` ${C.dim}Stake AETH first: aether stake --address ${address} --validator <val> --amount <aeth>${C.reset}\n`);
301
+ console.log(`\n ${warning('No stake accounts found for this wallet.')}`);
302
+ console.log(` ${dim('Stake AETH first:')} ${code(`aether stake --address ${address} --validator <val> --amount <aeth>`)}\n`);
274
303
  return;
275
304
  }
276
305
 
277
- // Fetch rewards for each stake account via SDK (REAL RPC CALLS)
306
+ // Fetch rewards for each stake account via SDK with spinner
307
+ startSpinner('Fetching rewards data');
278
308
  const rewardsResults = await Promise.all(
279
309
  stakeAccounts.map(sa => fetchStakeRewards(rpc, sa.address))
280
310
  );
311
+ stopSpinner(true, 'Rewards data retrieved');
281
312
 
282
313
  let totalEstimatedRewards = BigInt(0);
283
314
  let totalPendingRewards = BigInt(0);
@@ -331,39 +362,54 @@ async function rewardsList(args) {
331
362
  return;
332
363
  }
333
364
 
334
- // ASCII table header
335
- console.log(` ${C.dim}┌─────────────────────────────────────────────────────────────────────────┐${C.reset}`);
336
- console.log(` ${C.dim}│${C.reset} ${C.bright}Stake Account${C.reset} ${C.bright}Validator${C.reset} ${C.bright}Delegated${C.reset} ${C.bright}Total Rewards${C.reset} ${C.bright}APY${C.reset} ${C.dim}│${C.reset}`);
337
- console.log(` ${C.dim}├─────────────────────────────────────────────────────────────────────────┤${C.reset}`);
365
+ // Build table data for UI framework
366
+ const tableRows = rows.map(r => {
367
+ const statusIcon = r.isActive ? indicators.success : r.deactivationEpoch ? indicators.warning : indicators.error;
368
+ return [
369
+ shortAddress(r.stakeAddress),
370
+ shortAddress(r.validator),
371
+ r.delegatedStakeFormatted,
372
+ r.totalRewardsFormatted,
373
+ formatAPY(r.apyBps),
374
+ statusIcon
375
+ ];
376
+ });
338
377
 
339
- for (const r of rows) {
340
- const shortAddr = shortAddress(r.stakeAddress);
341
- const shortVal = shortAddress(r.validator);
342
- const delegated = r.delegatedStakeFormatted || '—';
343
- const totalRew = r.totalRewardsFormatted || '—';
344
- const apy = r.apyBps ? `${(r.apyBps / 100).toFixed(2)}%` : '—';
345
- const statusColor = r.isActive ? C.green : r.deactivationEpoch ? C.yellow : C.red;
346
- const status = r.isActive ? '●' : r.deactivationEpoch ? '○' : '✗';
378
+ console.log();
379
+ console.log(ui.section('Stake Accounts'));
380
+ console.log();
347
381
 
348
- console.log(
349
- ` ${C.dim}│${C.reset} ${shortAddr.padEnd(18)} ${shortVal.padEnd(14)} ${delegated.padEnd(11)} ${totalRew.padEnd(13)} ${apy.padEnd(6)} ${statusColor}${status}${C.reset} ${C.dim}│${C.reset}`
350
- );
382
+ if (tableRows.length > 0) {
383
+ const headers = ['Stake Account', 'Validator', 'Delegated', 'Total Rewards', 'APY', 'Status'];
384
+ console.log(drawTable(headers, tableRows, {
385
+ headerColor: C.yellow + C.bright,
386
+ borderColor: C.dim
387
+ }));
351
388
  }
352
389
 
353
- console.log(` ${C.dim}└─────────────────────────────────────────────────────────────────────────┘${C.reset}`);
354
390
  console.log();
355
- console.log(` ${C.bright}Total Delegated:${C.reset} ${C.cyan}${formatAether(totalDelegatedStake)}${C.reset}`);
356
- console.log(` ${C.bright}Total Rewards:${C.reset} ${C.green}${formatAether(totalEstimatedRewards)}${C.reset}`);
357
- console.log(` ${C.bright}Pending Rewards:${C.reset} ${C.magenta}${formatAether(totalPendingRewards)}${C.reset}`);
358
- console.log(` ${C.bright}Active Accounts:${C.reset} ${activeCount} of ${rows.length}`);
391
+ console.log(ui.section('Summary'));
392
+ console.log();
393
+ console.log(` ${key('Total Delegated:')} ${value(formatAether(totalDelegatedStake))}`);
394
+ console.log(` ${key('Total Rewards:')} ${C.green}${formatAether(totalEstimatedRewards)}${C.reset}`);
395
+ console.log(` ${key('Pending Rewards:')} ${C.magenta}${formatAether(totalPendingRewards)}${C.reset}`);
396
+ console.log(` ${key('Active Accounts:')} ${activeCount} of ${rows.length}`);
397
+ console.log();
398
+
399
+ // Show claim prompt if there are pending rewards
400
+ if (totalPendingRewards > BigInt(0)) {
401
+ const pendingPct = Number(totalPendingRewards) / Number(totalEstimatedRewards) * 100;
402
+ console.log(` ${C.yellow}${indicators.star}${C.reset} ${bright('You have unclaimed rewards!')}`);
403
+ console.log(` ${progressBarColored(Number(totalPendingRewards), Number(totalEstimatedRewards), 30)}`);
404
+ console.log(` ${dim('Run:')} ${code(`aether rewards claim --address ${address}`)}`);
405
+ }
406
+
359
407
  console.log();
360
- console.log(` ${C.dim}SDK Methods: getStakePositions(), getRewards(), getEpochInfo()${C.reset}`);
361
- console.log(` ${C.dim}Run "aether rewards claim --address ${address}" to claim pending rewards.${C.reset}\n`);
362
408
  }
363
409
 
364
- // ---------------------------------------------------------------------------
365
- // Rewards summary command - SDK WIRED
366
- // ---------------------------------------------------------------------------
410
+ // ============================================================================
411
+ // Rewards Summary Command - SDK WIRED with UI
412
+ // ============================================================================
367
413
 
368
414
  async function rewardsSummary(args) {
369
415
  const rpc = args.rpc || getDefaultRpc();
@@ -372,20 +418,24 @@ async function rewardsSummary(args) {
372
418
  if (!address) {
373
419
  const config = loadConfig();
374
420
  if (!config.defaultWallet) {
375
- console.log(`${C.red}✗ No default wallet and no address provided.${C.reset}`);
421
+ console.log(error('No default wallet and no address provided.'));
376
422
  return;
377
423
  }
378
424
  address = config.defaultWallet;
379
425
  }
380
426
 
381
427
  // SDK calls
428
+ startSpinner('Fetching stake data');
382
429
  const stakeAccounts = await fetchWalletStakeAccounts(rpc, address);
383
430
  if (stakeAccounts.length === 0) {
384
- console.log(`${C.yellow}⚠ No stake accounts for ${shortAddress(address)}${C.reset}`);
431
+ stopSpinner(false, 'No stake accounts');
432
+ console.log(warning(`No stake accounts for ${shortAddress(address)}`));
385
433
  return;
386
434
  }
387
435
 
388
436
  const results = await Promise.all(stakeAccounts.map(sa => fetchStakeRewards(rpc, sa.address)));
437
+ stopSpinner(true, 'Data retrieved');
438
+
389
439
  let totalRewards = BigInt(0);
390
440
  let totalPending = BigInt(0);
391
441
  let totalStake = BigInt(0);
@@ -400,103 +450,28 @@ async function rewardsSummary(args) {
400
450
  }
401
451
  }
402
452
 
403
- console.log(`${C.cyan}${shortAddress(address)}${C.reset} Stake: ${C.cyan}${formatAether(totalStake)}${C.reset} │ Total Rewards: ${C.green}${formatAether(totalRewards)}${C.reset} │ Pending: ${C.magenta}${formatAether(totalPending)}${C.reset} │ Active: ${activeCount}/${results.length}`);
404
- }
405
-
406
- // ---------------------------------------------------------------------------
407
- // Rewards pending command - SDK WIRED
408
- // ---------------------------------------------------------------------------
409
-
410
- async function rewardsPending(args) {
411
- const rpc = args.rpc || getDefaultRpc();
412
- const isJson = args.json || false;
413
- let address = args.address || null;
414
-
415
- const config = loadConfig();
416
- const rl = createRl();
417
-
418
- if (!address) {
419
- const ans = await question(rl, `\n${C.cyan}Enter wallet address: ${C.reset}`);
420
- address = ans.trim();
421
- }
422
-
423
- if (!address) {
424
- console.log(`\n${C.red}✗ No address provided.${C.reset}\n`);
425
- rl.close();
426
- return;
427
- }
428
-
429
- rl.close();
430
-
431
- // SDK calls
432
- const stakeAccounts = await fetchWalletStakeAccounts(rpc, address);
433
- if (stakeAccounts.length === 0) {
434
- if (isJson) {
435
- console.log(JSON.stringify({ address, pending: [], total_pending: '0', sdk_version: CLI_VERSION }, null, 2));
436
- } else {
437
- console.log(`\n${C.red}✗ No stake accounts found for ${address}${C.reset}\n`);
438
- }
439
- return;
440
- }
441
-
442
- const results = [];
443
- let totalPending = BigInt(0);
453
+ // Summary box
454
+ const summaryContent = [
455
+ `${C.cyan}${shortAddress(address)}${C.reset}`,
456
+ `${key('Stake:')} ${value(formatAether(totalStake))}`,
457
+ `${key('Total Rewards:')} ${C.green}${formatAether(totalRewards)}${C.reset}`,
458
+ `${key('Pending:')} ${C.magenta}${formatAether(totalPending)}${C.reset}`,
459
+ `${key('Active:')} ${activeCount}/${results.length}`,
460
+ ].join('\n');
444
461
 
445
- // SDK calls for each stake account
446
- for (const sa of stakeAccounts) {
447
- const rd = await fetchStakeRewards(rpc, sa.address);
448
- if (!rd.error) {
449
- const pending = BigInt(rd.pendingRewards || 0);
450
- totalPending += pending;
451
- results.push({
452
- stake_account: sa.address,
453
- validator: sa.validator || rd.validator || 'unknown',
454
- delegated_stake: rd.delegatedStakeFormatted || '0',
455
- pending_rewards: rd.pendingRewardsFormatted || '0',
456
- pending_lamports: pending.toString(),
457
- apy_bps: rd.apyBps || 0,
458
- is_active: rd.isActive,
459
- });
460
- }
461
- }
462
-
463
- if (isJson) {
464
- console.log(JSON.stringify({
465
- address,
466
- rpc,
467
- total_pending: totalPending.toString(),
468
- total_pending_formatted: formatAether(totalPending.toString()),
469
- accounts: results,
470
- cli_version: CLI_VERSION,
471
- fetched_at: new Date().toISOString(),
472
- }, null, 2));
473
- return;
474
- }
475
-
476
- console.log(`\n${C.bright}${C.cyan}╔══════════════════════════════════════════════════════════════╗${C.reset}`);
477
- console.log(`${C.bright}${C.cyan}║ Pending Staking Rewards (SDK-Wired) ║${C.reset}`);
478
- console.log(`${C.bright}${C.cyan}╚══════════════════════════════════════════════════════════════╝${C.reset}\n`);
479
- console.log(` ${C.dim}Wallet:${C.reset} ${C.bright}${address}${C.reset}`);
480
- console.log(` ${C.dim}RPC:${C.reset} ${rpc}`);
481
462
  console.log();
482
- console.log(` ${C.yellow}Stake Account${C.reset.padEnd(48)} ${C.yellow}Pending${C.reset} ${C.yellow}APY${C.reset}`);
483
- console.log(` ${C.dim}${''.repeat(72)}${C.reset}`);
484
-
485
- for (const r of results) {
486
- const shortSa = shortAddress(r.stake_account);
487
- console.log(` ${C.cyan}${shortSa}${C.reset.padEnd(52)} ${C.green}${r.pending_rewards.padStart(12)}${C.reset} ${(r.apy_bps / 100).toFixed(2)}%`);
488
- }
489
-
490
- console.log(` ${C.dim}${'─'.repeat(72)}${C.reset}`);
491
- console.log(` ${C.bright}TOTAL PENDING${C.reset.padEnd(52)} ${C.magenta}${formatAethFull(totalPending.toString()).padStart(12)}${C.reset}`);
463
+ console.log(drawBox(summaryContent, {
464
+ title: 'Rewards Summary',
465
+ titleColor: C.yellow,
466
+ borderColor: C.dim,
467
+ style: 'single'
468
+ }));
492
469
  console.log();
493
- console.log(` ${C.dim}SDK: getStakePositions(), getRewards()${C.reset}`);
494
- console.log(` ${C.dim}Run ${C.cyan}aether rewards claim --address ${address}${C.dim} to claim.${C.reset}\n`);
495
470
  }
496
471
 
497
- // ---------------------------------------------------------------------------
498
- // Rewards claim command - SDK WIRED with sendTransaction
499
- // ---------------------------------------------------------------------------
472
+ // ============================================================================
473
+ // Rewards Claim Command - SDK WIRED with Enhanced UI
474
+ // ============================================================================
500
475
 
501
476
  async function rewardsClaim(args) {
502
477
  const rpc = args.rpc || getDefaultRpc();
@@ -507,30 +482,40 @@ async function rewardsClaim(args) {
507
482
  const config = loadConfig();
508
483
  const rl = createRl();
509
484
 
485
+ // Print header
486
+ if (!isJson) {
487
+ console.log(REWARDS_LOGO);
488
+ console.log();
489
+ }
490
+
510
491
  if (!address) {
511
- const ans = await question(rl, `\n${C.cyan}Enter wallet address: ${C.reset}`);
492
+ const ans = await question(rl, `${C.cyan}${indicators.arrow}${C.reset} ${bright('Enter wallet address')}: `);
512
493
  address = ans.trim();
513
494
  }
514
495
 
515
496
  if (!stakeAccount) {
516
497
  // SDK call to fetch stake accounts
498
+ startSpinner('Fetching stake accounts');
517
499
  const stakeAccounts = await fetchWalletStakeAccounts(rpc, address);
500
+ stopSpinner(true, `${stakeAccounts.length} account(s) found`);
501
+
518
502
  if (stakeAccounts.length === 0) {
519
- console.log(`\n${C.red}✗ No stake accounts found for this wallet.${C.reset}\n`);
503
+ console.log(`\n ${error('No stake accounts found for this wallet.')}\n`);
520
504
  rl.close();
521
505
  return;
522
506
  }
523
507
  if (stakeAccounts.length === 1) {
524
508
  stakeAccount = stakeAccounts[0].address;
525
509
  } else {
526
- console.log(`\n${C.cyan}Select stake account:${C.reset}`);
510
+ console.log(`\n ${bright('Select stake account:')}`);
527
511
  stakeAccounts.forEach((sa, i) => {
528
- console.log(` ${i + 1}) ${shortAddress(sa.address)} ${shortAddress(sa.validator || 'unknown')}`);
512
+ const statusIcon = sa.deactivationEpoch ? indicators.warning : indicators.success;
513
+ console.log(` ${C.cyan}${i + 1})${C.reset} ${statusIcon} ${shortAddress(sa.address)} ${dim('→')} ${shortAddress(sa.validator || 'unknown')}`);
529
514
  });
530
- const ans = await question(rl, `${C.cyan}Enter number: ${C.reset}`);
515
+ const ans = await question(rl, `\n${C.cyan}${indicators.arrow}${C.reset} ${bright('Enter number')}: `);
531
516
  const idx = parseInt(ans.trim()) - 1;
532
517
  if (idx < 0 || idx >= stakeAccounts.length) {
533
- console.log(`\n${C.red}Invalid selection.${C.reset}\n`);
518
+ console.log(`\n ${error('Invalid selection.')}\n`);
534
519
  rl.close();
535
520
  return;
536
521
  }
@@ -541,42 +526,42 @@ async function rewardsClaim(args) {
541
526
  // Load wallet for signing
542
527
  const wallet = loadWallet(address);
543
528
  if (!wallet) {
544
- console.log(`\n${C.red}✗ Wallet not found locally: ${address}${C.reset}`);
545
- console.log(` ${C.dim}Import it: aether wallet import${C.reset}\n`);
529
+ console.log(`\n ${error(`Wallet not found locally: ${address}`)}`);
530
+ console.log(` ${dim('Import it:')} ${code('aether wallet import')}\n`);
546
531
  rl.close();
547
532
  return;
548
533
  }
549
534
 
550
- console.log(`\n${C.bright}${C.cyan}╔════════════════════════════════════════╗${C.reset}`);
551
- console.log(`${C.bright}${C.cyan}║ Claim Staking Rewards ║${C.reset}`);
552
- console.log(`${C.bright}${C.cyan}╚════════════════════════════════════════╝${C.reset}\n`);
553
- console.log(` ${C.dim}Wallet:${C.reset} ${address}`);
554
- console.log(` ${C.dim}Stake Account:${C.reset} ${stakeAccount}`);
535
+ console.log(`\n ${key('Wallet:')} ${address}`);
536
+ console.log(` ${key('Stake Account:')} ${stakeAccount}`);
555
537
 
556
538
  // SDK call to fetch current rewards
539
+ startSpinner('Fetching rewards data');
557
540
  const client = createClient(rpc);
558
541
  const rewardData = await fetchStakeRewards(rpc, stakeAccount);
542
+ stopSpinner(true, 'Rewards data retrieved');
543
+
559
544
  if (rewardData.error) {
560
- console.log(`\n${C.red}✗ Failed to fetch stake account: ${rewardData.error}${C.reset}\n`);
545
+ console.log(`\n ${error(`Failed to fetch stake account: ${rewardData.error}`)}\n`);
561
546
  rl.close();
562
547
  return;
563
548
  }
564
549
 
565
- console.log(` ${C.dim}Delegated Stake:${C.reset} ${rewardData.delegatedStakeFormatted}`);
566
- console.log(` ${C.dim}Est. Pending Rewards:${C.reset} ${C.green}${rewardData.pendingRewardsFormatted}${C.reset}`);
567
- console.log(` ${C.dim}Validator:${C.reset} ${rewardData.validator}`);
568
- console.log(` ${C.dim}APY:${C.reset} ${(rewardData.apyBps / 100).toFixed(2)}%`);
550
+ console.log(` ${key('Delegated Stake:')} ${rewardData.delegatedStakeFormatted}`);
551
+ console.log(` ${key('Est. Pending Rewards:')} ${C.green}${rewardData.pendingRewardsFormatted}${C.reset}`);
552
+ console.log(` ${key('Validator:')} ${shortAddress(rewardData.validator)}`);
553
+ console.log(` ${key('APY:')} ${formatAPY(rewardData.apyBps)}`);
569
554
 
570
555
  const pendingRewards = BigInt(rewardData.pendingRewards || 0);
571
556
  if (pendingRewards === BigInt(0)) {
572
- console.log(`\n${C.yellow}⚠ No rewards accumulated yet.${C.reset}\n`);
557
+ console.log(`\n ${warning('No rewards accumulated yet.')}\n`);
573
558
  rl.close();
574
559
  return;
575
560
  }
576
561
 
577
- const confirm = await question(rl, `\n ${C.yellow}Claim ${rewardData.pendingRewardsFormatted}? [y/N]${C.reset} > `);
562
+ const confirm = await question(rl, `\n ${C.yellow}${indicators.warning}${C.reset} ${bright('Claim')} ${C.green}${rewardData.pendingRewardsFormatted}${C.reset}${bright('?')} ${dim('[y/N]')}: `);
578
563
  if (confirm.trim().toLowerCase() !== 'y') {
579
- console.log(`${C.dim}Cancelled.${C.reset}\n`);
564
+ console.log(` ${dim('Cancelled.')}\n`);
580
565
  rl.close();
581
566
  return;
582
567
  }
@@ -584,20 +569,25 @@ async function rewardsClaim(args) {
584
569
  // Ask for mnemonic to derive signing keypair
585
570
  let keypair;
586
571
  try {
587
- const mnemonic = await askMnemonic(rl, 'Enter your 12/24-word passphrase to sign the claim');
572
+ console.log();
573
+ const mnemonic = await askMnemonic(rl, `${bright('Enter your 12/24-word passphrase')} ${dim('to sign the claim')}`);
574
+ startSpinner('Deriving keypair');
588
575
  keypair = deriveKeypair(mnemonic);
589
576
 
590
577
  // Verify derived address matches
591
578
  const derivedAddress = formatAddress(keypair.publicKey);
592
579
  if (derivedAddress !== address) {
593
- console.log(`\n${C.red}✗ Passphrase mismatch!${C.reset}`);
594
- console.log(` ${C.dim}Derived: ${derivedAddress}${C.reset}`);
595
- console.log(` ${C.dim}Expected: ${address}${C.reset}\n`);
580
+ stopSpinner(false, 'Passphrase mismatch');
581
+ console.log(`\n ${error('Passphrase mismatch!')}`);
582
+ console.log(` ${key('Derived:')} ${derivedAddress}`);
583
+ console.log(` ${key('Expected:')} ${address}\n`);
596
584
  rl.close();
597
585
  return;
598
586
  }
587
+ stopSpinner(true, 'Keypair verified');
599
588
  } catch (err) {
600
- console.log(`\n${C.red}✗ Failed to derive keypair: ${err.message}${C.reset}\n`);
589
+ stopSpinner(false, 'Failed');
590
+ console.log(`\n ${error(`Failed to derive keypair: ${err.message}`)}\n`);
601
591
  rl.close();
602
592
  return;
603
593
  }
@@ -621,11 +611,13 @@ async function rewardsClaim(args) {
621
611
  // Sign transaction
622
612
  tx.signature = signTransaction(tx, keypair.secretKey);
623
613
 
624
- console.log(`\n ${C.dim}Submitting via SDK to ${rpc}...${C.reset}`);
614
+ console.log(`\n ${dim('Submitting transaction via SDK...')}`);
615
+ startSpinner('Sending to blockchain');
625
616
 
626
617
  // SDK call: sendTransaction (REAL RPC POST /v1/transaction)
627
618
  try {
628
619
  const result = await client.sendTransaction(tx);
620
+ stopSpinner(true, 'Transaction submitted');
629
621
 
630
622
  if (result.error) {
631
623
  throw new Error(result.error.message || JSON.stringify(result.error));
@@ -645,28 +637,38 @@ async function rewardsClaim(args) {
645
637
  timestamp: new Date().toISOString(),
646
638
  }, null, 2));
647
639
  } else {
648
- console.log(`\n${C.green}✓ Rewards claimed successfully!${C.reset}`);
649
- console.log(` ${C.dim}TX Signature: ${C.cyan}${result.signature || result.txid}${C.reset}`);
650
- console.log(` ${C.dim}Amount Claimed: ${C.green}${rewardData.pendingRewardsFormatted}${C.reset}`);
651
- console.log(` ${C.dim}Slot: ${result.slot}${C.reset}`);
652
- console.log(` ${C.dim}SDK Method: sendTransaction()${C.reset}`);
653
- console.log(` ${C.dim}Check balance: aether wallet balance --address ${address}${C.reset}\n`);
640
+ console.log();
641
+ console.log(drawBox([
642
+ `${success('Rewards claimed successfully!')}`,
643
+ ``,
644
+ `${key('TX Signature:')} ${C.cyan}${result.signature || result.txid}${C.reset}`,
645
+ `${key('Amount Claimed:')} ${C.green}${rewardData.pendingRewardsFormatted}${C.reset}`,
646
+ `${key('Slot:')} ${result.slot}`,
647
+ `${key('SDK:')} ${dim('sendTransaction()')}`,
648
+ ].join('\n'), {
649
+ title: 'Transaction Success',
650
+ titleColor: C.green,
651
+ borderColor: C.dim,
652
+ style: 'single'
653
+ }));
654
+ console.log(` ${dim('Check balance:')} ${code(`aether wallet balance --address ${address}`)}\n`);
654
655
  }
655
656
  } catch (err) {
657
+ stopSpinner(false, 'Transaction failed');
656
658
  if (isJson) {
657
659
  console.log(JSON.stringify({ success: false, error: err.message, address, stake_account: stakeAccount }, null, 2));
658
660
  } else {
659
- console.log(`\n${C.red}✗ Failed to submit claim transaction: ${err.message}${C.reset}`);
660
- console.log(` ${C.dim}The rewards are accumulated on-chain and can be claimed later.${C.reset}\n`);
661
+ console.log(`\n ${error(`Failed to submit claim: ${err.message}`)}`);
662
+ console.log(` ${dim('The rewards are accumulated on-chain and can be claimed later.')}\n`);
661
663
  }
662
664
  }
663
665
 
664
666
  rl.close();
665
667
  }
666
668
 
667
- // ---------------------------------------------------------------------------
668
- // Rewards compound command - SDK WIRED
669
- // ---------------------------------------------------------------------------
669
+ // ============================================================================
670
+ // Rewards Compound Command - SDK WIRED with UI
671
+ // ============================================================================
670
672
 
671
673
  async function rewardsCompound(args) {
672
674
  const rpc = args.rpc || getDefaultRpc();
@@ -677,13 +679,19 @@ async function rewardsCompound(args) {
677
679
  const config = loadConfig();
678
680
  const rl = createRl();
679
681
 
682
+ if (!isJson) {
683
+ console.log(REWARDS_LOGO);
684
+ console.log();
685
+ console.log(` ${C.yellow}${indicators.star}${C.reset} ${bright('Compound Mode')}: Claim and auto-restake rewards\n`);
686
+ }
687
+
680
688
  if (!address) {
681
- const ans = await question(rl, `\n${C.cyan}Enter wallet address: ${C.reset}`);
689
+ const ans = await question(rl, `${C.cyan}${indicators.arrow}${C.reset} ${bright('Enter wallet address')}: `);
682
690
  address = ans.trim();
683
691
  }
684
692
 
685
693
  if (!address) {
686
- console.log(`\n${C.red}✗ No address provided.${C.reset}\n`);
694
+ console.log(`\n ${error('No address provided.')}\n`);
687
695
  rl.close();
688
696
  return;
689
697
  }
@@ -691,16 +699,19 @@ async function rewardsCompound(args) {
691
699
  // Load wallet for signing
692
700
  const wallet = loadWallet(address);
693
701
  if (!wallet) {
694
- console.log(`\n${C.red}✗ Wallet not found locally: ${address}${C.reset}`);
695
- console.log(` ${C.dim}Import it: aether wallet import${C.reset}\n`);
702
+ console.log(`\n ${error(`Wallet not found locally: ${address}`)}`);
703
+ console.log(` ${dim('Import it:')} ${code('aether wallet import')}\n`);
696
704
  rl.close();
697
705
  return;
698
706
  }
699
707
 
700
708
  // SDK call to fetch stake accounts
709
+ startSpinner('Fetching stake accounts');
701
710
  let stakeAccounts = await fetchWalletStakeAccounts(rpc, address);
711
+ stopSpinner(true, `${stakeAccounts.length} account(s) found`);
712
+
702
713
  if (stakeAccounts.length === 0) {
703
- console.log(`\n${C.red}✗ No stake accounts found for this wallet.${C.reset}\n`);
714
+ console.log(`\n ${error('No stake accounts found for this wallet.')}\n`);
704
715
  rl.close();
705
716
  return;
706
717
  }
@@ -709,37 +720,39 @@ async function rewardsCompound(args) {
709
720
  if (stakeAccount) {
710
721
  stakeAccounts = stakeAccounts.filter(sa => sa.address === stakeAccount);
711
722
  if (stakeAccounts.length === 0) {
712
- console.log(`\n${C.red}✗ Stake account not found: ${stakeAccount}${C.reset}\n`);
723
+ console.log(`\n ${error(`Stake account not found: ${stakeAccount}`)}\n`);
713
724
  rl.close();
714
725
  return;
715
726
  }
716
727
  }
717
728
 
718
- console.log(`\n${C.bright}${C.cyan}╔══════════════════════════════════════════════════════════════╗${C.reset}`);
719
- console.log(`${C.bright}${C.cyan}║ Compound Staking Rewards (SDK-Wired) ║${C.reset}`);
720
- console.log(`${C.bright}${C.cyan}╚══════════════════════════════════════════════════════════════╝${C.reset}\n`);
721
- console.log(` ${C.dim}Wallet:${C.reset} ${C.bright}${address}${C.reset}`);
722
- console.log(` ${C.dim}RPC:${C.reset} ${rpc}`);
723
- console.log(` ${C.dim}Stake accounts to process:${C.reset} ${stakeAccounts.length}\n`);
729
+ console.log(`\n ${key('Wallet:')} ${bright(address)}`);
730
+ console.log(` ${key('RPC:')} ${dim(rpc)}`);
731
+ console.log(` ${key('Accounts to process:')} ${stakeAccounts.length}\n`);
724
732
 
725
733
  // Ask for mnemonic upfront
726
- console.log(`${C.yellow} Compound requires your wallet passphrase to sign transactions.${C.reset}`);
734
+ console.log(` ${C.yellow}${indicators.warning}${C.reset} ${bright('Compound requires your wallet passphrase to sign transactions')}`);
727
735
  let keypair;
728
736
  try {
729
- const mnemonic = await askMnemonic(rl, 'Enter your 12/24-word passphrase:');
737
+ console.log();
738
+ const mnemonic = await askMnemonic(rl, `${bright('Enter your 12/24-word passphrase')}`);
739
+ startSpinner('Deriving keypair');
730
740
  keypair = deriveKeypair(mnemonic);
731
741
 
732
742
  // Verify address matches
733
743
  const derivedAddress = formatAddress(keypair.publicKey);
734
744
  if (derivedAddress !== address) {
735
- console.log(`\n${C.red}✗ Passphrase mismatch.${C.reset}`);
736
- console.log(` ${C.dim}Derived: ${derivedAddress}${C.reset}`);
737
- console.log(` ${C.dim}Expected: ${address}${C.reset}\n`);
745
+ stopSpinner(false, 'Passphrase mismatch');
746
+ console.log(`\n ${error('Passphrase mismatch.')}`);
747
+ console.log(` ${key('Derived:')} ${derivedAddress}`);
748
+ console.log(` ${key('Expected:')} ${address}\n`);
738
749
  rl.close();
739
750
  return;
740
751
  }
752
+ stopSpinner(true, 'Keypair verified');
741
753
  } catch (err) {
742
- console.log(`\n${C.red}✗ Failed to derive keypair: ${err.message}${C.reset}\n`);
754
+ stopSpinner(false, 'Failed');
755
+ console.log(`\n ${error(`Failed to derive keypair: ${err.message}`)}\n`);
743
756
  rl.close();
744
757
  return;
745
758
  }
@@ -749,26 +762,29 @@ async function rewardsCompound(args) {
749
762
  let totalCompounded = BigInt(0);
750
763
  let successCount = 0;
751
764
 
752
- for (const sa of stakeAccounts) {
753
- console.log(` ${C.dim}Processing:${C.reset} ${shortAddress(sa.address)}`);
765
+ console.log(`\n ${bright('Processing stake accounts...')}\n`);
766
+
767
+ for (let i = 0; i < stakeAccounts.length; i++) {
768
+ const sa = stakeAccounts[i];
769
+ console.log(` ${dim(`[${i + 1}/${stakeAccounts.length}]`)} ${shortAddress(sa.address)}`);
754
770
 
755
771
  try {
756
772
  // SDK call to fetch rewards
757
773
  const rewardData = await fetchStakeRewards(rpc, sa.address);
758
774
  if (rewardData.error) {
759
- console.log(` ${C.red}✗ Failed to fetch: ${rewardData.error}${C.reset}`);
775
+ console.log(` ${error(`Failed to fetch: ${rewardData.error}`)}`);
760
776
  compoundResults.push({ stake_account: sa.address, status: 'error', error: rewardData.error });
761
777
  continue;
762
778
  }
763
779
 
764
780
  const estimatedRewards = BigInt(rewardData.pendingRewards || 0);
765
781
  if (estimatedRewards === BigInt(0)) {
766
- console.log(` ${C.yellow}⚠ No rewards to compound${C.reset}`);
782
+ console.log(` ${warning('No rewards to compound')}`);
767
783
  compoundResults.push({ stake_account: sa.address, status: 'no_rewards', rewards: '0' });
768
784
  continue;
769
785
  }
770
786
 
771
- console.log(` ${C.dim}Rewards:${C.reset} ${rewardData.pendingRewardsFormatted} → ${shortAddress(sa.validator || rewardData.validator || 'unknown')}`);
787
+ console.log(` ${dim('Rewards:')} ${C.green}${rewardData.pendingRewardsFormatted}${C.reset} ${dim('')} ${shortAddress(sa.validator || rewardData.validator || 'unknown')}`);
772
788
 
773
789
  // Build compound transaction (ClaimRewards + Stake in one)
774
790
  const tx = {
@@ -790,11 +806,13 @@ async function rewardsCompound(args) {
790
806
  // Sign transaction
791
807
  tx.signature = signTransaction(tx, keypair.secretKey);
792
808
 
809
+ process.stdout.write(` ${dim('Submitting...')}`);
810
+
793
811
  // SDK call: sendTransaction
794
812
  const result = await client.sendTransaction(tx);
795
813
 
796
814
  if (result.signature || result.txid || result.success) {
797
- console.log(` ${C.green}✓ Compounded${C.reset}`);
815
+ process.stdout.write(`\r ${success('Compounded')}\n`);
798
816
  totalCompounded += estimatedRewards;
799
817
  successCount++;
800
818
  compoundResults.push({
@@ -805,26 +823,26 @@ async function rewardsCompound(args) {
805
823
  tx: result.signature || result.txid,
806
824
  });
807
825
  } else {
808
- console.log(` ${C.red}✗ Failed: ${result.error || 'Unknown error'}${C.reset}`);
826
+ process.stdout.write(`\r ${error(`Failed: ${result.error || 'Unknown error'}`)}\n`);
809
827
  compoundResults.push({ stake_account: sa.address, status: 'failed', error: result.error });
810
828
  }
811
829
  } catch (err) {
812
- console.log(` ${C.red}✗ Error: ${err.message}${C.reset}`);
830
+ process.stdout.write(`\r ${error(`Error: ${err.message}`)}\n`);
813
831
  compoundResults.push({ stake_account: sa.address, status: 'error', error: err.message });
814
832
  }
815
- console.log();
816
833
  }
817
834
 
818
835
  rl.close();
819
836
 
820
837
  // Summary
821
- console.log(`${C.bright}${C.cyan}╔══════════════════════════════════════════════════════════════╗${C.reset}`);
822
- console.log(`${C.bright}${C.cyan}║ Compound Summary ║${C.reset}`);
823
- console.log(`${C.bright}${C.cyan}╚══════════════════════════════════════════════════════════════╝${C.reset}\n`);
824
- console.log(` ${C.dim}Accounts processed:${C.reset} ${stakeAccounts.length}`);
825
- console.log(` ${C.green}✓ Successful:${C.reset} ${successCount}`);
826
- console.log(` ${C.dim}Total compounded:${C.reset} ${C.green}${formatAether(totalCompounded.toString())}${C.reset}`);
827
- console.log(` ${C.dim}SDK: getStakePositions(), getRewards(), sendTransaction()${C.reset}\n`);
838
+ console.log();
839
+ console.log(ui.section('Compound Summary'));
840
+ console.log();
841
+ console.log(` ${key('Accounts processed:')} ${stakeAccounts.length}`);
842
+ console.log(` ${success('Successful:')} ${successCount}`);
843
+ console.log(` ${key('Total compounded:')} ${C.green}${formatAether(totalCompounded.toString())}${C.reset}`);
844
+ console.log(` ${key('SDK:')} ${dim('getStakePositions(), getRewards(), sendTransaction()')}`);
845
+ console.log();
828
846
 
829
847
  if (isJson) {
830
848
  console.log(JSON.stringify({
@@ -841,9 +859,9 @@ async function rewardsCompound(args) {
841
859
  }
842
860
  }
843
861
 
844
- // ---------------------------------------------------------------------------
845
- // Parse CLI args
846
- // ---------------------------------------------------------------------------
862
+ // ============================================================================
863
+ // Parse CLI Args
864
+ // ============================================================================
847
865
 
848
866
  function parseArgs() {
849
867
  const rawArgs = process.argv.slice(3);
@@ -879,18 +897,50 @@ function question(rl, q) {
879
897
  }
880
898
 
881
899
  async function askMnemonic(rl, prompt) {
882
- console.log(`\n${C.cyan}${prompt}${C.reset}`);
883
- console.log(`${C.dim}Enter your 12 or 24-word passphrase, one space-separated line:${C.reset}`);
884
- const raw = await question(rl, ` > ${C.reset}`);
900
+ console.log(`\n ${C.cyan}${indicators.info}${C.reset} ${prompt}`);
901
+ console.log(` ${dim('Enter your 12 or 24-word passphrase:')}`);
902
+ const raw = await question(rl, ` > `);
885
903
  return raw.trim().toLowerCase();
886
904
  }
887
905
 
888
- // ---------------------------------------------------------------------------
889
- // Main entry point
890
- // ---------------------------------------------------------------------------
906
+ // ============================================================================
907
+ // Help Display
908
+ // ============================================================================
909
+
910
+ function showHelp() {
911
+ console.log();
912
+ console.log(BRANDING.logoCompact);
913
+ console.log();
914
+ console.log(formatHelp(
915
+ 'aether rewards',
916
+ 'View and claim staking rewards earned from delegated stake accounts.',
917
+ 'aether rewards <command> [options]',
918
+ [
919
+ { flag: 'list', desc: 'List all rewards per stake account (default)' },
920
+ { flag: 'summary', desc: 'One-line summary of total rewards' },
921
+ { flag: 'claim', desc: 'Claim accumulated rewards' },
922
+ { flag: 'compound', desc: 'Claim and auto-restake rewards' },
923
+ { flag: '--address, -a <addr>', desc: 'Wallet address' },
924
+ { flag: '--account, -s <acct>', desc: 'Specific stake account' },
925
+ { flag: '--rpc <url>', desc: 'Custom RPC endpoint' },
926
+ { flag: '--json, -j', desc: 'Output as JSON' },
927
+ ],
928
+ [
929
+ { cmd: 'aether rewards list --address ATH...', desc: 'List all rewards' },
930
+ { cmd: 'aether rewards claim --address ATH...', desc: 'Claim all pending rewards' },
931
+ { cmd: 'aether rewards compound --address ATH...', desc: 'Compound rewards' },
932
+ ]
933
+ ));
934
+ console.log(` ${success('Fully wired to @jellylegsai/aether-sdk')}`);
935
+ console.log(` ${dim('SDK: getStakePositions(), getRewards(), getEpochInfo(), sendTransaction()')}\n`);
936
+ }
937
+
938
+ // ============================================================================
939
+ // Main Entry Point
940
+ // ============================================================================
891
941
 
892
- async function main() {
893
- const args = parseArgs();
942
+ async function main(customArgs) {
943
+ const args = customArgs || parseArgs();
894
944
 
895
945
  switch (args.subcmd) {
896
946
  case 'list':
@@ -899,33 +949,28 @@ async function main() {
899
949
  case 'summary':
900
950
  await rewardsSummary(args);
901
951
  break;
902
- case 'pending':
903
- await rewardsPending(args);
904
- break;
905
952
  case 'claim':
906
953
  await rewardsClaim(args);
907
954
  break;
908
955
  case 'compound':
909
956
  await rewardsCompound(args);
910
957
  break;
958
+ case 'help':
959
+ case '--help':
960
+ case '-h':
961
+ showHelp();
962
+ break;
911
963
  default:
912
- console.log(`\n${C.cyan}Usage:${C.reset}`);
913
- console.log(` aether rewards list --address <addr> List all staking rewards (SDK-wired)`);
914
- console.log(` aether rewards summary --address <addr> One-line rewards summary`);
915
- console.log(` aether rewards pending --address <addr> Show pending rewards`);
916
- console.log(` aether rewards claim --address <addr> [--account <stakeAcct>] Claim rewards`);
917
- console.log(` aether rewards compound --address <addr> [--account <stakeAcct>] Claim and re-stake`);
918
- console.log();
919
- console.log(` ${C.dim}--json Output as JSON`);
920
- console.log(` --rpc <url> Use specific RPC endpoint${C.reset}`);
921
- console.log();
922
- console.log(` ${C.green}✓ Fully wired to @jellylegsai/aether-sdk${C.reset}`);
923
- console.log(` ${C.dim}SDK: getStakePositions(), getRewards(), getEpochInfo(), sendTransaction()${C.reset}\n`);
964
+ console.log(`\n ${error(`Unknown command: ${args.subcmd}`)}`);
965
+ console.log(` ${dim('Run')} ${code('aether rewards help')} ${dim('for usage information.')}\n`);
924
966
  }
925
967
  }
926
968
 
927
- main().catch(err => {
928
- console.error(`\n${C.red}Error running rewards command:${C.reset}`, err.message, '\n');
929
- });
969
+ // Run main only if executed directly
970
+ if (require.main === module) {
971
+ main().catch(err => {
972
+ console.error(`\n ${error('Error running rewards command:')} ${err.message}\n`);
973
+ });
974
+ }
930
975
 
931
- module.exports = { rewardsCommand: main };
976
+ module.exports = { rewardsCommand: main, rewardsList, rewardsSummary, rewardsClaim, rewardsCompound };