@yerofey/cryptowallet-cli 1.36.2 → 1.37.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/README.md CHANGED
@@ -13,9 +13,10 @@
13
13
  - [x] Generate brand new crypto wallet address (offline)
14
14
  - [x] Generate wallet address with prefix (string at the start): [`-p`] (case-insensitive) or [`-P`] (case-sensitive)
15
15
  - [x] Generate wallet address with suffix (string at the end): [`-s`] (case-insensitive) or [`-S`] (case-sensitive)
16
- - [x] Generate wallet with different formats (for Bitcoin: Legacy, SegWit, Bech32; for BNB: BEP2, BEP20): [`-f`]
16
+ - [x] Generate wallet with different formats (for Bitcoin: `legacy`, `segwit`, `bech32`, `taproot`; for BNB: `bep2`, `bep20`, `erc20`; for TON: `w5` and all previous ones): [`-f`]
17
17
  - [x] Generate wallet from your desired mnemonic string: [`-m`]
18
18
  - [x] Generate mnemonic string: [`-m`] or [`-m 12`] or [`-m 15`] or [`-m 18`] or [`-m 21`] or [`-m 24`]
19
+ - [x] Use multiple threads (cores) for faster generation [`-t`]
19
20
  - [x] Generate a lot of wallets at once: [`-n`]
20
21
  - [x] Save result into a CSV file: [`--csv`]
21
22
  - [x] Copy the generated mnemonic to the clipboard: [`-C` or `--copy`]
@@ -43,12 +44,18 @@ $ yarn global add @yerofey/cryptowallet-cli
43
44
  # generate random EVM-compatible wallet (for Ethereum, Polygon, any L1/L2 EVM-compatible chain, etc.)
44
45
  $ cw
45
46
 
47
+ # generate random wallet and copy the mnemonic to the clipboard
48
+ $ cw -C
49
+
46
50
  # generate random BTC wallet (default format: bech32 - "bc1q...")
47
51
  $ cw -c btc
48
52
 
49
53
  # generate random mnemonic string (12 words) to import in any wallet app
50
54
  $ cw -m
51
55
 
56
+ # generate random wallet faster (use all available CPU cores)
57
+ $ cw -t
58
+
52
59
  # generate random mnemonic string of a specific length (12, 15, 18, 21 or 24 words)
53
60
  $ cw -m 12
54
61
  $ cw -m 15
@@ -118,14 +125,15 @@ $ cw -l
118
125
  - `EVM` (Ethereum, Base, Arbitrum, Optimism, Polygon, L2/L3, etc.) **default**
119
126
  - `BTC` (Bitcoin) [legacy, segwit, bech32, taproot]
120
127
  - `ETH` (Ethereum)
128
+ - `SOL` (Solana)
121
129
  - `BNB` (Binance Coin) [BEP2, BEP20, ERC20]
122
130
  - `BSC` (Binance Smart Chain)
123
- - `SOL` (Solana)
131
+ - `DOGE` (Dogecoin) [legacy, segwit, bech32]
124
132
  - `TRX` (Tron)
133
+ - `SUI` (Sui)
125
134
  - `TON` (The Open Network) [W5, V2-V5, simple]
126
- - `DOGE` (Dogecoin) [legacy, segwit, bech32]
127
- - `BCH` (Bitcoin Cash)
128
135
  - `LTC` (Litecoin) [legacy, segwit, bech32]
136
+ - `BCH` (Bitcoin Cash)
129
137
  - `ETC` (Ethereum Classic)
130
138
  - `XTZ` (Tezos)
131
139
  - `DASH` (Dash)
@@ -168,6 +176,7 @@ $ cw -l
168
176
  - `-P` or `--prefix-sensitive`: Specify desired prefix of the wallet address (**case-sensitive**)
169
177
  - `-s` or `--suffix`: Specify desired suffix for the wallet address (**case-insensitive**)
170
178
  - `-S` or `--suffix-sensitive`: Specify desired suffix for the wallet address (**case-sensitive**)
179
+ - `-t` or `--threads`: Use multiple threads (cores) for faster generation (default: half of the available cores), or pass `0` to use all available cores (not recommended for laptops)
171
180
  - `-q` or `--qr`: Display QR code with the generated wallet address (works only without `-n` argument)
172
181
  - `-v` or `--version`: Display current version of CW tool
173
182
 
@@ -187,8 +196,6 @@ $ cw -l
187
196
  - [x] v22.x ✅
188
197
  - [x] v23.x ✅
189
198
 
190
- *tested on Ubuntu 22.04 & Mac M1*
191
-
192
199
  ## TODO
193
200
 
194
201
  - [ ] SegWit Bech32 wallet address support for all Bitcoin forks
package/cli.js CHANGED
@@ -1,41 +1,176 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ import os from 'node:os';
5
+ import {
6
+ Worker,
7
+ isMainThread,
8
+ parentPort,
9
+ workerData,
10
+ } from 'node:worker_threads';
11
+ import { fileURLToPath } from 'node:url';
4
12
  import chalk from 'chalk';
5
13
  import { options } from './src/options.js';
6
14
  import { log, supportedChains } from './src/utils.js';
7
15
  import Method from './src/Method.js';
8
16
 
9
- (async () => {
10
- if (options.list !== undefined) {
17
+ // get the current file path
18
+ const __filename = fileURLToPath(import.meta.url);
19
+
20
+ if (options.list !== undefined) {
21
+ (async () => {
11
22
  return new Method('list').init();
12
- }
23
+ })();
24
+ }
13
25
 
14
- // generate mnemonic string if no argument is passed or only the mnemonic length is passed
15
- if (
16
- options.mnemonic &&
17
- (options.mnemonic === true ||
18
- options.mnemonic === '' ||
19
- options.mnemonic.split(' ').length === 1)
20
- ) {
21
- return new Method('mnemonic').init({ mnemonic: options.mnemonic, copy: options?.copy || false });
22
- }
26
+ // generate mnemonic string if no argument is passed or only the mnemonic length is passed
27
+ if (
28
+ options.mnemonic &&
29
+ (options.mnemonic === true ||
30
+ options.mnemonic === '' ||
31
+ options.mnemonic.split(' ').length === 1)
32
+ ) {
33
+ (async () => {
34
+ return new Method('mnemonic').init({
35
+ mnemonic: options.mnemonic,
36
+ copy: options?.copy || false,
37
+ });
38
+ })();
39
+ }
23
40
 
24
- if (options.version) {
41
+ if (options.version) {
42
+ (async () => {
25
43
  return new Method('version').init();
26
- }
44
+ })();
45
+ }
27
46
 
28
- if (options.donate) {
47
+ if (options.donate) {
48
+ (async () => {
29
49
  return new Method('donate').init();
50
+ })();
51
+ }
52
+
53
+ const chain = (options.chain.toUpperCase() || 'EVM').trim();
54
+ if (!supportedChains.includes(chain)) {
55
+ log(chalk.red('⛔️ Error: this chain is not supported!'));
56
+ process.exit(1);
57
+ }
58
+ options.b = chain; // ensure the chain is passed to the Method class
59
+
60
+ // multi-threads mode (only for suffix, prefix, number)
61
+ const allMachineThreads = os.cpus().length;
62
+ const availableThreads = os.cpus().length - 1; // leave 1 core for the main thread
63
+ const defaultThreads = os.cpus().length / 2; // use half of the available threads
64
+ const inputThreads = parseInt(options.threads || 1, 10); // default to 1 thread
65
+ let numThreads = defaultThreads; // default to half of the available threads
66
+ if (inputThreads > availableThreads) {
67
+ numThreads = defaultThreads;
68
+ } else if (inputThreads <= 0) {
69
+ numThreads = availableThreads;
70
+ }
71
+
72
+ if (isMainThread) {
73
+ if (numThreads === 1) {
74
+ console.log(
75
+ chalk.green(
76
+ '🐢 Using only 1 thread to generate a wallet, this might take a while...'
77
+ )
78
+ );
79
+ } else {
80
+ console.log(
81
+ chalk.green(`🏎️💨 Using ${numThreads}/${allMachineThreads} threads to generate a wallet...`)
82
+ );
30
83
  }
31
84
 
32
- const chain = options.chain.toUpperCase() || 'EVM';
33
- if (supportedChains.includes(chain)) {
34
- return new Method('wallet', {
35
- chain,
36
- options,
37
- }).init();
85
+ const workers = [];
86
+
87
+ // create a shared buffer to communicate between workers
88
+ const sharedBuffer = new SharedArrayBuffer(4); // 4 bytes for the flag
89
+ const sharedArray = new Int32Array(sharedBuffer);
90
+ sharedArray[0] = 0; // 0 - not found, 1 - found
91
+
92
+ for (let i = 0; i < numThreads; i++) {
93
+ const worker = new Worker(__filename, {
94
+ workerData: {
95
+ workerId: i,
96
+ totalWorkers: numThreads,
97
+ chain,
98
+ options: JSON.parse(JSON.stringify(options)),
99
+ sharedBuffer,
100
+ },
101
+ stdout: true, // enable capturing stdout
102
+ });
103
+
104
+ // pipe worker stdout to main process
105
+ worker.stdout.on('data', (data) => {
106
+ if (Atomics.load(sharedArray, 0) === 0) {
107
+ process.stdout.write(data); // forward stdout to main process
108
+
109
+ return data;
110
+ }
111
+ });
112
+
113
+ worker.on('message', (message) => {
114
+ if (Atomics.load(sharedArray, 0) === 0) {
115
+ // set the shared flag to 1 to stop all workers
116
+ Atomics.store(sharedArray, 0, 1);
117
+
118
+ // print the log output from the worker
119
+ process.stdout.write(message.log);
120
+
121
+ // terminate all workers
122
+ workers.forEach((w) => w.terminate());
123
+
124
+ process.exit(0);
125
+ }
126
+ });
127
+
128
+ worker.on('error', (err) =>
129
+ console.error(`❌ Worker error: ${err.message}`)
130
+ );
131
+
132
+ workers.push(worker);
38
133
  }
134
+ } else {
135
+ // worker thread (multi-threaded mode)
136
+ (async () => {
137
+ try {
138
+ // read shared flag before starting work
139
+ const sharedArray = new Int32Array(workerData.sharedBuffer);
140
+ if (Atomics.load(sharedArray, 0) === 1) {
141
+ process.exit(0); // exit early if a wallet has already been found
142
+ }
143
+
144
+ // create a new Method instance
145
+ const method = new Method('wallet', {
146
+ b: workerData.chain,
147
+ chain: workerData.chain,
148
+ options: workerData.options,
149
+ });
150
+
151
+ // capture stdout logs in a buffer
152
+ let logOutput = '';
153
+ const originalWrite = process.stdout.write;
154
+ process.stdout.write = (chunk, encoding, callback) => {
155
+ logOutput += chunk.toString();
156
+ if (callback) callback();
157
+ };
158
+
159
+ // print the wallet to stdout
160
+ await method.init();
161
+
162
+ // restore stdout
163
+ process.stdout.write = originalWrite;
164
+
165
+ // check the shared flag again before sending the result
166
+ if (Atomics.load(sharedArray, 0) === 1) {
167
+ process.exit(0);
168
+ }
39
169
 
40
- log(chalk.red('⛔️ Error: this blockchain is not supported!'));
41
- })();
170
+ // send the log output to the main thread
171
+ parentPort.postMessage({ log: logOutput });
172
+ } catch (err) {
173
+ console.error(`Worker ${workerData.workerId} failed: ${err.message}`);
174
+ }
175
+ })();
176
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yerofey/cryptowallet-cli",
3
- "version": "1.36.2",
3
+ "version": "1.37.0",
4
4
  "description": "Crypto wallet generator CLI tool",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/yerofey/cryptowallet-cli",
package/src/Method.js CHANGED
@@ -312,10 +312,6 @@ class Method {
312
312
  addressHighlightedPart
313
313
  )}${addressLastPart}${magenta(addressHighlightedSuffix)}`
314
314
  );
315
- // DEBUG
316
- if (IS_DEV) {
317
- log(`___ ${item.address}`);
318
- }
319
315
  } else {
320
316
  log(`👛 ${item.address}`);
321
317
  }
@@ -362,10 +358,6 @@ class Method {
362
358
  addressHighlightedPart
363
359
  )}${addressLastPart}`
364
360
  );
365
- // DEBUG
366
- if (IS_DEV) {
367
- log(`___ ${item.address}`);
368
- }
369
361
  } else {
370
362
  log(`👛 ${item.address}`);
371
363
  }
@@ -389,10 +381,6 @@ class Method {
389
381
  log(
390
382
  `👛 ${addressFirstPart}${magenta(addressHighlightedSuffix)}`
391
383
  );
392
- // DEBUG
393
- if (IS_DEV) {
394
- log(`___ ${item.address}`);
395
- }
396
384
  } else {
397
385
  log(`👛 ${item.address}`);
398
386
  }
package/src/options.js CHANGED
@@ -3,10 +3,7 @@ import { program } from 'commander';
3
3
  program.option('-b <ticker>', 'Wallet for specific blockchain', 'EVM');
4
4
  program.option('-c, --chain <ticker>', 'Wallet for specific blockchain', 'EVM');
5
5
  program.option('-C, --copy', 'Copy the result to the clipboard');
6
- program.option(
7
- '-D, --csv [filename]',
8
- 'Save result into CSV file'
9
- );
6
+ program.option('-D, --csv [filename]', 'Save result into CSV file');
10
7
  program.option(
11
8
  '-f, --format <format>',
12
9
  'Wallet format type (for cryptos with multiple wallet formats)'
@@ -37,8 +34,12 @@ program.option(
37
34
  '-S, --suffix-sensitive <suffix>',
38
35
  'Desired wallet suffix (case-sensitive)'
39
36
  );
37
+ program.option(
38
+ '-t, --threads <threads>',
39
+ 'Number of threads (cores) to use for wallet generation'
40
+ );
40
41
  program.option('-v, --version', 'Display cryptowallet version');
41
- program.option('-T, --donate', 'Donate to the project');
42
+ program.option('--donate', 'Donate to the project');
42
43
  program.parse();
43
44
 
44
45
  export const options = program.opts();