UNPKG

@machinomy/hdwallet-provider

Version:

HD Wallet-enabled Web3 provider

136 lines 6.23 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LedgerSubprovider = exports.InvalidNetworkIdError = void 0; const hooked_wallet_1 = __importDefault(require("web3-provider-engine/subproviders/hooked-wallet")); const path_util_1 = require("./path.util"); const hw_app_eth_1 = __importDefault(require("@ledgerhq/hw-app-eth")); const util_1 = require("./util"); const transaction_util_1 = require("./util/transaction.util"); class InvalidNetworkIdError extends Error { } exports.InvalidNetworkIdError = InvalidNetworkIdError; class LedgerSubprovider extends hooked_wallet_1.default { constructor(getTransport, options) { const path = path_util_1.normalizePath(options.path || path_util_1.DEFAULT_PATH); const askConfirm = options.askConfirm || false; const accountsLength = options.accountsLength || 1; const accountsOffset = options.accountsOffset || 0; const pathComponents = path_util_1.componentsFromPath(path); const _addressToPath = new Map(); /** * @return address => path mapping */ async function getAccounts() { const transport = await getTransport(); try { const eth = new hw_app_eth_1.default(transport); const addresses = new Map(); for (let i = accountsOffset; i < accountsOffset + accountsLength; i++) { const path = pathComponents.basePath + (pathComponents.index + i).toString(); const address = await eth.getAddress(path, askConfirm, false); addresses.set(path, address.address); _addressToPath.set(address.address.toLowerCase(), path); } return addresses; } finally { transport.close(); } } /** * @return path => address mapping */ async function getAddressToPath() { if (_addressToPath.size == 0) { await getAccounts(); } return _addressToPath; } async function signPersonalMessage(msgData) { const addressToPath = await getAddressToPath(); const path = addressToPath.get(msgData.from.toLowerCase()); if (!path) throw new Error(`address unknown '${msgData.from}'`); const transport = await getTransport(); try { const eth = new hw_app_eth_1.default(transport); const result = await eth.signPersonalMessage(path, util_1.stripHexPrefix(msgData.data)); const v = parseInt(result.v.toString(), 10) - 27; let vHex = v.toString(16); if (vHex.length < 2) { vHex = `0${v}`; } return `0x${result.r}${result.s}${vHex}`; } finally { transport.close(); } } async function signTransaction(networkId, txData) { const addressToPath = await getAddressToPath(); const path = addressToPath.get(txData.from.toLowerCase()); if (!path) throw new Error("address unknown '" + txData.from + "'"); const transport = await getTransport(); try { const eth = new hw_app_eth_1.default(transport); const tx = transaction_util_1.buildTransaction(txData, networkId); // Set the EIP155 bits tx.raw[6] = Buffer.from([networkId]); // v tx.raw[7] = Buffer.from([]); // r tx.raw[8] = Buffer.from([]); // s // Pass hex-rlp to ledger for signing const result = await eth.signTransaction(path, tx.serialize().toString("hex")); // Store signature in transaction tx.v = Buffer.from(result.v, "hex"); tx.r = Buffer.from(result.r, "hex"); tx.s = Buffer.from(result.s, "hex"); // EIP155: v should be chain_id * 2 + {35, 36} const signedChainId = Math.floor((tx.v[0] - 35) / 2); const validChainId = networkId & 0xff; // FIXME this is to fixed a current workaround that app don't support > 0xff if (signedChainId !== validChainId) { throw new InvalidNetworkIdError(`Invalid networkId signature returned. Expected: ${networkId}, Got: ${signedChainId}`); } return `0x${tx.serialize().toString("hex")}`; } finally { transport.close(); } } super({ getAccounts: callback => { getAccounts() .then(res => callback(null, Array.from(res.values()))) .catch(err => callback(err, undefined)); }, signPersonalMessage: (txData, callback) => { signPersonalMessage(txData) .then(res => callback(null, res)) .catch(err => callback(err, undefined)); }, signMessage: (txData, callback) => { signPersonalMessage(txData) .then(res => callback(null, res)) .catch(err => callback(err, undefined)); }, signTransaction: (txData, callback) => { this.engine.sendAsync(util_1.createPayload({ method: "net_version" }), (err, result) => { if (err) { return callback(err); } else { const networkId = Number(result.result); signTransaction(networkId, txData) .then(res => callback(null, res)) .catch(err => callback(err, undefined)); } }); } }); } } exports.LedgerSubprovider = LedgerSubprovider; //# sourceMappingURL=ledger.subprovider.js.map