@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.
package/commands/claim.js CHANGED
@@ -30,20 +30,12 @@ const nacl = require('tweetnacl');
30
30
  const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
31
31
  const aether = require(sdkPath);
32
32
 
33
- // ANSI colours
34
- const C = {
35
- reset: '\x1b[0m',
36
- bright: '\x1b[1m',
37
- dim: '\x1b[2m',
38
- red: '\x1b[31m',
39
- green: '\x1b[32m',
40
- yellow: '\x1b[33m',
41
- cyan: '\x1b[36m',
42
- magenta: '\x1b[35m',
43
- };
33
+ // Import UI framework for consistent branding
34
+ const { BRANDING, C, indicators, startSpinner, stopSpinner, drawBox, drawTable,
35
+ success, error, warning, info, code, highlight } = require('../lib/ui');
44
36
 
45
37
  const DERIVATION_PATH = "m/44'/7777777'/0'/0'";
46
- const CLI_VERSION = '1.1.0';
38
+ const CLI_VERSION = '2.0.0';
47
39
 
48
40
  // ---------------------------------------------------------------------------
49
41
  // SDK Client Setup
@@ -238,30 +230,30 @@ async function claimCommand() {
238
230
  const rl = createRl();
239
231
 
240
232
  if (opts.help) {
241
- console.log(`
242
- ${C.bright}${C.cyan}claim${C.reset} — Claim accumulated staking rewards for a wallet
243
-
244
- ${C.bright}USAGE${C.reset}
245
- aether claim --address <addr> [--json] [--rpc <url>] [--dry-run]
246
-
247
- ${C.bright}OPTIONS${C.reset}
248
- --address <addr> Wallet address (ATH...)
249
- --json Output raw JSON
250
- --rpc <url> RPC endpoint (default: AETHER_RPC or localhost:8899)
251
- --dry-run Preview claim without submitting transaction
252
- --help Show this help
253
-
254
- ${C.bright}SDK METHODS USED${C.reset}
255
- client.getStakePositions(address) → GET /v1/stake/<addr>
256
- client.getRewards(address) → GET /v1/rewards/<addr>
257
- client.getSlot() GET /v1/slot
258
- client.sendTransaction(tx) → POST /v1/transaction
259
-
260
- ${C.bright}EXAMPLES${C.reset}
261
- aether claim --address ATH3abc...
262
- aether claim --address ATH3abc... --dry-run
263
- aether claim --address ATH3abc... --json
264
- `);
233
+ console.log(BRANDING.commandBanner('claim', 'Claim accumulated staking rewards'));
234
+
235
+ console.log(`\n ${C.bright}USAGE${C.reset}`);
236
+ console.log(` ${code('aether claim --address <addr> [--json] [--rpc <url>] [--dry-run]')}`);
237
+
238
+ console.log(`\n ${C.bright}OPTIONS${C.reset}`);
239
+ console.log(` ${code('--address <addr>')} Wallet address (ATH...)`);
240
+ console.log(` ${code('--json')} Output raw JSON`);
241
+ console.log(` ${code('--rpc <url>')} RPC endpoint (default: AETHER_RPC or localhost:8899)`);
242
+ console.log(` ${code('--dry-run')} Preview claim without submitting transaction`);
243
+ console.log(` ${code('--help')} Show this help`);
244
+
245
+ console.log(`\n ${C.bright}SDK METHODS USED${C.reset}`);
246
+ console.log(` ${C.dim}client.getStakePositions(address) → GET /v1/stake/<addr>${C.reset}`);
247
+ console.log(` ${C.dim}client.getRewards(address) → GET /v1/rewards/<addr>${C.reset}`);
248
+ console.log(` ${C.dim}client.getSlot() → GET /v1/slot${C.reset}`);
249
+ console.log(` ${C.dim}client.sendTransaction(tx) POST /v1/transaction${C.reset}`);
250
+
251
+ console.log(`\n ${C.bright}EXAMPLES${C.reset}`);
252
+ console.log(` ${C.dim}$${C.reset} ${code('aether claim --address ATH3abc...')}`);
253
+ console.log(` ${C.dim}$${C.reset} ${code('aether claim --address ATH3abc... --dry-run')}`);
254
+ console.log(` ${C.dim}$${C.reset} ${code('aether claim --address ATH3abc... --json')}`);
255
+ console.log();
256
+
265
257
  rl.close();
266
258
  return;
267
259
  }
@@ -272,8 +264,8 @@ ${C.bright}EXAMPLES${C.reset}
272
264
  if (config.defaultWallet) {
273
265
  opts.address = config.defaultWallet;
274
266
  } else {
275
- console.log(` ${C.red}✗ Missing --address${C.reset}\n`);
276
- console.log(` Usage: aether claim --address <addr> [--json] [--dry-run]\n`);
267
+ console.log(`\n ${error('Missing --address')}\n`);
268
+ console.log(` Usage: ${code('aether claim --address <addr> [--json] [--dry-run]')}\n`);
277
269
  rl.close();
278
270
  return;
279
271
  }
@@ -284,10 +276,10 @@ ${C.bright}EXAMPLES${C.reset}
284
276
  const rawAddr = address.startsWith('ATH') ? address.slice(3) : address;
285
277
 
286
278
  if (!opts.json) {
287
- console.log(`\n${C.bright}${C.cyan}── Claim Staking Rewards ────────────────────────────────${C.reset}\n`);
288
- console.log(` ${C.dim}Wallet:${C.reset} ${address}`);
289
- console.log(` ${C.dim}RPC: ${C.reset} ${rpcUrl}`);
290
- if (opts.dryRun) console.log(` ${C.yellow}(dry-run mode - no transaction will be submitted)${C.reset}`);
279
+ console.log(BRANDING.commandBanner('claim', 'Claim Staking Rewards'));
280
+ console.log(`\n ${C.cyan}Wallet:${C.reset} ${address}`);
281
+ console.log(` ${C.cyan}RPC:${C.reset} ${rpcUrl}`);
282
+ if (opts.dryRun) console.log(` ${warning('(dry-run mode - no transaction will be submitted)')}`);
291
283
  console.log();
292
284
  }
293
285
 
@@ -296,11 +288,15 @@ ${C.bright}EXAMPLES${C.reset}
296
288
  const client = createClient(rpcUrl);
297
289
 
298
290
  if (!opts.json) {
299
- console.log(` ${C.dim}Fetching stake positions via SDK...${C.reset}`);
291
+ startSpinner('Fetching stake positions via SDK');
300
292
  }
301
293
 
302
294
  const stakeAccounts = await fetchWalletStakeAccounts(rpcUrl, address);
303
295
 
296
+ if (!opts.json) {
297
+ stopSpinner(true, 'Stake positions retrieved');
298
+ }
299
+
304
300
  if (!stakeAccounts || stakeAccounts.length === 0) {
305
301
  if (opts.json) {
306
302
  console.log(JSON.stringify({
@@ -309,8 +305,8 @@ ${C.bright}EXAMPLES${C.reset}
309
305
  suggestion: 'Stake AETH first with: aether stake --validator <addr> --amount <aeth>',
310
306
  }, null, 2));
311
307
  } else {
312
- console.log(` ${C.yellow}⚠ No active stake positions found.${C.reset}`);
313
- console.log(` ${C.dim} Stake AETH with: ${C.cyan}aether stake --validator <addr> --amount <aeth>${C.reset}\n`);
308
+ console.log(`\n ${warning('No active stake positions found.')}`);
309
+ console.log(` ${C.dim}Stake AETH with: ${code('aether stake --validator <addr> --amount <aeth>')}${C.reset}\n`);
314
310
  }
315
311
  rl.close();
316
312
  return;
@@ -320,6 +316,10 @@ ${C.bright}EXAMPLES${C.reset}
320
316
  let totalPendingRewards = BigInt(0);
321
317
  const rewardBreakdown = [];
322
318
 
319
+ if (!opts.json) {
320
+ startSpinner('Fetching reward data');
321
+ }
322
+
323
323
  for (const acc of stakeAccounts) {
324
324
  // SDK call: getRewards for each stake account
325
325
  const rewardData = await fetchStakeRewards(rpcUrl, acc.address);
@@ -339,17 +339,29 @@ ${C.bright}EXAMPLES${C.reset}
339
339
  }
340
340
 
341
341
  if (!opts.json) {
342
- console.log(` ${C.bright}Stake Positions (${stakeAccounts.length})${C.reset}\n`);
343
-
344
- for (const pos of rewardBreakdown) {
345
- const shortVal = shortPubkey(pos.validator);
346
- const shortAcct = shortPubkey(pos.stakeAcct);
347
- console.log(` ${C.dim}├─ ${C.reset}${shortAcct} → ${C.cyan}${shortVal}${C.reset}`);
348
- console.log(` │ ${C.dim}Staked:${C.reset} ${formatAether(pos.stakeLamports)}`);
349
- console.log(` │ ${C.green}Pending:${C.reset} ${pos.pendingFormatted}\n`);
350
- }
342
+ stopSpinner(true, 'Reward data retrieved');
343
+ }
351
344
 
352
- console.log(` ${C.dim}────────────────────────────────────────${C.reset}`);
345
+ if (!opts.json) {
346
+ console.log(`\n ${C.bright}Stake Positions (${C.cyan}${stakeAccounts.length}${C.reset})\n`);
347
+
348
+ // Build table rows
349
+ const rows = rewardBreakdown.map(pos => {
350
+ return [
351
+ shortPubkey(pos.stakeAcct),
352
+ shortPubkey(pos.validator),
353
+ formatAether(pos.stakeLamports),
354
+ C.green + pos.pendingFormatted + C.reset
355
+ ];
356
+ });
357
+
358
+ console.log(drawTable(
359
+ ['Stake Account', 'Validator', 'Staked', 'Pending'],
360
+ rows,
361
+ { borderStyle: 'single', headerColor: C.cyan + C.bright }
362
+ ));
363
+
364
+ console.log(`\n ${C.dim}${'─'.repeat(50)}${C.reset}`);
353
365
  console.log(` ${C.bright}Total Pending Rewards:${C.reset} ${C.green}${formatFlux(totalPendingRewards.toString())}${C.reset}\n`);
354
366
  }
355
367
 
@@ -366,7 +378,7 @@ ${C.bright}EXAMPLES${C.reset}
366
378
  sdk_version: CLI_VERSION,
367
379
  }, null, 2));
368
380
  } else {
369
- console.log(` ${C.yellow}⚠ Dry run - not submitting claim transaction${C.reset}\n`);
381
+ console.log(` ${warning('Dry run - not submitting claim transaction')}\n`);
370
382
  }
371
383
  rl.close();
372
384
  return;
@@ -375,15 +387,15 @@ ${C.bright}EXAMPLES${C.reset}
375
387
  // Load wallet for signing
376
388
  const wallet = loadWallet(address);
377
389
  if (!wallet) {
378
- console.log(` ${C.red}✗ Wallet not found locally: ${address}${C.reset}`);
379
- console.log(` ${C.dim}Import it: aether wallet import${C.reset}\n`);
390
+ console.log(`\n ${error('Wallet not found locally:')} ${address}`);
391
+ console.log(` ${C.dim}Import it: ${code('aether wallet import')}${C.reset}\n`);
380
392
  rl.close();
381
393
  return;
382
394
  }
383
395
 
384
396
  // Step: Submit claim transaction
385
397
  if (!opts.json) {
386
- console.log(` ${C.dim}Preparing claim transaction...${C.reset}`);
398
+ console.log(` ${C.dim}Preparing claim transaction...${C.reset}\n`);
387
399
  }
388
400
 
389
401
  // Ask for mnemonic
@@ -394,7 +406,7 @@ ${C.bright}EXAMPLES${C.reset}
394
406
  try {
395
407
  keyPair = deriveKeypair(mnemonic);
396
408
  } catch (e) {
397
- console.log(` ${C.red}✗ Failed to derive keypair: ${e.message}${C.reset}\n`);
409
+ console.log(`\n ${error('Failed to derive keypair:')} ${e.message}\n`);
398
410
  rl.close();
399
411
  return;
400
412
  }
@@ -402,16 +414,24 @@ ${C.bright}EXAMPLES${C.reset}
402
414
  // Verify derived address matches
403
415
  const derivedAddress = formatAddress(keyPair.publicKey);
404
416
  if (derivedAddress !== address) {
405
- console.log(` ${C.red}✗ Passphrase mismatch.${C.reset}`);
406
- console.log(` ${C.dim} Derived: ${derivedAddress}${C.reset}`);
407
- console.log(` ${C.dim} Expected: ${address}${C.reset}`);
417
+ console.log(`\n ${error('Passphrase mismatch.')}`);
418
+ console.log(` ${C.dim}Derived: ${derivedAddress}${C.reset}`);
419
+ console.log(` ${C.dim}Expected: ${address}${C.reset}`);
408
420
  console.log(` ${C.dim}Check your passphrase and try again.${C.reset}\n`);
409
421
  rl.close();
410
422
  return;
411
423
  }
412
424
 
413
425
  // SDK call: get current slot
426
+ if (!opts.json) {
427
+ startSpinner('Getting current slot');
428
+ }
429
+
414
430
  const currentSlot = await client.getSlot().catch(() => 0);
431
+
432
+ if (!opts.json) {
433
+ stopSpinner(true, 'Slot retrieved');
434
+ }
415
435
 
416
436
  // Build claim transaction for SDK
417
437
  const tx = {
@@ -433,12 +453,16 @@ ${C.bright}EXAMPLES${C.reset}
433
453
  tx.signature = signTransaction(tx, keyPair.secretKey);
434
454
 
435
455
  if (!opts.json) {
436
- console.log(` ${C.dim}Submitting claim via SDK to ${rpcUrl}...${C.reset}`);
456
+ startSpinner('Submitting claim via SDK');
437
457
  }
438
458
 
439
459
  // SDK call: sendTransaction (REAL RPC POST /v1/transaction)
440
460
  const result = await client.sendTransaction(tx);
441
461
 
462
+ if (!opts.json) {
463
+ stopSpinner(true, 'Transaction submitted');
464
+ }
465
+
442
466
  if (opts.json) {
443
467
  console.log(JSON.stringify({
444
468
  wallet_address: address,
@@ -453,18 +477,19 @@ ${C.bright}EXAMPLES${C.reset}
453
477
  }, null, 2));
454
478
  } else {
455
479
  if (result.error) {
456
- console.log(` ${C.red}✗ Claim failed:${C.reset} ${result.error}\n`);
480
+ console.log(`\n ${error('Claim failed:')} ${result.error}\n`);
457
481
  rl.close();
458
482
  process.exit(1);
459
483
  }
460
484
 
461
- console.log(` ${C.green}✓ Rewards claimed!${C.reset}`);
462
- console.log(` ${C.dim} Amount:${C.reset} ${C.green}${formatFlux(result.claimed || totalPendingRewards.toString())}${C.reset}`);
485
+ console.log();
486
+ console.log(BRANDING.successBanner('Rewards claimed!'));
487
+ console.log(`\n ${C.cyan}Amount:${C.reset} ${C.green}${formatFlux(result.claimed || totalPendingRewards.toString())}${C.reset}`);
463
488
  if (result.signature || result.txid) {
464
- console.log(` ${C.dim} Tx:${C.reset} ${shortPubkey(result.signature || result.txid)}`);
489
+ console.log(` ${C.cyan}Tx:${C.reset} ${shortPubkey(result.signature || result.txid)}`);
465
490
  }
466
- console.log(` ${C.dim} Slot:${C.reset} ${result.slot || currentSlot}`);
467
- console.log(` ${C.dim} SDK: sendTransaction()${C.reset}\n`);
491
+ console.log(` ${C.cyan}Slot:${C.reset} ${result.slot || currentSlot}`);
492
+ console.log(` ${C.dim}SDK: sendTransaction()${C.reset}\n`);
468
493
  }
469
494
 
470
495
  rl.close();
@@ -473,8 +498,9 @@ ${C.bright}EXAMPLES${C.reset}
473
498
  if (opts.json) {
474
499
  console.log(JSON.stringify({ address, error: err.message, sdk_version: CLI_VERSION }, null, 2));
475
500
  } else {
476
- console.log(` ${C.red}✗ Failed to claim rewards:${C.reset} ${err.message}\n`);
477
- console.log(` ${C.dim} Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
501
+ stopSpinner(false, 'Failed');
502
+ console.log(`\n ${error('Failed to claim rewards:')} ${err.message}\n`);
503
+ console.log(` ${C.dim}Set custom RPC: AETHER_RPC=https://your-rpc-url${C.reset}\n`);
478
504
  }
479
505
  rl.close();
480
506
  process.exit(1);