aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/subproviders/CHANGELOG.json10
-rw-r--r--packages/subproviders/package-lock.json25
-rw-r--r--packages/subproviders/package.json9
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts73
-rw-r--r--packages/subproviders/src/subproviders/mnemonic_wallet.ts91
-rw-r--r--packages/subproviders/src/subproviders/private_key_wallet.ts21
-rw-r--r--packages/subproviders/src/types.ts18
-rw-r--r--packages/subproviders/src/utils/wallet_utils.ts40
-rw-r--r--packages/subproviders/test/integration/ledger_subprovider_test.ts2
-rw-r--r--packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts12
-rw-r--r--packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts16
-rw-r--r--packages/subproviders/test/utils/fixture_data.ts4
12 files changed, 164 insertions, 157 deletions
diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json
index 554fbd0cf..5287ee987 100644
--- a/packages/subproviders/CHANGELOG.json
+++ b/packages/subproviders/CHANGELOG.json
@@ -1,14 +1,18 @@
[
{
- "version": "0.8.5",
+ "version": "0.9.0",
"changes": [
{
- "note": "Add private key subprovider and refactor shared functionality into a base wallet subprovider",
+ "note": "Add private key subprovider and refactor shared functionality into a base wallet subprovider.",
"pr": 506
},
{
+ "note": "Add mnemonic wallet subprovider, deprecating our truffle-hdwallet-provider fork.",
+ "pr": 507
+ },
+ {
"note":
- "Add mnemonic wallet subprovider, deprecating our truffle-hdwallet-provider fork. Support multiple addresses in ledger and mnemonic wallets",
+ "Support multiple addresses in ledger and mnemonic wallets. Renamed derivationPath to baseDerivationPath.",
"pr": 507
}
]
diff --git a/packages/subproviders/package-lock.json b/packages/subproviders/package-lock.json
deleted file mode 100644
index 61675f9ea..000000000
--- a/packages/subproviders/package-lock.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "@0xproject/subproviders",
- "version": "0.8.4",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "@types/bip39": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@types/bip39/-/bip39-2.4.0.tgz",
- "integrity": "sha512-qxJBGh55SPbSGv+91D6H3WOR8vKdA/p8Oc58oK/DTbORgjO6Ebuo8MRzdy2OWi+dw/lxtX4VWJkkCUTSQvhAnw==",
- "dev": true,
- "requires": {
- "@types/node": "9.6.2"
- },
- "dependencies": {
- "@types/node": {
- "version": "9.6.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.2.tgz",
- "integrity": "sha512-UWkRY9X7RQHp5OhhRIIka58/gVVycL1zHZu0OTsT5LI86ABaMOSbUjAl+b0FeDhQcxclrkyft3kW5QWdMRs8wQ==",
- "dev": true
- }
- }
- }
- }
-}
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index d557eef29..afb6ef5d6 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -21,15 +21,14 @@
"manual:postpublish": "yarn build; node ./scripts/postpublish.js",
"docs:stage": "yarn build && node ./scripts/stage_docs.js",
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES",
- "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json"
+ "upload_docs_json":
+ "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json"
},
"config": {
"postpublish": {
"assets": [],
"docPublishConfigs": {
- "extraFileIncludes": [
- "../types/src/index.ts"
- ],
+ "extraFileIncludes": ["../types/src/index.ts"],
"s3BucketPath": "s3://doc-jsons/subproviders/",
"s3StagingBucketPath": "s3://staging-doc-jsons/subproviders/"
}
@@ -46,6 +45,7 @@
"ethereumjs-tx": "^1.3.3",
"ethereumjs-util": "^5.1.1",
"ganache-core": "0xProject/ganache-core",
+ "bip39": "^2.5.0",
"hdkey": "^0.7.1",
"lodash": "^4.17.4",
"semaphore-async-await": "^1.5.1",
@@ -60,7 +60,6 @@
"@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42",
"@types/node": "^8.0.53",
- "bip39": "^2.5.0",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"copyfiles": "^1.2.0",
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 9b2d9d7d0..33aa5c1bf 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -8,7 +8,7 @@ import { Lock } from 'semaphore-async-await';
import {
Callback,
- DerivedHDKey,
+ DerivedHDKeyInfo,
LedgerEthereumClient,
LedgerEthereumClientFactoryAsync,
LedgerSubproviderConfigs,
@@ -21,9 +21,11 @@ import { walletUtils } from '../utils/wallet_utils';
import { BaseWalletSubprovider } from './base_wallet_subprovider';
-const DEFAULT_DERIVATION_PATH = `44'/60'/0'`;
+const DEFAULT_BASE_DERIVATION_PATH = `44'/60'/0'`;
const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
const SHOULD_GET_CHAIN_CODE = true;
+const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10;
+const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000;
/**
* Subprovider for interfacing with a user's [Ledger Nano S](https://www.ledgerwallet.com/products/ledger-nano-s).
@@ -34,7 +36,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
private _nonceLock = new Lock();
private _connectionLock = new Lock();
private _networkId: number;
- private _derivationBasePath: string;
+ private _baseDerivationPath: string;
private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
private _ledgerClientIfExists?: LedgerEthereumClient;
private _shouldAlwaysAskForConfirmation: boolean;
@@ -49,7 +51,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
super();
this._networkId = config.networkId;
this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync;
- this._derivationBasePath = config.derivationPath || DEFAULT_DERIVATION_PATH;
+ this._baseDerivationPath = config.baseDerivationPath || DEFAULT_BASE_DERIVATION_PATH;
this._shouldAlwaysAskForConfirmation =
!_.isUndefined(config.accountFetchingConfigs) &&
!_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation)
@@ -59,21 +61,21 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
!_.isUndefined(config.accountFetchingConfigs) &&
!_.isUndefined(config.accountFetchingConfigs.addressSearchLimit)
? config.accountFetchingConfigs.addressSearchLimit
- : walletUtils.DEFAULT_ADDRESS_SEARCH_LIMIT;
+ : DEFAULT_ADDRESS_SEARCH_LIMIT;
}
/**
* Retrieve the set derivation path
* @returns derivation path
*/
public getPath(): string {
- return this._derivationBasePath;
+ return this._baseDerivationPath;
}
/**
* Set a desired derivation path when computing the available user addresses
- * @param derivationPath The desired derivation path (e.g `44'/60'/0'`)
+ * @param basDerivationPath The desired derivation path (e.g `44'/60'/0'`)
*/
- public setPath(derivationPath: string) {
- this._derivationBasePath = derivationPath;
+ public setPath(basDerivationPath: string) {
+ this._baseDerivationPath = basDerivationPath;
}
/**
* Retrieve a users Ledger accounts. The accounts are derived from the derivationPath,
@@ -84,12 +86,10 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
* @return An array of accounts
*/
- public async getAccountsAsync(
- numberOfAccounts: number = walletUtils.DEFAULT_NUM_ADDRESSES_TO_FETCH,
- ): Promise<string[]> {
- const initialDerivedKey = await this._initialDerivedKeyAsync();
- const derivedKeys = walletUtils.calculateDerivedHDKeys(initialDerivedKey, numberOfAccounts);
- const accounts = _.map(derivedKeys, 'address');
+ public async getAccountsAsync(numberOfAccounts: number = DEFAULT_NUM_ADDRESSES_TO_FETCH): Promise<string[]> {
+ const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync();
+ const derivedKeyInfos = walletUtils.calculateDerivedHDKeyInfos(initialDerivedKeyInfo, numberOfAccounts);
+ const accounts = _.map(derivedKeyInfos, k => k.address);
return accounts;
}
/**
@@ -102,8 +102,11 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
*/
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
LedgerSubprovider._validateTxParams(txParams);
- const initialDerivedKey = await this._initialDerivedKeyAsync();
- const derivedKey = this._findDerivedKeyByPublicAddress(initialDerivedKey, txParams.from);
+ if (_.isUndefined(txParams.from) || !addressUtils.isAddress(txParams.from)) {
+ throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
+ }
+ const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync();
+ const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, txParams.from);
const ledgerClient = await this._createLedgerClientAsync();
@@ -116,7 +119,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
const txHex = tx.serialize().toString('hex');
try {
- const fullDerivationPath = derivedKey.derivationPath;
+ const fullDerivationPath = derivedKeyInfo.derivationPath;
const result = await ledgerClient.signTransaction(fullDerivationPath, txHex);
// Store signature in transaction
tx.r = Buffer.from(result.r, 'hex');
@@ -155,12 +158,13 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
}
assert.isHexString('data', data);
- const initialDerivedKey = await this._initialDerivedKeyAsync();
- const derivedKey = this._findDerivedKeyByPublicAddress(initialDerivedKey, address);
+ assert.isETHAddressHex('address', address);
+ const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync();
+ const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, address);
const ledgerClient = await this._createLedgerClientAsync();
try {
- const fullDerivationPath = derivedKey.derivationPath;
+ const fullDerivationPath = derivedKeyInfo.derivationPath;
const result = await ledgerClient.signPersonalMessage(fullDerivationPath, ethUtil.stripHexPrefix(data));
const v = result.v - 27;
let vHex = v.toString(16);
@@ -196,13 +200,13 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
this._ledgerClientIfExists = undefined;
this._connectionLock.release();
}
- private async _initialDerivedKeyAsync(): Promise<DerivedHDKey> {
+ private async _initialDerivedKeyInfoAsync(): Promise<DerivedHDKeyInfo> {
const ledgerClient = await this._createLedgerClientAsync();
let ledgerResponse;
try {
ledgerResponse = await ledgerClient.getAddress(
- this._derivationBasePath,
+ this._baseDerivationPath,
this._shouldAlwaysAskForConfirmation,
SHOULD_GET_CHAIN_CODE,
);
@@ -212,23 +216,26 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
const hdKey = new HDNode();
hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
+ const derivationPath = `${this._baseDerivationPath}/0`;
+ const derivationIndex = 0;
return {
hdKey,
address: ledgerResponse.address,
isChildKey: true,
- derivationBasePath: this._derivationBasePath,
- derivationPath: `${this._derivationBasePath}/0`,
- derivationIndex: 0,
+ baseDerivationPath: this._baseDerivationPath,
+ derivationPath,
+ derivationIndex,
};
}
- private _findDerivedKeyByPublicAddress(initalHDKey: DerivedHDKey, address: string): DerivedHDKey {
- if (_.isUndefined(address) || !addressUtils.isAddress(address)) {
- throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
- }
- const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(address, initalHDKey, this._addressSearchLimit);
- if (_.isUndefined(matchedDerivedKey)) {
+ private _findDerivedKeyInfoForAddress(initalHDKey: DerivedHDKeyInfo, address: string): DerivedHDKeyInfo {
+ const matchedDerivedKeyInfo = walletUtils.findDerivedKeyInfoForAddressIfExists(
+ address,
+ initalHDKey,
+ this._addressSearchLimit,
+ );
+ if (_.isUndefined(matchedDerivedKeyInfo)) {
throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
}
- return matchedDerivedKey;
+ return matchedDerivedKeyInfo;
}
}
diff --git a/packages/subproviders/src/subproviders/mnemonic_wallet.ts b/packages/subproviders/src/subproviders/mnemonic_wallet.ts
index de34a9d11..9a627b1ec 100644
--- a/packages/subproviders/src/subproviders/mnemonic_wallet.ts
+++ b/packages/subproviders/src/subproviders/mnemonic_wallet.ts
@@ -5,13 +5,17 @@ import ethUtil = require('ethereumjs-util');
import HDNode = require('hdkey');
import * as _ from 'lodash';
-import { DerivedHDKey, PartialTxParams, WalletSubproviderErrors } from '../types';
+import { DerivedHDKeyInfo, MnemonicWalletSubproviderConfigs, PartialTxParams, WalletSubproviderErrors } from '../types';
import { walletUtils } from '../utils/wallet_utils';
import { BaseWalletSubprovider } from './base_wallet_subprovider';
import { PrivateKeyWalletSubprovider } from './private_key_wallet';
-const DEFAULT_DERIVATION_PATH = `44'/60'/0'/0`;
+const DEFAULT_BASE_DERIVATION_PATH = `44'/60'/0'/0`;
+const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10;
+const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000;
+const ROOT_KEY_DERIVATION_PATH = 'm/';
+const ROOT_KEY_DERIVATION_INDEX = 0;
/**
* This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
@@ -20,35 +24,33 @@ const DEFAULT_DERIVATION_PATH = `44'/60'/0'/0`;
*/
export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
private _addressSearchLimit: number;
- private _derivationBasePath: string;
- private _derivedKey: DerivedHDKey;
+ private _baseDerivationPath: string;
+ private _derivedKeyInfo: DerivedHDKeyInfo;
/**
- * Instantiates a MnemonicWalletSubprovider. Defaults to derivationPath set to `44'/60'/0'/0`.
- * This is the default in TestRPC/Ganache, this can be overridden if desired.
- * @param mnemonic The mnemonic seed
- * @param derivationBasePath The derivation path, defaults to `44'/60'/0'/0`
- * @param addressSearchLimit The limit on address search attempts before raising `WalletSubproviderErrors.AddressNotFound`
+ * 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 Several available configurations
* @return MnemonicWalletSubprovider instance
*/
- constructor(
- mnemonic: string,
- derivationBasePath: string = DEFAULT_DERIVATION_PATH,
- addressSearchLimit: number = walletUtils.DEFAULT_ADDRESS_SEARCH_LIMIT,
- ) {
+ constructor(config: MnemonicWalletSubproviderConfigs) {
+ const mnemonic = config.mnemonic;
assert.isString('mnemonic', mnemonic);
- assert.isString('derivationPath', derivationBasePath);
+ const baseDerivationPath = config.baseDerivationPath || DEFAULT_BASE_DERIVATION_PATH;
+ assert.isString('baseDerivationPath', baseDerivationPath);
+ const addressSearchLimit = config.addressSearchLimit || DEFAULT_ADDRESS_SEARCH_LIMIT;
assert.isNumber('addressSearchLimit', addressSearchLimit);
super();
+
const seed = bip39.mnemonicToSeed(mnemonic);
const hdKey = HDNode.fromMasterSeed(seed);
- this._derivationBasePath = derivationBasePath;
- this._derivedKey = {
+ this._baseDerivationPath = baseDerivationPath;
+ this._derivedKeyInfo = {
address: walletUtils.addressOfHDKey(hdKey),
// HACK this isn't the base path for this root key, but is is the base path
- // we want all of the derived children to spawn from
- derivationBasePath: this._derivationBasePath,
- derivationPath: 'm/0',
- derivationIndex: 0,
+ // we want all of the derived children to branched from
+ baseDerivationPath: this._baseDerivationPath,
+ derivationPath: ROOT_KEY_DERIVATION_PATH,
+ derivationIndex: ROOT_KEY_DERIVATION_INDEX,
hdKey,
isChildKey: false,
};
@@ -59,17 +61,17 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
* @returns derivation path
*/
public getPath(): string {
- return this._derivationBasePath;
+ return this._baseDerivationPath;
}
/**
* Set a desired derivation path when computing the available user addresses
- * @param derivationPath The desired derivation path (e.g `44'/60'/0'`)
+ * @param baseDerivationPath The desired derivation path (e.g `44'/60'/0'`)
*/
- public setPath(derivationPath: string) {
- this._derivationBasePath = derivationPath;
- this._derivedKey = {
- ...this._derivedKey,
- derivationBasePath: this._derivationBasePath,
+ public setPath(baseDerivationPath: string) {
+ this._baseDerivationPath = baseDerivationPath;
+ this._derivedKeyInfo = {
+ ...this._derivedKeyInfo,
+ baseDerivationPath,
};
}
/**
@@ -79,11 +81,9 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
* @return An array of accounts
*/
- public async getAccountsAsync(
- numberOfAccounts: number = walletUtils.DEFAULT_NUM_ADDRESSES_TO_FETCH,
- ): Promise<string[]> {
- const derivedKeys = walletUtils.calculateDerivedHDKeys(this._derivedKey, numberOfAccounts);
- const accounts = _.map(derivedKeys, 'address');
+ public async getAccountsAsync(numberOfAccounts: number = DEFAULT_NUM_ADDRESSES_TO_FETCH): Promise<string[]> {
+ const derivedKeys = walletUtils.calculateDerivedHDKeyInfos(this._derivedKeyInfo, numberOfAccounts);
+ const accounts = _.map(derivedKeys, k => k.address);
return accounts;
}
@@ -96,6 +96,9 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
* @return Signed transaction hex string
*/
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
+ if (_.isUndefined(txParams.from) || !addressUtils.isAddress(txParams.from)) {
+ throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
+ }
const privateKeyWallet = this._privateKeyWalletForAddress(txParams.from);
const signedTx = privateKeyWallet.signTransactionAsync(txParams);
return signedTx;
@@ -111,28 +114,30 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
* @return Signature hex string (order: rsv)
*/
public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
+ if (_.isUndefined(data)) {
+ throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
+ }
+ assert.isHexString('data', data);
+ assert.isETHAddressHex('address', address);
const privateKeyWallet = this._privateKeyWalletForAddress(address);
const sig = await privateKeyWallet.signPersonalMessageAsync(data, address);
return sig;
}
private _privateKeyWalletForAddress(address: string): PrivateKeyWalletSubprovider {
- const derivedKey = this._findDerivedKeyByPublicAddress(address);
- const privateKeyHex = derivedKey.hdKey.privateKey.toString('hex');
+ const derivedKeyInfo = this._findDerivedKeyInfoForAddress(address);
+ const privateKeyHex = derivedKeyInfo.hdKey.privateKey.toString('hex');
const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKeyHex);
return privateKeyWallet;
}
- private _findDerivedKeyByPublicAddress(address: string): DerivedHDKey {
- if (_.isUndefined(address) || !addressUtils.isAddress(address)) {
- throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
- }
- const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(
+ private _findDerivedKeyInfoForAddress(address: string): DerivedHDKeyInfo {
+ const matchedDerivedKeyInfo = walletUtils.findDerivedKeyInfoForAddressIfExists(
address,
- this._derivedKey,
+ this._derivedKeyInfo,
this._addressSearchLimit,
);
- if (_.isUndefined(matchedDerivedKey)) {
+ if (_.isUndefined(matchedDerivedKeyInfo)) {
throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
}
- return matchedDerivedKey;
+ return matchedDerivedKeyInfo;
}
}
diff --git a/packages/subproviders/src/subproviders/private_key_wallet.ts b/packages/subproviders/src/subproviders/private_key_wallet.ts
index f8afab722..f3727039c 100644
--- a/packages/subproviders/src/subproviders/private_key_wallet.ts
+++ b/packages/subproviders/src/subproviders/private_key_wallet.ts
@@ -17,7 +17,7 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
private _privateKeyBuffer: Buffer;
/**
* Instantiates a PrivateKeyWalletSubprovider.
- * @param privateKey The private key of the ethereum address
+ * @param privateKey The corresponding private key to an Ethereum address
* @return PrivateKeyWalletSubprovider instance
*/
constructor(privateKey: string) {
@@ -46,7 +46,11 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
PrivateKeyWalletSubprovider._validateTxParams(txParams);
if (!_.isUndefined(txParams.from) && txParams.from !== this._address) {
- throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${txParams.from}`);
+ throw new Error(
+ `Requested to sign transaction with address: ${txParams.from}, instantiated with address: ${
+ this._address
+ }`,
+ );
}
const tx = new EthereumTx(txParams);
tx.sign(this._privateKeyBuffer);
@@ -54,8 +58,8 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
return rawTx;
}
/**
- * Sign a personal Ethereum signed message. The signing address will be
- * calculated from the private key, if an address is provided it must match the address calculated from the private key.
+ * Sign a personal Ethereum signed message. The signing address will be calculated from the private key.
+ * if an address is provided it must match the address calculated from the private key.
* If you've added this Subprovider 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.
@@ -67,10 +71,13 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
if (_.isUndefined(data)) {
throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
}
- if (_.isUndefined(address) || address !== this._address) {
- throw new Error(`${WalletSubproviderErrors.FromAddressMissingOrInvalid}: ${address}`);
- }
assert.isHexString('data', data);
+ assert.isETHAddressHex('address', address);
+ if (address !== this._address) {
+ throw new Error(
+ `Requested to sign message with address: ${address}, instantiated with address: ${this._address}`,
+ );
+ }
const dataBuff = ethUtil.toBuffer(data);
const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
const sig = ethUtil.ecsign(msgHashBuff, this._privateKeyBuffer);
diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts
index 121992278..b9d9d08ee 100644
--- a/packages/subproviders/src/types.ts
+++ b/packages/subproviders/src/types.ts
@@ -41,11 +41,12 @@ export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClien
export interface LedgerSubproviderConfigs {
networkId: number;
ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
- derivationPath?: string;
+ baseDerivationPath?: string;
accountFetchingConfigs?: AccountFetchingConfigs;
}
/*
+ * addressSearchLimit: The maximum number of addresses to search through, defaults to 1000
* numAddressesToReturn: Number of addresses to return from 'eth_accounts' call
* shouldAskForOnDeviceConfirmation: Whether you wish to prompt the user on their Ledger
* before fetching their addresses
@@ -56,6 +57,17 @@ export interface AccountFetchingConfigs {
shouldAskForOnDeviceConfirmation?: boolean;
}
+/*
+ * mnemonic: The string mnemonic seed
+ * addressSearchLimit: The maximum number of addresses to search through, defaults to 1000
+ * baseDerivationPath: The base derivation path (e.g 44'/60'/0'/0)
+ */
+export interface MnemonicWalletSubproviderConfigs {
+ mnemonic: string;
+ addressSearchLimit?: number;
+ baseDerivationPath?: string;
+}
+
export interface SignatureData {
hash: string;
r: string;
@@ -106,10 +118,10 @@ export enum NonceSubproviderErrors {
EmptyParametersFound = 'EMPTY_PARAMETERS_FOUND',
CannotDetermineAddressFromPayload = 'CANNOT_DETERMINE_ADDRESS_FROM_PAYLOAD',
}
-export interface DerivedHDKey {
+export interface DerivedHDKeyInfo {
address: string;
derivationIndex: number;
- derivationBasePath: string;
+ baseDerivationPath: string;
derivationPath: string;
hdKey: HDNode;
isChildKey: boolean;
diff --git a/packages/subproviders/src/utils/wallet_utils.ts b/packages/subproviders/src/utils/wallet_utils.ts
index d5ebf5ce6..a37597dff 100644
--- a/packages/subproviders/src/utils/wallet_utils.ts
+++ b/packages/subproviders/src/utils/wallet_utils.ts
@@ -2,37 +2,35 @@ import ethUtil = require('ethereumjs-util');
import HDNode = require('hdkey');
import * as _ from 'lodash';
-import { DerivedHDKey, WalletSubproviderErrors } from '../types';
+import { DerivedHDKeyInfo, WalletSubproviderErrors } from '../types';
-const DEFAULT_ADDRESS_SEARCH_OFFSET = 0;
-const BATCH_SIZE = 10;
const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000;
-class DerivedHDKeyIterator implements IterableIterator<DerivedHDKey> {
- private _initialDerivedKey: DerivedHDKey;
+class DerivedHDKeyInfoIterator implements IterableIterator<DerivedHDKeyInfo> {
+ private _initialDerivedKey: DerivedHDKeyInfo;
private _searchLimit: number;
private _index: number;
- constructor(initialDerivedKey: DerivedHDKey, searchLimit: number = DEFAULT_ADDRESS_SEARCH_OFFSET) {
+ constructor(initialDerivedKey: DerivedHDKeyInfo, searchLimit: number = DEFAULT_ADDRESS_SEARCH_LIMIT) {
this._searchLimit = searchLimit;
this._initialDerivedKey = initialDerivedKey;
this._index = 0;
}
- public next(): IteratorResult<DerivedHDKey> {
- const derivationBasePath = this._initialDerivedKey.derivationBasePath;
+ public next(): IteratorResult<DerivedHDKeyInfo> {
+ const baseDerivationPath = this._initialDerivedKey.baseDerivationPath;
const derivationIndex = this._index;
const isChildKey = this._initialDerivedKey.isChildKey;
// If the DerivedHDKey is a child then we walk relative, if not we walk the full derivation path
- const fullDerivationPath = `m/${derivationBasePath}/${derivationIndex}`;
+ const fullDerivationPath = `m/${baseDerivationPath}/${derivationIndex}`;
const relativeDerivationPath = `m/${derivationIndex}`;
const path = isChildKey ? relativeDerivationPath : fullDerivationPath;
const hdKey = this._initialDerivedKey.hdKey.derive(path);
const address = walletUtils.addressOfHDKey(hdKey);
- const derivedKey: DerivedHDKey = {
+ const derivedKey: DerivedHDKeyInfo = {
address,
hdKey,
- derivationBasePath,
+ baseDerivationPath,
derivationIndex,
derivationPath: fullDerivationPath,
isChildKey,
@@ -45,29 +43,27 @@ class DerivedHDKeyIterator implements IterableIterator<DerivedHDKey> {
};
}
- public [Symbol.iterator](): IterableIterator<DerivedHDKey> {
+ public [Symbol.iterator](): IterableIterator<DerivedHDKeyInfo> {
return this;
}
}
export const walletUtils = {
- DEFAULT_ADDRESS_SEARCH_LIMIT,
- DEFAULT_NUM_ADDRESSES_TO_FETCH: 10,
- calculateDerivedHDKeys(initialDerivedKey: DerivedHDKey, numberOfKeys: number): DerivedHDKey[] {
- const derivedKeys: DerivedHDKey[] = [];
- const derivedKeyIterator = new DerivedHDKeyIterator(initialDerivedKey, numberOfKeys);
+ calculateDerivedHDKeyInfos(initialDerivedKey: DerivedHDKeyInfo, numberOfKeys: number): DerivedHDKeyInfo[] {
+ const derivedKeys: DerivedHDKeyInfo[] = [];
+ const derivedKeyIterator = new DerivedHDKeyInfoIterator(initialDerivedKey, numberOfKeys);
for (const key of derivedKeyIterator) {
derivedKeys.push(key);
}
return derivedKeys;
},
- findDerivedKeyByAddress(
+ findDerivedKeyInfoForAddressIfExists(
address: string,
- initialDerivedKey: DerivedHDKey,
+ initialDerivedKey: DerivedHDKeyInfo,
searchLimit: number,
- ): DerivedHDKey | undefined {
- let matchedKey: DerivedHDKey | undefined;
- const derivedKeyIterator = new DerivedHDKeyIterator(initialDerivedKey, searchLimit);
+ ): DerivedHDKeyInfo | undefined {
+ let matchedKey: DerivedHDKeyInfo | undefined;
+ const derivedKeyIterator = new DerivedHDKeyInfoIterator(initialDerivedKey, searchLimit);
for (const key of derivedKeyIterator) {
if (key.address === address) {
matchedKey = key;
diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts
index 2db4befb3..11b5f6410 100644
--- a/packages/subproviders/test/integration/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts
@@ -33,7 +33,7 @@ describe('LedgerSubprovider', () => {
ledgerSubprovider = new LedgerSubprovider({
networkId,
ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
- derivationPath: fixtureData.TESTRPC_DERIVATION_PATH,
+ baseDerivationPath: fixtureData.TESTRPC_BASE_DERIVATION_PATH,
});
});
describe('direct method calls', () => {
diff --git a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
index 9131a8b6a..93300f47d 100644
--- a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
+++ b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
@@ -21,10 +21,10 @@ const expect = chai.expect;
describe('MnemonicWalletSubprovider', () => {
let subprovider: MnemonicWalletSubprovider;
before(async () => {
- subprovider = new MnemonicWalletSubprovider(
- fixtureData.TEST_RPC_MNEMONIC,
- fixtureData.TEST_RPC_MNEMONIC_DERIVATION_PATH,
- );
+ subprovider = new MnemonicWalletSubprovider({
+ mnemonic: fixtureData.TEST_RPC_MNEMONIC,
+ baseDerivationPath: fixtureData.TEST_RPC_MNEMONIC_BASE_DERIVATION_PATH,
+ });
});
describe('direct method calls', () => {
describe('success cases', () => {
@@ -157,11 +157,11 @@ describe('MnemonicWalletSubprovider', () => {
provider.sendAsync(payload, callback);
});
it('should throw if `address` param not found when calling personal_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const payload = {
jsonrpc: '2.0',
method: 'personal_sign',
- params: [nonHexMessage, fixtureData.NULL_ADDRESS],
+ params: [messageHex, fixtureData.NULL_ADDRESS],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
diff --git a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
index b84aebb18..5c1b5cd25 100644
--- a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
+++ b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
@@ -128,18 +128,20 @@ describe('PrivateKeyWalletSubprovider', () => {
});
provider.sendAsync(payload, callback);
});
- it('should throw if `address` param is not an address from private key when calling personal_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
+ it('should throw if `address` param is not the address from private key when calling personal_sign', (done: DoneCallback) => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const payload = {
jsonrpc: '2.0',
method: 'personal_sign',
- params: [nonHexMessage, fixtureData.TEST_RPC_ACCOUNT_1],
+ params: [messageHex, fixtureData.TEST_RPC_ACCOUNT_1],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.not.be.a('null');
expect(err.message).to.be.equal(
- `${WalletSubproviderErrors.FromAddressMissingOrInvalid}: ${fixtureData.TEST_RPC_ACCOUNT_1}`,
+ `Requested to sign message with address: ${
+ fixtureData.TEST_RPC_ACCOUNT_1
+ }, instantiated with address: ${fixtureData.TEST_RPC_ACCOUNT_0}`,
);
done();
});
@@ -183,16 +185,16 @@ describe('PrivateKeyWalletSubprovider', () => {
provider.sendAsync(payload, callback);
});
it('should throw if `address` param not found when calling personal_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const payload = {
jsonrpc: '2.0',
method: 'personal_sign',
- params: [nonHexMessage, '0x0'],
+ params: [messageHex, '0x0'],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal(`${WalletSubproviderErrors.FromAddressMissingOrInvalid}: 0x0`);
+ expect(err.message).to.be.equal(`Expected address to be of type ETHAddressHex, encountered: 0x0`);
done();
});
provider.sendAsync(payload, callback);
diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts
index a973961ce..3137e08b0 100644
--- a/packages/subproviders/test/utils/fixture_data.ts
+++ b/packages/subproviders/test/utils/fixture_data.ts
@@ -8,13 +8,13 @@ export const fixtureData = {
TEST_RPC_ACCOUNT_0_ACCOUNT_PRIVATE_KEY: 'F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D',
TEST_RPC_ACCOUNT_1,
TEST_RPC_MNEMONIC: 'concert load couple harbor equip island argue ramp clarify fence smart topic',
- TEST_RPC_MNEMONIC_DERIVATION_PATH: `44'/60'/0'/0`,
+ TEST_RPC_MNEMONIC_BASE_DERIVATION_PATH: `44'/60'/0'/0`,
PERSONAL_MESSAGE_STRING: 'hello world',
PERSONAL_MESSAGE_SIGNED_RESULT:
'0x1b0ec5e2908e993d0c8ab6b46da46be2688fdf03c7ea6686075de37392e50a7d7fcc531446699132fbda915bd989882e0064d417018773a315fb8d43ed063c9b00',
PERSONAL_MESSAGE_ACCOUNT_1_SIGNED_RESULT:
'0xe7ae0c21d02eb38f2c2a20d9d7876a98cc7ef035b7a4559d49375e2ec735e06f0d0ab0ff92ee56c5ffc28d516e6ed0692d0270feae8796408dbef060c6c7100f01',
- TESTRPC_DERIVATION_PATH: `m/44'/60'/0'/0`,
+ TESTRPC_BASE_DERIVATION_PATH: `m/44'/60'/0'/0`,
NETWORK_ID: networkId,
TX_DATA: {
nonce: '0x00',