aether-hub 1.1.4 → 1.1.6
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/rewards.js +83 -0
- package/commands/wallet.js +97 -0
- package/index.js +11 -1
- package/package.json +1 -1
package/commands/rewards.js
CHANGED
|
@@ -397,6 +397,85 @@ async function rewardsSummary(args) {
|
|
|
397
397
|
// Rewards claim command
|
|
398
398
|
// ---------------------------------------------------------------------------
|
|
399
399
|
|
|
400
|
+
async function rewardsPending(args) {
|
|
401
|
+
const rpc = args.rpc || getDefaultRpc();
|
|
402
|
+
const isJson = args.json || false;
|
|
403
|
+
let address = args.address || null;
|
|
404
|
+
|
|
405
|
+
const config = loadConfig();
|
|
406
|
+
const rl = createRl();
|
|
407
|
+
|
|
408
|
+
if (!address) {
|
|
409
|
+
const ans = await question(rl, `\n${C.cyan}Enter wallet address: ${C.reset}`);
|
|
410
|
+
address = ans.trim();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (!address) {
|
|
414
|
+
console.log(`\n${C.red}✗ No address provided.${C.reset}\n`);
|
|
415
|
+
rl.close();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
rl.close();
|
|
420
|
+
|
|
421
|
+
const stakeAccounts = await fetchWalletStakeAccounts(address);
|
|
422
|
+
if (stakeAccounts.length === 0) {
|
|
423
|
+
if (isJson) {
|
|
424
|
+
console.log(JSON.stringify({ address, pending: [], total_pending: '0' }, null, 2));
|
|
425
|
+
} else {
|
|
426
|
+
console.log(`\n${C.red}✗ No stake accounts found for ${address}${C.reset}\n`);
|
|
427
|
+
}
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const results = [];
|
|
432
|
+
let totalPending = BigInt(0);
|
|
433
|
+
|
|
434
|
+
for (const sa of stakeAccounts) {
|
|
435
|
+
const rd = await fetchStakeRewards(rpc, sa);
|
|
436
|
+
if (!rd.error) {
|
|
437
|
+
const pending = BigInt(rd.estimatedRewards || 0);
|
|
438
|
+
totalPending += pending;
|
|
439
|
+
results.push({
|
|
440
|
+
stake_account: sa,
|
|
441
|
+
validator: rd.validator || 'unknown',
|
|
442
|
+
delegated_stake: rd.delegatedStakeFormatted || '0',
|
|
443
|
+
pending_rewards: rd.estimatedRewardsFormatted || '0',
|
|
444
|
+
pending_lamports: pending.toString(),
|
|
445
|
+
apy_bps: rd.apyBps || 0,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (isJson) {
|
|
451
|
+
console.log(JSON.stringify({
|
|
452
|
+
address,
|
|
453
|
+
total_pending: totalPending.toString(),
|
|
454
|
+
total_pending_formatted: formatAether(totalPending.toString()),
|
|
455
|
+
accounts: results,
|
|
456
|
+
}, null, 2));
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
console.log(`\n${C.bright}${C.cyan}╔══════════════════════════════════════════════════════════════╗${C.reset}`);
|
|
461
|
+
console.log(`${C.bright}${C.cyan}║ Pending Staking Rewards ║${C.reset}`);
|
|
462
|
+
console.log(`${C.bright}${C.cyan}╚══════════════════════════════════════════════════════════════╝${C.reset}\n`);
|
|
463
|
+
console.log(` ${C.dim}Wallet:${C.reset} ${C.bright}${address}${C.reset}`);
|
|
464
|
+
console.log();
|
|
465
|
+
console.log(` ${C.yellow}Stake Account${C.reset.padEnd(48)} ${C.yellow}Pending${C.reset} ${C.yellow}APY${C.reset}`);
|
|
466
|
+
console.log(` ${C.dim}${'─'.repeat(72)}${C.reset}`);
|
|
467
|
+
|
|
468
|
+
for (const r of results) {
|
|
469
|
+
const shortSa = r.stake_account.substring(0, 12) + '...' + r.stake_account.slice(-6);
|
|
470
|
+
console.log(` ${C.cyan}${shortSa}${C.reset.padEnd(52)} ${C.green}${r.pending_rewards.padStart(12)}${C.reset} ${(r.apy_bps / 100).toFixed(2)}%`);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
console.log(` ${C.dim}${'─'.repeat(72)}${C.reset}`);
|
|
474
|
+
console.log(` ${C.bright}TOTAL PENDING${C.reset.padEnd(52)} ${C.green}${formatAethFull(totalPending.toString()).padStart(12)}${C.reset}`);
|
|
475
|
+
console.log();
|
|
476
|
+
console.log(` ${C.dim}Run ${C.cyan}aether rewards claim${C.dim} to claim.${C.reset}\n`);
|
|
477
|
+
}
|
|
478
|
+
|
|
400
479
|
async function rewardsClaim(args) {
|
|
401
480
|
const rpc = args.rpc || getDefaultRpc();
|
|
402
481
|
const isJson = args.json || false;
|
|
@@ -579,6 +658,9 @@ async function main() {
|
|
|
579
658
|
case 'summary':
|
|
580
659
|
await rewardsSummary(parsed);
|
|
581
660
|
break;
|
|
661
|
+
case 'pending':
|
|
662
|
+
await rewardsPending(parsed);
|
|
663
|
+
break;
|
|
582
664
|
case 'claim':
|
|
583
665
|
await rewardsClaim(parsed);
|
|
584
666
|
break;
|
|
@@ -586,6 +668,7 @@ async function main() {
|
|
|
586
668
|
console.log(`\n${C.cyan}Usage:${C.reset}`);
|
|
587
669
|
console.log(` aether rewards list --address <addr> List all staking rewards`);
|
|
588
670
|
console.log(` aether rewards summary --address <addr> One-line rewards summary`);
|
|
671
|
+
console.log(` aether rewards pending --address <addr> Show pending (unclaimed) rewards`);
|
|
589
672
|
console.log(` aether rewards claim --address <addr> [--account <stakeAcct>] Claim rewards`);
|
|
590
673
|
console.log();
|
|
591
674
|
console.log(` ${C.dim}--json Output as JSON`);
|
package/commands/wallet.js
CHANGED
|
@@ -1107,6 +1107,100 @@ async function txHistory(rl) {
|
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
// ---------------------------------------------------------------------------
|
|
1110
|
+
// ---------------------------------------------------------------------------
|
|
1111
|
+
// EXPORT WALLET
|
|
1112
|
+
// Export wallet data for backup — public data by default, mnemonic with --mnemonic flag
|
|
1113
|
+
// ---------------------------------------------------------------------------
|
|
1114
|
+
|
|
1115
|
+
async function exportWallet(rl) {
|
|
1116
|
+
console.log(`\n${C.bright}${C.cyan}── Wallet Export ─────────────────────────────────────────${C.reset}\n`);
|
|
1117
|
+
|
|
1118
|
+
const args = process.argv.slice(4);
|
|
1119
|
+
let address = null;
|
|
1120
|
+
let asJson = false;
|
|
1121
|
+
let includeMnemonic = false;
|
|
1122
|
+
|
|
1123
|
+
const addrIdx = args.findIndex((a) => a === '--address' || a === '-a');
|
|
1124
|
+
if (addrIdx !== -1 && args[addrIdx + 1]) {
|
|
1125
|
+
address = args[addrIdx + 1];
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
asJson = args.includes('--json') || args.includes('-j');
|
|
1129
|
+
includeMnemonic = args.includes('--mnemonic') || args.includes('-m');
|
|
1130
|
+
|
|
1131
|
+
if (!address) {
|
|
1132
|
+
const cfg = loadConfig();
|
|
1133
|
+
address = cfg.defaultWallet;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (!address) {
|
|
1137
|
+
console.log(` ${C.red}✗ No wallet address specified and no default wallet set.${C.reset}`);
|
|
1138
|
+
console.log(` ${C.dim}Usage: aether wallet export --address <addr> [--mnemonic] [--json]${C.reset}\n`);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
const walletData = loadWallet(address);
|
|
1143
|
+
if (!walletData) {
|
|
1144
|
+
console.log(` ${C.red}✗ Wallet not found:${C.reset} ${address}`);
|
|
1145
|
+
console.log(` ${C.dim} Available wallets: ${C.cyan}aether wallet list${C.reset}\n`);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
const exportData = {
|
|
1150
|
+
version: walletData.version || 1,
|
|
1151
|
+
address: walletData.address,
|
|
1152
|
+
public_key: walletData.public_key,
|
|
1153
|
+
derivation_path: walletData.derivation_path,
|
|
1154
|
+
created_at: walletData.created_at,
|
|
1155
|
+
source: 'aether-cli',
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
// Mnemonic export requires interactive confirmation for security
|
|
1159
|
+
if (includeMnemonic) {
|
|
1160
|
+
console.log(` ${C.yellow}⚠ WARNING: You are about to export your SECRET MNEMONIC.${C.reset}`);
|
|
1161
|
+
console.log(` ${C.dim} Anyone with this phrase can access your funds.${C.reset}\n`);
|
|
1162
|
+
const confirmed = await question(rl, ` Type ${C.bright}EXPORT${C.reset} to confirm: `);
|
|
1163
|
+
if (confirmed.trim().toUpperCase() !== 'EXPORT') {
|
|
1164
|
+
console.log(`\n ${C.dim}Aborted. Mnemonic not exported.${C.reset}\n`);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
console.log(` ${C.green}✓ Confirmed${C.reset}\n`);
|
|
1168
|
+
|
|
1169
|
+
// Re-derive mnemonic from keypair is NOT possible — we must ask for it
|
|
1170
|
+
console.log(` ${C.dim}The CLI cannot retrieve your mnemonic from the stored keypair.${C.reset}`);
|
|
1171
|
+
console.log(` ${C.dim}If you have a backup of your mnemonic, enter it below to include it.${C.reset}`);
|
|
1172
|
+
console.log(` ${C.dim}Otherwise, press Enter to export public data only.${C.reset}\n`);
|
|
1173
|
+
const mnemonicInput = await question(rl, ` ${C.cyan}Mnemonic (or Enter to skip):${C.reset} `);
|
|
1174
|
+
const mnemonic = mnemonicInput.trim();
|
|
1175
|
+
if (mnemonic && bip39.validateMnemonic(mnemonic)) {
|
|
1176
|
+
exportData.mnemonic = mnemonic;
|
|
1177
|
+
} else if (mnemonic) {
|
|
1178
|
+
console.log(` ${C.red}✗ Invalid mnemonic phrase. Skipping.${C.reset}`);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (asJson) {
|
|
1183
|
+
console.log(JSON.stringify(exportData, null, 2));
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// Human-readable output
|
|
1188
|
+
console.log(` ${C.green}★${C.reset} Wallet exported successfully\n`);
|
|
1189
|
+
console.log(` ${C.dim}Address:${C.reset} ${exportData.address}`);
|
|
1190
|
+
console.log(` ${C.dim}Public key:${C.reset} ${exportData.public_key}`);
|
|
1191
|
+
console.log(` ${C.dim}Derivation path:${C.reset} ${exportData.derivation_path}`);
|
|
1192
|
+
console.log(` ${C.dim}Created:${C.reset} ${exportData.created_at ? new Date(exportData.created_at).toLocaleString() : 'unknown'}`);
|
|
1193
|
+
if (exportData.mnemonic) {
|
|
1194
|
+
console.log();
|
|
1195
|
+
console.log(` ${C.yellow}★ MNEMONIC (keep this secret!):${C.reset}`);
|
|
1196
|
+
console.log(` ${C.bright}${exportData.mnemonic}${C.reset}`);
|
|
1197
|
+
}
|
|
1198
|
+
console.log();
|
|
1199
|
+
console.log(` ${C.dim}Export saved to stdout in JSON format with:${C.reset}`);
|
|
1200
|
+
console.log(` ${C.cyan}aether wallet export --address ${address} --json${C.reset}`);
|
|
1201
|
+
console.log();
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1110
1204
|
// STAKE POSITIONS
|
|
1111
1205
|
// Query and display current stake positions/delegations for a wallet
|
|
1112
1206
|
// ---------------------------------------------------------------------------
|
|
@@ -1437,6 +1531,8 @@ async function walletCommand() {
|
|
|
1437
1531
|
await stakeWallet(rl);
|
|
1438
1532
|
} else if (subcmd === 'stake-positions') {
|
|
1439
1533
|
await stakePositions(rl);
|
|
1534
|
+
} else if (subcmd === 'export') {
|
|
1535
|
+
await exportWallet(rl);
|
|
1440
1536
|
} else if (subcmd === 'unstake') {
|
|
1441
1537
|
await unstakeWallet(rl);
|
|
1442
1538
|
} else if (subcmd === 'transfer') {
|
|
@@ -1449,6 +1545,7 @@ async function walletCommand() {
|
|
|
1449
1545
|
console.log(` ${C.cyan}aether wallet create${C.reset} Create new or import wallet`);
|
|
1450
1546
|
console.log(` ${C.cyan}aether wallet list${C.reset} List all wallets`);
|
|
1451
1547
|
console.log(` ${C.cyan}aether wallet import${C.reset} Import wallet from mnemonic`);
|
|
1548
|
+
console.log(` ${C.cyan}aether wallet export${C.reset} Export wallet data (pubkey, address) — --mnemonic to include phrase`);
|
|
1452
1549
|
console.log(` ${C.cyan}aether wallet default${C.reset} Show/set default wallet`);
|
|
1453
1550
|
console.log(` ${C.cyan}aether wallet connect${C.reset} Connect wallet via browser verification`);
|
|
1454
1551
|
console.log(` ${C.cyan}aether wallet balance${C.reset} Query chain balance for an address`);
|
package/index.js
CHANGED
|
@@ -208,6 +208,16 @@ const COMMANDS = {
|
|
|
208
208
|
process.argv = originalArgv;
|
|
209
209
|
},
|
|
210
210
|
},
|
|
211
|
+
export: {
|
|
212
|
+
description: 'Export wallet data — aether export --address <addr> [--mnemonic] [--json]',
|
|
213
|
+
handler: () => {
|
|
214
|
+
const { walletCommand } = require('./commands/wallet');
|
|
215
|
+
const originalArgv = process.argv;
|
|
216
|
+
process.argv = [...originalArgv.slice(0, 2), 'wallet', 'export', ...originalArgv.slice(3)];
|
|
217
|
+
walletCommand();
|
|
218
|
+
process.argv = originalArgv;
|
|
219
|
+
},
|
|
220
|
+
},
|
|
211
221
|
transfer: {
|
|
212
222
|
description: 'Transfer AETH to another address — aether transfer --to <addr> --amount <aeth>',
|
|
213
223
|
handler: () => {
|
|
@@ -282,7 +292,7 @@ const COMMANDS = {
|
|
|
282
292
|
},
|
|
283
293
|
},
|
|
284
294
|
rewards: {
|
|
285
|
-
description: 'View staking rewards — aether rewards list
|
|
295
|
+
description: 'View staking rewards — aether rewards list | rewards summary | rewards pending | rewards claim',
|
|
286
296
|
handler: () => {
|
|
287
297
|
rewardsCommand();
|
|
288
298
|
},
|