minara 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,12 +3,14 @@ import { input, select, confirm } from '@inquirer/prompts';
3
3
  import chalk from 'chalk';
4
4
  import { swap, swapsSimulate } from '../api/crosschain.js';
5
5
  import { requireAuth } from '../config.js';
6
- import { success, info, spinner, formatOrderSide, assertApiOk, selectChain, wrapAction } from '../utils.js';
6
+ import { success, info, spinner, formatOrderSide, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
7
+ import { requireTouchId } from '../touchid.js';
8
+ import { printTxResult, printKV } from '../formatters.js';
7
9
  export const swapCommand = new Command('swap')
8
10
  .description('Swap tokens (cross-chain spot trading)')
9
11
  .option('-c, --chain <chain>', 'Blockchain (e.g. solana, base, ethereum)')
10
12
  .option('-s, --side <side>', 'buy or sell')
11
- .option('-t, --token <address>', 'Token contract address')
13
+ .option('-t, --token <address|ticker>', 'Token contract address or ticker symbol')
12
14
  .option('-a, --amount <amount>', 'USD amount (buy) or token amount (sell)')
13
15
  .option('-y, --yes', 'Skip confirmation')
14
16
  .option('--dry-run', 'Simulate without executing')
@@ -27,11 +29,12 @@ export const swapCommand = new Command('swap')
27
29
  ],
28
30
  });
29
31
  }
30
- // ── 3. Token address ─────────────────────────────────────────────────
31
- const tokenAddress = opts.token ?? await input({
32
- message: 'Token contract address:',
33
- validate: (v) => (v.length > 5 ? true : 'Please enter a valid address'),
32
+ // ── 3. Token ───────────────────────────────────────────────────────────
33
+ const tokenInput = opts.token ?? await input({
34
+ message: 'Token (contract address or ticker):',
35
+ validate: (v) => (v.length > 0 ? true : 'Please enter a token address or ticker'),
34
36
  });
37
+ const tokenInfo = await lookupToken(tokenInput);
35
38
  // ── 4. Amount ────────────────────────────────────────────────────────
36
39
  const amountLabel = side === 'buy' ? 'USD amount to spend' : 'Token amount to sell';
37
40
  const amount = opts.amount ?? await input({
@@ -46,7 +49,8 @@ export const swapCommand = new Command('swap')
46
49
  console.log(chalk.bold('Swap Summary:'));
47
50
  console.log(` Chain : ${chalk.cyan(chain)}`);
48
51
  console.log(` Action : ${formatOrderSide(side)}`);
49
- console.log(` Token : ${chalk.yellow(tokenAddress)}`);
52
+ console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
53
+ console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
50
54
  console.log(` Amount : ${chalk.bold(amount)} ${side === 'buy' ? 'USD' : '(token)'}`);
51
55
  console.log('');
52
56
  // ── 6. Dry run ───────────────────────────────────────────────────────
@@ -54,11 +58,21 @@ export const swapCommand = new Command('swap')
54
58
  info('Simulating swap (dry-run)…');
55
59
  const spin = spinner('Simulating…');
56
60
  const simRes = await swapsSimulate(creds.accessToken, [{
57
- chain, side, tokenAddress, buyUsdAmountOrSellTokenAmount: amount,
61
+ chain, side, tokenAddress: tokenInfo.address, buyUsdAmountOrSellTokenAmount: amount,
58
62
  }]);
59
63
  spin.stop();
60
64
  assertApiOk(simRes, 'Simulation failed');
61
- console.log(JSON.stringify(simRes.data, null, 2));
65
+ console.log('');
66
+ console.log(chalk.bold('Simulation Result:'));
67
+ if (Array.isArray(simRes.data)) {
68
+ for (const item of simRes.data) {
69
+ printKV(item);
70
+ console.log('');
71
+ }
72
+ }
73
+ else if (simRes.data) {
74
+ printKV(simRes.data);
75
+ }
62
76
  return;
63
77
  }
64
78
  // ── 7. Confirm ───────────────────────────────────────────────────────
@@ -72,14 +86,16 @@ export const swapCommand = new Command('swap')
72
86
  return;
73
87
  }
74
88
  }
75
- // ── 8. Execute ───────────────────────────────────────────────────────
89
+ // ── 8. Transaction confirmation & Touch ID ────────────────────────────
90
+ await requireTransactionConfirmation(`${side.toUpperCase()} swap · ${amount} ${side === 'buy' ? 'USD' : 'tokens'} · ${chain}`, tokenInfo);
91
+ await requireTouchId();
92
+ // ── 9. Execute ───────────────────────────────────────────────────────
76
93
  const spin = spinner('Executing swap…');
77
94
  const res = await swap(creds.accessToken, {
78
- chain, side, tokenAddress, buyUsdAmountOrSellTokenAmount: amount,
95
+ chain, side, tokenAddress: tokenInfo.address, buyUsdAmountOrSellTokenAmount: amount,
79
96
  });
80
97
  spin.stop();
81
98
  assertApiOk(res, 'Swap failed');
82
99
  success('Swap submitted!');
83
- if (res.data)
84
- console.log(JSON.stringify(res.data, null, 2));
100
+ printTxResult(res.data);
85
101
  }));
@@ -3,11 +3,13 @@ import { input, confirm } from '@inquirer/prompts';
3
3
  import chalk from 'chalk';
4
4
  import { transfer } from '../api/crosschain.js';
5
5
  import { requireAuth } from '../config.js';
6
- import { success, warn, spinner, assertApiOk, selectChain, wrapAction } from '../utils.js';
6
+ import { success, warn, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
7
+ import { requireTouchId } from '../touchid.js';
8
+ import { printTxResult } from '../formatters.js';
7
9
  export const transferCommand = new Command('transfer')
8
10
  .description('Transfer tokens to another address')
9
11
  .option('-c, --chain <chain>', 'Blockchain')
10
- .option('-t, --token <address>', 'Token contract address')
12
+ .option('-t, --token <address|ticker>', 'Token contract address or ticker symbol')
11
13
  .option('-a, --amount <amount>', 'Token amount to send')
12
14
  .option('--to <address>', 'Recipient address')
13
15
  .option('-y, --yes', 'Skip confirmation')
@@ -16,10 +18,11 @@ export const transferCommand = new Command('transfer')
16
18
  // ── 1. Chain ─────────────────────────────────────────────────────────
17
19
  const chain = opts.chain ?? await selectChain();
18
20
  // ── 2. Token ─────────────────────────────────────────────────────────
19
- const tokenAddress = opts.token ?? await input({
20
- message: 'Token contract address (native token = 0x0…0):',
21
- validate: (v) => (v.length > 0 ? true : 'Address is required'),
21
+ const tokenInput = opts.token ?? await input({
22
+ message: 'Token (address or ticker, native = 0x0…0):',
23
+ validate: (v) => (v.length > 0 ? true : 'Token is required'),
22
24
  });
25
+ const tokenInfo = await lookupToken(tokenInput);
23
26
  // ── 3. Amount ────────────────────────────────────────────────────────
24
27
  const amount = opts.amount ?? await input({
25
28
  message: 'Amount to send:',
@@ -33,11 +36,12 @@ export const transferCommand = new Command('transfer')
33
36
  message: 'Recipient address:',
34
37
  validate: (v) => (v.length > 5 ? true : 'Enter a valid address'),
35
38
  });
36
- // ── 5. Summary & confirm ─────────────────────────────────────────────
39
+ // ── 5. Summary ───────────────────────────────────────────────────────
37
40
  console.log('');
38
41
  console.log(chalk.bold.red('⚠ Transfer Summary:'));
39
42
  console.log(` Chain : ${chalk.cyan(chain)}`);
40
- console.log(` Token : ${chalk.yellow(tokenAddress)}`);
43
+ console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
44
+ console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
41
45
  console.log(` Amount : ${chalk.bold(amount)}`);
42
46
  console.log(` To : ${chalk.yellow(recipient)}`);
43
47
  console.log('');
@@ -49,12 +53,14 @@ export const transferCommand = new Command('transfer')
49
53
  return;
50
54
  }
51
55
  }
52
- // ── 6. Execute ───────────────────────────────────────────────────────
56
+ // ── 6. Transaction confirmation & Touch ID ────────────────────────────
57
+ await requireTransactionConfirmation(`Transfer ${amount} tokens → ${recipient} · ${chain}`, tokenInfo);
58
+ await requireTouchId();
59
+ // ── 7. Execute ───────────────────────────────────────────────────────
53
60
  const spin = spinner('Processing transfer…');
54
- const res = await transfer(creds.accessToken, { chain, tokenAddress, tokenAmount: amount, recipient });
61
+ const res = await transfer(creds.accessToken, { chain, tokenAddress: tokenInfo.address, tokenAmount: amount, recipient });
55
62
  spin.stop();
56
63
  assertApiOk(res, 'Transfer failed');
57
64
  success('Transfer submitted!');
58
- if (res.data)
59
- console.log(JSON.stringify(res.data, null, 2));
65
+ printTxResult(res.data);
60
66
  }));
@@ -3,11 +3,13 @@ import { input, confirm } from '@inquirer/prompts';
3
3
  import chalk from 'chalk';
4
4
  import { transfer, getAssets } from '../api/crosschain.js';
5
5
  import { requireAuth } from '../config.js';
6
- import { success, warn, spinner, assertApiOk, selectChain, wrapAction } from '../utils.js';
6
+ import { success, warn, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
7
+ import { requireTouchId } from '../touchid.js';
8
+ import { printTxResult } from '../formatters.js';
7
9
  export const withdrawCommand = new Command('withdraw')
8
10
  .description('Withdraw tokens from your Minara wallet to an external address')
9
11
  .option('-c, --chain <chain>', 'Blockchain network')
10
- .option('-t, --token <address>', 'Token contract address')
12
+ .option('-t, --token <address|ticker>', 'Token contract address or ticker symbol')
11
13
  .option('-a, --amount <amount>', 'Amount to withdraw')
12
14
  .option('--to <address>', 'Destination address')
13
15
  .option('-y, --yes', 'Skip confirmation')
@@ -39,10 +41,11 @@ export const withdrawCommand = new Command('withdraw')
39
41
  // ── 2. Chain ─────────────────────────────────────────────────────────
40
42
  const chain = opts.chain ?? await selectChain('Withdraw on which blockchain?');
41
43
  // ── 3. Token ─────────────────────────────────────────────────────────
42
- const tokenAddress = opts.token ?? await input({
43
- message: `Token contract address on ${chalk.cyan(chain)}:\n (native gas token = ${'0x' + '0'.repeat(40)})\n Address:`,
44
- validate: (v) => (v.length > 0 ? true : 'Token address is required'),
44
+ const tokenInput = opts.token ?? await input({
45
+ message: `Token on ${chalk.cyan(chain)} (address or ticker, native = ${'0x' + '0'.repeat(40)}):`,
46
+ validate: (v) => (v.length > 0 ? true : 'Token is required'),
45
47
  });
48
+ const tokenInfo = await lookupToken(tokenInput);
46
49
  // ── 4. Amount ────────────────────────────────────────────────────────
47
50
  const amount = opts.amount ?? await input({
48
51
  message: 'Amount to withdraw:',
@@ -56,11 +59,12 @@ export const withdrawCommand = new Command('withdraw')
56
59
  message: 'Destination address (your external wallet):',
57
60
  validate: (v) => (v.length > 5 ? true : 'Enter a valid address'),
58
61
  });
59
- // ── 6. Summary & double-confirm ──────────────────────────────────────
62
+ // ── 6. Summary ───────────────────────────────────────────────────────
60
63
  console.log('');
61
64
  console.log(chalk.bold.red('⚠ Withdrawal Summary'));
62
65
  console.log(` Chain : ${chalk.cyan(chain)}`);
63
- console.log(` Token : ${chalk.yellow(tokenAddress)}`);
66
+ console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
67
+ console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
64
68
  console.log(` Amount : ${chalk.bold(amount)}`);
65
69
  console.log(` Destination : ${chalk.yellow(recipient)}`);
66
70
  console.log('');
@@ -77,13 +81,15 @@ export const withdrawCommand = new Command('withdraw')
77
81
  return;
78
82
  }
79
83
  }
80
- // ── 7. Execute ───────────────────────────────────────────────────────
84
+ // ── 7. Transaction confirmation & Touch ID ────────────────────────────
85
+ await requireTransactionConfirmation(`Withdraw ${amount} tokens → ${recipient} · ${chain}`, tokenInfo);
86
+ await requireTouchId();
87
+ // ── 8. Execute ───────────────────────────────────────────────────────
81
88
  const spin = spinner('Processing withdrawal…');
82
- const res = await transfer(creds.accessToken, { chain, tokenAddress, tokenAmount: amount, recipient });
89
+ const res = await transfer(creds.accessToken, { chain, tokenAddress: tokenInfo.address, tokenAmount: amount, recipient });
83
90
  spin.stop();
84
91
  assertApiOk(res, 'Withdrawal failed');
85
92
  success('Withdrawal submitted!');
86
- if (res.data)
87
- console.log(JSON.stringify(res.data, null, 2));
93
+ printTxResult(res.data);
88
94
  console.log(chalk.dim('\nIt may take a few minutes for the transaction to be confirmed on-chain.'));
89
95
  }));
package/dist/config.d.ts CHANGED
@@ -5,6 +5,8 @@ export declare function clearCredentials(): void;
5
5
  export declare function requireAuth(): Credentials;
6
6
  export interface AppConfig {
7
7
  baseUrl: string;
8
+ touchId?: boolean;
9
+ confirmBeforeTransaction?: boolean;
8
10
  }
9
11
  export declare function loadConfig(): AppConfig;
10
12
  export declare function saveConfig(config: Partial<AppConfig>): void;
package/dist/config.js CHANGED
@@ -48,6 +48,7 @@ export function requireAuth() {
48
48
  }
49
49
  const DEFAULT_CONFIG = {
50
50
  baseUrl: 'https://api.minara.ai',
51
+ confirmBeforeTransaction: true,
51
52
  };
52
53
  export function loadConfig() {
53
54
  if (!existsSync(CONFIG_FILE))
@@ -0,0 +1,56 @@
1
+ /** Enable raw JSON output (all formatters fall back to JSON.stringify). */
2
+ export declare function setRawJson(enabled: boolean): void;
3
+ /** Check if raw JSON output mode is active. */
4
+ export declare function isRawJson(): boolean;
5
+ /** Convert camelCase / snake_case key to "Title Case" label. */
6
+ export declare function formatLabel(key: string): string;
7
+ /** Smart-format a single value for terminal display. */
8
+ export declare function formatValue(value: unknown, key?: string): string;
9
+ /**
10
+ * Print an object as aligned key-value pairs.
11
+ *
12
+ * ```
13
+ * Transaction Id : 0x1234…abcd
14
+ * Status : pending
15
+ * ```
16
+ */
17
+ export declare function printKV(data: Record<string, unknown> | object, indent?: number): void;
18
+ export interface ColumnDef {
19
+ /** Object key (supports nested: "a.b") */
20
+ key: string;
21
+ /** Column header label (defaults to formatLabel(key)) */
22
+ label?: string;
23
+ /** Custom formatter for cell value */
24
+ format?: (value: unknown, row: Record<string, unknown>) => string;
25
+ /** Max column width (content will be truncated) */
26
+ maxWidth?: number;
27
+ }
28
+ /**
29
+ * Print an array of objects as a CLI table.
30
+ *
31
+ * If `columns` is omitted, columns are auto-detected from the data.
32
+ */
33
+ export declare function printTable(data: Record<string, unknown>[] | object[], columns?: ColumnDef[]): void;
34
+ /**
35
+ * Pretty-print a transaction result (deposit, withdraw, swap, transfer, order, etc.)
36
+ * Falls back silently if data is empty/undefined.
37
+ */
38
+ export declare function printTxResult(data: unknown): void;
39
+ /** Spot wallet assets (WalletAsset[]) */
40
+ export declare const ASSET_COLUMNS: ColumnDef[];
41
+ /** Perps positions (PerpsPosition[]) */
42
+ export declare const POSITION_COLUMNS: ColumnDef[];
43
+ /** Limit orders (LimitOrderInfo[]) */
44
+ export declare const LIMIT_ORDER_COLUMNS: ColumnDef[];
45
+ /** Copy trades (CopyTradeInfo[]) */
46
+ export declare const COPY_TRADE_COLUMNS: ColumnDef[];
47
+ /** Trending / search tokens (TokenInfo[]) */
48
+ export declare const TOKEN_COLUMNS: ColumnDef[];
49
+ /**
50
+ * Pretty-print Fear & Greed Index — hides redundant `timestamp` and `price`.
51
+ */
52
+ export declare function printFearGreed(data: Record<string, unknown>): void;
53
+ /**
54
+ * Pretty-print BTC/crypto metrics — flatten currentQuote, skip ohlcvQuotes.
55
+ */
56
+ export declare function printCryptoMetrics(data: Record<string, unknown>): void;