eth-hd-wallet
Version:
Lightweight Ethereum HD wallet implementation based on BIP-44 standard
311 lines (246 loc) • 8.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EthHdWallet = exports.generateMnemonic = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _ethereumjsUtil = require('ethereumjs-util');
var _hdkey = require('ethereumjs-wallet/hdkey');
var _ethereumjsTx = require('ethereumjs-tx');
var _ethereumjsTx2 = _interopRequireDefault(_ethereumjsTx);
var _ethSigUtil = require('eth-sig-util');
var _ethSigUtil2 = _interopRequireDefault(_ethSigUtil);
var _bitcoreMnemonic = require('bitcore-mnemonic');
var _bitcoreMnemonic2 = _interopRequireDefault(_bitcoreMnemonic);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
// See https://github.com/ethereum/EIPs/issues/85
var BIP44_PATH = 'm/44\'/60\'/0\'/0';
/**
* Normalize an Etherum address
* @param {String} addr Address
* @return {Striung}
*/
var normalizeAddress = function normalizeAddress(addr) {
return addr ? (0, _ethereumjsUtil.addHexPrefix)(addr.toLowerCase()) : addr;
};
/**
* Generate a 12-word mnemonic in English.
* @return {[String]}
*/
var generateMnemonic = exports.generateMnemonic = function generateMnemonic() {
return new _bitcoreMnemonic2.default(_bitcoreMnemonic2.default.Words.ENGLISH).toString();
};
/**
* Represents a wallet instance.
*/
var EthHdWallet = exports.EthHdWallet = function () {
_createClass(EthHdWallet, null, [{
key: 'fromMnemonic',
/**
* Construct HD wallet instance from given mnemonic
* @param {String} mnemonic Mnemonic/seed string.
* @return {EthHdWallet}
*/
value: function fromMnemonic(mnemonic) {
var _toHDPrivateKey = new _bitcoreMnemonic2.default(mnemonic).toHDPrivateKey(),
xprivkey = _toHDPrivateKey.xprivkey;
return new EthHdWallet(xprivkey);
}
/**
* @constructor
* @param {String} hdKey Extended HD private key
*/
}]);
function EthHdWallet(xPrivKey) {
_classCallCheck(this, EthHdWallet);
this._hdKey = (0, _hdkey.fromExtendedKey)(xPrivKey);
this._root = this._hdKey.derivePath(BIP44_PATH);
this._children = [];
}
/**
* Generate new addresses.
* @param {Number} num No. of new addresses to generate.
* @return {[String]}
*/
_createClass(EthHdWallet, [{
key: 'generateAddresses',
value: function generateAddresses(num) {
var newKeys = this._deriveNewKeys(num);
return newKeys.map(function (k) {
return k.address;
});
}
/**
* Discard generated addresses.
*
* This is in effect the reverse of `generateAddresses()`.
*
* @param {Number} num The number of addresses to remove from the end of the list of addresses.
* @return {[String]} The discarded addresses
*/
}, {
key: 'discardAddresses',
value: function discardAddresses(num) {
var discard = this._children.splice(-num);
return discard.map(function (k) {
return k.address;
});
}
/**
* Get all addresses.
* @return {[String]}
*/
}, {
key: 'getAddresses',
value: function getAddresses() {
return this._children.map(function (k) {
return k.address;
});
}
/**
* Get no. of addresses.
* @return {Number}
*/
}, {
key: 'getAddressCount',
value: function getAddressCount() {
return this._children.length;
}
/**
* Check whether given address is present in current list of generated addresses.
* @param {String} addr
* @return {Boolean}
*/
}, {
key: 'hasAddress',
value: function hasAddress(addr) {
addr = normalizeAddress(addr);
return !!this._children.find(function (_ref) {
var address = _ref.address;
return addr === address;
});
}
/**
* Get private key for given address.
* @param {String} addr Public key address
* @return {Buffer} private key buffer
*/
}, {
key: 'getPrivateKey',
value: function getPrivateKey(addr) {
addr = normalizeAddress(addr);
var _ref2 = this._children.find(function (_ref3) {
var a = _ref3.address;
return addr === a;
}) || {},
wallet = _ref2.wallet;
if (!wallet) {
throw new Error('Invalid address');
}
return wallet.getPrivateKey();
}
/**
* Sign transaction data.
*
* @param {String} from From address
* @param {String} [to] If omitted then deploying a contract
* @param {Number} value Amount of wei to send
* @param {String} data Data
* @param {Number} gasLimit Total Gas to use
* @param {Number} gasPrice Gas price (wei per gas unit)
* @param {String} chainId Chain id
*
* @return {String} Raw transaction string.
*/
}, {
key: 'signTransaction',
value: function signTransaction(_ref4) {
var nonce = _ref4.nonce,
from = _ref4.from,
to = _ref4.to,
value = _ref4.value,
data = _ref4.data,
gasLimit = _ref4.gasLimit,
gasPrice = _ref4.gasPrice,
chainId = _ref4.chainId;
from = normalizeAddress(from);
to = normalizeAddress(to);
var _ref5 = this._children.find(function (_ref6) {
var address = _ref6.address;
return from === address;
}) || {},
wallet = _ref5.wallet;
if (!wallet) {
throw new Error('Invalid from address');
}
var tx = new _ethereumjsTx2.default({
nonce: nonce, to: to, value: value, data: data, gasLimit: gasLimit, gasPrice: gasPrice, chainId: chainId
});
tx.sign(wallet.getPrivateKey());
return (0, _ethereumjsUtil.addHexPrefix)(tx.serialize().toString('hex'));
}
/**
* Sign data.
*
* @param {String} address Address whos private key to sign with
* @param {String|Buffer|BN} data Data
*
* @return {String} Signed data..
*/
}, {
key: 'sign',
value: function sign(_ref7) {
var address = _ref7.address,
data = _ref7.data;
address = normalizeAddress(address);
var _ref8 = this._children.find(function (_ref9) {
var a = _ref9.address;
return address === a;
}) || {},
wallet = _ref8.wallet;
if (!wallet) {
throw new Error('Invalid address');
}
return (0, _ethereumjsUtil.addHexPrefix)(_ethSigUtil2.default.personalSign(wallet.getPrivateKey(), { data: data }));
}
/**
* Recover public key of signing account.
*
* @param {String} signature The signed message..
* @param {String|Buffer|BN} data The original input data.
*
* @return {String} Public signing key.
*/
}, {
key: 'recoverSignerPublicKey',
value: function recoverSignerPublicKey(_ref10) {
var signature = _ref10.signature,
data = _ref10.data;
return _ethSigUtil2.default.recoverPersonalSignature({ sig: signature, data: data });
}
/**
* Derive new key pairs.
*
* This will increment the internal key index counter and add the
* generated keypairs to the internal list.
*
* @param {Number} num no. of new keypairs to generate
* @return {[String]} Generated keypairs.
*/
}, {
key: '_deriveNewKeys',
value: function _deriveNewKeys(num) {
var count = num;
while (0 <= --count) {
var child = this._root.deriveChild(this._children.length).getWallet();
this._children.push({
wallet: child,
address: normalizeAddress(child.getAddress().toString('hex'))
});
}
return this._children.slice(-num);
}
}]);
return EthHdWallet;
}();