@darksol/terminal 0.1.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.
@@ -0,0 +1,60 @@
1
+ import figlet from 'figlet';
2
+ import gradient from 'gradient-string';
3
+ import chalk from 'chalk';
4
+ import { theme } from './theme.js';
5
+
6
+ const darksol_gradient = gradient(['#B8860B', '#FFD700', '#FFF8DC', '#FFD700', '#B8860B']);
7
+
8
+ export function showBanner(opts = {}) {
9
+ const banner = figlet.textSync('DARKSOL', {
10
+ font: 'ANSI Shadow',
11
+ horizontalLayout: 'fitted',
12
+ });
13
+
14
+ console.log('');
15
+ console.log(darksol_gradient(banner));
16
+ console.log('');
17
+ console.log(
18
+ theme.dim(' ╔══════════════════════════════════════════════════════════╗')
19
+ );
20
+ console.log(
21
+ theme.dim(' ║ ') +
22
+ theme.gold.bold(' DARKSOL TERMINAL') +
23
+ theme.dim(' — ') +
24
+ theme.subtle('Ghost in the machine with teeth') +
25
+ theme.dim(' ║')
26
+ );
27
+ console.log(
28
+ theme.dim(' ║ ') +
29
+ theme.subtle(' v0.1.0') +
30
+ theme.dim(' ') +
31
+ theme.gold('🌑') +
32
+ theme.dim(' ║')
33
+ );
34
+ console.log(
35
+ theme.dim(' ╚══════════════════════════════════════════════════════════╝')
36
+ );
37
+ console.log('');
38
+
39
+ if (opts.tagline !== false) {
40
+ console.log(theme.subtle(' All services. One terminal. Zero trust required.'));
41
+ console.log('');
42
+ }
43
+ }
44
+
45
+ export function showMiniBanner() {
46
+ console.log('');
47
+ console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.1.0'));
48
+ console.log(theme.dim(' ─────────────────────────────'));
49
+ console.log('');
50
+ }
51
+
52
+ export function showSection(title) {
53
+ console.log('');
54
+ console.log(theme.gold(' ◆ ') + theme.header(title));
55
+ console.log(theme.dim(' ' + '─'.repeat(50)));
56
+ }
57
+
58
+ export function showDivider() {
59
+ console.log(theme.dim(' ' + '─'.repeat(50)));
60
+ }
@@ -0,0 +1,126 @@
1
+ import chalk from 'chalk';
2
+ import boxen from 'boxen';
3
+ import Table from 'cli-table3';
4
+ import ora from 'ora';
5
+ import { theme } from './theme.js';
6
+
7
+ // Branded spinner
8
+ export function spinner(text) {
9
+ return ora({
10
+ text: theme.dim(text),
11
+ spinner: 'dots',
12
+ color: 'yellow',
13
+ });
14
+ }
15
+
16
+ // Info card
17
+ export function card(title, content, opts = {}) {
18
+ const box = boxen(content, {
19
+ title: theme.gold.bold(` ${title} `),
20
+ titleAlignment: 'left',
21
+ padding: 1,
22
+ margin: { top: 0, bottom: 0, left: 2, right: 0 },
23
+ borderStyle: 'round',
24
+ borderColor: '#FFD700',
25
+ ...opts,
26
+ });
27
+ console.log(box);
28
+ }
29
+
30
+ // Key-value display
31
+ export function kvDisplay(pairs, opts = {}) {
32
+ const maxKey = Math.max(...pairs.map(([k]) => k.length));
33
+ const lines = pairs.map(([key, value]) => {
34
+ const paddedKey = key.padEnd(maxKey);
35
+ return ` ${theme.label(paddedKey)} ${theme.value(value)}`;
36
+ });
37
+ if (opts.title) {
38
+ console.log('');
39
+ console.log(theme.gold(' ◆ ') + theme.header(opts.title));
40
+ console.log(theme.dim(' ' + '─'.repeat(50)));
41
+ }
42
+ lines.forEach(l => console.log(l));
43
+ if (opts.footer) {
44
+ console.log(theme.dim(' ' + '─'.repeat(50)));
45
+ console.log(theme.subtle(` ${opts.footer}`));
46
+ }
47
+ }
48
+
49
+ // Styled table
50
+ export function table(headers, rows, opts = {}) {
51
+ const t = new Table({
52
+ head: headers.map(h => theme.gold.bold(h)),
53
+ chars: {
54
+ 'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
55
+ 'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
56
+ 'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
57
+ 'right': '│', 'right-mid': '┤', 'middle': '│'
58
+ },
59
+ style: {
60
+ head: [],
61
+ border: ['grey'],
62
+ ...opts.style,
63
+ },
64
+ ...(opts.colWidths ? { colWidths: opts.colWidths } : {}),
65
+ });
66
+
67
+ rows.forEach(row => t.push(row));
68
+ console.log(t.toString());
69
+ }
70
+
71
+ // Price formatting with color
72
+ export function formatPrice(price, opts = {}) {
73
+ const num = parseFloat(price);
74
+ if (isNaN(num)) return theme.dim('N/A');
75
+
76
+ const formatted = opts.decimals !== undefined
77
+ ? num.toFixed(opts.decimals)
78
+ : num < 0.01 ? num.toPrecision(4) : num.toFixed(2);
79
+
80
+ return `$${formatted}`;
81
+ }
82
+
83
+ export function formatChange(change) {
84
+ const num = parseFloat(change);
85
+ if (isNaN(num)) return theme.dim('N/A');
86
+ const sign = num >= 0 ? '+' : '';
87
+ const color = num > 0 ? theme.price.up : num < 0 ? theme.price.down : theme.price.neutral;
88
+ return color(`${sign}${num.toFixed(2)}%`);
89
+ }
90
+
91
+ export function formatAddress(address, chars = 6) {
92
+ if (!address) return theme.dim('N/A');
93
+ return `${address.slice(0, chars)}...${address.slice(-4)}`;
94
+ }
95
+
96
+ export function formatETH(wei, decimals = 6) {
97
+ const eth = parseFloat(wei) / 1e18;
98
+ return `${eth.toFixed(decimals)} ETH`;
99
+ }
100
+
101
+ export function formatUSDC(raw, decimals = 2) {
102
+ const usdc = parseFloat(raw) / 1e6;
103
+ return `$${usdc.toFixed(decimals)} USDC`;
104
+ }
105
+
106
+ // Success/error messages
107
+ export function success(msg) {
108
+ console.log(theme.success(' ✓ ') + msg);
109
+ }
110
+
111
+ export function error(msg) {
112
+ console.log(theme.error(' ✗ ') + msg);
113
+ }
114
+
115
+ export function warn(msg) {
116
+ console.log(theme.warning(' ⚠ ') + msg);
117
+ }
118
+
119
+ export function info(msg) {
120
+ console.log(theme.info(' ℹ ') + msg);
121
+ }
122
+
123
+ // Confirmation prompt formatting
124
+ export function confirmLine(label, value) {
125
+ return `${theme.label(label.padEnd(16))} ${theme.value(value)}`;
126
+ }
@@ -0,0 +1,46 @@
1
+ import chalk from 'chalk';
2
+
3
+ // DARKSOL Terminal Theme — gold/dark palette
4
+ export const theme = {
5
+ // Primary colors
6
+ gold: chalk.hex('#FFD700'),
7
+ darkGold: chalk.hex('#B8860B'),
8
+ dim: chalk.hex('#666666'),
9
+ bright: chalk.hex('#FFFFFF'),
10
+ dark: chalk.hex('#1a1a2e'),
11
+ accent: chalk.hex('#e94560'),
12
+ success: chalk.hex('#00ff88'),
13
+ warning: chalk.hex('#ffaa00'),
14
+ error: chalk.hex('#ff4444'),
15
+ info: chalk.hex('#4488ff'),
16
+ muted: chalk.hex('#555555'),
17
+
18
+ // Semantic
19
+ price: {
20
+ up: chalk.hex('#00ff88'),
21
+ down: chalk.hex('#ff4444'),
22
+ neutral: chalk.hex('#888888'),
23
+ },
24
+
25
+ // Box styles
26
+ border: chalk.hex('#FFD700'),
27
+
28
+ // Format helpers
29
+ header: (text) => chalk.hex('#FFD700').bold(text),
30
+ label: (text) => chalk.hex('#B8860B')(text),
31
+ value: (text) => chalk.white.bold(text),
32
+ subtle: (text) => chalk.hex('#666666')(text),
33
+ link: (text) => chalk.hex('#4488ff').underline(text),
34
+ badge: (text) => chalk.bgHex('#FFD700').hex('#000000').bold(` ${text} `),
35
+ errorBadge: (text) => chalk.bgHex('#ff4444').white.bold(` ${text} `),
36
+ successBadge: (text) => chalk.bgHex('#00ff88').hex('#000000').bold(` ${text} `),
37
+ };
38
+
39
+ // Box drawing chars for custom borders
40
+ export const box = {
41
+ tl: '╔', tr: '╗', bl: '╚', br: '╝',
42
+ h: '═', v: '║',
43
+ t: '╦', b: '╩', l: '╠', r: '╣', x: '╬',
44
+ };
45
+
46
+ export default theme;
@@ -0,0 +1,127 @@
1
+ import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { homedir } from 'os';
5
+
6
+ const WALLET_DIR = join(homedir(), '.darksol', 'wallets');
7
+ const ALGORITHM = 'aes-256-gcm';
8
+ const SCRYPT_N = 2 ** 18;
9
+ const SCRYPT_r = 8;
10
+ const SCRYPT_p = 1;
11
+ const KEY_LENGTH = 32;
12
+ const SALT_LENGTH = 32;
13
+ const IV_LENGTH = 16;
14
+ const SCRYPT_MAXMEM = 512 * 1024 * 1024;
15
+
16
+ function ensureDir() {
17
+ if (!existsSync(WALLET_DIR)) {
18
+ mkdirSync(WALLET_DIR, { recursive: true });
19
+ }
20
+ }
21
+
22
+ // Encrypt a private key with a password
23
+ export function encryptKey(privateKey, password) {
24
+ const salt = randomBytes(SALT_LENGTH);
25
+ const iv = randomBytes(IV_LENGTH);
26
+ const key = scryptSync(password, salt, KEY_LENGTH, {
27
+ N: SCRYPT_N,
28
+ r: SCRYPT_r,
29
+ p: SCRYPT_p,
30
+ maxmem: SCRYPT_MAXMEM,
31
+ });
32
+
33
+ const cipher = createCipheriv(ALGORITHM, key, iv);
34
+ let encrypted = cipher.update(privateKey, 'utf8', 'hex');
35
+ encrypted += cipher.final('hex');
36
+ const tag = cipher.getAuthTag();
37
+
38
+ return {
39
+ version: 1,
40
+ algorithm: ALGORITHM,
41
+ kdf: 'scrypt',
42
+ kdfParams: { N: SCRYPT_N, r: SCRYPT_r, p: SCRYPT_p },
43
+ salt: salt.toString('hex'),
44
+ iv: iv.toString('hex'),
45
+ tag: tag.toString('hex'),
46
+ ciphertext: encrypted,
47
+ };
48
+ }
49
+
50
+ // Decrypt a private key with a password
51
+ export function decryptKey(keystore, password) {
52
+ const salt = Buffer.from(keystore.salt, 'hex');
53
+ const iv = Buffer.from(keystore.iv, 'hex');
54
+ const tag = Buffer.from(keystore.tag, 'hex');
55
+
56
+ const key = scryptSync(password, salt, KEY_LENGTH, {
57
+ N: keystore.kdfParams.N,
58
+ r: keystore.kdfParams.r,
59
+ p: keystore.kdfParams.p,
60
+ maxmem: SCRYPT_MAXMEM,
61
+ });
62
+
63
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
64
+ decipher.setAuthTag(tag);
65
+ let decrypted = decipher.update(keystore.ciphertext, 'hex', 'utf8');
66
+ decrypted += decipher.final('utf8');
67
+
68
+ return decrypted;
69
+ }
70
+
71
+ // Save wallet to disk
72
+ export function saveWallet(name, address, keystoreData, metadata = {}) {
73
+ ensureDir();
74
+ const walletFile = join(WALLET_DIR, `${name}.json`);
75
+
76
+ const wallet = {
77
+ name,
78
+ address,
79
+ keystore: keystoreData,
80
+ chain: metadata.chain || 'base',
81
+ createdAt: new Date().toISOString(),
82
+ ...metadata,
83
+ };
84
+
85
+ writeFileSync(walletFile, JSON.stringify(wallet, null, 2));
86
+ return walletFile;
87
+ }
88
+
89
+ // Load wallet from disk
90
+ export function loadWallet(name) {
91
+ const walletFile = join(WALLET_DIR, `${name}.json`);
92
+ if (!existsSync(walletFile)) {
93
+ throw new Error(`Wallet "${name}" not found`);
94
+ }
95
+ return JSON.parse(readFileSync(walletFile, 'utf8'));
96
+ }
97
+
98
+ // List all wallets
99
+ export function listWallets() {
100
+ ensureDir();
101
+ const files = readdirSync(WALLET_DIR).filter(f => f.endsWith('.json'));
102
+ return files.map(f => {
103
+ const data = JSON.parse(readFileSync(join(WALLET_DIR, f), 'utf8'));
104
+ return {
105
+ name: data.name,
106
+ address: data.address,
107
+ chain: data.chain,
108
+ createdAt: data.createdAt,
109
+ };
110
+ });
111
+ }
112
+
113
+ // Delete wallet
114
+ export function deleteWallet(name) {
115
+ const walletFile = join(WALLET_DIR, `${name}.json`);
116
+ if (!existsSync(walletFile)) {
117
+ throw new Error(`Wallet "${name}" not found`);
118
+ }
119
+ unlinkSync(walletFile);
120
+ }
121
+
122
+ // Check if wallet exists
123
+ export function walletExists(name) {
124
+ return existsSync(join(WALLET_DIR, `${name}.json`));
125
+ }
126
+
127
+ export { WALLET_DIR };
@@ -0,0 +1,287 @@
1
+ import { ethers } from 'ethers';
2
+ import { encryptKey, decryptKey, saveWallet, loadWallet, listWallets, walletExists, WALLET_DIR } from './keystore.js';
3
+ import { getConfig, setConfig, getRPC } from '../config/store.js';
4
+ import { theme } from '../ui/theme.js';
5
+ import { card, kvDisplay, success, error, warn, spinner, table } from '../ui/components.js';
6
+ import { showSection } from '../ui/banner.js';
7
+ import inquirer from 'inquirer';
8
+
9
+ // Create a new wallet
10
+ export async function createWallet(name, opts = {}) {
11
+ if (!name) {
12
+ const { walletName } = await inquirer.prompt([{
13
+ type: 'input',
14
+ name: 'walletName',
15
+ message: theme.gold('Wallet name:'),
16
+ validate: (v) => v.length > 0 || 'Name required',
17
+ }]);
18
+ name = walletName;
19
+ }
20
+
21
+ if (walletExists(name)) {
22
+ error(`Wallet "${name}" already exists`);
23
+ return;
24
+ }
25
+
26
+ const { password } = await inquirer.prompt([{
27
+ type: 'password',
28
+ name: 'password',
29
+ message: theme.gold('Encryption password:'),
30
+ mask: '●',
31
+ validate: (v) => v.length >= 8 || 'Minimum 8 characters',
32
+ }]);
33
+
34
+ const { confirmPassword } = await inquirer.prompt([{
35
+ type: 'password',
36
+ name: 'confirmPassword',
37
+ message: theme.gold('Confirm password:'),
38
+ mask: '●',
39
+ }]);
40
+
41
+ if (password !== confirmPassword) {
42
+ error('Passwords do not match');
43
+ return;
44
+ }
45
+
46
+ const spin = spinner('Generating wallet...').start();
47
+
48
+ const wallet = ethers.Wallet.createRandom();
49
+ const keystoreData = encryptKey(wallet.privateKey, password);
50
+ const chain = opts.chain || getConfig('chain') || 'base';
51
+ saveWallet(name, wallet.address, keystoreData, { chain });
52
+
53
+ // Set as active if first wallet
54
+ const wallets = listWallets();
55
+ if (wallets.length === 1) {
56
+ setConfig('activeWallet', name);
57
+ }
58
+
59
+ spin.succeed(theme.success('Wallet created'));
60
+
61
+ console.log('');
62
+ showSection('NEW WALLET');
63
+ kvDisplay([
64
+ ['Name', name],
65
+ ['Address', wallet.address],
66
+ ['Chain', chain],
67
+ ['Stored', WALLET_DIR],
68
+ ]);
69
+ console.log('');
70
+ warn('Back up your password — there is NO recovery if lost.');
71
+ warn('Private key is AES-256-GCM encrypted with scrypt KDF.');
72
+ console.log('');
73
+ }
74
+
75
+ // Import wallet from private key
76
+ export async function importWallet(name, opts = {}) {
77
+ if (!name) {
78
+ const { walletName } = await inquirer.prompt([{
79
+ type: 'input',
80
+ name: 'walletName',
81
+ message: theme.gold('Wallet name:'),
82
+ validate: (v) => v.length > 0 || 'Name required',
83
+ }]);
84
+ name = walletName;
85
+ }
86
+
87
+ if (walletExists(name)) {
88
+ error(`Wallet "${name}" already exists`);
89
+ return;
90
+ }
91
+
92
+ const { privateKey } = await inquirer.prompt([{
93
+ type: 'password',
94
+ name: 'privateKey',
95
+ message: theme.gold('Private key (0x...):'),
96
+ mask: '●',
97
+ validate: (v) => {
98
+ try {
99
+ new ethers.Wallet(v);
100
+ return true;
101
+ } catch {
102
+ return 'Invalid private key';
103
+ }
104
+ },
105
+ }]);
106
+
107
+ const { password } = await inquirer.prompt([{
108
+ type: 'password',
109
+ name: 'password',
110
+ message: theme.gold('Encryption password:'),
111
+ mask: '●',
112
+ validate: (v) => v.length >= 8 || 'Minimum 8 characters',
113
+ }]);
114
+
115
+ const spin = spinner('Encrypting and storing...').start();
116
+
117
+ const wallet = new ethers.Wallet(privateKey);
118
+ const keystoreData = encryptKey(privateKey, password);
119
+ const chain = opts.chain || getConfig('chain') || 'base';
120
+ saveWallet(name, wallet.address, keystoreData, { chain });
121
+
122
+ spin.succeed(theme.success('Wallet imported'));
123
+
124
+ console.log('');
125
+ showSection('IMPORTED WALLET');
126
+ kvDisplay([
127
+ ['Name', name],
128
+ ['Address', wallet.address],
129
+ ['Chain', chain],
130
+ ]);
131
+ console.log('');
132
+ success('Private key encrypted and stored securely.');
133
+ }
134
+
135
+ // List wallets
136
+ export async function showWallets() {
137
+ const wallets = listWallets();
138
+ const active = getConfig('activeWallet');
139
+
140
+ if (wallets.length === 0) {
141
+ warn('No wallets found. Create one with: darksol wallet create');
142
+ return;
143
+ }
144
+
145
+ showSection('WALLETS');
146
+
147
+ const rows = wallets.map(w => [
148
+ w.name === active ? theme.gold('► ' + w.name) : ' ' + w.name,
149
+ w.address,
150
+ w.chain,
151
+ new Date(w.createdAt).toLocaleDateString(),
152
+ ]);
153
+
154
+ table(['Name', 'Address', 'Chain', 'Created'], rows);
155
+ }
156
+
157
+ // Get a signer (unlocked wallet) for transactions
158
+ export async function getSigner(walletName, password) {
159
+ const name = walletName || getConfig('activeWallet');
160
+ if (!name) {
161
+ throw new Error('No active wallet. Set one with: darksol wallet use <name>');
162
+ }
163
+
164
+ const walletData = loadWallet(name);
165
+ const privateKey = decryptKey(walletData.keystore, password);
166
+ const rpcUrl = getRPC(walletData.chain);
167
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
168
+ const signer = new ethers.Wallet(privateKey, provider);
169
+
170
+ return { signer, provider, address: walletData.address, chain: walletData.chain };
171
+ }
172
+
173
+ // Get wallet balance
174
+ export async function getBalance(walletName) {
175
+ const name = walletName || getConfig('activeWallet');
176
+ if (!name) {
177
+ error('No active wallet. Set one with: darksol wallet use <name>');
178
+ return;
179
+ }
180
+
181
+ const walletData = loadWallet(name);
182
+ const rpcUrl = getRPC(walletData.chain);
183
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
184
+
185
+ const spin = spinner('Fetching balance...').start();
186
+
187
+ try {
188
+ const balance = await provider.getBalance(walletData.address);
189
+ const ethBalance = ethers.formatEther(balance);
190
+
191
+ // Also check USDC balance
192
+ const usdcAddresses = {
193
+ base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
194
+ ethereum: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
195
+ polygon: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
196
+ arbitrum: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
197
+ optimism: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
198
+ };
199
+
200
+ let usdcBalance = '0.00';
201
+ const usdcAddr = usdcAddresses[walletData.chain];
202
+ if (usdcAddr) {
203
+ const usdc = new ethers.Contract(usdcAddr, [
204
+ 'function balanceOf(address) view returns (uint256)',
205
+ 'function decimals() view returns (uint8)',
206
+ ], provider);
207
+ try {
208
+ const raw = await usdc.balanceOf(walletData.address);
209
+ const decimals = await usdc.decimals();
210
+ usdcBalance = ethers.formatUnits(raw, decimals);
211
+ } catch { }
212
+ }
213
+
214
+ spin.succeed('Balance fetched');
215
+
216
+ console.log('');
217
+ showSection(`BALANCE — ${name}`);
218
+ kvDisplay([
219
+ ['Address', walletData.address],
220
+ ['Chain', walletData.chain],
221
+ ['Native', `${parseFloat(ethBalance).toFixed(6)} ETH`],
222
+ ['USDC', `$${parseFloat(usdcBalance).toFixed(2)}`],
223
+ ]);
224
+ console.log('');
225
+ } catch (err) {
226
+ spin.fail('Failed to fetch balance');
227
+ error(err.message);
228
+ }
229
+ }
230
+
231
+ // Set active wallet
232
+ export function useWallet(name) {
233
+ if (!walletExists(name)) {
234
+ error(`Wallet "${name}" not found`);
235
+ return;
236
+ }
237
+ setConfig('activeWallet', name);
238
+ success(`Active wallet set to "${name}"`);
239
+ }
240
+
241
+ // Export wallet (show address only, never PK without password)
242
+ export async function exportWallet(name) {
243
+ if (!name) {
244
+ name = getConfig('activeWallet');
245
+ }
246
+ if (!name || !walletExists(name)) {
247
+ error('Wallet not found');
248
+ return;
249
+ }
250
+
251
+ const walletData = loadWallet(name);
252
+
253
+ showSection(`WALLET — ${name}`);
254
+ kvDisplay([
255
+ ['Address', walletData.address],
256
+ ['Chain', walletData.chain],
257
+ ['Created', walletData.createdAt],
258
+ ['Keystore', 'AES-256-GCM + scrypt'],
259
+ ]);
260
+
261
+ const { confirm } = await inquirer.prompt([{
262
+ type: 'confirm',
263
+ name: 'confirm',
264
+ message: theme.accent('Export private key? (requires password)'),
265
+ default: false,
266
+ }]);
267
+
268
+ if (!confirm) return;
269
+
270
+ const { password } = await inquirer.prompt([{
271
+ type: 'password',
272
+ name: 'password',
273
+ message: theme.gold('Password:'),
274
+ mask: '●',
275
+ }]);
276
+
277
+ try {
278
+ const pk = decryptKey(walletData.keystore, password);
279
+ console.log('');
280
+ warn('PRIVATE KEY — DO NOT SHARE');
281
+ console.log(theme.accent(` ${pk}`));
282
+ console.log('');
283
+ warn('This key controls all funds. Keep it safe.');
284
+ } catch {
285
+ error('Wrong password');
286
+ }
287
+ }