UNPKG

@0x/subproviders

Version:

A few useful web3 subproviders including a LedgerSubprovider useful for adding Ledger Nano S support.

172 lines 9.04 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MnemonicWalletSubprovider = void 0; const assert_1 = require("@0x/assert"); const utils_1 = require("@0x/utils"); const bip39 = require("bip39"); const HDNode = require("hdkey"); const _ = require("lodash"); const types_1 = require("../types"); const wallet_utils_1 = require("../utils/wallet_utils"); const base_wallet_subprovider_1 = require("./base_wallet_subprovider"); const private_key_wallet_1 = require("./private_key_wallet"); const DEFAULT_BASE_DERIVATION_PATH = `44'/60'/0'/0`; const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10; const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; /** * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and handles * all requests with accounts derived from the supplied mnemonic. */ class MnemonicWalletSubprovider extends base_wallet_subprovider_1.BaseWalletSubprovider { /** * Instantiates a MnemonicWalletSubprovider. Defaults to baseDerivationPath set to `44'/60'/0'/0`. * This is the default in TestRPC/Ganache, it can be overridden if desired. * @param config Configuration for the mnemonic wallet, must contain the mnemonic * @return MnemonicWalletSubprovider instance */ constructor(config) { assert_1.assert.isString('mnemonic', config.mnemonic); const baseDerivationPath = config.baseDerivationPath || DEFAULT_BASE_DERIVATION_PATH; assert_1.assert.isString('baseDerivationPath', baseDerivationPath); const addressSearchLimit = config.addressSearchLimit || DEFAULT_ADDRESS_SEARCH_LIMIT; assert_1.assert.isNumber('addressSearchLimit', addressSearchLimit); super(); this._mnemonic = config.mnemonic; this._baseDerivationPath = baseDerivationPath; this._addressSearchLimit = addressSearchLimit; this._derivedKeyInfo = this._initialDerivedKeyInfo(this._baseDerivationPath); this.chainId = config.chainId || 1; this.hardfork = config.hardfork; } /** * Retrieve the set derivation path * @returns derivation path */ getPath() { return this._baseDerivationPath; } /** * Set a desired derivation path when computing the available user addresses * @param baseDerivationPath The desired derivation path (e.g `44'/60'/0'`) */ setPath(baseDerivationPath) { this._baseDerivationPath = baseDerivationPath; this._derivedKeyInfo = this._initialDerivedKeyInfo(this._baseDerivationPath); } /** * Retrieve the accounts associated with the mnemonic. * This method is implicitly called when issuing a `eth_accounts` JSON RPC request * via your providerEngine instance. * @param numberOfAccounts Number of accounts to retrieve (default: 10) * @return An array of accounts */ getAccountsAsync(numberOfAccounts = DEFAULT_NUM_ADDRESSES_TO_FETCH) { return __awaiter(this, void 0, void 0, function* () { const derivedKeys = wallet_utils_1.walletUtils.calculateDerivedHDKeyInfos(this._derivedKeyInfo, numberOfAccounts); const accounts = _.map(derivedKeys, k => k.address); return accounts; }); } /** * Signs a transaction with the account specificed by the `from` field in txParams. * If you've added this Subprovider to your app's provider, you can simply send * an `eth_sendTransaction` JSON RPC request, and this method will be called auto-magically. * If you are not using this via a ProviderEngine instance, you can call it directly. * @param txParams Parameters of the transaction to sign * @return Signed transaction hex string */ signTransactionAsync(txParams) { return __awaiter(this, void 0, void 0, function* () { if (txParams.from === undefined || !utils_1.addressUtils.isAddress(txParams.from)) { throw new Error(types_1.WalletSubproviderErrors.FromAddressMissingOrInvalid); } const privateKeyWallet = this._privateKeyWalletForAddress(txParams.from); const signedTx = privateKeyWallet.signTransactionAsync(txParams); return signedTx; }); } /** * Sign a personal Ethereum signed message. The signing account will be the account * associated with the provided address. If you've added the MnemonicWalletSubprovider to * your app's provider, you can simply send an `eth_sign` or `personal_sign` JSON RPC request, * and this method will be called auto-magically. If you are not using this via a ProviderEngine * instance, you can call it directly. * @param data Hex string message to sign * @param address Address of the account to sign with * @return Signature hex string (order: rsv) */ signPersonalMessageAsync(data, address) { return __awaiter(this, void 0, void 0, function* () { if (data === undefined) { throw new Error(types_1.WalletSubproviderErrors.DataMissingForSignPersonalMessage); } assert_1.assert.isHexString('data', data); assert_1.assert.isETHAddressHex('address', address); const privateKeyWallet = this._privateKeyWalletForAddress(address); const sig = yield privateKeyWallet.signPersonalMessageAsync(data, address); return sig; }); } /** * Sign an EIP712 Typed Data message. The signing account will be the account * associated with the provided address. If you've added this MnemonicWalletSubprovider to * your app's provider, you can simply send an `eth_signTypedData` JSON RPC request, and * this method will be called auto-magically. If you are not using this via a ProviderEngine * instance, you can call it directly. * @param address Address of the account to sign with * @param data the typed data object * @return Signature hex string (order: rsv) */ signTypedDataAsync(address, typedData) { return __awaiter(this, void 0, void 0, function* () { if (typedData === undefined) { throw new Error(types_1.WalletSubproviderErrors.DataMissingForSignPersonalMessage); } assert_1.assert.isETHAddressHex('address', address); const privateKeyWallet = this._privateKeyWalletForAddress(address); const sig = yield privateKeyWallet.signTypedDataAsync(address, typedData); return sig; }); } _privateKeyWalletForAddress(address) { const derivedKeyInfo = this._findDerivedKeyInfoForAddress(address); const privateKeyHex = derivedKeyInfo.hdKey.privateKey.toString('hex'); const privateKeyWallet = new private_key_wallet_1.PrivateKeyWalletSubprovider(privateKeyHex, this.chainId, this.hardfork); return privateKeyWallet; } _findDerivedKeyInfoForAddress(address) { const matchedDerivedKeyInfo = wallet_utils_1.walletUtils.findDerivedKeyInfoForAddressIfExists(address, this._derivedKeyInfo, this._addressSearchLimit); if (matchedDerivedKeyInfo === undefined) { throw new Error(`${types_1.WalletSubproviderErrors.AddressNotFound}: ${address}`); } return matchedDerivedKeyInfo; } _initialDerivedKeyInfo(baseDerivationPath) { const seed = bip39.mnemonicToSeed(this._mnemonic); const hdKey = HDNode.fromMasterSeed(seed); // Walk down to base derivation level (i.e m/44'/60'/0') and create an initial key at that level // all children will then be walked relative (i.e m/0) const parentKeyDerivationPath = `m/${baseDerivationPath}`; const parentHDKeyAtDerivationPath = hdKey.derive(parentKeyDerivationPath); const address = wallet_utils_1.walletUtils.addressOfHDKey(parentHDKeyAtDerivationPath); const derivedKeyInfo = { address, baseDerivationPath, derivationPath: parentKeyDerivationPath, hdKey: parentHDKeyAtDerivationPath, }; return derivedKeyInfo; } } exports.MnemonicWalletSubprovider = MnemonicWalletSubprovider; //# sourceMappingURL=mnemonic_wallet.js.map