minterjs-wallet
Version:
Utilities for handling Minter keys
227 lines (196 loc) • 5.42 kB
JavaScript
// @TODO remove some wordlists
import * as bip39 from 'bip39';
import { HDKey as hdKey } from 'ethereum-cryptography/hdkey';
import { isValidPrivate, privateToPublic, publicToAddress } from 'ethereumjs-util/dist/account';
import { publicToString } from 'minterjs-util';
function assert(val, msg) {
if (!val) {
throw new Error(msg || 'Assertion failed');
}
}
/**
* BIP39 Master seed from mnemonic phrase
* @param mnemonic - 12 words
* @return {Buffer}
*/
export function seedFromMnemonic(mnemonic) {
return bip39.mnemonicToSeedSync(mnemonic);
}
/**
* BIP39 Master seed from mnemonic phrase (async)
* @param mnemonic - 12 words
* @return {Promise<Buffer>}
*/
export function seedFromMnemonicAsync(mnemonic) {
return bip39.mnemonicToSeed(mnemonic);
}
/**
* @typedef {{purpose?: number, coin?: number, account?: number, change?: number, index?: number}} HDKeyPathObject
*/
/**
* @typedef {string|HDKeyPathObject} HDKeyPath
*/
/**
* BIP44 HD key from master seed
* https://en.bitcoin.it/wiki/BIP_0044
* @param {Buffer} seed - 64 bytes
* @param {HDKeyPath} [path] - Ethereum path by default "m/44'/60'/0'/0/0"
* @return {HDKey}
*/
export function hdKeyFromSeed(seed, path = {}) {
if (typeof path !== 'string') {
const {
purpose = 44,
coin = 60,
account = 0,
change = 0,
index = 0,
} = path;
path = `m/${purpose}'/${coin}'/${account}'/${change}/${index}`;
}
return hdKey.fromMasterSeed(seed).derive(path);
}
/**
* @param {Buffer} [priv]
* @param {string} [mnemonic]
* @param {boolean} [doAsync]
* @constructor
*/
const Wallet = function WalletConstructor(priv, mnemonic, doAsync, hdPath) {
if (priv && mnemonic) {
throw new Error('Cannot supply both a private and a mnemonic phrase to the constructor');
}
if (priv && !isValidPrivate(priv)) {
throw new Error('Private key does not satisfy the curve requirements (ie. it is invalid)');
}
if (mnemonic && !bip39.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic phrase');
}
if (mnemonic && doAsync) {
return seedFromMnemonicAsync(mnemonic)
.then((seed) => {
this._privKey = hdKeyFromSeed(seed, hdPath)._privateKey;
this._mnemonic = mnemonic;
return this;
});
}
if (mnemonic) {
const seed = seedFromMnemonic(mnemonic);
priv = hdKeyFromSeed(seed, hdPath)._privateKey;
}
this._privKey = priv;
this._mnemonic = mnemonic;
};
Object.defineProperty(Wallet.prototype, 'mnemonic', {
get() {
assert(this._mnemonic, 'This is a private key only wallet');
return this._mnemonic;
},
});
Object.defineProperty(Wallet.prototype, 'privKey', {
get() {
return this._privKey;
},
});
// uncompressed public key
Object.defineProperty(Wallet.prototype, 'pubKey', {
get() {
if (!this._pubKey) {
this._pubKey = privateToPublic(this.privKey);
}
return this._pubKey;
},
});
/**
* @return {string}
*/
Wallet.prototype.getMnemonic = function getMnemonic() {
return this.mnemonic;
};
/**
* @return {Buffer}
*/
Wallet.prototype.getPrivateKey = function getPrivateKey() {
return this.privKey;
};
/**
* @return {string}
*/
Wallet.prototype.getPrivateKeyString = function getPrivateKeyString() {
return `0x${this.getPrivateKey().toString('hex')}`;
};
/**
* @return {Buffer}
*/
Wallet.prototype.getPublicKey = function getPublicKey() {
return this.pubKey;
};
/**
* @return {string}
*/
Wallet.prototype.getPublicKeyString = function getPublicKeyString() {
return publicToString(this.getPublicKey());
};
/**
* @return {Buffer}
*/
Wallet.prototype.getAddress = function getAddress() {
return publicToAddress(this.pubKey);
};
/**
* @return {string}
*/
Wallet.prototype.getAddressString = function getAddressString() {
return `Mx${this.getAddress().toString('hex')}`;
};
/**
* Generate Wallet from random mnemonic
* @param {HDKeyPath} [path]
* @return {Wallet}
*/
export function generateWallet(path) {
const mnemonic = bip39.generateMnemonic();
return walletFromMnemonic(mnemonic, path);
}
/**
* MinterWallet from mnemonic phrase
* @param {string} mnemonic - 12 words
* @param {HDKeyPath} [path]
* @return {Wallet}
*/
export function walletFromMnemonic(mnemonic, path) {
return new Wallet(undefined, mnemonic, false, path);
}
/**
* MinterWallet from mnemonic phrase
* @param {string} mnemonic - 12 words
* @param {HDKeyPath} [path]
* @return {Promise<Wallet>}
*/
export function walletFromMnemonicAsync(mnemonic, path) {
return new Wallet(undefined, mnemonic, true, path);
}
/**
* MinterWallet from private key
* @param {Buffer} priv - 64 bytes
* @return {Wallet}
*/
export function walletFromPrivateKey(priv) {
return new Wallet(priv);
}
/**
* Generate 12 words mnemonic phrase
* @return {string}
*/
export function generateMnemonic() {
return bip39.generateMnemonic();
}
/**
* Check that mnemonic phrase has 12 words and represents valid entropy
* @param {string} mnemonic
* @return {boolean}
*/
export function isValidMnemonic(mnemonic) {
return typeof mnemonic === 'string' && mnemonic.trim().split(/\s+/g).length >= 12 && bip39.validateMnemonic(mnemonic);
}
export default Wallet;