Skip to content

Commit

Permalink
v1.1.16: added BTC bech32 wallets support, -n
Browse files Browse the repository at this point in the history
  • Loading branch information
yerofey committed Aug 27, 2021
1 parent ea24f5c commit 79ec6cf
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 71 deletions.
54 changes: 32 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ $ cryptowallet -c BTC -p ABC
# generate random BTC wallet with desired prefix (case insensitive)
$ cryptowallet -c BTC -pi abc

# generate BTC bech32 wallet
$ cryptowallet -c BTC -f bech32

# generate BTC bech32 wallet from mnemonic string
$ cryptowallet -c BTC -f bech32 -m "radio bright pizza pluck family crawl palm flame forget focus stock stadium"

# generate N of BTC bech32 wallets from mnemonic string
$ cryptowallet -c BTC -f bech32 -m "radio bright pizza pluck family crawl palm flame forget focus stock stadium" -n 10

# generate ERC-like wallet from mnemonic string
$ cryptowallet -m "radio bright pizza pluck family crawl palm flame forget focus stock stadium"

Expand All @@ -46,33 +55,34 @@ $ cryptowallet -l
```

## Cryptos supported
- `BTC` (Bitcoin) [legacy: 1...]
- `ETH` (Ethereum) *supports -m "mnemonic string"*
- `BNB` (Binance Coin: BEP2, BEP20 - BSC) [-f: BEP2, BEP20] *supports -m "mnemonic string"*
- `BTC` (Bitcoin) [legacy, bech32]
- `ETH` (Ethereum)
- `BNB` (Binance Coin) [BEP2, BEP20]
- `DOGE` (Dogecoin)
- `BCH` (Bitcoin Cash)
- `LTC` (Litecoin)
- `POLYGON` (Polygon) *supports -m "mnemonic string"*
- `TRX` (Tron)
- `XTZ` (Tezos)
- `DASH` (Dash)
- `DCR` (Decred)
- `ZEC` (Zcash)
- `QTUM` (Qtum)
- `BTG` (Bitcoin Gold)
- `DGB` (DigiByte)
- `RDD` (ReddCoin)
- `VTC` (Vertcoin)
- `MONA` (MonaCoin)
- `NMC` (NameCoin)
- `PPC` (PeerCoin)
- `BLK` (BlackCoin)
- `VIA` (Viacoin)
- `NBT` (NIX Bridge Token)
- `POLYGON` (Polygon)
- `TRX` (Tron)
- `XTZ` (Tezos)
- `DASH` (Dash)
- `DCR` (Decred)
- `ZEC` (Zcash)
- `QTUM` (Qtum)
- `BTG` (Bitcoin Gold)
- `DGB` (DigiByte)
- `RDD` (ReddCoin)
- `VTC` (Vertcoin)
- `MONA` (MonaCoin)
- `NMC` (NameCoin)
- `PPC` (PeerCoin)
- `BLK` (BlackCoin)
- `VIA` (Viacoin)
- `NBT` (NIX Bridge Token)

## Highlights
- Supports 22 cryptos
- Supports address prefix
- 22 cryptos supported
- Wallet prefix supported
- Wallet from mnemonic
- Works fully offline

## Author
Expand Down
70 changes: 54 additions & 16 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ program.option('-c, --coin <ticker>', 'Wallet for specific coin', 'ERC');
program.option('-f, --format <format>', 'Wallet format type (for cryptos with multiple wallet formats)');
program.option('-l, --list', 'List all supported cryptos');
program.option('-m, --mnemonic <mnemonic>', 'Generate wallet from mnemonic string');
program.option('-n, --number <number>', 'Number of wallets to generate (if supported)');
program.option('-p, --prefix <prefix>', 'Desired wallet prefix (case sensitive)');
program.option('-pi, --prefix-ignorecase <prefix>', 'Desired wallet prefix (case insensitive)');
program.parse();
Expand All @@ -20,6 +21,7 @@ const options = program.opts();
const coin = (options.coin).toUpperCase() || '';
const format = options.format || '';
const mnemonic = options.mnemonic || '';
const number = options.number || 1;
const prefix = options.prefix || options.prefixIgnorecase || '';
const prefixIgnoreCase = options.prefixIgnorecase !== undefined;

Expand Down Expand Up @@ -49,6 +51,7 @@ async function run() {
}

const coinRow = supportedCoins[coin];
// TODO: prefix in formats

let wallet = {};
let prefixFound = false;
Expand All @@ -62,7 +65,12 @@ async function run() {
const startsWithSymbols = coinRow.startsWith.split('|');
loop:
while (true) {
wallet = await generateWallet(coin, coinRow, format, mnemonic);
wallet = await generateWallet(coin, {
coinRow,
format,
mnemonic,
number
});
for (let firstSymbol of startsWithSymbols) {
if (!prefixIgnoreCase && wallet.address.startsWith(firstSymbol + '' + prefix) || prefixIgnoreCase && (wallet.address).toUpperCase().startsWith((firstSymbol + '' + prefix).toUpperCase())) {
prefixFound = true;
Expand All @@ -83,39 +91,69 @@ async function run() {
if (prefix) {
log(`😢 ${chalk.yellow('Sorry, ' + coin + ' doesn\'t support prefix yet...')}`);
}
wallet = await generateWallet(coin, coinRow, format, mnemonic);
wallet = await generateWallet(coin, {
coinRow,
format,
mnemonic,
number
});
}

if (mnemonic != '' && wallet.mnemonic == undefined) {
log(`😢 ${chalk.yellow('Sorry, ' + coin + (wallet.format != undefined ? ' (' + wallet.format + ')' : '') + ' doesn\'t support mnemonic yet...')}`);
}

if (wallet.error !== undefined) {
log(`⛔️ ${chalk.red(`Error: ${wallet.error}`)}`);
return;
}

log(`✨ ${chalk.green('Done!')} ${chalk.blueBright('Here is your brand new ' + (coinRow.name || coin) + ' wallet' + (wallet.format ? (' (' + wallet.format + ')') : '') + (prefixFound ? ' with "' + prefix + '" prefix' : '') + ':')}\n`);
if (prefixFound) {
const addressCutLength = coinRow.startsWith.length + prefix.length;
log(`👛 ${coinRow.startsWith}${chalk.magenta(wallet.address.slice(coinRow.startsWith.length, addressCutLength))}${wallet.address.slice(addressCutLength)}`);
log(`✨ ${chalk.green('Done!')} ${chalk.blueBright('Here is your brand new ' + (coinRow.name || coin) + (wallet.format ? (' (' + wallet.format + ')') : '') + ' wallet' + (prefixFound ? ' with "' + prefix + '" prefix' : '') + ':')}\n`);

if (wallet.addresses !== undefined) {
if (wallet.privateExtendedKey) {
log(`🔐 ${wallet.privateExtendedKey}`);
}
if (wallet.mnemonic) {
log(`📄 ${wallet.mnemonic}`);
}

for (const item of wallet.addresses) {
log();
log(`🆔 ${item.index}`);
log(`👛 ${item.address}`);
log(`🔑 ${item.privateKey}`);
}

log();
log(`wallet address path: m/84'/0'/0'/0/ID`);
} else {
log(`👛 ${wallet.address}`);
}
log(`🔑 ${wallet.privateKey}`);
if (wallet.privateExtendedKey) {
log(`🔐 ${wallet.privateExtendedKey}`);
if (prefixFound) {
const addressCutLength = coinRow.startsWith.length + prefix.length;
log(`👛 ${coinRow.startsWith}${chalk.magenta(wallet.address.slice(coinRow.startsWith.length, addressCutLength))}${wallet.address.slice(addressCutLength)}`);
} else {
log(`👛 ${wallet.address}`);
}
log(`🔑 ${wallet.privateKey}`);
if (wallet.mnemonic) {
log(`📄 ${wallet.mnemonic}`);
}
}
if (wallet.mnemonic) {
log(`📄 ${wallet.mnemonic}`);

if (wallet.formats !== undefined || coinRow.type == 'ERC' || coinRow.multi || wallet.tested !== undefined) {
log();
}

if (wallet.formats !== undefined || coinRow.type == 'ERC' || coinRow.multi) {
log();
if (wallet.tested !== undefined) {
log(chalk.red('‼️ This wallet generation format was not tested yet, do not use it!'));
}

if (wallet.formats !== undefined) {
let formatsString = '';
for (const val of wallet.formats) {
formatsString += chalk.blue(val) + ', ';
}
log(chalk.yellow('🔢 You can create different types of wallets for this coin: ' + formatsString.substring(0, formatsString.length - 2) + ' (use it with -f flag)'));
log(chalk.yellow('🔢 You can create different wallet formats: ' + formatsString.substring(0, formatsString.length - 2) + ' (use it with -f flag)'));
}

if (coinRow.type == 'ERC') {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yerofey/cryptowallet-cli",
"version": "1.1.15",
"version": "1.1.16",
"homepage": "https://github.com/yerofey/cryptowallet-cli",
"author": "Yerofey S. <[email protected]> (https://github.com/yerofey)",
"bin": {
Expand All @@ -27,6 +27,7 @@
"dependencies": {
"@binance-chain/javascript-sdk": "^4.1.1",
"bip39": "^3.0.4",
"bip84": "^0.2.5",
"chalk": "^4.1.2",
"coininfo": "^5.1.0",
"coinkey": "^3.0.0",
Expand Down
18 changes: 11 additions & 7 deletions src/coins.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
"BEP2": {
"format": "BEP2",
"startsWith": "bnb",
"prefixTest": "[0-9a-z]"
"prefixTest": "[0-9a-z]",
"mnemonic": true
},
"BEP20": {
"format": "BEP20",
"type": "ERC",
"startsWith": "0x",
"prefixTest": "[0-9a-f]"
"prefixTest": "[0-9a-f]",
"mnemonic": true
}
},
"multi": true
Expand All @@ -36,7 +38,6 @@
"defaultFormat": "legacy",
"formats": {
"bech32": {
"script": "dev",
"startsWith": "bc1",
"prefixTest": "[0-9a-z]",
"rareSymbols": "[0-9]",
Expand Down Expand Up @@ -90,18 +91,20 @@
"type": "ERC",
"multi": true,
"startsWith": "0x",
"prefixTest": "[0-9a-f]"
"prefixTest": "[0-9a-f]",
"mnemonic": true
},
"ETH": {
"title": "Ethereum",
"type": "ERC",
"multi": true,
"startsWith": "0x",
"prefixTest": "[0-9a-f]"
"prefixTest": "[0-9a-f]",
"mnemonic": true
},
"LTC": {
"title": "Litecoin",
"script": "dev",
"script": "coinkey",
"startsWith": "L",
"prefixTest": "(?![0OI])[1-9a-zA-Z]",
"rareSymbols": "[1-9]"
Expand Down Expand Up @@ -132,7 +135,8 @@
"type": "ERC",
"multi": true,
"startsWith": "0x",
"prefixTest": "[0-9a-f]"
"prefixTest": "[0-9a-f]",
"mnemonic": true
},
"PPC": {
"title": "PeerCoin",
Expand Down
50 changes: 30 additions & 20 deletions src/wallet.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
async function generateWallet(coin, coinRow, format = '', mnemonicString = '') {
async function generateWallet(coin, options = {}) {
let coinRow = options.coinRow || {};
let format = options.format || '';
let mnemonicString = options.mnemonic || '';
let number = options.number || 1;

let coinData = [];
let result = {};

Expand Down Expand Up @@ -34,12 +39,9 @@ async function generateWallet(coin, coinRow, format = '', mnemonicString = '') {
address: wallet.publicAddress,
privateKey: wallet.privateWif,
});
} else if (coinData.script == 'dev') {
} else if (format == 'bech32') {
const bip39 = require('bip39');
const cs = require('coinstring');
const CoinInfo = require('coininfo');
const HDKey = require('hdkey');
const { addressFromExtPubKey } = require('@swan-bitcoin/xpub-lib');
const bip84 = require('bip84');

if (mnemonicString != '' && !bip39.validateMnemonic(mnemonicString)) {
return {
Expand All @@ -48,23 +50,25 @@ async function generateWallet(coin, coinRow, format = '', mnemonicString = '') {
}

const mnemonic = mnemonicString || bip39.generateMnemonic();
const seed = bip39.mnemonicToSeedSync(mnemonic); // Buffer
const seedHex = seed.toString('hex');
const key = HDKey.fromMasterSeed(Buffer.from(seedHex, 'hex'));
const keyObj = key.toJSON();
const privateKeyHex = key._privateKey.toString('hex');
const wallet = addressFromExtPubKey({ extPubKey: keyObj.xpub, network: 'mainnet' });
const privateKeyHexFixed = privateKeyHex + '01';
const privateKeyHexBuf = Buffer.from(privateKeyHexFixed, 'hex');
const info = CoinInfo(coin).versions;
const version = info.private;
const privateKey = cs.encode(privateKeyHexBuf, version);
const root = new bip84.fromSeed(mnemonic);
const child = root.deriveAccount(0);
const account = new bip84.fromZPrv(child);

let addresses = [];
if (number >= 1) {
for (let i = 0; i < number; i++) {
addresses.push({
index: i,
address: account.getAddress(i),
privateKey: account.getPrivateKey(i)
});
}
}

Object.assign(result, {
format: 'bech32',
address: wallet.address,
privateKey,
privateExtendedKey: keyObj.xpriv,
addresses,
privateExtendedKey: account.getAccountPrivateKey(),
mnemonic
});
} else if (coinData.format == 'BEP2') {
Expand Down Expand Up @@ -139,6 +143,12 @@ async function generateWallet(coin, coinRow, format = '', mnemonicString = '') {
}
}

if (coinData.tested !== undefined && coinData.tested == false) {
Object.assign(result, {
tested: false
});
}

return result;
}

Expand Down
19 changes: 14 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -624,9 +624,9 @@
integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==

"@types/node@*":
version "16.7.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0"
integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==
version "16.7.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.3.tgz#8eef3b12df245e6275f083dba37730f29700a75a"
integrity sha512-S6gm2sm9xIRWTxD7Ttj8N1ZrYfqdqZEU38Nwnrhd6krk7zf8vdgMgzz8hpAX9CfmXaJfP+Vqy2EhJpVavNEocg==

"@types/[email protected]":
version "10.12.18"
Expand Down Expand Up @@ -915,7 +915,7 @@ bip39@^2.5.0:
safe-buffer "^5.0.1"
unorm "^1.3.3"

bip39@^3.0.2, bip39@^3.0.4:
bip39@^3.0.1, bip39@^3.0.2, bip39@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0"
integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==
Expand All @@ -932,6 +932,15 @@ bip66@^1.1.0, bip66@^1.1.5:
dependencies:
safe-buffer "^5.0.1"

bip84@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/bip84/-/bip84-0.2.5.tgz#ca25915b49d06fae4b61681dd5455121deafd8f5"
integrity sha512-a3Yr1QqcC0SZSjwMZtZasAA4C1HAC12j2F8fUTfQTgvTIlI28anWrk5D5OkdBV7rke4qTpl33fxYb180Y/zD/w==
dependencies:
bip39 "^3.0.1"
bitcoinjs-lib "^5.0.3"
bs58check "^2.1.2"

bitcoin-address-validation@^0.2.9:
version "0.2.9"
resolved "https://registry.yarnpkg.com/bitcoin-address-validation/-/bitcoin-address-validation-0.2.9.tgz#aef9882f8ad48e021bd8ade2fa71f8ec539c86bb"
Expand Down Expand Up @@ -967,7 +976,7 @@ bitcoinjs-lib@^4.0.5:
varuint-bitcoin "^1.0.4"
wif "^2.0.1"

bitcoinjs-lib@^5.2.0:
bitcoinjs-lib@^5.0.3, bitcoinjs-lib@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz#caf8b5efb04274ded1b67e0706960b93afb9d332"
integrity sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ==
Expand Down

0 comments on commit 79ec6cf

Please sign in to comment.