cryptoseed 1.0.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.
package/src/bin/cli.js ADDED
@@ -0,0 +1,1785 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CryptoSeedRecovery - CLI Terminal Interativo Multilíngue
5
+ *
6
+ * Desenvolvido sob o ecossistema b2 wallet (better2better, diegooris).
7
+ * Interface interativa profissional para restauração de sementes perdidas ou embaralhadas.
8
+ * Suporte a idiomas: Português, English, Español, 简体中文, 日本語.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const chalk = require('chalk');
14
+ const cliProgress = require('cli-progress');
15
+ const ethers = require('ethers');
16
+
17
+ const inquirerModule = require('inquirer');
18
+ const inquirer = inquirerModule.default || inquirerModule;
19
+
20
+ const { bip39, electrumPoet } = require('../lib/wordlists');
21
+ const { validateElectrumMnemonic } = require('../lib/electrum-legacy');
22
+ const { deriveAddress } = require('../lib/address-deriver');
23
+ const { getSuggestions, getPrefixSuggestions } = require('../lib/typo');
24
+ const {
25
+ searchMode1,
26
+ searchMode2And3,
27
+ searchMode4,
28
+ getMode1Prefixes,
29
+ getMode23PartialStates,
30
+ getMode4PartialStates
31
+ } = require('../lib/search-engine');
32
+
33
+ const { getBalance, EVM_NETWORKS, NON_EVM_DEFAULTS } = require('../lib/balance-checker');
34
+
35
+ const locales = require('../lib/cli-locales');
36
+
37
+ let currentLocale = 'pt'; // Idioma de interface padrão
38
+
39
+ // Helper de tradução dinâmico com suporte a formatação {0}, {1}, etc.
40
+ function t(key, ...args) {
41
+ const locale = locales[currentLocale] || locales['en'] || locales['pt'];
42
+ let str = locale[key] || locales['en'][key] || locales['pt'][key] || '';
43
+ if (args.length > 0) {
44
+ args.forEach((val, i) => {
45
+ str = str.replace(new RegExp(`\\{${i}\\}`, 'g'), val);
46
+ });
47
+ }
48
+ return str;
49
+ }
50
+
51
+ // Detecção inteligente de idioma do sistema operacional do usuário
52
+ function getSystemLocale() {
53
+ const envLang = process.env.LANG || process.env.LANGUAGE || '';
54
+ if (envLang.startsWith('en')) return 'en';
55
+ if (envLang.startsWith('es')) return 'es';
56
+ if (envLang.startsWith('zh')) return 'zh';
57
+ if (envLang.startsWith('ja')) return 'ja';
58
+ return 'pt'; // Padrão
59
+ }
60
+
61
+ // Helper para links clicáveis no terminal (Hyperlinks ANSI)
62
+ function link(text, url) {
63
+ return `\u001b]8;;${url}\u0007${text}\u001b]8;;\u0007`;
64
+ }
65
+
66
+ // Cabeçalho da Marca Traduzido
67
+ function printHeader() {
68
+ console.clear();
69
+ console.log(chalk.cyan('================================================================'));
70
+ console.log(chalk.cyan(` 🛡️ CryptoSeedRecovery - ${t('headerTitle')}`));
71
+ console.log(chalk.cyan(` ${t('headerIntegration')} `) + chalk.cyan.bold.underline(link('Better2Better', 'https://better2better.net')));
72
+ console.log(chalk.cyan(` ${t('headerLead')} `) + chalk.cyan.bold.underline(link('Diego Oris', 'https://diegohorantunes.web.app/')));
73
+ console.log(chalk.cyan('================================================================\n'));
74
+ }
75
+
76
+ /**
77
+ * Formata duração em milissegundos para leitura humana
78
+ */
79
+ function formatDuration(ms) {
80
+ if (ms < 1000) return `${ms.toFixed(1)} ms`;
81
+ let secs = ms / 1000;
82
+ if (secs < 60) return `${secs.toFixed(1)} s`;
83
+ let mins = secs / 60;
84
+ if (mins < 60) return `${mins.toFixed(1)} m`;
85
+ let hours = mins / 60;
86
+ if (hours < 24) return `${hours.toFixed(1)} h`;
87
+ let days = hours / 24;
88
+ if (days < 365) return `${days.toFixed(1)} d`;
89
+ let years = days / 365;
90
+ return `${years.toFixed(1)} y`;
91
+ }
92
+
93
+ /**
94
+ * Auxiliar para fatorial (Mode 4 progress)
95
+ */
96
+ function factorial(n) {
97
+ let res = 1n;
98
+ for (let i = 2n; i <= BigInt(n); i++) res *= i;
99
+ return res;
100
+ }
101
+
102
+ /**
103
+ * Helper to check if user input is a step-back command
104
+ */
105
+ function isBackInput(val) {
106
+ if (typeof val !== 'string') return false;
107
+ const clean = val.trim().toLowerCase();
108
+ return clean === 'back' || clean === 'voltar' || clean === 'volver' || clean === '返回' || clean === '戻る' || clean === '<' || clean === 'b';
109
+ }
110
+
111
+ /**
112
+ * Corrige erros ortográficos de forma interativa para uma semente fornecida pelo usuário.
113
+ */
114
+ async function interactiveTypoHelper(rawPhrase, wordlist) {
115
+ const words = rawPhrase.trim().split(/\s+/);
116
+ const correctedWords = [];
117
+
118
+ for (let i = 0; i < words.length; i++) {
119
+ const word = words[i].toLowerCase().trim();
120
+
121
+ // Se for curinga, ignora validação
122
+ if (word === '*' || word === '?' || word.endsWith('*')) {
123
+ correctedWords.push(word);
124
+ continue;
125
+ }
126
+
127
+ if (wordlist.includes(word)) {
128
+ correctedWords.push(word);
129
+ } else {
130
+ // 1. Tenta buscar por iniciais (comer por letras de trás para frente)
131
+ let prefixSuggestions = getPrefixSuggestions(word, wordlist);
132
+ let isDistanceTypo = false;
133
+
134
+ // 2. Se não houver sugestões de prefixo, tenta por distância de edição Levenshtein e teclado adjacente
135
+ if (prefixSuggestions.length === 0) {
136
+ prefixSuggestions = getSuggestions(word, wordlist, 15);
137
+ isDistanceTypo = true;
138
+ }
139
+
140
+ if (prefixSuggestions.length > 0) {
141
+ console.log(chalk.yellow(t('typoTitle', word, i + 1)));
142
+
143
+ if (isDistanceTypo) {
144
+ console.log(chalk.cyan(t('typoDistanceCoincidence')));
145
+ } else {
146
+ // Determina o prefixo mais longo que bateu
147
+ let longestPrefix = '';
148
+ for (let len = word.length; len >= 1; len--) {
149
+ const testPref = word.substring(0, len);
150
+ if (wordlist.some(w => w.startsWith(testPref))) {
151
+ longestPrefix = testPref;
152
+ break;
153
+ }
154
+ }
155
+ console.log(chalk.cyan(t('typoPrefixCoincidence', longestPrefix)));
156
+ }
157
+
158
+ const choices = [...prefixSuggestions.slice(0, 15)];
159
+ if (!isDistanceTypo && prefixSuggestions.length >= 2) {
160
+ choices.push(t('typoChoicePrefixCandidates'));
161
+ }
162
+ choices.push(t('typoChoiceLost'), t('typoChoiceManual'), t('typoChoiceKeep'));
163
+
164
+ const ans = await inquirer.prompt([{
165
+ type: 'select',
166
+ name: 'choice',
167
+ message: t('typoChoicePrompt', i + 1),
168
+ choices: choices
169
+ }]);
170
+
171
+ if (ans.choice === t('typoChoiceLost')) {
172
+ correctedWords.push('*');
173
+ } else if (ans.choice === t('typoChoicePrefixCandidates')) {
174
+ correctedWords.push(prefixSuggestions.slice(0, 15).join(','));
175
+ } else if (ans.choice === t('typoChoiceManual')) {
176
+ const ansManual = await inquirer.prompt([{
177
+ type: 'input',
178
+ name: 'typed',
179
+ message: t('typoManualPrompt')
180
+ }]);
181
+ const manualWord = ansManual.typed.trim().toLowerCase();
182
+ if (wordlist.includes(manualWord)) {
183
+ correctedWords.push(manualWord);
184
+ } else {
185
+ console.log(chalk.yellow(t('typoManualInvalid', manualWord)));
186
+ correctedWords.push('*');
187
+ }
188
+ } else if (ans.choice === t('typoChoiceKeep')) {
189
+ correctedWords.push(word);
190
+ } else {
191
+ correctedWords.push(ans.choice);
192
+ }
193
+ } else {
194
+ // Se não bater com nenhuma inicial conhecida nem distância de edição, trata automaticamente como desconhecida (*) para testar 1 por 1 de toda a lista
195
+ console.log(chalk.red(t('typoNoPrefix', word, i + 1)));
196
+ console.log(chalk.green(t('typoNoPrefixSub')));
197
+ correctedWords.push('*');
198
+ }
199
+ }
200
+ }
201
+
202
+ return correctedWords;
203
+ }
204
+
205
+ function runParallelSearch({
206
+ mode,
207
+ phraseWords,
208
+ constraints,
209
+ format,
210
+ wordlist,
211
+ wallet,
212
+ coin,
213
+ targetAddress,
214
+ pattern,
215
+ threads,
216
+ totalCombinations,
217
+ onProgress,
218
+ language
219
+ }) {
220
+ return new Promise((resolve, reject) => {
221
+ const { Worker } = require('worker_threads');
222
+ const path = require('path');
223
+
224
+ let subtasks = [];
225
+ if (mode === 1) {
226
+ subtasks = getMode1Prefixes(phraseWords, format, wordlist, coin, threads);
227
+ } else if (mode === 2 || mode === 3) {
228
+ subtasks = getMode23PartialStates(phraseWords, constraints, format, wordlist, coin, threads);
229
+ } else if (mode === 4) {
230
+ subtasks = getMode4PartialStates(phraseWords, threads);
231
+ }
232
+
233
+ if (subtasks.length === 0) {
234
+ return reject(new Error('No subtasks generated to distribute.'));
235
+ }
236
+
237
+ const actualThreads = Math.min(threads, subtasks.length);
238
+ const chunks = Array.from({ length: actualThreads }, () => []);
239
+ for (let i = 0; i < subtasks.length; i++) {
240
+ chunks[i % actualThreads].push(subtasks[i]);
241
+ }
242
+
243
+ const workers = [];
244
+ const workerProgress = Array(actualThreads).fill(0);
245
+ const allResults = [];
246
+ const allAssembled = [];
247
+ let completedWorkers = 0;
248
+
249
+ const workerPath = path.join(__dirname, '../lib/search-worker.js');
250
+
251
+ function cleanUpWorkers() {
252
+ for (const w of workers) {
253
+ w.terminate().catch(() => {});
254
+ }
255
+ }
256
+
257
+ for (let i = 0; i < actualThreads; i++) {
258
+ const worker = new Worker(workerPath);
259
+ workers.push(worker);
260
+
261
+ worker.on('message', (msg) => {
262
+ if (msg.type === 'progress') {
263
+ workerProgress[i] = msg.checked;
264
+ const currentTotal = workerProgress.reduce((sum, val) => sum + val, 0);
265
+ onProgress(currentTotal, totalCombinations);
266
+ } else if (msg.type === 'done') {
267
+ workerProgress[i] = msg.totalChecked;
268
+
269
+ if (msg.results && msg.results.length > 0) {
270
+ allResults.push(...msg.results);
271
+ }
272
+ if (msg.assembledCandidates && msg.assembledCandidates.length > 0) {
273
+ allAssembled.push(...msg.assembledCandidates);
274
+ }
275
+
276
+ completedWorkers++;
277
+ if (completedWorkers === actualThreads) {
278
+ cleanUpWorkers();
279
+
280
+ const uniqueResults = [...new Set(allResults)].slice(0, 50000);
281
+
282
+ const seenPhrases = new Set();
283
+ const uniqueAssembled = [];
284
+ for (const cand of allAssembled) {
285
+ if (!seenPhrases.has(cand.phrase)) {
286
+ seenPhrases.add(cand.phrase);
287
+ uniqueAssembled.push(cand);
288
+ if (uniqueAssembled.length >= 50000) break;
289
+ }
290
+ }
291
+
292
+ const totalChecked = workerProgress.reduce((sum, val) => sum + val, 0);
293
+ onProgress(totalChecked, totalCombinations);
294
+
295
+ resolve({
296
+ results: uniqueResults,
297
+ totalChecked: BigInt(totalChecked),
298
+ assembledCandidates: uniqueAssembled
299
+ });
300
+ }
301
+ } else if (msg.type === 'error') {
302
+ cleanUpWorkers();
303
+ reject(new Error(msg.message));
304
+ }
305
+ });
306
+
307
+ worker.on('error', (err) => {
308
+ cleanUpWorkers();
309
+ reject(err);
310
+ });
311
+
312
+ worker.on('exit', (code) => {
313
+ if (code !== 0 && completedWorkers < actualThreads) {
314
+ cleanUpWorkers();
315
+ reject(new Error(`Worker stopped with exit code ${code}`));
316
+ }
317
+ });
318
+
319
+ const startMsg = {
320
+ type: 'start',
321
+ mode,
322
+ phraseWords,
323
+ constraints,
324
+ format,
325
+ wordlist,
326
+ walletType: wallet,
327
+ coinKey: coin,
328
+ targetAddress,
329
+ pattern,
330
+ startPrefixes: mode === 1 ? chunks[i] : null,
331
+ startStates: (mode === 2 || mode === 3 || mode === 4) ? chunks[i] : null,
332
+ language
333
+ };
334
+
335
+ worker.postMessage(startMsg);
336
+ }
337
+ });
338
+ }
339
+
340
+ async function main() {
341
+ // Imprime o cabeçalho traduzido
342
+ printHeader();
343
+
344
+ let step = 'format';
345
+ const history = [];
346
+
347
+ // Declaração de todas as variáveis do wizard
348
+ let format = '';
349
+ let yearProfile = '';
350
+ let coin = '';
351
+ let wallet = '';
352
+ let pattern = null;
353
+ let wordlist = [];
354
+ let language = 'en';
355
+ let mode = null;
356
+ let rawPhrase = '';
357
+ let phraseWords = [];
358
+ let targetAddress = '';
359
+ let threadMode = 'single';
360
+ let threads = 1;
361
+ const constraints = {};
362
+ let totalCombinations = 1n;
363
+
364
+ while (step !== 'execute') {
365
+ switch (step) {
366
+ case 'format': {
367
+ const formatAnswers = await inquirer.prompt([{
368
+ type: 'select',
369
+ name: 'format',
370
+ message: t('chooseFormat'),
371
+ choices: [
372
+ { name: t('formatBip39'), value: 'bip39' },
373
+ { name: t('formatElectrum'), value: 'electrum' },
374
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
375
+ ]
376
+ }]);
377
+ if (formatAnswers.format === '__back__') {
378
+ return '__back_to_lang__';
379
+ }
380
+ format = formatAnswers.format;
381
+ history.push('format');
382
+ step = 'year';
383
+ break;
384
+ }
385
+
386
+ case 'year': {
387
+ const yearAnswers = await inquirer.prompt([{
388
+ type: 'select',
389
+ name: 'year',
390
+ message: t('chooseYear'),
391
+ choices: [
392
+ { name: t('yearModern'), value: 'modern_2020_present' },
393
+ { name: t('yearMiddle'), value: 'middle_2015_2019' },
394
+ { name: t('yearLegacy'), value: 'legacy_2011_2014' },
395
+ { name: t('yearGenesis'), value: 'genesis_pre_2011' },
396
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
397
+ ]
398
+ }]);
399
+
400
+ if (yearAnswers.year === '__back__') {
401
+ step = history.pop() || 'format';
402
+ } else {
403
+ yearProfile = yearAnswers.year;
404
+ if (yearProfile === 'genesis_pre_2011') {
405
+ console.log(chalk.yellow.bold('\n================================================================================'));
406
+ console.log(chalk.yellow.bold(` 🎓 ${t('satoshiTitle')} 🎓`));
407
+ console.log(chalk.yellow.bold('================================================================================'));
408
+ console.log(chalk.white(t('satoshiDesc1')));
409
+ console.log('\n' + chalk.bold(t('satoshiHowWorked')));
410
+ console.log(chalk.gray(` • ${t('satoshiPoint1')}`));
411
+ console.log(chalk.gray(` • ${t('satoshiPoint2')}`));
412
+ console.log(chalk.gray(` • ${t('satoshiPoint3')}`));
413
+ console.log(chalk.gray(` • ${t('satoshiPoint4')}`));
414
+ console.log('\n' + chalk.bold(t('satoshiInvention')));
415
+ console.log(chalk.gray(` • ${t('satoshiInventionPoint1')}`));
416
+ console.log(chalk.gray(` • ${t('satoshiInventionPoint2')}`));
417
+ console.log('\n' + chalk.bold(t('satoshiWhatToDo')));
418
+ console.log(chalk.gray(` • ${t('satoshiWhatToDoPoint1')}`));
419
+ console.log(chalk.yellow.bold('================================================================================\n'));
420
+
421
+ await inquirer.prompt([{
422
+ type: 'select',
423
+ name: 'back',
424
+ message: t('satoshiWhatToDo'),
425
+ choices: [
426
+ { name: t('backChoice'), value: '__back__' }
427
+ ]
428
+ }]);
429
+ step = 'year';
430
+ } else {
431
+ history.push('year');
432
+ step = 'coin';
433
+ }
434
+ }
435
+ break;
436
+ }
437
+
438
+ case 'coin': {
439
+ const blockchainChoices = [
440
+ { name: 'Ethereum / EVMs (ETH, BNB, Polygon, Arbitrum, Optimism, Avalanche, Base, Fantom, Cronos, Harmony)', value: 'ETH' },
441
+ { name: 'Bitcoin (BTC)', value: 'BTC' },
442
+ { name: 'Litecoin (LTC)', value: 'LTC' },
443
+ { name: 'Dogecoin (DOGE)', value: 'DOGE' },
444
+ { name: 'Solana (SOL)', value: 'SOL' },
445
+ { name: 'TRON (TRX)', value: 'TRX' },
446
+ { name: 'Cardano (ADA)', value: 'ADA' },
447
+ { name: 'Ripple (XRP)', value: 'XRP' },
448
+ { name: 'Polkadot (DOT)', value: 'DOT' },
449
+ { name: 'Kusama (KSM)', value: 'KSM' },
450
+ { name: 'Cosmos (ATOM)', value: 'ATOM' },
451
+ { name: 'Osmosis (OSMO)', value: 'OSMO' },
452
+ { name: 'Waves (WAVES)', value: 'WAVES' },
453
+ { name: 'Kaspa (KAS)', value: 'KAS' },
454
+ { name: 'Stellar (XLM)', value: 'XLM' },
455
+ { name: 'Zcash (ZEC)', value: 'ZEC' },
456
+ { name: 'Bitcoin Cash (BCH)', value: 'BCH' },
457
+ { name: 'Monero (XMR)', value: 'XMR' },
458
+ { name: 'Dash (DASH)', value: 'DASH' },
459
+ { name: 'Secret Network (SCRT)', value: 'SCRT' },
460
+ { name: 'Injective (INJ)', value: 'INJ' },
461
+ { name: 'Hedera (HBAR)', value: 'HBAR' },
462
+ { name: 'NEM (XEM)', value: 'XEM' },
463
+ { name: 'Chia (XCH)', value: 'XCH' },
464
+ { name: 'Gnosis Chain (GNOSIS)', value: 'GNOSIS' },
465
+ { name: 'Tezos (XTZ)', value: 'XTZ' },
466
+ { name: 'Algorand (ALGO)', value: 'ALGO' },
467
+ { name: 'Near (NEAR)', value: 'NEAR' },
468
+ { name: 'Sui (SUI)', value: 'SUI' },
469
+ { name: 'Aptos (APT)', value: 'APT' },
470
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
471
+ ];
472
+
473
+ const blockchainAnswers = await inquirer.prompt([{
474
+ type: 'select',
475
+ name: 'coin',
476
+ message: t('chooseBlockchain'),
477
+ choices: blockchainChoices
478
+ }]);
479
+
480
+ if (blockchainAnswers.coin === '__back__') {
481
+ step = history.pop() || 'year';
482
+ } else {
483
+ coin = blockchainAnswers.coin;
484
+ history.push('coin');
485
+ step = 'wallet';
486
+ }
487
+ break;
488
+ }
489
+
490
+ case 'wallet': {
491
+ let walletChoices = [];
492
+ if (coin === 'ETH' || coin === 'GNOSIS') {
493
+ walletChoices = [
494
+ { name: t('walletB2'), value: 'b2wallet' },
495
+ { name: t('walletMeta'), value: 'metamask' },
496
+ { name: t('walletTrust'), value: 'trust' },
497
+ { name: `Ledger (${t('walletLedger')} m/44'/60'/0'/0)`, value: 'ledger' },
498
+ { name: `Trezor (${t('walletTrezor')} m/44'/60'/0'/0)`, value: 'trezor' }
499
+ ];
500
+ } else if (coin === 'BTC') {
501
+ walletChoices = [
502
+ { name: t('walletB2'), value: 'b2wallet' },
503
+ { name: 'Electrum', value: 'electrum' },
504
+ { name: t('walletTrust'), value: 'trust' },
505
+ { name: t('walletLedger'), value: 'ledger' },
506
+ { name: t('walletTrezor'), value: 'trezor' }
507
+ ];
508
+ } else if (coin === 'SOL') {
509
+ walletChoices = [
510
+ { name: t('walletB2'), value: 'b2wallet' },
511
+ { name: t('walletPhantom'), value: 'phantom' },
512
+ { name: t('walletTrust'), value: 'trust' },
513
+ { name: t('walletLedger'), value: 'ledger' }
514
+ ];
515
+ } else if (coin === 'ADA') {
516
+ walletChoices = [
517
+ { name: t('walletYoroi'), value: 'yoroi' },
518
+ { name: 'Daedalus Wallet', value: 'daedalus' },
519
+ { name: t('walletLedger'), value: 'ledger' }
520
+ ];
521
+ } else {
522
+ walletChoices = [
523
+ { name: t('walletB2'), value: 'b2wallet' },
524
+ { name: t('walletTrust'), value: 'trust' },
525
+ { name: t('walletLedger'), value: 'ledger' },
526
+ { name: t('walletTrezor'), value: 'trezor' }
527
+ ];
528
+ }
529
+ walletChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
530
+
531
+ const walletAnswers = await inquirer.prompt([{
532
+ type: 'select',
533
+ name: 'wallet',
534
+ message: t('chooseWallet'),
535
+ choices: walletChoices
536
+ }]);
537
+
538
+ if (walletAnswers.wallet === '__back__') {
539
+ step = history.pop() || 'coin';
540
+ } else {
541
+ wallet = walletAnswers.wallet;
542
+ history.push('wallet');
543
+ if (wallet !== 'b2wallet' && (coin === 'BTC' || coin === 'LTC' || coin === 'SOL' || coin === 'ADA')) {
544
+ step = 'pattern';
545
+ } else {
546
+ pattern = null;
547
+ step = 'language';
548
+ }
549
+ }
550
+ break;
551
+ }
552
+
553
+ case 'pattern': {
554
+ let patternChoices = [];
555
+ if (coin === 'BTC') {
556
+ patternChoices = [
557
+ { name: t('btcSegwit'), value: 'segwit' },
558
+ { name: t('btcNested'), value: 'nested' },
559
+ { name: t('btcLegacy'), value: 'legacy' },
560
+ { name: t('btcTaproot'), value: 'taproot' }
561
+ ];
562
+ } else if (coin === 'LTC') {
563
+ patternChoices = [
564
+ { name: t('ltcSegwit'), value: 'segwit' },
565
+ { name: t('ltcLegacy'), value: 'legacy' }
566
+ ];
567
+ } else if (coin === 'SOL') {
568
+ patternChoices = [
569
+ { name: t('solPhantom'), value: 'phantom' },
570
+ { name: t('solSollet'), value: 'sollet' }
571
+ ];
572
+ } else if (coin === 'ADA') {
573
+ patternChoices = [
574
+ { name: t('adaShelley'), value: 'shelley' },
575
+ { name: t('adaByron'), value: 'byron' }
576
+ ];
577
+ }
578
+ patternChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
579
+
580
+ const patternAnswers = await inquirer.prompt([{
581
+ type: 'select',
582
+ name: 'pattern',
583
+ message: coin === 'BTC' ? t('choosePatternBTC') : (coin === 'LTC' ? t('choosePatternLTC') : (coin === 'SOL' ? t('choosePatternSOL') : t('choosePatternADA'))),
584
+ choices: patternChoices
585
+ }]);
586
+
587
+ if (patternAnswers.pattern === '__back__') {
588
+ step = history.pop() || 'wallet';
589
+ } else {
590
+ pattern = patternAnswers.pattern;
591
+ history.push('pattern');
592
+ step = 'language';
593
+ }
594
+ break;
595
+ }
596
+
597
+ case 'language': {
598
+ if (format === 'bip39') {
599
+ let langChoices = [
600
+ { name: 'Inglês (English)', value: 'en' },
601
+ { name: 'Português (Portuguese)', value: 'pt' },
602
+ { name: 'Espanhol (Spanish)', value: 'es' },
603
+ { name: 'Francês (French)', value: 'fr' },
604
+ { name: 'Italiano (Italian)', value: 'it' },
605
+ { name: 'Tcheco (Czech)', value: 'cz' },
606
+ { name: 'Japonês (Japanese)', value: 'ja' },
607
+ { name: 'Coreano (Korean)', value: 'ko' },
608
+ { name: 'Chinês Simplificado (Simplified Chinese)', value: 'zh_cn' },
609
+ { name: 'Chinês Tradicional (Traditional Chinese)', value: 'zh_tw' }
610
+ ];
611
+ let langMessage = t('chooseLang');
612
+
613
+ if (coin === 'ALGO') {
614
+ langChoices = [{ name: 'Inglês (English)', value: 'en' }];
615
+ langMessage = `${chalk.yellow(t('algoLangOnly'))}\n ${t('chooseLang')}`;
616
+ } else if (coin === 'ADA' && pattern === 'byron') {
617
+ langChoices = [{ name: 'Inglês (English)', value: 'en' }];
618
+ langMessage = `${chalk.yellow(t('adaByronLangOnly'))}\n ${t('chooseLang')}`;
619
+ } else if (yearProfile === 'legacy_2011_2014') {
620
+ langChoices = [{ name: 'Inglês (English)', value: 'en' }];
621
+ langMessage = `${chalk.yellow(t('legacyYearLangWarning'))}\n ${t('chooseLang')}`;
622
+ }
623
+
624
+ langChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
625
+
626
+ const langAnswers = await inquirer.prompt([{
627
+ type: 'select',
628
+ name: 'lang',
629
+ message: langMessage,
630
+ choices: langChoices
631
+ }]);
632
+
633
+ if (langAnswers.lang === '__back__') {
634
+ step = history.pop() || 'wallet';
635
+ } else {
636
+ language = langAnswers.lang;
637
+ wordlist = bip39[language];
638
+ history.push('language');
639
+ step = 'mode';
640
+ }
641
+ } else {
642
+ wordlist = electrumPoet;
643
+ language = 'en';
644
+ step = 'mode';
645
+ }
646
+ break;
647
+ }
648
+
649
+ case 'mode': {
650
+ const modeAnswers = await inquirer.prompt([{
651
+ type: 'select',
652
+ name: 'mode',
653
+ message: t('chooseMode'),
654
+ choices: [
655
+ { name: t('mode1'), value: 1 },
656
+ { name: t('mode2'), value: 2 },
657
+ { name: t('mode3'), value: 3 },
658
+ { name: t('mode4'), value: 4 },
659
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
660
+ ]
661
+ }]);
662
+
663
+ if (modeAnswers.mode === '__back__') {
664
+ step = history.pop() || 'language';
665
+ } else {
666
+ mode = modeAnswers.mode;
667
+ history.push('mode');
668
+ step = 'phrase';
669
+ }
670
+ break;
671
+ }
672
+
673
+ case 'phrase': {
674
+ let wordsInputMessage = '';
675
+ const guidanceNote = chalk.cyan(t('guidanceTitle')) + chalk.white(t('guidanceBody'));
676
+
677
+ if (mode === 1) {
678
+ wordsInputMessage = guidanceNote + t('inputMsgMode1');
679
+ } else if (mode === 2) {
680
+ wordsInputMessage = guidanceNote + t('inputMsgMode2');
681
+ } else if (mode === 3 || mode === 4) {
682
+ wordsInputMessage = guidanceNote + t('inputMsgMode3');
683
+ }
684
+
685
+ wordsInputMessage += chalk.gray('\n' + t('backInputHint') + '\n>');
686
+
687
+ const phraseAnswers = await inquirer.prompt([{
688
+ type: 'input',
689
+ name: 'rawPhrase',
690
+ message: wordsInputMessage,
691
+ validate: (val) => {
692
+ if (isBackInput(val)) return true;
693
+ return val.trim().split(/\s+/).length >= 4 ? true : t('validateMinWords');
694
+ }
695
+ }]);
696
+
697
+ rawPhrase = phraseAnswers.rawPhrase.trim();
698
+
699
+ if (isBackInput(rawPhrase)) {
700
+ step = history.pop() || 'mode';
701
+ } else {
702
+ console.log(chalk.cyan(t('validatingSpell')));
703
+ phraseWords = await interactiveTypoHelper(rawPhrase, wordlist);
704
+ console.log(chalk.green(t('spellValidated', phraseWords.join(' '))));
705
+
706
+ const unknownWordsCount = phraseWords.filter(w => w === '*' || w === '?').length;
707
+
708
+ if (unknownWordsCount > 1) {
709
+ console.log(chalk.red.bold('\n================================================================================'));
710
+ console.log(chalk.red.bold(t('complexityTitle')));
711
+ console.log(chalk.red.bold('================================================================================'));
712
+ console.log(chalk.yellow(t('complexityWarn', unknownWordsCount)));
713
+ console.log(chalk.yellow(t('complexityBeAware')));
714
+
715
+ console.log(chalk.bold(t('complexity24Seeds')));
716
+ console.log(chalk.white(t('complexity1Word')));
717
+ console.log(chalk.white(t('complexity2Words')));
718
+ console.log(chalk.white(t('complexity3Words')));
719
+ console.log(chalk.white(t('complexity4Words')));
720
+ console.log(chalk.white(t('complexity5Words')));
721
+
722
+ console.log(chalk.bold(t('complexityInPractice')));
723
+ console.log(chalk.green(t('complexityPractice1')));
724
+ console.log(chalk.yellow(t('complexityPractice2')));
725
+ console.log(chalk.red(t('complexityPractice3')));
726
+ console.log(chalk.red.bold(t('complexityPractice4')));
727
+
728
+ console.log('\n' + chalk.bold(t('complexityInPractice')));
729
+ console.log(chalk.gray(t('complexityChecksumNote')));
730
+ console.log(chalk.red.bold('================================================================================'));
731
+
732
+ const confirmAnswers = await inquirer.prompt([{
733
+ type: 'select',
734
+ name: 'proceed',
735
+ message: chalk.red.bold(t('complexityProceedConfirm')),
736
+ choices: [
737
+ { name: t('complexityConfirmYes'), value: 'yes' },
738
+ { name: t('complexityConfirmNo'), value: 'no' }
739
+ ]
740
+ }]);
741
+
742
+ if (confirmAnswers.proceed === 'no') {
743
+ console.log(chalk.yellow(t('complexityCancelled')));
744
+ step = 'phrase'; // Stay/Loop back to phrase input
745
+ break;
746
+ }
747
+ }
748
+
749
+ history.push('phrase');
750
+ step = 'targetAddress';
751
+ }
752
+ break;
753
+ }
754
+
755
+ case 'targetAddress': {
756
+ const addressAnswers = await inquirer.prompt([{
757
+ type: 'input',
758
+ name: 'targetAddress',
759
+ message: t('targetAddressPrompt') + chalk.gray('\n' + t('backInputHint') + '\n>'),
760
+ validate: (val) => {
761
+ if (isBackInput(val)) return true;
762
+ return val.trim() === '' ? true : (val.trim().length > 10 ? true : t('invalidAddress'));
763
+ }
764
+ }]);
765
+
766
+ const rawAddr = addressAnswers.targetAddress.trim();
767
+
768
+ if (isBackInput(rawAddr)) {
769
+ step = history.pop() || 'phrase';
770
+ } else {
771
+ targetAddress = rawAddr;
772
+ history.push('targetAddress');
773
+ step = 'threadMode';
774
+ }
775
+ break;
776
+ }
777
+
778
+ case 'threadMode': {
779
+ const threadModeAnswer = await inquirer.prompt([{
780
+ type: 'select',
781
+ name: 'threadMode',
782
+ message: t('chooseThreadMode'),
783
+ choices: [
784
+ { name: t('threadModeSingle'), value: 'single' },
785
+ { name: t('threadModeMulti'), value: 'multi' },
786
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
787
+ ]
788
+ }]);
789
+
790
+ if (threadModeAnswer.threadMode === '__back__') {
791
+ step = history.pop() || 'targetAddress';
792
+ } else {
793
+ threadMode = threadModeAnswer.threadMode;
794
+ history.push('threadMode');
795
+ if (threadMode === 'multi') {
796
+ step = 'threads';
797
+ } else {
798
+ step = 'constraints';
799
+ }
800
+ }
801
+ break;
802
+ }
803
+
804
+ case 'threads': {
805
+ const os = require('os');
806
+ const maxThreads = os.cpus().length || 1;
807
+ const threadCountAnswer = await inquirer.prompt([{
808
+ type: 'input',
809
+ name: 'threads',
810
+ message: t('threadCountPrompt', maxThreads) + chalk.gray('\n' + t('backInputHint') + '\n>'),
811
+ default: String(maxThreads),
812
+ validate: (val) => {
813
+ if (isBackInput(val)) return true;
814
+ const num = Number(val);
815
+ return (!isNaN(num) && Number.isInteger(num) && num >= 1 && num <= maxThreads) ? true : `Invalid thread count (1 - ${maxThreads})`;
816
+ }
817
+ }]);
818
+
819
+ const rawThr = threadCountAnswer.threads.trim();
820
+
821
+ if (isBackInput(rawThr)) {
822
+ step = history.pop() || 'threadMode';
823
+ } else {
824
+ threads = parseInt(rawThr, 10) || 1;
825
+ history.push('threads');
826
+ step = 'constraints';
827
+ }
828
+ break;
829
+ }
830
+
831
+ case 'constraints': {
832
+ if (mode === 2 || mode === 3) {
833
+ const constraintSetup = await inquirer.prompt([{
834
+ type: 'select',
835
+ name: 'addConstraints',
836
+ message: t('addConstraintsPrompt'),
837
+ choices: [
838
+ { name: t('yes'), value: 'yes' },
839
+ { name: t('no'), value: 'no' },
840
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
841
+ ]
842
+ }]);
843
+
844
+ if (constraintSetup.addConstraints === '__back__') {
845
+ step = history.pop() || 'threadMode';
846
+ } else if (constraintSetup.addConstraints === 'yes') {
847
+ let adding = true;
848
+ for (const key of Object.keys(constraints)) delete constraints[key];
849
+
850
+ while (adding) {
851
+ const typeAnswer = await inquirer.prompt([{
852
+ type: 'select',
853
+ name: 'type',
854
+ message: t('addConstraintType'),
855
+ choices: [
856
+ { name: t('constraintTypeOK'), value: 'OK' },
857
+ { name: t('constraintTypeNOK'), value: 'NOK' },
858
+ { name: t('constraintTypeDONE'), value: 'DONE' }
859
+ ]
860
+ }]);
861
+
862
+ if (typeAnswer.type === 'DONE') {
863
+ adding = false;
864
+ break;
865
+ }
866
+
867
+ const slotAnswers = await inquirer.prompt([
868
+ {
869
+ type: 'input',
870
+ name: 'slot',
871
+ message: t('constraintSlotPrompt', phraseWords.length),
872
+ validate: (val) => {
873
+ const num = Number(val);
874
+ return (!isNaN(num) && num >= 1 && num <= phraseWords.length) ? true : t('invalidSlot');
875
+ }
876
+ },
877
+ {
878
+ type: 'input',
879
+ name: 'word',
880
+ message: t('constraintWordPrompt'),
881
+ validate: (val) => wordlist.includes(val.trim().toLowerCase()) ? true : t('wordNotInDict')
882
+ }
883
+ ]);
884
+
885
+ const slotIdx = parseInt(slotAnswers.slot, 10) - 1;
886
+ const cleanWord = slotAnswers.word.trim().toLowerCase();
887
+
888
+ if (!constraints[slotIdx]) {
889
+ constraints[slotIdx] = { excludedWords: [] };
890
+ }
891
+
892
+ if (typeAnswer.type === 'OK') {
893
+ constraints[slotIdx].requiredWord = cleanWord;
894
+ } else {
895
+ constraints[slotIdx].excludedWords.push(cleanWord);
896
+ }
897
+
898
+ const continueAnswer = await inquirer.prompt([{
899
+ type: 'confirm',
900
+ name: 'continue',
901
+ message: t('addAnotherConstraint'),
902
+ default: true
903
+ }]);
904
+ adding = continueAnswer.continue;
905
+ }
906
+
907
+ history.push('constraints');
908
+ step = 'confirmSearch';
909
+ } else {
910
+ for (const key of Object.keys(constraints)) delete constraints[key];
911
+ history.push('constraints');
912
+ step = 'confirmSearch';
913
+ }
914
+ } else {
915
+ step = 'confirmSearch';
916
+ }
917
+ break;
918
+ }
919
+
920
+ case 'confirmSearch': {
921
+ // 13. Cálculo de combinações totais
922
+ totalCombinations = 1n;
923
+ if (mode === 1) {
924
+ const slots = phraseWords.map(pat => {
925
+ const clean = pat.toLowerCase().trim();
926
+ if (clean.includes(',')) {
927
+ return clean.split(',').map(s => s.trim()).filter(Boolean);
928
+ } else if (clean === '*' || clean === '?') {
929
+ return wordlist;
930
+ } else if (clean.endsWith('*')) {
931
+ const prefix = clean.slice(0, -1);
932
+ return wordlist.filter(w => w.startsWith(prefix));
933
+ } else {
934
+ return [clean];
935
+ }
936
+ });
937
+ totalCombinations = slots.reduce((acc, slot) => acc * BigInt(slot.length), 1n);
938
+ } else if (mode === 2 || mode === 3) {
939
+ const seedSize = phraseWords.length;
940
+ const fixedPositions = {};
941
+ const activeConstraints = constraints || {};
942
+ for (let idx = 0; idx < seedSize; idx++) {
943
+ if (activeConstraints[idx] && activeConstraints[idx].requiredWord) {
944
+ fixedPositions[idx] = true;
945
+ }
946
+ }
947
+ const floatingWords = phraseWords.map(w => w.toLowerCase().trim());
948
+ for (let idx = 0; idx < seedSize; idx++) {
949
+ if (fixedPositions[idx]) {
950
+ const fixedWord = activeConstraints[idx].requiredWord.toLowerCase().trim();
951
+ const matchIdx = floatingWords.indexOf(fixedWord);
952
+ if (matchIdx !== -1) {
953
+ floatingWords.splice(matchIdx, 1);
954
+ }
955
+ }
956
+ }
957
+ const expandedFloatingPools = floatingWords.map(word => {
958
+ const clean = word.toLowerCase().trim();
959
+ if (clean.includes(',')) {
960
+ return clean.split(',').map(s => s.trim()).filter(Boolean);
961
+ } else if (clean === '*' || clean === '?') {
962
+ return wordlist;
963
+ } else if (clean.endsWith('*')) {
964
+ const prefix = clean.slice(0, -1);
965
+ return wordlist.filter(w => w.startsWith(prefix));
966
+ } else {
967
+ return [clean];
968
+ }
969
+ });
970
+ let remainingWordsCount = expandedFloatingPools.length;
971
+ for (let i = 0; i < expandedFloatingPools.length; i++) {
972
+ totalCombinations *= BigInt(expandedFloatingPools[i].length) * BigInt(remainingWordsCount);
973
+ remainingWordsCount--;
974
+ }
975
+ } else if (mode === 4) {
976
+ totalCombinations = factorial(phraseWords.length);
977
+ }
978
+
979
+ // 14. Simulador de Velocidade de Consulta de Saldos
980
+ const combosNum = Number(totalCombinations);
981
+ const timePublicAPI = combosNum * 50;
982
+ const timeLocalNode = combosNum * 0.1;
983
+
984
+ console.log(chalk.cyan('\n================================================================'));
985
+ console.log(chalk.cyan(t('speedSimHeader')));
986
+ console.log(chalk.cyan('================================================================'));
987
+ console.log(`${t('speedSimTotalCombos', totalCombinations.toString())}`);
988
+ console.log('----------------------------------------------------------------');
989
+ console.log(chalk.white(t('speedSimTimeEst')));
990
+ console.log(chalk.yellow(t('speedSimPublic')) + chalk.bold(formatDuration(timePublicAPI)));
991
+ console.log(chalk.green(t('speedSimLocal')) + chalk.bold(formatDuration(timeLocalNode)));
992
+ console.log(chalk.gray(t('speedSimNote')));
993
+ console.log(chalk.cyan('================================================================\n'));
994
+
995
+ // Confirmação de Execução
996
+ console.log(chalk.cyan('================================================================'));
997
+ console.log(chalk.cyan(t('prepEngineTitle')));
998
+ console.log(chalk.cyan('================================================================'));
999
+ console.log(t('prepFormat', format.toUpperCase()));
1000
+ console.log(t('prepWallet', wallet.toUpperCase()));
1001
+ console.log(t('prepYear', yearProfile.toUpperCase()));
1002
+ if (pattern) {
1003
+ console.log(t('prepPattern', pattern.toUpperCase()));
1004
+ }
1005
+ console.log(t('prepCoin', coin));
1006
+ let targetAddrDisplay = targetAddress;
1007
+ if (!targetAddrDisplay) {
1008
+ if (currentLocale === 'pt') targetAddrDisplay = 'QUALQUER SEMENTE VÁLIDA (COLETAR TODAS)';
1009
+ else if (currentLocale === 'es') targetAddrDisplay = 'CUALQUIER SEMILLA VÁLIDA (COLECTAR TODAS)';
1010
+ else if (currentLocale === 'zh') targetAddrDisplay = '任何有效助记词 (收集全部)';
1011
+ else if (currentLocale === 'ja') targetAddrDisplay = '任意の有效なシード (すべて収集)';
1012
+ else targetAddrDisplay = 'ANY VALID SEED (COLLECT ALL)';
1013
+ }
1014
+ console.log(t('prepTargetAddr', targetAddrDisplay));
1015
+ console.log(t('prepWords', phraseWords.join(' ')));
1016
+ console.log(chalk.cyan('================================================================\n'));
1017
+
1018
+ const execAns = await inquirer.prompt([{
1019
+ type: 'select',
1020
+ name: 'confirm',
1021
+ message: t('confirmStartSearch'),
1022
+ choices: [
1023
+ { name: t('yes') + ' (Start)', value: 'yes' },
1024
+ { name: t('backChoice'), value: '__back__' }
1025
+ ]
1026
+ }]);
1027
+
1028
+ if (execAns.confirm === '__back__') {
1029
+ step = history.pop() || 'constraints';
1030
+ } else {
1031
+ step = 'execute';
1032
+ }
1033
+ break;
1034
+ }
1035
+ }
1036
+ }
1037
+
1038
+ // Prepara barra de progresso
1039
+ const multibar = new cliProgress.MultiBar({
1040
+ clearOnComplete: false,
1041
+ hideCursor: true,
1042
+ format: t('progressBarFormat')
1043
+ }, cliProgress.Presets.shades_grey);
1044
+
1045
+ let pbar;
1046
+ let startTime = Date.now();
1047
+
1048
+ const onProgress = (checked, total) => {
1049
+ if (!pbar) {
1050
+ pbar = multibar.create(Number(total), 0);
1051
+ }
1052
+ const elapsedSecs = (Date.now() - startTime) / 1000;
1053
+ const speed = elapsedSecs > 0 ? Math.round(Number(checked) / elapsedSecs) : 0;
1054
+ pbar.update(Number(checked), { speed });
1055
+ };
1056
+
1057
+ let searchResult;
1058
+ try {
1059
+ if (threadMode === 'multi') {
1060
+ console.log(chalk.cyan(t('threadsStarted', threads)));
1061
+ searchResult = await runParallelSearch({
1062
+ mode,
1063
+ phraseWords,
1064
+ constraints,
1065
+ format,
1066
+ wordlist,
1067
+ wallet,
1068
+ coin,
1069
+ targetAddress,
1070
+ pattern,
1071
+ threads,
1072
+ totalCombinations,
1073
+ onProgress,
1074
+ language
1075
+ });
1076
+ } else {
1077
+ if (mode === 1) {
1078
+ searchResult = searchMode1(phraseWords, format, wordlist, wallet, coin, targetAddress, onProgress, pattern, null, language);
1079
+ } else if (mode === 2 || mode === 3) {
1080
+ searchResult = searchMode2And3(phraseWords, constraints, format, wordlist, wallet, coin, targetAddress, onProgress, pattern, null, language);
1081
+ } else if (mode === 4) {
1082
+ // For safety, warn if permutating too many items
1083
+ if (phraseWords.length > 10) {
1084
+ const confirmPerm = await inquirer.prompt([{
1085
+ type: 'confirm',
1086
+ name: 'proceed',
1087
+ message: chalk.red(t('permutingWarning', phraseWords.length, phraseWords.length)),
1088
+ default: false
1089
+ }]);
1090
+
1091
+ if (!confirmPerm.proceed) {
1092
+ console.log(chalk.yellow(t('permutingCancelled')));
1093
+ process.exit(0);
1094
+ }
1095
+ }
1096
+ searchResult = searchMode4(phraseWords, format, wallet, coin, targetAddress, onProgress, pattern, null, language);
1097
+ }
1098
+ }
1099
+ } catch (err) {
1100
+ multibar.stop();
1101
+ console.error(chalk.red(t('searchError', err.message)));
1102
+ process.exit(1);
1103
+ }
1104
+
1105
+ multibar.stop();
1106
+ const endTime = Date.now();
1107
+ const elapsedSecs = ((endTime - startTime) / 1000).toFixed(2);
1108
+
1109
+ // 16. Relatório de Resultados
1110
+ console.log(chalk.cyan('\n================================================================'));
1111
+ console.log(chalk.cyan(t('endExecutionTitle')));
1112
+ console.log(chalk.cyan('================================================================'));
1113
+ console.log(t('elapsedTime', elapsedSecs));
1114
+ console.log(t('testedSeeds', searchResult.totalChecked.toString()));
1115
+ console.log(t('averageSpeed', Math.round(Number(searchResult.totalChecked) / parseFloat(elapsedSecs))));
1116
+
1117
+ const results = searchResult.results;
1118
+ const assembled = searchResult.assembledCandidates || [];
1119
+
1120
+ console.log(t('totalChecksumPassed', assembled.length.toString()));
1121
+
1122
+ const reportLines = [
1123
+ '===========================================================',
1124
+ t('reportTitle'),
1125
+ '===========================================================',
1126
+ t('reportDateTime', new Date().toLocaleString()),
1127
+ t('reportWallet', wallet.toUpperCase()),
1128
+ t('reportYear', yearProfile.toUpperCase()),
1129
+ t('reportBlockchain', coin),
1130
+ t('reportDerivation', pattern ? pattern.toUpperCase() : 'STANDARD/PADRÃO'),
1131
+ t('reportTested', searchResult.totalChecked.toString()),
1132
+ t('reportChecksumValid', assembled.length),
1133
+ t('reportSearchTime', elapsedSecs),
1134
+ '-----------------------------------------------------------',
1135
+ t('reportEstSpeedHeader'),
1136
+ t('reportEstPublic', formatDuration(assembled.length * 50)),
1137
+ t('reportEstLocal', formatDuration(assembled.length * 0.1)),
1138
+ '-----------------------------------------------------------',
1139
+ t('reportExactCompatibleHeader'),
1140
+ ...(results.length > 0 ? results.map((r, i) => t('reportOptionIdx', i + 1, r)) : [t('reportNoneFound')]),
1141
+ '-----------------------------------------------------------',
1142
+ t('reportAssembledHeader'),
1143
+ ...assembled.slice(0, 1000).map((cand, idx) => t('reportAssembledItem', idx + 1, cand.phrase, cand.address)),
1144
+ ...(assembled.length > 1000 ? [t('reportMoreOmitted', assembled.length - 1000)] : []),
1145
+ '===========================================================',
1146
+ t('reportFooterSafe'),
1147
+ t('reportFooterDev')
1148
+ ];
1149
+
1150
+ if (results.length > 0) {
1151
+ console.log(t('successFound'));
1152
+ console.log(chalk.green(results.map((r, i) => t('reportOptionIdx', i + 1, r)).join('\n')));
1153
+ } else {
1154
+ console.log(t('noneFound'));
1155
+ console.log(chalk.gray(t('noneFoundDica1')));
1156
+ console.log(chalk.gray(t('noneFoundDica2')));
1157
+ }
1158
+
1159
+ // Escrever em arquivo txt
1160
+ const reportFilename = `resultado_recuperacao_${Date.now()}.txt`;
1161
+ const reportPath = path.join(process.cwd(), reportFilename);
1162
+ fs.writeFileSync(reportPath, reportLines.join('\n'), 'utf8');
1163
+
1164
+ console.log(chalk.cyan(t('reportSaved', reportPath)));
1165
+
1166
+ // 17. Consulta de Saldos (Opcional)
1167
+ if (assembled.length > 0) {
1168
+ const uniqueAddresses = [...new Set(assembled.map(a => a.address))];
1169
+
1170
+ console.log(chalk.cyan('\n================================================================'));
1171
+ const balanceAns = await inquirer.prompt([{
1172
+ type: 'confirm',
1173
+ name: 'query',
1174
+ message: t('queryBalancesPrompt'),
1175
+ default: false
1176
+ }]);
1177
+
1178
+ if (balanceAns.query) {
1179
+ // 17a. Choose Connection Type
1180
+ const connAns = await inquirer.prompt([{
1181
+ type: 'select',
1182
+ name: 'connType',
1183
+ message: t('queryConnectionType'),
1184
+ choices: [
1185
+ { name: t('connectionPublic'), value: 'public' },
1186
+ { name: t('connectionPrivate'), value: 'private' }
1187
+ ]
1188
+ }]);
1189
+
1190
+ let selectedRpcUrl = '';
1191
+ let defaultLocalUrl = 'http://localhost:8545';
1192
+ let selectedCoinKey = coin;
1193
+
1194
+ // Check if EVM
1195
+ const isEvm = coin === 'ETH' || coin === 'GNOSIS' || EVM_NETWORKS.some(n => n.id === coin);
1196
+
1197
+ if (connAns.connType === 'public') {
1198
+ if (isEvm) {
1199
+ // EVM blockchain list
1200
+ const evmChoices = EVM_NETWORKS.map(net => ({
1201
+ name: `${net.name} (ChainID: ${net.chainId})`,
1202
+ value: net
1203
+ }));
1204
+
1205
+ const chainAns = await inquirer.prompt([{
1206
+ type: 'select',
1207
+ name: 'network',
1208
+ message: t('chooseEvmChain'),
1209
+ choices: evmChoices
1210
+ }]);
1211
+
1212
+ selectedRpcUrl = chainAns.network.rpc;
1213
+ defaultLocalUrl = chainAns.network.localRpc;
1214
+ selectedCoinKey = chainAns.network.id;
1215
+ } else {
1216
+ // Non-EVM
1217
+ const defaultInfo = NON_EVM_DEFAULTS[coin];
1218
+ if (defaultInfo) {
1219
+ selectedRpcUrl = defaultInfo.rpc;
1220
+ defaultLocalUrl = defaultInfo.localRpc;
1221
+ } else {
1222
+ selectedRpcUrl = 'http://localhost:8545';
1223
+ }
1224
+ }
1225
+ } else {
1226
+ // Private
1227
+ if (isEvm) {
1228
+ defaultLocalUrl = 'http://localhost:8545';
1229
+ } else {
1230
+ defaultLocalUrl = NON_EVM_DEFAULTS[coin]?.localRpc || 'http://localhost:8545';
1231
+ }
1232
+
1233
+ const privAns = await inquirer.prompt([{
1234
+ type: 'input',
1235
+ name: 'rpc',
1236
+ message: t('privateRpcPrompt', defaultLocalUrl)
1237
+ }]);
1238
+
1239
+ selectedRpcUrl = privAns.rpc.trim() || defaultLocalUrl;
1240
+ }
1241
+
1242
+ console.log(chalk.cyan(`\n🔌 Node URL: ${selectedRpcUrl}`));
1243
+
1244
+ // 17b. Query balances loop
1245
+ for (let i = 0; i < uniqueAddresses.length; i++) {
1246
+ const addr = uniqueAddresses[i];
1247
+ console.log(chalk.cyan(`⏳ ${t('fetchingBalance', addr)}`));
1248
+ try {
1249
+ const bal = await getBalance(addr, selectedCoinKey, selectedRpcUrl);
1250
+ console.log(chalk.green(`✔️ ${t('balanceResult', addr, bal)}`));
1251
+ } catch (err) {
1252
+ console.log(chalk.red(`\n❌ Error querying balance for ${addr}: ${err.message}`));
1253
+ console.log(chalk.yellow(t('rateLimitError')));
1254
+
1255
+ const rateAns = await inquirer.prompt([{
1256
+ type: 'select',
1257
+ name: 'action',
1258
+ message: t('rateLimitTitle'),
1259
+ choices: [
1260
+ { name: t('rateLimitCustom'), value: 'custom' },
1261
+ { name: t('rateLimitLocal'), value: 'local' },
1262
+ { name: t('rateLimitSkip'), value: 'skip' }
1263
+ ]
1264
+ }]);
1265
+
1266
+ if (rateAns.action === 'custom') {
1267
+ const customAns = await inquirer.prompt([{
1268
+ type: 'input',
1269
+ name: 'url',
1270
+ message: t('customNodePrompt')
1271
+ }]);
1272
+ selectedRpcUrl = customAns.url.trim();
1273
+ i--; // retry current address
1274
+ } else if (rateAns.action === 'local') {
1275
+ selectedRpcUrl = defaultLocalUrl;
1276
+ i--; // retry current address
1277
+ } else {
1278
+ console.log(chalk.yellow(t('rateLimitSkip')));
1279
+ break;
1280
+ }
1281
+ }
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ function printHelp() {
1288
+ console.clear();
1289
+ console.log(chalk.cyan('================================================================'));
1290
+ console.log(chalk.cyan(` 🛡️ CryptoSeedRecovery - ${t('helpTitle')}`));
1291
+ console.log(chalk.cyan('================================================================\n'));
1292
+ console.log(chalk.bold(`${t('helpUsage')}`));
1293
+ console.log(' cryptoseed [arguments]\n');
1294
+ console.log(chalk.bold(`${t('helpArguments')}`));
1295
+ console.log(chalk.green(' -h, --help') + ` ${t('helpOptHelp')}`);
1296
+ console.log(chalk.green(' -i, --info') + ` ${t('helpOptInfo')}`);
1297
+ console.log(chalk.green(' -c, --create') + ` ${t('helpCommandCreate')}\n`);
1298
+ console.log(chalk.bold(`${t('helpWizard')}`));
1299
+ console.log(` ${t('helpWizardDesc')}\n`);
1300
+ console.log(chalk.bold(`Developed under b2 wallet ecosystem (${link('better2better', 'https://better2better.net')}, ${link('diegooris', 'https://diegohorantunes.web.app/')}).\n`));
1301
+ }
1302
+
1303
+ function printInfo() {
1304
+ console.clear();
1305
+ console.log(chalk.cyan('================================================================'));
1306
+ console.log(chalk.cyan(` 🛡️ CryptoSeedRecovery - ${t('infoTitle')}`));
1307
+ console.log(chalk.cyan('================================================================\n'));
1308
+
1309
+ console.log(chalk.yellow.bold(t('infoTimelines')));
1310
+ console.log('----------------------------------------------------------------');
1311
+ console.log(chalk.cyan(t('infoEra1')));
1312
+ console.log(` ${t('infoEra1Desc')}`);
1313
+ console.log(chalk.cyan(t('infoEra2')));
1314
+ console.log(` ${t('infoEra2Desc')}`);
1315
+ console.log(chalk.cyan(t('infoEra3')));
1316
+ console.log(` ${t('infoEra3Desc')}`);
1317
+ console.log(chalk.cyan(t('infoEra4')));
1318
+ console.log(` ${t('infoEra4Desc')}\n`);
1319
+
1320
+ console.log(chalk.yellow.bold(t('infoSupported')));
1321
+ console.log('----------------------------------------------------------------');
1322
+ console.log(t('infoBTC'));
1323
+ console.log(t('infoEVM'));
1324
+ console.log(t('infoWaves'));
1325
+ console.log(t('infoSol'));
1326
+ console.log(t('infoOthers') + '\n');
1327
+
1328
+ console.log(chalk.yellow.bold(t('infoFeatured')));
1329
+ console.log('----------------------------------------------------------------');
1330
+ console.log(t('infoB2Wallet'));
1331
+ console.log(t('infoMeta'));
1332
+ console.log(t('infoHardware') + '\n');
1333
+ console.log(chalk.cyan('================================================================'));
1334
+ }
1335
+
1336
+ async function runCreateSeedWizard() {
1337
+ console.clear();
1338
+ console.log(chalk.cyan('================================================================'));
1339
+ console.log(chalk.cyan(` 🛡️ CryptoSeedRecovery - ${t('createSeedTitle')}`));
1340
+ console.log(chalk.cyan('================================================================\n'));
1341
+
1342
+ let step = 'format';
1343
+ const history = [];
1344
+
1345
+ let format = '';
1346
+ let coin = 'BTC';
1347
+ let wallet = 'b2wallet';
1348
+ let pattern = null;
1349
+ let wordCount = 12;
1350
+ let language = 'en';
1351
+
1352
+ const blockchainChoices = [
1353
+ { name: 'Ethereum / EVMs (ETH, BNB, Polygon, Arbitrum, Optimism, Avalanche, Base, Fantom, Cronos, Harmony)', value: 'ETH' },
1354
+ { name: 'Bitcoin (BTC)', value: 'BTC' },
1355
+ { name: 'Litecoin (LTC)', value: 'LTC' },
1356
+ { name: 'Dogecoin (DOGE)', value: 'DOGE' },
1357
+ { name: 'Solana (SOL)', value: 'SOL' },
1358
+ { name: 'TRON (TRX)', value: 'TRX' },
1359
+ { name: 'Cardano (ADA)', value: 'ADA' },
1360
+ { name: 'Ripple (XRP)', value: 'XRP' },
1361
+ { name: 'Polkadot (DOT)', value: 'DOT' },
1362
+ { name: 'Kusama (KSM)', value: 'KSM' },
1363
+ { name: 'Cosmos (ATOM)', value: 'ATOM' },
1364
+ { name: 'Osmosis (OSMO)', value: 'OSMO' },
1365
+ { name: 'Waves (WAVES)', value: 'WAVES' },
1366
+ { name: 'Kaspa (KAS)', value: 'KAS' },
1367
+ { name: 'Stellar (XLM)', value: 'XLM' },
1368
+ { name: 'Zcash (ZEC)', value: 'ZEC' },
1369
+ { name: 'Bitcoin Cash (BCH)', value: 'BCH' },
1370
+ { name: 'Monero (XMR)', value: 'XMR' },
1371
+ { name: 'Dash (DASH)', value: 'DASH' },
1372
+ { name: 'Secret Network (SCRT)', value: 'SCRT' },
1373
+ { name: 'Injective (INJ)', value: 'INJ' },
1374
+ { name: 'Hedera (HBAR)', value: 'HBAR' },
1375
+ { name: 'NEM (XEM)', value: 'XEM' },
1376
+ { name: 'Chia (XCH)', value: 'XCH' },
1377
+ { name: 'Gnosis Chain (GNOSIS)', value: 'GNOSIS' },
1378
+ { name: 'Tezos (XTZ)', value: 'XTZ' },
1379
+ { name: 'Algorand (ALGO)', value: 'ALGO' },
1380
+ { name: 'Near (NEAR)', value: 'NEAR' },
1381
+ { name: 'Sui (SUI)', value: 'SUI' },
1382
+ { name: 'Aptos (APT)', value: 'APT' },
1383
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
1384
+ ];
1385
+
1386
+ while (step !== 'execute') {
1387
+ switch (step) {
1388
+ case 'format': {
1389
+ const formatAnswers = await inquirer.prompt([{
1390
+ type: 'select',
1391
+ name: 'format',
1392
+ message: t('chooseFormat'),
1393
+ choices: [
1394
+ { name: t('formatBip39'), value: 'bip39' },
1395
+ { name: t('formatElectrum'), value: 'electrum' },
1396
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
1397
+ ]
1398
+ }]);
1399
+ if (formatAnswers.format === '__back__') {
1400
+ return '__back_to_lang__';
1401
+ }
1402
+ format = formatAnswers.format;
1403
+ history.push('format');
1404
+ step = 'coin';
1405
+ break;
1406
+ }
1407
+
1408
+ case 'coin': {
1409
+ const blockchainAnswers = await inquirer.prompt([{
1410
+ type: 'select',
1411
+ name: 'coin',
1412
+ message: t('chooseBlockchain'),
1413
+ choices: blockchainChoices
1414
+ }]);
1415
+
1416
+ if (blockchainAnswers.coin === '__back__') {
1417
+ step = history.pop() || 'format';
1418
+ } else {
1419
+ coin = blockchainAnswers.coin;
1420
+ history.push('coin');
1421
+ step = 'wallet';
1422
+ }
1423
+ break;
1424
+ }
1425
+
1426
+ case 'wallet': {
1427
+ let walletChoices = [];
1428
+ if (coin === 'ETH' || coin === 'GNOSIS') {
1429
+ walletChoices = [
1430
+ { name: t('walletB2'), value: 'b2wallet' },
1431
+ { name: t('walletMeta'), value: 'metamask' },
1432
+ { name: t('walletTrust'), value: 'trust' },
1433
+ { name: `Ledger (${t('walletLedger')} m/44'/60'/0'/0)`, value: 'ledger' },
1434
+ { name: `Trezor (${t('walletTrezor')} m/44'/60'/0'/0)`, value: 'trezor' }
1435
+ ];
1436
+ } else if (coin === 'BTC') {
1437
+ walletChoices = [
1438
+ { name: t('walletB2'), value: 'b2wallet' },
1439
+ { name: 'Electrum', value: 'electrum' },
1440
+ { name: t('walletTrust'), value: 'trust' },
1441
+ { name: t('walletLedger'), value: 'ledger' },
1442
+ { name: t('walletTrezor'), value: 'trezor' }
1443
+ ];
1444
+ } else if (coin === 'SOL') {
1445
+ walletChoices = [
1446
+ { name: t('walletB2'), value: 'b2wallet' },
1447
+ { name: t('walletPhantom'), value: 'phantom' },
1448
+ { name: t('walletTrust'), value: 'trust' },
1449
+ { name: t('walletLedger'), value: 'ledger' }
1450
+ ];
1451
+ } else if (coin === 'ADA') {
1452
+ walletChoices = [
1453
+ { name: t('walletYoroi'), value: 'yoroi' },
1454
+ { name: 'Daedalus Wallet', value: 'daedalus' },
1455
+ { name: t('walletLedger'), value: 'ledger' }
1456
+ ];
1457
+ } else {
1458
+ walletChoices = [
1459
+ { name: t('walletB2'), value: 'b2wallet' },
1460
+ { name: t('walletTrust'), value: 'trust' },
1461
+ { name: t('walletLedger'), value: 'ledger' },
1462
+ { name: t('walletTrezor'), value: 'trezor' }
1463
+ ];
1464
+ }
1465
+ walletChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
1466
+
1467
+ const walletAnswers = await inquirer.prompt([{
1468
+ type: 'select',
1469
+ name: 'wallet',
1470
+ message: t('chooseWallet'),
1471
+ choices: walletChoices
1472
+ }]);
1473
+
1474
+ if (walletAnswers.wallet === '__back__') {
1475
+ step = history.pop() || 'coin';
1476
+ } else {
1477
+ wallet = walletAnswers.wallet;
1478
+ history.push('wallet');
1479
+ if (wallet !== 'b2wallet' && (coin === 'BTC' || coin === 'LTC' || coin === 'SOL' || coin === 'ADA')) {
1480
+ step = 'pattern';
1481
+ } else {
1482
+ pattern = null;
1483
+ step = (format === 'bip39') ? 'wordCount' : 'language';
1484
+ }
1485
+ }
1486
+ break;
1487
+ }
1488
+
1489
+ case 'pattern': {
1490
+ let patternChoices = [];
1491
+ if (coin === 'BTC') {
1492
+ patternChoices = [
1493
+ { name: t('btcSegwit'), value: 'segwit' },
1494
+ { name: t('btcNested'), value: 'nested' },
1495
+ { name: t('btcLegacy'), value: 'legacy' },
1496
+ { name: t('btcTaproot'), value: 'taproot' }
1497
+ ];
1498
+ } else if (coin === 'LTC') {
1499
+ patternChoices = [
1500
+ { name: t('ltcSegwit'), value: 'segwit' },
1501
+ { name: t('ltcLegacy'), value: 'legacy' }
1502
+ ];
1503
+ } else if (coin === 'SOL') {
1504
+ patternChoices = [
1505
+ { name: t('solPhantom'), value: 'phantom' },
1506
+ { name: t('solSollet'), value: 'sollet' }
1507
+ ];
1508
+ } else if (coin === 'ADA') {
1509
+ patternChoices = [
1510
+ { name: t('adaShelley'), value: 'shelley' },
1511
+ { name: t('adaByron'), value: 'byron' }
1512
+ ];
1513
+ }
1514
+ patternChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
1515
+
1516
+ const patternAnswers = await inquirer.prompt([{
1517
+ type: 'select',
1518
+ name: 'pattern',
1519
+ message: coin === 'BTC' ? t('choosePatternBTC') : (coin === 'LTC' ? t('choosePatternLTC') : (coin === 'SOL' ? t('choosePatternSOL') : t('choosePatternADA'))),
1520
+ choices: patternChoices
1521
+ }]);
1522
+
1523
+ if (patternAnswers.pattern === '__back__') {
1524
+ step = history.pop() || 'wallet';
1525
+ } else {
1526
+ pattern = patternAnswers.pattern;
1527
+ history.push('pattern');
1528
+ step = (format === 'bip39') ? 'wordCount' : 'language';
1529
+ }
1530
+ break;
1531
+ }
1532
+
1533
+ case 'wordCount': {
1534
+ const wordCountAnswers = await inquirer.prompt([{
1535
+ type: 'select',
1536
+ name: 'wordCount',
1537
+ message: t('chooseWordCount'),
1538
+ choices: [
1539
+ { name: '12 Words / Palavras', value: 12 },
1540
+ { name: '24 Words / Palavras', value: 24 },
1541
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
1542
+ ]
1543
+ }]);
1544
+
1545
+ if (wordCountAnswers.wordCount === '__back__') {
1546
+ step = history.pop() || 'wallet';
1547
+ } else {
1548
+ wordCount = wordCountAnswers.wordCount;
1549
+ history.push('wordCount');
1550
+ step = 'language';
1551
+ }
1552
+ break;
1553
+ }
1554
+
1555
+ case 'language': {
1556
+ if (format === 'bip39') {
1557
+ let langChoices = [
1558
+ { name: 'Inglês (English)', value: 'en' },
1559
+ { name: 'Português (Portuguese)', value: 'pt' },
1560
+ { name: 'Espanhol (Spanish)', value: 'es' },
1561
+ { name: 'Francês (French)', value: 'fr' },
1562
+ { name: 'Italiano (Italian)', value: 'it' },
1563
+ { name: 'Tcheco (Czech)', value: 'cz' },
1564
+ { name: 'Japonês (Japanese)', value: 'ja' },
1565
+ { name: 'Coreano (Korean)', value: 'ko' },
1566
+ { name: 'Chinês Simplificado (Simplified Chinese)', value: 'zh_cn' },
1567
+ { name: 'Chinês Tradicional (Traditional Chinese)', value: 'zh_tw' }
1568
+ ];
1569
+ let langMessage = t('chooseLang');
1570
+
1571
+ if (coin === 'ALGO') {
1572
+ langChoices = [{ name: `Inglês (English) - [${t('algoLangOnly')}]`, value: 'en' }];
1573
+ langMessage = `${chalk.yellow(t('algoLangOnly'))}\n ${t('chooseLang')}`;
1574
+ } else if (coin === 'ADA' && pattern === 'byron') {
1575
+ langChoices = [{ name: `Inglês (English) - [${t('adaByronLangOnly')}]`, value: 'en' }];
1576
+ langMessage = `${chalk.yellow(t('adaByronLangOnly'))}\n ${t('chooseLang')}`;
1577
+ }
1578
+
1579
+ langChoices.push({ name: chalk.yellow(t('backChoice')), value: '__back__' });
1580
+
1581
+ const langAnswers = await inquirer.prompt([{
1582
+ type: 'select',
1583
+ name: 'lang',
1584
+ message: langMessage,
1585
+ choices: langChoices
1586
+ }]);
1587
+
1588
+ if (langAnswers.lang === '__back__') {
1589
+ step = history.pop() || 'wordCount';
1590
+ } else {
1591
+ language = langAnswers.lang;
1592
+ step = 'execute';
1593
+ }
1594
+ } else {
1595
+ let langChoices = [
1596
+ { name: `Inglês (English) - [${t('electrumLangOnly')}]`, value: 'en' },
1597
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
1598
+ ];
1599
+
1600
+ const langAnswers = await inquirer.prompt([{
1601
+ type: 'select',
1602
+ name: 'lang',
1603
+ message: `${chalk.yellow(t('electrumLangOnly'))}\n ${t('chooseLang')}`,
1604
+ choices: langChoices
1605
+ }]);
1606
+
1607
+ if (langAnswers.lang === '__back__') {
1608
+ step = history.pop() || 'wallet';
1609
+ } else {
1610
+ language = 'en';
1611
+ step = 'execute';
1612
+ }
1613
+ }
1614
+ break;
1615
+ }
1616
+ }
1617
+ }
1618
+
1619
+ // 6. Generate Mnemonic Phrase securely
1620
+ console.log('\n' + chalk.cyan(t('generatingSeed')));
1621
+ const crypto = require('crypto');
1622
+ let generatedPhrase = '';
1623
+
1624
+ if (format === 'bip39') {
1625
+ const entropyBytes = wordCount === 12 ? 16 : 32;
1626
+ const entropy = crypto.randomBytes(entropyBytes);
1627
+ const wlName = language === 'en' ? 'en' : language;
1628
+ const wl = ethers.wordlists[wlName] || ethers.wordlists[wlName.replace('-', '_')];
1629
+ generatedPhrase = ethers.Mnemonic.entropyToPhrase(entropy, wl);
1630
+ } else {
1631
+ const entropy = crypto.randomBytes(16);
1632
+ const hex = entropy.toString('hex');
1633
+ const { mnEncode } = require('../lib/electrum-legacy');
1634
+ generatedPhrase = mnEncode(hex).join(' ');
1635
+ }
1636
+
1637
+ // 7. Derive Address
1638
+ console.log(chalk.cyan(t('derivingAddress')));
1639
+ let derivedAddr = '';
1640
+ try {
1641
+ derivedAddr = deriveAddress(generatedPhrase, wallet, coin, 0, pattern, language);
1642
+ } catch (err) {
1643
+ console.log(chalk.red(`Error deriving address: ${err.message}`));
1644
+ }
1645
+
1646
+ // 8. Output results beautifully
1647
+ console.log('\n' + chalk.green.bold('================================================================'));
1648
+ console.log(chalk.green.bold(`🎉 ${t('generatedSeedHeader')}`));
1649
+ console.log(chalk.green.bold('================================================================'));
1650
+ console.log(chalk.yellow.bold(`\n ${generatedPhrase}\n`));
1651
+ console.log(chalk.green.bold('----------------------------------------------------------------'));
1652
+ console.log(chalk.white(`${t('derivedAddressResult')} `) + chalk.cyan.bold(derivedAddr));
1653
+
1654
+ // Reconstruct path for info
1655
+ let derivationPath = `m/44'/60'/0'/0/0`;
1656
+ if (wallet === 'b2wallet') {
1657
+ derivationPath = `B2 Wallet Custom Multichain PBKDF2/XOR`;
1658
+ } else {
1659
+ if (coin === 'BTC') {
1660
+ if (pattern === 'legacy') derivationPath = `m/44'/0'/0'/0/0`;
1661
+ else if (pattern === 'nested') derivationPath = `m/49'/0'/0'/0/0`;
1662
+ else if (pattern === 'taproot') derivationPath = `m/86'/0'/0'/0/0`;
1663
+ else derivationPath = `m/84'/0'/0'/0/0`;
1664
+ } else if (coin === 'LTC') {
1665
+ if (pattern === 'legacy') derivationPath = `m/44'/2'/0'/0/0`;
1666
+ else derivationPath = `m/84'/2'/0'/0/0`;
1667
+ } else if (coin === 'SOL') {
1668
+ if (pattern === 'sollet') derivationPath = `m/44'/501'/0'/0/0`;
1669
+ else derivationPath = `m/44'/501'/0'/0'`;
1670
+ } else if (coin === 'ADA') {
1671
+ if (pattern === 'byron') derivationPath = `m/44'/1815'/0'/0/0`;
1672
+ else derivationPath = `m/1852'/1815'/0'/0/0`;
1673
+ } else if (coin === 'ETH') {
1674
+ derivationPath = `m/44'/60'/0'/0/0`;
1675
+ } else if (coin === 'ALGO') {
1676
+ derivationPath = `m/44'/283'/0'/0'/0'`;
1677
+ }
1678
+ }
1679
+ console.log(chalk.white(`${t('derivationPathLabel')} `) + chalk.gray(derivationPath));
1680
+ console.log(chalk.green.bold('================================================================\n'));
1681
+
1682
+ // Escreve o relatório TXT físico de semente gerada
1683
+ const reportLines = [
1684
+ '===========================================================',
1685
+ t('generatedSeedReportHeader'),
1686
+ '===========================================================',
1687
+ t('reportDateTime', new Date().toLocaleString()),
1688
+ t('reportWallet', wallet.toUpperCase()),
1689
+ t('reportBlockchain', coin),
1690
+ t('reportDerivation', pattern ? pattern.toUpperCase() : 'STANDARD/PADRÃO'),
1691
+ '-----------------------------------------------------------',
1692
+ 'SEED PHRASE / FRASE DA SEMENTE:',
1693
+ ` ${generatedPhrase}`,
1694
+ '-----------------------------------------------------------',
1695
+ `${t('derivedAddressResult')} ${derivedAddr}`,
1696
+ `${t('derivationPathLabel')} ${derivationPath}`,
1697
+ '===========================================================',
1698
+ t('reportFooterSafe'),
1699
+ t('reportFooterDev')
1700
+ ];
1701
+
1702
+ const reportFilename = `semente_gerada_${Date.now()}.txt`;
1703
+ const reportPath = path.join(process.cwd(), reportFilename);
1704
+ fs.writeFileSync(reportPath, reportLines.join('\n'), 'utf8');
1705
+
1706
+ console.log(chalk.cyan(t('seedGeneratedFileSaved', reportPath)) + '\n');
1707
+ console.log(chalk.red.bold(`⚠️ ${t('reportFooterSafe')}\n`));
1708
+ }
1709
+
1710
+ if (require.main === module) {
1711
+ (async () => {
1712
+ while (true) {
1713
+ // 1. ESCOLHA DO IDIOMA DA INTERFACE DO USUÁRIO (Sempre o primeiro passo)
1714
+ console.clear();
1715
+ console.log(chalk.cyan('================================================================'));
1716
+ console.log(chalk.cyan(' 🛡️ CryptoSeedRecovery - Select Interface Language'));
1717
+ console.log(chalk.cyan('================================================================\n'));
1718
+
1719
+ const uiLangAnswers = await inquirer.prompt([{
1720
+ type: 'select',
1721
+ name: 'uiLang',
1722
+ message: 'Choose your interface language / Selecione o idioma da interface:',
1723
+ choices: [
1724
+ { name: 'English (en)', value: 'en' },
1725
+ { name: 'Português (pt)', value: 'pt' },
1726
+ { name: 'Español (es)', value: 'es' },
1727
+ { name: '简体中文 (zh)', value: 'zh' },
1728
+ { name: '日本語 (ja)', value: 'ja' }
1729
+ ]
1730
+ }]);
1731
+
1732
+ currentLocale = uiLangAnswers.uiLang;
1733
+
1734
+ const args = process.argv.slice(2);
1735
+ if (args.includes('--help') || args.includes('-h')) {
1736
+ printHelp();
1737
+ break;
1738
+ } else if (args.includes('--info') || args.includes('-i')) {
1739
+ printInfo();
1740
+ break;
1741
+ } else if (args.includes('--create') || args.includes('-c')) {
1742
+ const res = await runCreateSeedWizard();
1743
+ if (res === '__back_to_lang__') {
1744
+ continue;
1745
+ }
1746
+ break;
1747
+ } else {
1748
+ // Exibe o menu principal interativo para escolha da ação
1749
+ console.clear();
1750
+ printHeader();
1751
+ const actionAnswer = await inquirer.prompt([{
1752
+ type: 'select',
1753
+ name: 'action',
1754
+ message: t('menuChooseAction'),
1755
+ choices: [
1756
+ { name: t('actionRecover'), value: 'recover' },
1757
+ { name: t('actionCreate'), value: 'create' },
1758
+ { name: chalk.yellow(t('backChoice')), value: '__back__' }
1759
+ ]
1760
+ }]);
1761
+
1762
+ if (actionAnswer.action === '__back__') {
1763
+ continue;
1764
+ }
1765
+
1766
+ if (actionAnswer.action === 'create') {
1767
+ const res = await runCreateSeedWizard();
1768
+ if (res === '__back_to_lang__') {
1769
+ continue;
1770
+ }
1771
+ break;
1772
+ } else {
1773
+ const res = await main();
1774
+ if (res === '__back_to_lang__') {
1775
+ continue;
1776
+ }
1777
+ break;
1778
+ }
1779
+ }
1780
+ }
1781
+ })().catch(err => {
1782
+ console.error(err);
1783
+ process.exit(1);
1784
+ });
1785
+ }