UNPKG

parity-hdwallet-provider

Version:
247 lines 11.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const bip39 = __importStar(require("ethereum-cryptography/bip39")); const english_1 = require("ethereum-cryptography/bip39/wordlists/english"); const EthUtil = __importStar(require("ethereumjs-util")); const ethereumjs_wallet_1 = __importDefault(require("ethereumjs-wallet")); const ethereumjs_wallet_2 = require("ethereumjs-wallet"); const eth_sig_util_1 = require("eth-sig-util"); const ethereumjs_tx_1 = require("ethereumjs-tx"); // @ts-ignore const web3_provider_engine_1 = __importDefault(require("@trufflesuite/web3-provider-engine")); const filters_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/filters")); const nonce_tracker_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/nonce-tracker")); const hooked_wallet_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/hooked-wallet")); const provider_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/provider")); // @ts-ignore const rpc_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/rpc")); // @ts-ignore const websocket_1 = __importDefault(require("@trufflesuite/web3-provider-engine/subproviders/websocket")); const url_1 = __importDefault(require("url")); const getOptions_1 = require("./constructor/getOptions"); const getPrivateKeys_1 = require("./constructor/getPrivateKeys"); const getMnemonic_1 = require("./constructor/getMnemonic"); // Important: do not use debug module. Reason: https://github.com/trufflesuite/truffle/issues/2374#issuecomment-536109086 // This line shares nonce state across multiple provider instances. Necessary // because within truffle the wallet is repeatedly newed if it's declared in the config within a // function, resetting nonce from tx to tx. An instance can opt out // of this behavior by passing `shareNonce=false` to the constructor. // See issue #65 for more const singletonNonceSubProvider = new nonce_tracker_1.default(); class HDWalletProvider { constructor(...args) { const _a = getOptions_1.getOptions(...args), { providerOrUrl, // required addressIndex = 0, numberOfAddresses = 10, shareNonce = true, derivationPath = `m/44'/60'/0'/0/`, pollingInterval = 4000, chainId } = _a, // what's left is either a mnemonic or a list of private keys signingAuthority = __rest(_a, ["providerOrUrl", "addressIndex", "numberOfAddresses", "shareNonce", "derivationPath", "pollingInterval", "chainId"]); const mnemonic = getMnemonic_1.getMnemonic(signingAuthority); const privateKeys = getPrivateKeys_1.getPrivateKeys(signingAuthority); this.walletHdpath = derivationPath; this.wallets = {}; this.addresses = []; this.engine = new web3_provider_engine_1.default({ pollingInterval }); // Hack so that parity nodes won't barf on the "skipFlag: true" flag in the // RPC call: https://github.com/MetaMask/web3-provider-engine/issues/346 this.engine._blockTracker._setSkipCacheFlag = false; if (!HDWalletProvider.isValidProvider(providerOrUrl)) { throw new Error([ `Malformed provider URL: '${providerOrUrl}'`, "Please specify a correct URL, using the http, https, ws, or wss protocol.", "" ].join("\n")); } // private helper to check if given mnemonic uses BIP39 passphrase protection const checkBIP39Mnemonic = ({ phrase, password }) => { this.hdwallet = ethereumjs_wallet_2.hdkey.fromMasterSeed(bip39.mnemonicToSeedSync(phrase, password)); if (!bip39.validateMnemonic(phrase, english_1.wordlist)) { throw new Error("Mnemonic invalid or undefined"); } // crank the addresses out for (let i = addressIndex; i < addressIndex + numberOfAddresses; i++) { const wallet = this.hdwallet .derivePath(this.walletHdpath + i) .getWallet(); const addr = `0x${wallet.getAddress().toString("hex")}`; this.addresses.push(addr); this.wallets[addr] = wallet; } }; // private helper leveraging ethUtils to populate wallets/addresses const ethUtilValidation = (privateKeys) => { // crank the addresses out for (let i = addressIndex; i < privateKeys.length; i++) { const privateKey = Buffer.from(privateKeys[i].replace("0x", ""), "hex"); if (EthUtil.isValidPrivate(privateKey)) { const wallet = ethereumjs_wallet_1.default.fromPrivateKey(privateKey); const address = wallet.getAddressString(); this.addresses.push(address); this.wallets[address] = wallet; } } }; if (mnemonic && mnemonic.phrase) { checkBIP39Mnemonic(mnemonic); } else if (privateKeys) { ethUtilValidation(privateKeys); } // no need to handle else case here, since matchesNewOptions() covers it if (this.addresses.length === 0) { throw new Error(`Could not create addresses from your mnemonic or private key(s). ` + `Please check that your inputs are correct.`); } const tmpAccounts = this.addresses; const tmpWallets = this.wallets; this.engine.addProvider(new hooked_wallet_1.default({ getAccounts(cb) { cb(null, tmpAccounts); }, getPrivateKey(address, cb) { if (!tmpWallets[address]) { return cb("Account not found"); } else { cb(null, tmpWallets[address].getPrivateKey().toString("hex")); } }, signTransaction(txParams, cb) { // we need to rename the 'gas' field txParams.gasLimit = txParams.gas; delete txParams.gas; let pkey; const from = txParams.from.toLowerCase(); if (tmpWallets[from]) { pkey = tmpWallets[from].getPrivateKey(); } else { cb("Account not found"); } const chain = (chainId && typeof chainId === "string") ? parseInt(chainId) : chainId; const tx = new ethereumjs_tx_1.Transaction(txParams, { chain }); tx.sign(pkey); const rawTx = `0x${tx.serialize().toString("hex")}`; cb(null, rawTx); }, signMessage({ data, from }, cb) { const dataIfExists = data; if (!dataIfExists) { cb("No data to sign"); } if (!tmpWallets[from]) { cb("Account not found"); } let pkey = tmpWallets[from].getPrivateKey(); const dataBuff = EthUtil.toBuffer(dataIfExists); const msgHashBuff = EthUtil.hashPersonalMessage(dataBuff); const sig = EthUtil.ecsign(msgHashBuff, pkey); const rpcSig = EthUtil.toRpcSig(sig.v, sig.r, sig.s); cb(null, rpcSig); }, signPersonalMessage(...args) { this.signMessage(...args); }, signTypedMessage({ from: checkSummedFrom, data }, cb) { if (!data) { cb("No data to sign"); } let pkey; let from = checkSummedFrom.toLowerCase(); if (tmpWallets[from]) { pkey = tmpWallets[from].getPrivateKey(); const result = eth_sig_util_1.signTypedData_v4(pkey, { data }); cb(null, result); } else { cb("Account not found"); } } })); !shareNonce ? this.engine.addProvider(new nonce_tracker_1.default()) : this.engine.addProvider(singletonNonceSubProvider); this.engine.addProvider(new filters_1.default()); if (typeof providerOrUrl === "string") { const url = providerOrUrl; const providerProtocol = (url_1.default.parse(url).protocol || "http:").toLowerCase(); switch (providerProtocol) { case "ws:": case "wss:": this.engine.addProvider(new websocket_1.default({ rpcUrl: url })); break; default: this.engine.addProvider(new rpc_1.default({ rpcUrl: url })); } } else { const provider = providerOrUrl; this.engine.addProvider(new provider_1.default(provider)); } // Required by the provider engine. this.engine.start((err) => { if (err) throw err; }); } send(payload, callback) { return this.engine.send.call(this.engine, payload, callback); } sendAsync(payload, callback) { this.engine.sendAsync.call(this.engine, payload, callback); } getAddress(idx) { if (!idx) { return this.addresses[0]; } else { return this.addresses[idx]; } } getAddresses() { return this.addresses; } static isValidProvider(provider) { const validProtocols = ["http:", "https:", "ws:", "wss:"]; if (typeof provider === "string") { const url = url_1.default.parse(provider.toLowerCase()); return !!(validProtocols.includes(url.protocol || "") && url.slashes); } return true; } } module.exports = HDWalletProvider; //# sourceMappingURL=index.js.map