@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/sdk.js CHANGED
@@ -1,656 +1,791 @@
1
- /**
2
- * aether-cli sdk
3
- *
4
- * Provides download links and install instructions for the Aether SDK,
5
- * Aether JS client, and FLUX/ATH token libraries.
6
- *
7
- * Usage:
8
- * aether-cli sdk # Show all SDK options
9
- * aether-cli sdk js # Aether JS client
10
- * aether-cli sdk rust # Aether Rust SDK
11
- * aether-cli sdk tokens # FLUX/ATH token libraries
12
- * aether-cli sdk types # TypeScript/Rust type definitions for TX payloads
13
- */
14
-
15
- const os = require('os');
16
-
17
- // ANSI colors
18
- const colors = {
19
- reset: '\x1b[0m',
20
- bright: '\x1b[1m',
21
- green: '\x1b[32m',
22
- yellow: '\x1b[33m',
23
- cyan: '\x1b[36m',
24
- red: '\x1b[31m',
25
- dim: '\x1b[2m',
26
- magenta: '\x1b[35m',
27
- blue: '\x1b[34m',
28
- };
29
-
30
- /**
31
- * Print the SDK banner
32
- */
33
- function printBanner() {
34
- console.log(`
35
- ${colors.cyan}╔═══════════════════════════════════════════════════════════════╗
36
- ${colors.cyan}║ ║
37
- ${colors.cyan}║ ${colors.bright}AETHER SDK${colors.reset}${colors.cyan} ║
38
- ${colors.cyan}║ ${colors.bright}Developer Tools & Libraries${colors.reset}${colors.cyan} ║
39
- ${colors.cyan}║ ║
40
- ${colors.cyan}╚═══════════════════════════════════════════════════════════════╝${colors.reset}
41
- `);
42
- }
43
-
44
- /**
45
- * Print a section header
46
- */
47
- function printSection(title, icon = '📦') {
48
- console.log();
49
- console.log(`${colors.bright}${colors.cyan}${''.repeat(60)}${colors.reset}`);
50
- console.log(`${colors.bright} ${icon} ${title}${colors.reset}`);
51
- console.log(`${colors.bright}${colors.cyan}${''.repeat(60)}${colors.reset}`);
52
- console.log();
53
- }
54
-
55
- /**
56
- * Print a code block
57
- */
58
- function printCode(code, lang = 'bash') {
59
- console.log(` ${colors.dim}[ ${lang} ]${colors.reset}`);
60
- console.log(` ${colors.bright}${code}${colors.reset}`);
61
- console.log();
62
- }
63
-
64
- /**
65
- * Print a link
66
- */
67
- function printLink(label, url) {
68
- console.log(` ${colors.cyan}🔗 ${label}:${colors.reset}`);
69
- console.log(` ${colors.blue}${url}${colors.reset}`);
70
- console.log();
71
- }
72
-
73
- /**
74
- * Show all SDK options
75
- */
76
- function showAllSdks() {
77
- printBanner();
78
-
79
- console.log(` ${colors.bright}Available SDKs and Libraries:${colors.reset}\n`);
80
-
81
- console.log(` ${colors.green}npm${colors.reset} aether-cli sdk install - Install @jellylegsai/aether-sdk ← NEW!`);
82
- console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk js - JavaScript/TypeScript client`);
83
- console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk rust - Rust SDK for native development`);
84
- console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk tokens - FLUX/ATH token libraries`);
85
- console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk docs - Documentation portal`);
86
- console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk types - TypeScript/Rust type definitions`);
87
- console.log();
88
-
89
- // Quick start
90
- printSection('⚡ Quick Start', '🚀');
91
- console.log(' Get started with Aether development in 3 steps:\n');
92
- console.log(` 1. ${colors.bright}Install the SDK:${colors.reset}`);
93
- printCode('npx aether-cli sdk install');
94
- console.log(` 2. ${colors.bright}Initialize your connection:${colors.reset}`);
95
- printCode("const aether = require('@jellylegsai/aether-sdk');\nconst client = new aether.AetherClient({ rpcUrl: 'http://localhost:8899' });");
96
- console.log(` 3. ${colors.bright}Start building!${colors.reset}`);
97
- console.log(` ${colors.dim}See docs for full API reference${colors.reset}`);
98
- console.log();
99
-
100
- printLink('Documentation', 'https://docs.aether.network');
101
- printLink('GitHub Organization', 'https://github.com/aether-network');
102
- printLink('Discord Community', 'https://discord.gg/aether');
103
- }
104
-
105
- /**
106
- * Show JavaScript SDK info
107
- */
108
- function showJsSdk() {
109
- printSection('Aether JavaScript Client', '📜');
110
-
111
- console.log(` ${colors.bright}The official Aether JavaScript/TypeScript client library.${colors.reset}`);
112
- console.log(` Provides a simple API for interacting with the Aether blockchain.${colors.reset}\n`);
113
-
114
- console.log(` ${colors.green}✓ Stable Release${colors.reset}`);
115
- console.log(` ${colors.dim}Version: 1.2.0${colors.reset}`);
116
- console.log();
117
-
118
- printSection('Transaction Types');
119
- console.log(` ${colors.cyan}Transfer${colors.reset} — ${colors.dim}Send AETH to another address${colors.reset}`);
120
- console.log(` { recipient: string, amount: u64, nonce: u64 }`);
121
- console.log();
122
- console.log(` ${colors.cyan}Stake${colors.reset} — ${colors.dim}Delegate tokens to a validator${colors.reset}`);
123
- console.log(` { validator: string, amount: u64 }`);
124
- console.log();
125
- console.log(` ${colors.cyan}Unstake${colors.reset} — ${colors.dim}Request withdrawal of staked tokens${colors.reset}`);
126
- console.log(` { stake_account: string, amount: u64 }`);
127
- console.log();
128
- console.log(` ${colors.cyan}ClaimRewards${colors.reset} — ${colors.dim}Claim accumulated staking rewards${colors.reset}`);
129
- console.log(` { stake_account: string }`);
130
- console.log();
131
- console.log(` ${colors.cyan}CreateNFT${colors.reset} ${colors.dim}Create a new NFT on-chain${colors.reset}`);
132
- console.log(` { metadata_url: string, royalties: u16 }`);
133
- console.log();
134
- console.log(` ${colors.cyan}MintNFT${colors.reset} — ${colors.dim}Mint additional supply of an existing NFT${colors.reset}`);
135
- console.log(` { nft_id: string, amount: u64 }`);
136
- console.log();
137
- console.log(` ${colors.cyan}TransferNFT${colors.reset} — ${colors.dim}Transfer an NFT to another address${colors.reset}`);
138
- console.log(` { nft_id: string, recipient: string }`);
139
- console.log();
140
- console.log(` ${colors.cyan}UpdateMetadata${colors.reset} — ${colors.dim}Update NFT metadata URL${colors.reset}`);
141
- console.log(` { nft_id: string, metadata_url: string }`);
142
- console.log();
143
-
144
- printSection('Installation');
145
- printCode('npm install @aether-network/client');
146
- console.log(' or');
147
- printCode('yarn add @aether-network/client');
148
- console.log(' or');
149
- printCode('pnpm add @aether-network/client');
150
-
151
- printSection('Usage Example');
152
- const example = `const aether = require('@aether-network/client');
153
-
154
- // Initialize client
155
- const client = new aether.Client({
156
- rpcUrl: 'http://localhost:8899',
157
- wsUrl: 'ws://localhost:8900',
158
- });
159
-
160
- // Get slot info
161
- const slot = await client.getSlot();
162
- console.log('Current slot:', slot);
163
-
164
- // Get account info (includes balance)
165
- const account = await client.getAccountInfo(pubkey);
166
- console.log('Balance:', account.lamports, 'lamports');
167
-
168
- // Send Transfer transaction
169
- const tx = await client.sendTransaction({
170
- type: 'Transfer',
171
- payload: {
172
- recipient: 'ATH...',
173
- amount: 1000000000, // 1 AETH in lamports
174
- nonce: 0,
175
- },
176
- });
177
- console.log('Transaction signature:', tx.signature);`;
178
-
179
- console.log(` ${colors.dim}[ javascript ]${colors.reset}`);
180
- example.split('\n').forEach(line => {
181
- console.log(` ${colors.bright}${line}${colors.reset}`);
182
- });
183
- console.log();
184
-
185
- printSection('RPC API Reference');
186
- console.log(` ${colors.cyan}GET /v1/account/<addr>${colors.reset} — ${colors.dim}Fetch account info + lamports balance${colors.reset}`);
187
- console.log(` ${colors.cyan}GET /v1/slot${colors.reset} — ${colors.dim}Get current slot number${colors.reset}`);
188
- console.log(` ${colors.cyan}GET /v1/validators${colors.reset} — ${colors.dim}List active validators${colors.reset}`);
189
- console.log(` ${colors.cyan}POST /v1/tx${colors.reset} — ${colors.dim}Submit signed transaction${colors.reset}`);
190
- console.log(` ${colors.cyan}GET /v1/tx/<signature>${colors.reset} — ${colors.dim}Get transaction receipt${colors.reset}`);
191
- console.log();
192
-
193
- printLink('NPM Package', 'https://www.npmjs.com/package/@aether-network/client');
194
- printLink('TypeScript Docs', 'https://docs.aether.network/sdk/js');
195
- printLink('GitHub Repo', 'https://github.com/aether-network/aether-js');
196
- }
197
-
198
- /**
199
- * Show Rust SDK info
200
- */
201
- function showRustSdk() {
202
- printSection('Aether Rust SDK', '🦀');
203
-
204
- console.log(` ${colors.bright}Native Rust SDK for building Aether programs and clients.${colors.reset}`);
205
- console.log(` Use this for validator plugins, custom programs, and high-performance tools.${colors.reset}\n`);
206
-
207
- console.log(` ${colors.green}✓ Stable Release${colors.reset}`);
208
- console.log(` ${colors.dim}Version: 1.2.0${colors.reset}`);
209
- console.log();
210
-
211
- printSection('Installation');
212
- printCode('cargo add aether-sdk');
213
- console.log(' or add to your Cargo.toml:');
214
- console.log();
215
- console.log(` ${colors.dim}${colors.bgRed}toml${colors.reset}`);
216
- console.log(` ${colors.bright}[dependencies]${colors.reset}`);
217
- console.log(` ${colors.bright}aether-sdk = "1.2"${colors.reset}`);
218
- console.log();
219
-
220
- printSection('Usage Example');
221
- const rustExample = `use aether_sdk::{client::Client, pubkey::Pubkey};
222
-
223
- #[tokio::main]
224
- async fn main() -> Result<(), Box<dyn std::error::Error>> {
225
- // Initialize client
226
- let client = Client::new("http://localhost:8899");
227
-
228
- // Get slot
229
- let slot = client.get_slot().await?;
230
- println!("Current slot: {}", slot);
231
-
232
- // Get balance
233
- let pubkey = Pubkey::from_str("...")?;
234
- let balance = client.get_balance(&pubkey).await?;
235
- println!("Balance: {} lamports", balance);
236
-
237
- Ok(())
238
- }`;
239
-
240
- console.log(` ${colors.dim}${colors.bgRed}rust${colors.reset}`);
241
- rustExample.split('\n').forEach(line => {
242
- console.log(` ${colors.bright}${line}${colors.reset}`);
243
- });
244
- console.log();
245
-
246
- printSection('Features');
247
- console.log(` ${colors.cyan}• Full RPC client${colors.reset}`);
248
- console.log(` ${colors.cyan}• Program development framework${colors.reset}`);
249
- console.log(` ${colors.cyan}• Account serialization/deserialization${colors.reset}`);
250
- console.log(` ${colors.cyan}• Transaction building and signing${colors.reset}`);
251
- console.log(` ${colors.cyan}• Async runtime support (tokio)${colors.reset}`);
252
- console.log();
253
-
254
- printLink('Crates.io', 'https://crates.io/crates/aether-sdk');
255
- printLink('API Docs', 'https://docs.rs/aether-sdk');
256
- printLink('GitHub Repo', 'https://github.com/aether-network/aether-rust');
257
- }
258
-
259
- /**
260
- * Show token libraries info
261
- */
262
- function showTokensSdk() {
263
- printSection('FLUX / ATH Token Libraries', '🪙');
264
-
265
- console.log(` ${colors.bright}Token libraries for FLUX (utility) and ATH (governance) tokens.${colors.reset}`);
266
- console.log(` Use these to integrate Aether tokens into your applications.${colors.reset}\n`);
267
-
268
- console.log(` ${colors.yellow}⚠ Beta Release${colors.reset}`);
269
- console.log(` ${colors.dim}Version: 0.9.0 (testnet only)${colors.reset}`);
270
- console.log();
271
-
272
- printSection('Installation');
273
- console.log(` ${colors.bright}JavaScript:${colors.reset}`);
274
- printCode('npm install @aether-network/tokens');
275
- console.log();
276
- console.log(` ${colors.bright}Rust:${colors.reset}`);
277
- printCode('cargo add aether-tokens');
278
-
279
- printSection('Supported Tokens');
280
- console.log();
281
- console.log(` ${colors.magenta}FLUX${colors.reset} - Utility Token`);
282
- console.log(` • Purpose: Transaction fees, staking rewards`);
283
- console.log(` Decimals: 9`);
284
- console.log(` • Mint: ${colors.dim}flux7x... (testnet)${colors.reset}`);
285
- console.log();
286
- console.log(` ${colors.blue}ATH${colors.reset} - Governance Token`);
287
- console.log(` • Purpose: Voting, protocol upgrades`);
288
- console.log(` Decimals: 6`);
289
- console.log(` • Mint: ${colors.dim}athgov... (testnet)${colors.reset}`);
290
- console.log();
291
-
292
- printSection('Usage Example (JavaScript)');
293
- const tokenExample = `const { TokenClient, TOKENS } = require('@aether-network/tokens');
294
-
295
- const client = new TokenClient(rpcUrl);
296
-
297
- // Get FLUX balance
298
- const fluxBalance = await client.getTokenBalance(pubkey, TOKENS.FLUX);
299
- console.log('FLUX:', fluxBalance);
300
-
301
- // Transfer FLUX
302
- const tx = await client.transfer({
303
- mint: TOKENS.FLUX,
304
- from: senderPubkey,
305
- to: recipientPubkey,
306
- amount: 1000,
307
- });`;
308
-
309
- console.log(` ${colors.dim}${colors.bgRed}javascript${colors.reset}`);
310
- tokenExample.split('\n').forEach(line => {
311
- console.log(` ${colors.bright}${line}${colors.reset}`);
312
- });
313
- console.log();
314
-
315
- printLink('Token Documentation', 'https://docs.aether.network/tokens');
316
- printLink('Token Registry', 'https://github.com/aether-network/token-registry');
317
- printLink('Testnet Faucet', 'https://faucet.aether.network');
318
- }
319
-
320
- /**
321
- * Show install instructions + run npm install for the user
322
- */
323
- function showInstall() {
324
- printSection('Install Aether SDK', '📦');
325
-
326
- console.log(` ${colors.bright}The @jellylegsai/aether-sdk package lets you:${colors.reset}`);
327
- console.log(` ${colors.cyan}•${colors.reset} Query the Aether blockchain (balances, accounts, validators)`);
328
- console.log(` ${colors.cyan}•${colors.reset} Submit real transactions (transfer, stake, unstake, claim)`);
329
- console.log(` ${colors.cyan}•${colors.reset} Build DApps and automation scripts on top of Aether`);
330
- console.log();
331
-
332
- console.log(` ${colors.bright}Installation options:${colors.reset}\n`);
333
- console.log(` ${colors.green}1)${colors.reset} ${colors.cyan}Install all SDK packages (recommended):${colors.reset}`);
334
- console.log(` ${colors.dim}npm install @jellylegsai/aether-sdk${colors.reset}`);
335
- console.log();
336
-
337
- printSection('Quick Install');
338
-
339
- console.log(` ${colors.dim}The CLI can run the install command for you in your project directory.${colors.reset}`);
340
- console.log();
341
-
342
- const installCmd = 'npm install @jellylegsai/aether-sdk';
343
- console.log(` ${colors.bright}Running:${colors.reset} ${colors.cyan}${installCmd}${colors.reset}`);
344
- console.log();
345
- }
346
-
347
- /**
348
- * Execute `npm install @jellylegsai/aether-sdk` in the user's project directory.
349
- * Detects the target directory from the nearest package.json, or uses cwd.
350
- */
351
- async function runInstall(args) {
352
- const { exec } = require('child_process');
353
- const fs = require('fs');
354
- const path = require('path');
355
-
356
- // Detect install target: nearest package.json up from cwd, else cwd
357
- let targetDir = process.cwd();
358
- let searchDir = targetDir;
359
- for (let i = 0; i < 10; i++) {
360
- if (fs.existsSync(path.join(searchDir, 'package.json'))) {
361
- targetDir = searchDir;
362
- break;
363
- }
364
- const parent = path.dirname(searchDir);
365
- if (parent === searchDir) break;
366
- searchDir = parent;
367
- }
368
-
369
- const isCoreOnly = args.includes('--core');
370
-
371
- let pkgToInstall = '@jellylegsai/aether-sdk';
372
-
373
- const installCmd = `npm install ${pkgToInstall}`;
374
-
375
- printBanner();
376
- printSection('Installing Aether SDK', '📦');
377
-
378
- console.log(` ${colors.bright}Target directory:${colors.reset} ${colors.cyan}${targetDir}${colors.reset}`);
379
- console.log(` ${colors.bright}Package:${colors.reset} ${colors.cyan}${pkgToInstall}${colors.reset}`);
380
- console.log(` ${colors.dim}Registry:${colors.reset} ${colors.blue}https://registry.npmjs.org${colors.reset}`);
381
- console.log();
382
- console.log(` ${colors.dim}Running: ${installCmd}${colors.reset}`);
383
- console.log();
384
- console.log(` ${colors.yellow}This may take a moment...${colors.reset}\n`);
385
-
386
- return new Promise((resolve) => {
387
- const child = exec(installCmd, { cwd: targetDir }, (err, stdout, stderr) => {
388
- if (err) {
389
- console.log(` ${colors.red}✗ Install failed:${colors.reset} ${err.message}`);
390
- if (stderr) console.log(` ${colors.dim}${stderr}${colors.reset}`);
391
- console.log();
392
- console.log(` ${colors.dim}You can try manually:${colors.reset}`);
393
- console.log(` ${colors.cyan}cd ${targetDir} && npm install @jellylegsai/aether-sdk${colors.reset}`);
394
- resolve({ success: false, error: err.message });
395
- } else {
396
- console.log(` ${colors.green}✓ Install succeeded!${colors.reset}`);
397
- if (stdout) {
398
- const lines = stdout.split('\n').filter(l => l.trim());
399
- for (const line of lines.slice(-5)) {
400
- console.log(` ${colors.dim}${line}${colors.reset}`);
401
- }
402
- }
403
- console.log();
404
- console.log(` ${colors.bright}Next steps:${colors.reset}`);
405
- console.log(` ${colors.dim}1. Import in your code:${colors.reset}`);
406
- console.log(` ${colors.cyan}const aether = require('@jellylegsai/aether-sdk');${colors.reset}`);
407
- console.log();
408
- console.log(` ${colors.dim}2. Initialize the client:${colors.reset}`);
409
- console.log(` ${colors.cyan}const client = new aether.AetherClient({ rpcUrl: 'http://localhost:8899' });${colors.reset}`);
410
- console.log();
411
- console.log(` ${colors.dim}3. Query the chain:${colors.reset}`);
412
- console.log(` ${colors.cyan}const slot = await client.getSlot();${colors.reset}`);
413
- console.log(` ${colors.cyan}console.log('Current slot:', slot);${colors.reset}`);
414
- console.log();
415
- console.log(` ${colors.bright}Docs:${colors.reset} ${colors.blue}https://docs.aether.network/sdk/js${colors.reset}`);
416
- console.log();
417
- resolve({ success: true, targetDir, pkg: pkgToInstall });
418
- }
419
- });
420
-
421
- child.stdout?.on('data', (chunk) => {
422
- const line = chunk.toString().trim();
423
- if (line) process.stdout.write(` ${colors.dim}${line}${colors.reset}\n`);
424
- });
425
- child.stderr?.on('data', (chunk) => {
426
- const line = chunk.toString().trim();
427
- if (line && !line.includes('npm warn')) process.stdout.write(` ${colors.dim}${line}${colors.reset}\n`);
428
- });
429
- });
430
- }
431
-
432
- /**
433
- * Show TypeScript/Rust type definitions for Aether transactions
434
- */
435
- function showTypes() {
436
- printSection('Aether Transaction Type Definitions', '🏷️');
437
-
438
- console.log(` ${colors.bright}Exported from ${colors.cyan}@aether-network/client${colors.reset} and ${colors.cyan}aether-sdk${colors.reset}\n`);
439
-
440
- printSection('TransactionPayload (Rust enum — serde JSON tag)');
441
-
442
- const tsTypes = `// TypeScript / JavaScript
443
- // Import from @aether-network/client
444
-
445
- // TransactionPayload — discriminated union via 'type' field
446
- type TransferPayload = { type: 'Transfer'; data: { recipient: string; amount: u64; nonce: u64 } };
447
- type StakePayload = { type: 'Stake'; data: { validator: string; amount: u64 } };
448
- type UnstakePayload = { type: 'Unstake'; data: { stake_account: string; amount: u64 } };
449
- type ClaimRewardsPayload = { type: 'ClaimRewards'; data: { stake_account: string } };
450
- type CreateNFTPayload = { type: 'CreateNFT'; data: { metadata_url: string; royalties: u16 } };
451
- type MintNFTPayload = { type: 'MintNFT'; data: { nft_id: string; amount: u64 } };
452
- type TransferNFTPayload = { type: 'TransferNFT'; data: { nft_id: string; recipient: string } };
453
- type UpdateMetadataPayload = { type: 'UpdateMetadata'; data: { nft_id: string; metadata_url: string } };
454
-
455
- type TransactionPayload =
456
- | TransferPayload | StakePayload | UnstakePayload | ClaimRewardsPayload
457
- | CreateNFTPayload | MintNFTPayload | TransferNFTPayload | UpdateMetadataPayload;
458
-
459
- // Full AetherTransaction
460
- interface AetherTransaction {
461
- signature: string; // base58 of [u8; 64]
462
- signer: string; // base58 of [u8; 32]
463
- tx_type: string; // e.g. "Transfer", "Stake"
464
- payload: TransactionPayload;
465
- fee: u64;
466
- slot: u64;
467
- timestamp: u64;
468
- }
469
-
470
- // Account response from GET /v1/account/<addr>
471
- interface Account {
472
- lamports: u64;
473
- owner: string; // base58 of [u8; 32]
474
- data: Uint8Array;
475
- rent_epoch: u64;
476
- }`;
477
-
478
- console.log(` ${colors.dim}[ typescript ]${colors.reset}`);
479
- tsTypes.split('\n').forEach(line => {
480
- console.log(` ${line}`);
481
- });
482
- console.log();
483
-
484
- printSection('Rust struct definitions (from crates/aether-core/src/types.rs)');
485
-
486
- const rustTypes = `// Rust — use aether_sdk::types;
487
-
488
- use serde::{Deserialize, Serialize};
489
-
490
- #[derive(Debug, Clone, Serialize, Deserialize)]
491
- pub enum TransactionPayload {
492
- #[serde(tag = "type", content = "data")]
493
- Transfer { recipient: String, amount: u64, nonce: u64 },
494
- #[serde(tag = "type", content = "data")]
495
- Stake { validator: String, amount: u64 },
496
- #[serde(tag = "type", content = "data")]
497
- Unstake { stake_account: String, amount: u64 },
498
- #[serde(tag = "type", content = "data")]
499
- ClaimRewards { stake_account: String },
500
- #[serde(tag = "type", content = "data")]
501
- CreateNFT { metadata_url: String, royalties: u16 },
502
- #[serde(tag = "type", content = "data")]
503
- MintNFT { nft_id: String, amount: u64 },
504
- #[serde(tag = "type", content = "data")]
505
- TransferNFT { nft_id: String, recipient: String },
506
- #[serde(tag = "type", content = "data")]
507
- UpdateMetadata { nft_id: String, metadata_url: String },
508
- }
509
-
510
- #[derive(Debug, Clone, Serialize, Deserialize)]
511
- pub struct AetherTransaction {
512
- #[serde(with = "serde_bytes_64")]
513
- pub signature: [u8; 64],
514
- #[serde(with = "serde_bytes_32")]
515
- pub signer: [u8; 32],
516
- pub tx_type: TransactionType,
517
- pub payload: TransactionPayload,
518
- pub fee: u64,
519
- pub slot: u64,
520
- pub timestamp: u64,
521
- }
522
-
523
- pub type Address = [u8; 32];
524
-
525
- #[derive(Debug, Clone, Serialize, Deserialize)]
526
- pub struct Account {
527
- pub lamports: u64,
528
- pub owner: [u8; 32],
529
- pub data: Vec<u8>,
530
- pub rent_epoch: u64,
531
- }`;
532
-
533
- console.log(` ${colors.dim}[ rust ]${colors.reset}`);
534
- rustTypes.split('\n').forEach(line => {
535
- console.log(` ${line}`);
536
- });
537
- console.log();
538
-
539
- printSection('TransactionType enum');
540
- console.log(` ${colors.cyan}Transfer${colors.reset} — Send AETH`);
541
- console.log(` ${colors.cyan}Stake${colors.reset} Delegate to validator`);
542
- console.log(` ${colors.cyan}Unstake${colors.reset} — Request withdrawal`);
543
- console.log(` ${colors.cyan}ClaimRewards${colors.reset} — Claim staking rewards`);
544
- console.log(` ${colors.cyan}CreateNFT${colors.reset} — Create on-chain NFT`);
545
- console.log(` ${colors.cyan}MintNFT${colors.reset} — Mint additional NFT supply`);
546
- console.log(` ${colors.cyan}TransferNFT${colors.reset} — Transfer NFT to another address`);
547
- console.log(` ${colors.cyan}UpdateMetadata${colors.reset} — Update NFT metadata URL`);
548
- console.log();
549
- }
550
-
551
- /**
552
- * Show documentation portal info
553
- */
554
- function showDocs() {
555
- printSection('Aether Documentation', '📚');
556
-
557
- console.log(` ${colors.bright}Comprehensive documentation for Aether developers.${colors.reset}\n`);
558
-
559
- console.log(` ${colors.cyan}📖 Documentation Portal:${colors.reset}`);
560
- console.log(` ${colors.blue}https://docs.aether.network${colors.reset}`);
561
- console.log();
562
-
563
- console.log(` ${colors.cyan}Sections:${colors.reset}`);
564
- console.log(` • Getting Started - Quick start guides`);
565
- console.log(` • Core Concepts - Accounts, programs, transactions`);
566
- console.log(` SDK Reference - Full API docs for JS and Rust`);
567
- console.log(` • Tutorials - Step-by-step projects`);
568
- console.log(` • Validator Guide - Running and maintaining validators`);
569
- console.log(` Economics - Staking, rewards, fees`);
570
- console.log();
571
-
572
- printLink('Main Docs', 'https://docs.aether.network');
573
- printLink('API Reference', 'https://docs.aether.network/api');
574
- printLink('Tutorials', 'https://docs.aether.network/tutorials');
575
- printLink('Validator Docs', 'https://docs.aether.network/validators');
576
- }
577
-
578
- /**
579
- * Parse command line args
580
- */
581
- function parseArgs() {
582
- const args = process.argv.slice(3); // Skip 'aether-cli sdk'
583
-
584
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
585
- return 'all';
586
- }
587
-
588
- const subcmd = args[0].toLowerCase();
589
-
590
- switch (subcmd) {
591
- case 'js':
592
- case 'javascript':
593
- case 'node':
594
- return 'js';
595
- case 'rust':
596
- case 'rs':
597
- return 'rust';
598
- case 'tokens':
599
- case 'token':
600
- case 'flux':
601
- case 'ath':
602
- return 'tokens';
603
- case 'docs':
604
- case 'doc':
605
- case 'documentation':
606
- return 'docs';
607
- case 'types':
608
- case 'type':
609
- case 'typedef':
610
- case 'typedefs':
611
- return 'types';
612
- case 'install':
613
- case 'i':
614
- return 'install';
615
- default:
616
- return 'all';
617
- }
618
- }
619
-
620
- /**
621
- * Main SDK command
622
- */
623
- async function sdkCommand() {
624
- const subcmd = parseArgs();
625
-
626
- switch (subcmd) {
627
- case 'js':
628
- showJsSdk();
629
- break;
630
- case 'rust':
631
- showRustSdk();
632
- break;
633
- case 'tokens':
634
- showTokensSdk();
635
- break;
636
- case 'docs':
637
- showDocs();
638
- break;
639
- case 'types':
640
- showTypes();
641
- break;
642
- case 'install':
643
- await runInstall(process.argv.slice(4));
644
- break;
645
- default:
646
- showAllSdks();
647
- }
648
- }
649
-
650
- // Export for use as module
651
- module.exports = { sdkCommand };
652
-
653
- // Run if called directly
654
- if (require.main === module) {
655
- sdkCommand();
656
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli sdk
4
+ *
5
+ * Direct SDK access for developers - query any SDK method from the command line.
6
+ * Makes REAL HTTP RPC calls to the Aether blockchain.
7
+ * No stubs, no mocks - every function makes actual RPC calls.
8
+ *
9
+ * Usage:
10
+ * aether sdk getSlot Get current slot
11
+ * aether sdk getBalance <address> Get account balance
12
+ * aether sdk getAccountInfo <address> Get full account info
13
+ * aether sdk getEpochInfo Get epoch information
14
+ * aether sdk getBlockHeight Get block height
15
+ * aether sdk getTransaction <signature> Get transaction by signature
16
+ * aether sdk getStakePositions <address> Get stake positions
17
+ * aether sdk getRewards <address> Get rewards info
18
+ * aether sdk getValidators List validators
19
+ * aether sdk getSupply Get token supply
20
+ * aether sdk getTPS Get transactions per second
21
+ * aether sdk getHealth Check node health
22
+ * aether sdk getVersion Get node version
23
+ * aether sdk getRecentBlockhash Get recent blockhash
24
+ * aether sdk getSlotProduction Get slot production stats
25
+ * aether sdk getClusterPeers Get peer nodes
26
+ * aether sdk getFees Get fee schedule
27
+ * aether sdk getNFT <id> Get NFT details
28
+ * aether sdk getNFTHoldings <address> Get NFT holdings
29
+ * aether sdk getRecentTransactions <addr> Get recent transactions
30
+ * aether sdk ping Ping RPC endpoint
31
+ * aether sdk --rpc <url> Use specific RPC endpoint
32
+ * aether sdk --json Output JSON format
33
+ * aether sdk --list List all available SDK methods
34
+ *
35
+ * SDK Methods Used:
36
+ * - All methods from @jellylegsai/aether-sdk
37
+ * - Real HTTP RPC calls to http://127.0.0.1:8899
38
+ */
39
+
40
+ const path = require('path');
41
+ const readline = require('readline');
42
+
43
+ // Import SDK - REAL blockchain RPC calls
44
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
45
+ const aether = require(sdkPath);
46
+
47
+ // ANSI colours
48
+ const C = {
49
+ reset: '\x1b[0m',
50
+ bright: '\x1b[1m',
51
+ dim: '\x1b[2m',
52
+ red: '\x1b[31m',
53
+ green: '\x1b[32m',
54
+ yellow: '\x1b[33m',
55
+ cyan: '\x1b[36m',
56
+ magenta: '\x1b[35m',
57
+ };
58
+
59
+ const CLI_VERSION = '1.2.0';
60
+
61
+ // ============================================================================
62
+ // SDK Client Setup
63
+ // ============================================================================
64
+
65
+ function getDefaultRpc() {
66
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
67
+ }
68
+
69
+ function createClient(rpcUrl) {
70
+ return new aether.AetherClient({ rpcUrl });
71
+ }
72
+
73
+ // ============================================================================
74
+ // Format Helpers
75
+ // ============================================================================
76
+
77
+ function formatAether(lamports) {
78
+ if (!lamports || lamports === '0') return '0 AETH';
79
+ const aeth = Number(lamports) / 1e9;
80
+ return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
81
+ }
82
+
83
+ function formatNumber(n) {
84
+ if (n === null || n === undefined) return 'N/A';
85
+ return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
86
+ }
87
+
88
+ function shortAddress(addr) {
89
+ if (!addr || addr.length < 16) return addr || 'unknown';
90
+ return addr.slice(0, 8) + '...' + addr.slice(-8);
91
+ }
92
+
93
+ // ============================================================================
94
+ // SDK Method Definitions
95
+ // ============================================================================
96
+
97
+ const SDK_METHODS = {
98
+ // Core chain queries
99
+ getSlot: {
100
+ desc: 'Get current slot number',
101
+ args: [],
102
+ returns: 'slot number',
103
+ example: 'aether sdk getSlot',
104
+ },
105
+ getBlockHeight: {
106
+ desc: 'Get current block height',
107
+ args: [],
108
+ returns: 'block height',
109
+ example: 'aether sdk getBlockHeight',
110
+ },
111
+ getEpochInfo: {
112
+ desc: 'Get epoch information',
113
+ args: [],
114
+ returns: 'epoch data',
115
+ example: 'aether sdk getEpochInfo',
116
+ },
117
+ getHealth: {
118
+ desc: 'Check node health',
119
+ args: [],
120
+ returns: 'health status',
121
+ example: 'aether sdk getHealth',
122
+ },
123
+ getVersion: {
124
+ desc: 'Get node version',
125
+ args: [],
126
+ returns: 'version info',
127
+ example: 'aether sdk getVersion',
128
+ },
129
+ getTPS: {
130
+ desc: 'Get transactions per second',
131
+ args: [],
132
+ returns: 'TPS number',
133
+ example: 'aether sdk getTPS',
134
+ },
135
+ getSupply: {
136
+ desc: 'Get token supply info',
137
+ args: [],
138
+ returns: 'supply data',
139
+ example: 'aether sdk getSupply',
140
+ },
141
+ getFees: {
142
+ desc: 'Get fee schedule',
143
+ args: [],
144
+ returns: 'fee info',
145
+ example: 'aether sdk getFees',
146
+ },
147
+ getRecentBlockhash: {
148
+ desc: 'Get recent blockhash',
149
+ args: [],
150
+ returns: 'blockhash',
151
+ example: 'aether sdk getRecentBlockhash',
152
+ },
153
+ getSlotProduction: {
154
+ desc: 'Get slot production stats',
155
+ args: [],
156
+ returns: 'production stats',
157
+ example: 'aether sdk getSlotProduction',
158
+ },
159
+ getClusterPeers: {
160
+ desc: 'Get cluster peers',
161
+ args: [],
162
+ returns: 'peers list',
163
+ example: 'aether sdk getClusterPeers',
164
+ },
165
+ getValidators: {
166
+ desc: 'Get validators list',
167
+ args: [],
168
+ returns: 'validators',
169
+ example: 'aether sdk getValidators',
170
+ },
171
+
172
+ // Account queries
173
+ getAccountInfo: {
174
+ desc: 'Get account info',
175
+ args: ['address'],
176
+ returns: 'account data',
177
+ example: 'aether sdk getAccountInfo ATHxxx...',
178
+ },
179
+ getBalance: {
180
+ desc: 'Get account balance',
181
+ args: ['address'],
182
+ returns: 'balance in lamports',
183
+ example: 'aether sdk getBalance ATHxxx...',
184
+ },
185
+ getStakePositions: {
186
+ desc: 'Get stake positions',
187
+ args: ['address'],
188
+ returns: 'stake positions',
189
+ example: 'aether sdk getStakePositions ATHxxx...',
190
+ },
191
+ getRewards: {
192
+ desc: 'Get rewards info',
193
+ args: ['address'],
194
+ returns: 'rewards data',
195
+ example: 'aether sdk getRewards ATHxxx...',
196
+ },
197
+ getStakeAccounts: {
198
+ desc: 'Get all stake accounts',
199
+ args: ['address'],
200
+ returns: 'stake accounts',
201
+ example: 'aether sdk getStakeAccounts ATHxxx...',
202
+ },
203
+ getTokenAccounts: {
204
+ desc: 'Get token accounts',
205
+ args: ['address'],
206
+ returns: 'token accounts',
207
+ example: 'aether sdk getTokenAccounts ATHxxx...',
208
+ },
209
+ getNFTHoldings: {
210
+ desc: 'Get NFT holdings',
211
+ args: ['address'],
212
+ returns: 'NFT holdings',
213
+ example: 'aether sdk getNFTHoldings ATHxxx...',
214
+ },
215
+ getNFTsByCreator: {
216
+ desc: 'Get NFTs by creator',
217
+ args: ['address'],
218
+ returns: 'NFTs list',
219
+ example: 'aether sdk getNFTsByCreator ATHxxx...',
220
+ },
221
+
222
+ // Transaction queries
223
+ getTransaction: {
224
+ desc: 'Get transaction by signature',
225
+ args: ['signature'],
226
+ returns: 'transaction data',
227
+ example: 'aether sdk getTransaction SIGxxx...',
228
+ },
229
+ getRecentTransactions: {
230
+ desc: 'Get recent transactions',
231
+ args: ['address', 'limit?'],
232
+ returns: 'transactions list',
233
+ example: 'aether sdk getRecentTransactions ATHxxx...',
234
+ },
235
+ getTransactionHistory: {
236
+ desc: 'Get full tx history with details',
237
+ args: ['address', 'limit?'],
238
+ returns: 'tx history',
239
+ example: 'aether sdk getTransactionHistory ATHxxx...',
240
+ },
241
+
242
+ // NFT queries
243
+ getNFT: {
244
+ desc: 'Get NFT details',
245
+ args: ['id'],
246
+ returns: 'NFT data',
247
+ example: 'aether sdk getNFT <id>',
248
+ },
249
+
250
+ // Utilities
251
+ ping: {
252
+ desc: 'Ping RPC endpoint',
253
+ args: ['url?'],
254
+ returns: 'ping result',
255
+ example: 'aether sdk ping',
256
+ },
257
+ };
258
+
259
+ // ============================================================================
260
+ // Argument Parsing
261
+ // ============================================================================
262
+
263
+ function parseArgs() {
264
+ const args = process.argv.slice(2);
265
+ const opts = {
266
+ method: null,
267
+ args: [],
268
+ rpc: getDefaultRpc(),
269
+ json: false,
270
+ list: false,
271
+ help: false,
272
+ };
273
+
274
+ // Check for --list
275
+ if (args.includes('--list') || args.includes('-l')) {
276
+ opts.list = true;
277
+ return opts;
278
+ }
279
+
280
+ // Check for --help
281
+ if (args.includes('--help') || args.includes('-h')) {
282
+ opts.help = true;
283
+ return opts;
284
+ }
285
+
286
+ // Parse method and arguments
287
+ for (let i = 0; i < args.length; i++) {
288
+ const arg = args[i];
289
+
290
+ if (arg === '--rpc' || arg === '-r') {
291
+ opts.rpc = args[++i];
292
+ } else if (arg === '--json' || arg === '-j') {
293
+ opts.json = true;
294
+ } else if (arg === '--list' || arg === '-l') {
295
+ opts.list = true;
296
+ } else if (arg === '--help' || arg === '-h') {
297
+ opts.help = true;
298
+ } else if (!opts.method && !arg.startsWith('-')) {
299
+ // First non-flag argument is the method
300
+ opts.method = arg;
301
+ } else if (opts.method && !arg.startsWith('-')) {
302
+ // Subsequent non-flag arguments are method args
303
+ opts.args.push(arg);
304
+ }
305
+ }
306
+
307
+ return opts;
308
+ }
309
+
310
+ function showHelp() {
311
+ console.log(`
312
+ ${C.bright}${C.cyan}aether-cli sdk${C.reset} - Direct SDK access for developers
313
+
314
+ ${C.bright}USAGE${C.reset}
315
+ aether sdk <method> [args...] [options]
316
+
317
+ ${C.bright}CORE CHAIN QUERIES${C.reset}
318
+ getSlot Get current slot number
319
+ getBlockHeight Get current block height
320
+ getEpochInfo Get epoch information
321
+ getHealth Check node health
322
+ getVersion Get node version info
323
+ getTPS Get transactions per second
324
+ getSupply Get token supply
325
+ getFees Get fee schedule
326
+ getRecentBlockhash Get recent blockhash
327
+ getSlotProduction Get slot production stats
328
+ getClusterPeers Get cluster peer nodes
329
+ getValidators List validators
330
+
331
+ ${C.bright}ACCOUNT QUERIES${C.reset}
332
+ getAccountInfo <address> Get account info
333
+ getBalance <address> Get balance in lamports
334
+ getStakePositions <address> Get stake positions
335
+ getRewards <address> Get rewards info
336
+ getStakeAccounts <address> Get all stake accounts
337
+ getTokenAccounts <address> Get token accounts
338
+ getNFTHoldings <address> Get NFT holdings
339
+ getNFTsByCreator <address> Get NFTs by creator
340
+
341
+ ${C.bright}TRANSACTION QUERIES${C.reset}
342
+ getTransaction <signature> Get transaction by signature
343
+ getRecentTransactions <addr> Get recent transactions
344
+ getTransactionHistory <addr> Get tx history with details
345
+
346
+ ${C.bright}NFT QUERIES${C.reset}
347
+ getNFT <id> Get NFT details
348
+
349
+ ${C.bright}UTILITIES${C.reset}
350
+ ping [url] Ping RPC endpoint
351
+ --list List all SDK methods
352
+
353
+ ${C.bright}OPTIONS${C.reset}
354
+ --rpc <url> RPC endpoint (default: ${getDefaultRpc()})
355
+ --json Output as JSON
356
+ --help Show this help
357
+
358
+ ${C.bright}EXAMPLES${C.reset}
359
+ aether sdk getSlot
360
+ aether sdk getBalance ATH3abc...
361
+ aether sdk getAccountInfo ATH3abc... --json
362
+ aether sdk getValidators --json
363
+ aether sdk getStakePositions ATH3abc...
364
+ aether sdk ping --rpc http://custom-rpc:8899
365
+ AETHER_RPC=https://rpc.aether.network aether sdk getEpochInfo
366
+ `);
367
+ }
368
+
369
+ function showMethodList() {
370
+ console.log(`\n${C.bright}${C.cyan}══ Available SDK Methods ══${C.reset}\n`);
371
+
372
+ const categories = {
373
+ 'Core Chain': ['getSlot', 'getBlockHeight', 'getEpochInfo', 'getHealth', 'getVersion', 'getTPS', 'getSupply', 'getFees', 'getRecentBlockhash', 'getSlotProduction', 'getClusterPeers', 'getValidators'],
374
+ 'Account': ['getAccountInfo', 'getBalance', 'getStakePositions', 'getRewards', 'getStakeAccounts', 'getTokenAccounts', 'getNFTHoldings', 'getNFTsByCreator'],
375
+ 'Transaction': ['getTransaction', 'getRecentTransactions', 'getTransactionHistory'],
376
+ 'NFT': ['getNFT'],
377
+ 'Utility': ['ping'],
378
+ };
379
+
380
+ for (const [category, methods] of Object.entries(categories)) {
381
+ console.log(`\n${C.bright}${C.cyan}${category}${C.reset}`);
382
+ for (const method of methods) {
383
+ const info = SDK_METHODS[method];
384
+ if (info) {
385
+ const argsStr = info.args.length > 0 ? ` <${info.args.join('> <')}>` : '';
386
+ console.log(` ${C.green}${method.padEnd(25)}${C.reset} ${C.dim}${info.desc}${C.reset}`);
387
+ console.log(` ${C.dim}Usage: ${info.example}${C.reset}`);
388
+ }
389
+ }
390
+ }
391
+
392
+ console.log(`\n${C.dim}All methods make REAL RPC calls to ${getDefaultRpc()}${C.reset}\n`);
393
+ }
394
+
395
+ // ============================================================================
396
+ // SDK Method Execution
397
+ // ============================================================================
398
+
399
+ async function executeMethod(opts) {
400
+ const { method, args, rpc, json } = opts;
401
+
402
+ if (!method) {
403
+ throw new Error('No method specified. Use --list to see available methods.');
404
+ }
405
+
406
+ // Validate method exists
407
+ if (!SDK_METHODS[method]) {
408
+ throw new Error(`Unknown method: ${method}. Use --list to see available methods.`);
409
+ }
410
+
411
+ const client = createClient(rpc);
412
+ const methodInfo = SDK_METHODS[method];
413
+
414
+ // Validate required arguments
415
+ if (methodInfo.args.length > args.length) {
416
+ throw new Error(`Method ${method} requires ${methodInfo.args.length} argument(s): ${methodInfo.args.join(', ')}`);
417
+ }
418
+
419
+ // Execute the method
420
+ let result;
421
+
422
+ switch (method) {
423
+ // Core chain queries
424
+ case 'getSlot':
425
+ result = await client.getSlot();
426
+ break;
427
+ case 'getBlockHeight':
428
+ result = await client.getBlockHeight();
429
+ break;
430
+ case 'getEpochInfo':
431
+ result = await client.getEpochInfo();
432
+ break;
433
+ case 'getHealth':
434
+ result = await client.getHealth();
435
+ break;
436
+ case 'getVersion':
437
+ result = await client.getVersion();
438
+ break;
439
+ case 'getTPS':
440
+ result = await client.getTPS();
441
+ break;
442
+ case 'getSupply':
443
+ result = await client.getSupply();
444
+ break;
445
+ case 'getFees':
446
+ result = await client.getFees();
447
+ break;
448
+ case 'getRecentBlockhash':
449
+ result = await client.getRecentBlockhash();
450
+ break;
451
+ case 'getSlotProduction':
452
+ result = await client.getSlotProduction();
453
+ break;
454
+ case 'getClusterPeers':
455
+ result = await client.getClusterPeers();
456
+ break;
457
+ case 'getValidators':
458
+ result = await client.getValidators();
459
+ break;
460
+
461
+ // Account queries
462
+ case 'getAccountInfo':
463
+ result = await client.getAccountInfo(args[0]);
464
+ break;
465
+ case 'getBalance':
466
+ result = await client.getBalance(args[0]);
467
+ break;
468
+ case 'getStakePositions':
469
+ result = await client.getStakePositions(args[0]);
470
+ break;
471
+ case 'getRewards':
472
+ result = await client.getRewards(args[0]);
473
+ break;
474
+ case 'getStakeAccounts':
475
+ result = await client.getStakeAccounts(args[0]);
476
+ break;
477
+ case 'getTokenAccounts':
478
+ result = await client.getTokenAccounts(args[0]);
479
+ break;
480
+ case 'getNFTHoldings':
481
+ result = await client.getNFTHoldings(args[0]);
482
+ break;
483
+ case 'getNFTsByCreator':
484
+ result = await client.getNFTsByCreator(args[0]);
485
+ break;
486
+
487
+ // Transaction queries
488
+ case 'getTransaction':
489
+ result = await client.getTransaction(args[0]);
490
+ break;
491
+ case 'getRecentTransactions':
492
+ result = await client.getRecentTransactions(args[0], parseInt(args[1]) || 20);
493
+ break;
494
+ case 'getTransactionHistory':
495
+ result = await client.getTransactionHistory(args[0], parseInt(args[1]) || 20);
496
+ break;
497
+
498
+ // NFT queries
499
+ case 'getNFT':
500
+ result = await client.getNFT(args[0]);
501
+ break;
502
+
503
+ // Utilities
504
+ case 'ping':
505
+ result = await aether.ping(args[0] || rpc);
506
+ break;
507
+
508
+ default:
509
+ throw new Error(`Method ${method} not implemented in CLI`);
510
+ }
511
+
512
+ // Output result
513
+ if (json) {
514
+ console.log(JSON.stringify({
515
+ method,
516
+ args,
517
+ rpc,
518
+ result,
519
+ timestamp: new Date().toISOString(),
520
+ cli_version: CLI_VERSION,
521
+ }, null, 2));
522
+ } else {
523
+ // Pretty output
524
+ console.log(`\n${C.bright}${C.cyan}══ SDK Result: ${method} ══${C.reset}\n`);
525
+ console.log(` ${C.dim}RPC:${C.reset} ${rpc}`);
526
+ console.log(` ${C.dim}Method:${C.reset} ${method}`);
527
+ if (args.length > 0) {
528
+ console.log(` ${C.dim}Args:${C.reset} ${args.join(' ')}`);
529
+ }
530
+ console.log();
531
+
532
+ // Format result based on type
533
+ if (typeof result === 'object') {
534
+ // Special formatting for specific methods
535
+ if (method === 'getBalance') {
536
+ console.log(` ${C.green}Balance:${C.reset} ${formatNumber(result)} lamports`);
537
+ console.log(` ${C.green}Formatted:${C.reset} ${formatAether(result)}`);
538
+ } else if (method === 'getSlot' || method === 'getBlockHeight') {
539
+ console.log(` ${C.green}Result:${C.reset} ${formatNumber(result)}`);
540
+ } else if (method === 'getTPS') {
541
+ console.log(` ${C.green}TPS:${C.reset} ${result}`);
542
+ } else if (method === 'getHealth') {
543
+ const status = result === 'ok' ? C.green : C.yellow;
544
+ console.log(` ${C.green}Status:${C.reset} ${status}${result}${C.reset}`);
545
+ } else {
546
+ // Generic object output
547
+ console.log(` ${C.green}Result:${C.reset}`);
548
+ console.log(JSON.stringify(result, null, 2).split('\n').map(l => ` ${C.dim}${l}${C.reset}`).join('\n'));
549
+ }
550
+ } else if (Array.isArray(result)) {
551
+ console.log(` ${C.green}Count:${C.reset} ${result.length}`);
552
+ if (result.length === 0) {
553
+ console.log(` ${C.dim}(empty array)${C.reset}`);
554
+ } else {
555
+ // Show first few items
556
+ const preview = result.slice(0, 3);
557
+ preview.forEach((item, i) => {
558
+ if (typeof item === 'object') {
559
+ const id = item.pubkey || item.address || item.signature || item.id || `Item ${i + 1}`;
560
+ console.log(` ${C.dim}[${i}]${C.reset} ${shortAddress(id)}`);
561
+ } else {
562
+ console.log(` ${C.dim}[${i}]${C.reset} ${item}`);
563
+ }
564
+ });
565
+ if (result.length > 3) {
566
+ console.log(` ${C.dim}... and ${result.length - 3} more${C.reset}`);
567
+ }
568
+ }
569
+ } else {
570
+ console.log(` ${C.green}Result:${C.reset} ${result}`);
571
+ }
572
+ console.log();
573
+ console.log(` ${C.dim}SDK: @jellylegsai/aether-sdk v${CLI_VERSION}${C.reset}`);
574
+ }
575
+
576
+ return result;
577
+ }
578
+
579
+ // ============================================================================
580
+ // Interactive Mode
581
+ // ============================================================================
582
+
583
+ async function interactiveMode() {
584
+ const rl = readline.createInterface({
585
+ input: process.stdin,
586
+ output: process.stdout,
587
+ });
588
+
589
+ console.log(`\n${C.bright}${C.cyan}══ Aether SDK Interactive Mode ══${C.reset}\n`);
590
+ console.log(`${C.dim}Type a method name or 'help' for usage, 'exit' to quit.${C.reset}\n`);
591
+
592
+ const ask = () => {
593
+ return new Promise((resolve) => {
594
+ rl.question(`${C.cyan}sdk>${C.reset} `, (answer) => {
595
+ resolve(answer.trim());
596
+ });
597
+ });
598
+ };
599
+
600
+ while (true) {
601
+ const input = await ask();
602
+
603
+ if (input === 'exit' || input === 'quit') {
604
+ console.log(`${C.dim}Goodbye!${C.reset}\n`);
605
+ rl.close();
606
+ break;
607
+ }
608
+
609
+ if (input === 'help' || input === '?') {
610
+ showMethodList();
611
+ continue;
612
+ }
613
+
614
+ if (input === 'list' || input === 'ls') {
615
+ showMethodList();
616
+ continue;
617
+ }
618
+
619
+ if (!input) continue;
620
+
621
+ // Parse input as command
622
+ const parts = input.split(/\s+/);
623
+ const method = parts[0];
624
+ const args = parts.slice(1);
625
+
626
+ if (!SDK_METHODS[method]) {
627
+ console.log(`${C.red}✗ Unknown method: ${method}${C.reset}`);
628
+ console.log(`${C.dim}Use 'list' to see available methods${C.reset}\n`);
629
+ continue;
630
+ }
631
+
632
+ // Execute the method
633
+ try {
634
+ const client = createClient(getDefaultRpc());
635
+ let result;
636
+
637
+ switch (method) {
638
+ case 'getSlot':
639
+ result = await client.getSlot();
640
+ break;
641
+ case 'getBlockHeight':
642
+ result = await client.getBlockHeight();
643
+ break;
644
+ case 'getEpochInfo':
645
+ result = await client.getEpochInfo();
646
+ break;
647
+ case 'getHealth':
648
+ result = await client.getHealth();
649
+ break;
650
+ case 'getVersion':
651
+ result = await client.getVersion();
652
+ break;
653
+ case 'getTPS':
654
+ result = await client.getTPS();
655
+ break;
656
+ case 'getSupply':
657
+ result = await client.getSupply();
658
+ break;
659
+ case 'getFees':
660
+ result = await client.getFees();
661
+ break;
662
+ case 'getRecentBlockhash':
663
+ result = await client.getRecentBlockhash();
664
+ break;
665
+ case 'getSlotProduction':
666
+ result = await client.getSlotProduction();
667
+ break;
668
+ case 'getClusterPeers':
669
+ result = await client.getClusterPeers();
670
+ break;
671
+ case 'getValidators':
672
+ result = await client.getValidators();
673
+ break;
674
+ case 'getAccountInfo':
675
+ result = await client.getAccountInfo(args[0]);
676
+ break;
677
+ case 'getBalance':
678
+ result = await client.getBalance(args[0]);
679
+ break;
680
+ case 'getStakePositions':
681
+ result = await client.getStakePositions(args[0]);
682
+ break;
683
+ case 'getRewards':
684
+ result = await client.getRewards(args[0]);
685
+ break;
686
+ case 'getStakeAccounts':
687
+ result = await client.getStakeAccounts(args[0]);
688
+ break;
689
+ case 'getTokenAccounts':
690
+ result = await client.getTokenAccounts(args[0]);
691
+ break;
692
+ case 'getNFTHoldings':
693
+ result = await client.getNFTHoldings(args[0]);
694
+ break;
695
+ case 'getNFTsByCreator':
696
+ result = await client.getNFTsByCreator(args[0]);
697
+ break;
698
+ case 'getTransaction':
699
+ result = await client.getTransaction(args[0]);
700
+ break;
701
+ case 'getRecentTransactions':
702
+ result = await client.getRecentTransactions(args[0], parseInt(args[1]) || 20);
703
+ break;
704
+ case 'getTransactionHistory':
705
+ result = await client.getTransactionHistory(args[0], parseInt(args[1]) || 20);
706
+ break;
707
+ case 'getNFT':
708
+ result = await client.getNFT(args[0]);
709
+ break;
710
+ case 'ping':
711
+ result = await aether.ping(args[0] || getDefaultRpc());
712
+ break;
713
+ default:
714
+ throw new Error(`Method ${method} not implemented`);
715
+ }
716
+
717
+ // Show result
718
+ if (typeof result === 'object') {
719
+ if (method === 'getBalance') {
720
+ console.log(` ${C.green}Balance:${C.reset} ${formatNumber(result)} lamports (${formatAether(result)})`);
721
+ } else if (method === 'getSlot' || method === 'getBlockHeight') {
722
+ console.log(` ${C.green}Result:${C.reset} ${formatNumber(result)}`);
723
+ } else if (method === 'getTPS') {
724
+ console.log(` ${C.green}TPS:${C.reset} ${result}`);
725
+ } else if (method === 'getHealth') {
726
+ const status = result === 'ok' ? `${C.green}OK${C.reset}` : `${C.yellow}${result}${C.reset}`;
727
+ console.log(` ${C.green}Status:${C.reset} ${status}`);
728
+ } else if (Array.isArray(result)) {
729
+ console.log(` ${C.green}Count:${C.reset} ${result.length}`);
730
+ result.slice(0, 5).forEach((item, i) => {
731
+ const id = item.pubkey || item.address || item.signature || item.id || `Item ${i + 1}`;
732
+ console.log(` ${C.dim}[${i}]${C.reset} ${shortAddress(id)}`);
733
+ });
734
+ if (result.length > 5) {
735
+ console.log(` ${C.dim}... and ${result.length - 5} more${C.reset}`);
736
+ }
737
+ } else {
738
+ console.log(` ${C.green}Result:${C.reset}`);
739
+ console.log(JSON.stringify(result, null, 2).split('\n').map(l => ` ${C.dim}${l}${C.reset}`).join('\n'));
740
+ }
741
+ } else {
742
+ console.log(` ${C.green}Result:${C.reset} ${result}`);
743
+ }
744
+ console.log();
745
+
746
+ } catch (err) {
747
+ console.log(`${C.red}✗ Error: ${err.message}${C.reset}\n`);
748
+ }
749
+ }
750
+ }
751
+
752
+ // ============================================================================
753
+ // Main Entry Point
754
+ // ============================================================================
755
+
756
+ async function sdkCommand() {
757
+ const opts = parseArgs();
758
+
759
+ if (opts.help) {
760
+ showHelp();
761
+ return;
762
+ }
763
+
764
+ if (opts.list) {
765
+ showMethodList();
766
+ return;
767
+ }
768
+
769
+ if (!opts.method) {
770
+ // Interactive mode if no method specified
771
+ await interactiveMode();
772
+ return;
773
+ }
774
+
775
+ try {
776
+ await executeMethod(opts);
777
+ } catch (err) {
778
+ console.error(`\n${C.red}✗ SDK command failed:${C.reset} ${err.message}\n`);
779
+ console.error(`${C.dim}Run ${C.cyan}aether sdk --list${C.reset}${C.dim} to see available methods${C.reset}\n`);
780
+ process.exit(1);
781
+ }
782
+ }
783
+
784
+ module.exports = { sdkCommand };
785
+
786
+ if (require.main === module) {
787
+ sdkCommand().catch(err => {
788
+ console.error(`${C.red}✗ SDK command failed:${C.reset} ${err.message}`);
789
+ process.exit(1);
790
+ });
791
+ }