aboutsummaryrefslogtreecommitdiffstats
path: root/packages/subproviders
diff options
context:
space:
mode:
Diffstat (limited to 'packages/subproviders')
-rw-r--r--packages/subproviders/package.json106
-rw-r--r--packages/subproviders/src/globals.d.ts150
-rw-r--r--packages/subproviders/src/index.ts18
-rw-r--r--packages/subproviders/src/subproviders/injected_web3.ts68
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts544
-rw-r--r--packages/subproviders/src/subproviders/redundant_rpc.ts84
-rw-r--r--packages/subproviders/src/subproviders/subprovider.ts58
-rw-r--r--packages/subproviders/src/types.ts102
-rw-r--r--packages/subproviders/test/chai_setup.ts10
-rw-r--r--packages/subproviders/test/integration/ledger_subprovider_test.ts322
-rw-r--r--packages/subproviders/test/unit/ledger_subprovider_test.ts452
-rw-r--r--packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts78
-rw-r--r--packages/subproviders/test/utils/report_callback_errors.ts20
-rw-r--r--packages/subproviders/tsconfig.json26
-rw-r--r--packages/subproviders/tslint.json2
15 files changed, 1020 insertions, 1020 deletions
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index 146c94496..1da019e2d 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -1,55 +1,55 @@
{
- "name": "@0xproject/subproviders",
- "version": "0.3.3",
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "license": "Apache-2.0",
- "scripts": {
- "clean": "shx rm -rf lib",
- "build": "tsc",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "run_mocha_unit": "mocha lib/test/unit/**/*_test.js --timeout 10000 --bail --exit",
- "run_mocha_integration": "mocha lib/test/integration/**/*_test.js --timeout 10000 --bail --exit",
- "test": "npm run test:unit",
- "test:circleci": "npm run test:unit",
- "test:all": "run-s test:unit test:integration",
- "test:unit": "run-s clean build run_mocha_unit",
- "test:integration": "run-s clean build run_mocha_integration"
- },
- "dependencies": {
- "@0xproject/assert": "^0.0.13",
- "@0xproject/utils": "^0.2.2",
- "bn.js": "^4.11.8",
- "es6-promisify": "^5.0.0",
- "ethereumjs-tx": "^1.3.3",
- "ethereumjs-util": "^5.1.1",
- "hdkey": "^0.7.1",
- "ledgerco": "0xProject/ledger-node-js-api",
- "lodash": "^4.17.4",
- "semaphore-async-await": "^1.5.1",
- "web3": "^0.20.0",
- "web3-provider-engine": "^13.0.1"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@0xproject/utils": "^0.2.2",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/node": "^8.0.53",
- "awesome-typescript-loader": "^3.1.3",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-typescript-typings": "^0.0.2",
- "dirty-chai": "^2.0.1",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "types-bn": "^0.0.1",
- "types-ethereumjs-util": "0xproject/types-ethereumjs-util",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.7.2",
- "webpack": "^3.1.0"
- }
+ "name": "@0xproject/subproviders",
+ "version": "0.3.3",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "license": "Apache-2.0",
+ "scripts": {
+ "clean": "shx rm -rf lib",
+ "build": "tsc",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "run_mocha_unit": "mocha lib/test/unit/**/*_test.js --timeout 10000 --bail --exit",
+ "run_mocha_integration": "mocha lib/test/integration/**/*_test.js --timeout 10000 --bail --exit",
+ "test": "npm run test:unit",
+ "test:circleci": "npm run test:unit",
+ "test:all": "run-s test:unit test:integration",
+ "test:unit": "run-s clean build run_mocha_unit",
+ "test:integration": "run-s clean build run_mocha_integration"
+ },
+ "dependencies": {
+ "@0xproject/assert": "^0.0.13",
+ "@0xproject/utils": "^0.2.2",
+ "bn.js": "^4.11.8",
+ "es6-promisify": "^5.0.0",
+ "ethereumjs-tx": "^1.3.3",
+ "ethereumjs-util": "^5.1.1",
+ "hdkey": "^0.7.1",
+ "ledgerco": "0xProject/ledger-node-js-api",
+ "lodash": "^4.17.4",
+ "semaphore-async-await": "^1.5.1",
+ "web3": "^0.20.0",
+ "web3-provider-engine": "^13.0.1"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@0xproject/utils": "^0.2.2",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/node": "^8.0.53",
+ "awesome-typescript-loader": "^3.1.3",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-typescript-typings": "^0.0.2",
+ "dirty-chai": "^2.0.1",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "types-bn": "^0.0.1",
+ "types-ethereumjs-util": "0xproject/types-ethereumjs-util",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.7.2",
+ "webpack": "^3.1.0"
+ }
}
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts
index 53457fa24..ed4fbc145 100644
--- a/packages/subproviders/src/globals.d.ts
+++ b/packages/subproviders/src/globals.d.ts
@@ -8,103 +8,103 @@ declare module 'es6-promisify';
// Ethereumjs-tx declarations
declare module 'ethereumjs-tx' {
- class EthereumTx {
- public raw: Buffer[];
- public r: Buffer;
- public s: Buffer;
- public v: Buffer;
- public serialize(): Buffer;
- constructor(txParams: any);
- }
- export = EthereumTx;
+ class EthereumTx {
+ public raw: Buffer[];
+ public r: Buffer;
+ public s: Buffer;
+ public v: Buffer;
+ public serialize(): Buffer;
+ constructor(txParams: any);
+ }
+ export = EthereumTx;
}
// Ledgerco declarations
interface ECSignatureString {
- v: string;
- r: string;
- s: string;
+ v: string;
+ r: string;
+ s: string;
}
interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
declare module 'ledgerco' {
- interface comm {
- close_async(): Promise<void>;
- }
- export class comm_node implements comm {
- public static create_async(timeoutMilliseconds?: number): Promise<comm_node>;
- public close_async(): Promise<void>;
- }
- export class comm_u2f implements comm {
- public static create_async(): Promise<comm_u2f>;
- public close_async(): Promise<void>;
- }
- export class eth {
- public comm: comm;
- constructor(comm: comm);
- public getAddress_async(
- path: string,
- display?: boolean,
- chaincode?: boolean,
- ): Promise<{ publicKey: string; address: string; chainCode: string }>;
- public signTransaction_async(path: string, rawTxHex: string): Promise<ECSignatureString>;
- public getAppConfiguration_async(): Promise<{
- arbitraryDataEnabled: number;
- version: string;
- }>;
- public signPersonalMessage_async(path: string, messageHex: string): Promise<ECSignature>;
- }
+ interface comm {
+ close_async(): Promise<void>;
+ }
+ export class comm_node implements comm {
+ public static create_async(timeoutMilliseconds?: number): Promise<comm_node>;
+ public close_async(): Promise<void>;
+ }
+ export class comm_u2f implements comm {
+ public static create_async(): Promise<comm_u2f>;
+ public close_async(): Promise<void>;
+ }
+ export class eth {
+ public comm: comm;
+ constructor(comm: comm);
+ public getAddress_async(
+ path: string,
+ display?: boolean,
+ chaincode?: boolean,
+ ): Promise<{ publicKey: string; address: string; chainCode: string }>;
+ public signTransaction_async(path: string, rawTxHex: string): Promise<ECSignatureString>;
+ public getAppConfiguration_async(): Promise<{
+ arbitraryDataEnabled: number;
+ version: string;
+ }>;
+ public signPersonalMessage_async(path: string, messageHex: string): Promise<ECSignature>;
+ }
}
// Semaphore-async-await declarations
declare module 'semaphore-async-await' {
- class Semaphore {
- constructor(permits: number);
- public wait(): Promise<void>;
- public signal(): void;
- }
- export default Semaphore;
+ class Semaphore {
+ constructor(permits: number);
+ public wait(): Promise<void>;
+ public signal(): void;
+ }
+ export default Semaphore;
}
// web3-provider-engine declarations
declare module 'web3-provider-engine/subproviders/subprovider' {
- class Subprovider {}
- export = Subprovider;
+ class Subprovider {}
+ export = Subprovider;
}
declare module 'web3-provider-engine/subproviders/rpc' {
- import * as Web3 from 'web3';
- class RpcSubprovider {
- constructor(options: { rpcUrl: string });
- public handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, data?: any) => void,
- ): void;
- }
- export = RpcSubprovider;
+ import * as Web3 from 'web3';
+ class RpcSubprovider {
+ constructor(options: { rpcUrl: string });
+ public handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, data?: any) => void,
+ ): void;
+ }
+ export = RpcSubprovider;
}
declare module 'web3-provider-engine' {
- class Web3ProviderEngine {
- public on(event: string, handler: () => void): void;
- public send(payload: any): void;
- public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
- public addProvider(provider: any): void;
- public start(): void;
- public stop(): void;
- }
- export = Web3ProviderEngine;
+ class Web3ProviderEngine {
+ public on(event: string, handler: () => void): void;
+ public send(payload: any): void;
+ public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
+ public addProvider(provider: any): void;
+ public start(): void;
+ public stop(): void;
+ }
+ export = Web3ProviderEngine;
}
// hdkey declarations
declare module 'hdkey' {
- class HDNode {
- public publicKey: Buffer;
- public chainCode: Buffer;
- public constructor();
- public derive(path: string): HDNode;
- }
- export = HDNode;
+ class HDNode {
+ public publicKey: Buffer;
+ public chainCode: Buffer;
+ public constructor();
+ public derive(path: string): HDNode;
+ }
+ export = HDNode;
}
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index 720c4362f..ecba186df 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -1,7 +1,7 @@
import {
- comm_node as LedgerNodeCommunication,
- comm_u2f as LedgerBrowserCommunication,
- eth as LedgerEthereumClientFn,
+ comm_node as LedgerNodeCommunication,
+ comm_u2f as LedgerBrowserCommunication,
+ eth as LedgerEthereumClientFn,
} from 'ledgerco';
import { LedgerEthereumClient } from './types';
@@ -16,9 +16,9 @@ export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient } from
* @return LedgerEthereumClient A browser client
*/
export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerBrowserCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
- return ledgerEthClient;
+ const ledgerConnection = await LedgerBrowserCommunication.create_async();
+ const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
+ return ledgerEthClient;
}
/**
@@ -26,7 +26,7 @@ export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerE
* @return LedgerEthereumClient A Node.js client
*/
export async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerNodeCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
- return ledgerEthClient;
+ const ledgerConnection = await LedgerNodeCommunication.create_async();
+ const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
+ return ledgerEthClient;
}
diff --git a/packages/subproviders/src/subproviders/injected_web3.ts b/packages/subproviders/src/subproviders/injected_web3.ts
index bd29acb22..dc1b8b24c 100644
--- a/packages/subproviders/src/subproviders/injected_web3.ts
+++ b/packages/subproviders/src/subproviders/injected_web3.ts
@@ -9,41 +9,41 @@ import Web3ProviderEngine = require('web3-provider-engine');
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class InjectedWeb3Subprovider {
- private _injectedWeb3: Web3;
- constructor(injectedWeb3: Web3) {
- this._injectedWeb3 = injectedWeb3;
- }
- public handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, result: any) => void,
- ) {
- switch (payload.method) {
- case 'web3_clientVersion':
- this._injectedWeb3.version.getNode(end);
- return;
- case 'eth_accounts':
- this._injectedWeb3.eth.getAccounts(end);
- return;
+ private _injectedWeb3: Web3;
+ constructor(injectedWeb3: Web3) {
+ this._injectedWeb3 = injectedWeb3;
+ }
+ public handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, result: any) => void,
+ ) {
+ switch (payload.method) {
+ case 'web3_clientVersion':
+ this._injectedWeb3.version.getNode(end);
+ return;
+ case 'eth_accounts':
+ this._injectedWeb3.eth.getAccounts(end);
+ return;
- case 'eth_sendTransaction':
- const [txParams] = payload.params;
- this._injectedWeb3.eth.sendTransaction(txParams, end);
- return;
+ case 'eth_sendTransaction':
+ const [txParams] = payload.params;
+ this._injectedWeb3.eth.sendTransaction(txParams, end);
+ return;
- case 'eth_sign':
- const [address, message] = payload.params;
- this._injectedWeb3.eth.sign(address, message, end);
- return;
+ case 'eth_sign':
+ const [address, message] = payload.params;
+ this._injectedWeb3.eth.sign(address, message, end);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: Web3ProviderEngine) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: Web3ProviderEngine) {
+ // noop
+ }
}
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 7267a793e..00649873b 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -8,12 +8,12 @@ import Semaphore from 'semaphore-async-await';
import Web3 = require('web3');
import {
- LedgerEthereumClient,
- LedgerEthereumClientFactoryAsync,
- LedgerSubproviderConfigs,
- LedgerSubproviderErrors,
- PartialTxParams,
- ResponseWithTxParams,
+ LedgerEthereumClient,
+ LedgerEthereumClientFactoryAsync,
+ LedgerSubproviderConfigs,
+ LedgerSubproviderErrors,
+ PartialTxParams,
+ ResponseWithTxParams,
} from '../types';
import { Subprovider } from './subprovider';
@@ -24,284 +24,284 @@ const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
const SHOULD_GET_CHAIN_CODE = true;
export class LedgerSubprovider extends Subprovider {
- private _nonceLock: Semaphore;
- private _connectionLock: Semaphore;
- private _networkId: number;
- private _derivationPath: string;
- private _derivationPathIndex: number;
- private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
- private _ledgerClientIfExists?: LedgerEthereumClient;
- private _shouldAlwaysAskForConfirmation: boolean;
- private static _validateSender(sender: string) {
- if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) {
- throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- }
- }
- constructor(config: LedgerSubproviderConfigs) {
- super();
- this._nonceLock = new Semaphore(1);
- this._connectionLock = new Semaphore(1);
- this._networkId = config.networkId;
- this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync;
- this._derivationPath = config.derivationPath || DEFAULT_DERIVATION_PATH;
- this._shouldAlwaysAskForConfirmation =
- !_.isUndefined(config.accountFetchingConfigs) &&
- !_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation)
- ? config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation
- : ASK_FOR_ON_DEVICE_CONFIRMATION;
- this._derivationPathIndex = 0;
- }
- public getPath(): string {
- return this._derivationPath;
- }
- public setPath(derivationPath: string) {
- this._derivationPath = derivationPath;
- }
- public setPathIndex(pathIndex: number) {
- this._derivationPathIndex = pathIndex;
- }
- public async handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, result?: any) => void,
- ) {
- let accounts;
- let txParams;
- switch (payload.method) {
- case 'eth_coinbase':
- try {
- accounts = await this.getAccountsAsync();
- end(null, accounts[0]);
- } catch (err) {
- end(err);
- }
- return;
+ private _nonceLock: Semaphore;
+ private _connectionLock: Semaphore;
+ private _networkId: number;
+ private _derivationPath: string;
+ private _derivationPathIndex: number;
+ private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
+ private _ledgerClientIfExists?: LedgerEthereumClient;
+ private _shouldAlwaysAskForConfirmation: boolean;
+ private static _validateSender(sender: string) {
+ if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) {
+ throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ }
+ }
+ constructor(config: LedgerSubproviderConfigs) {
+ super();
+ this._nonceLock = new Semaphore(1);
+ this._connectionLock = new Semaphore(1);
+ this._networkId = config.networkId;
+ this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync;
+ this._derivationPath = config.derivationPath || DEFAULT_DERIVATION_PATH;
+ this._shouldAlwaysAskForConfirmation =
+ !_.isUndefined(config.accountFetchingConfigs) &&
+ !_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation)
+ ? config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation
+ : ASK_FOR_ON_DEVICE_CONFIRMATION;
+ this._derivationPathIndex = 0;
+ }
+ public getPath(): string {
+ return this._derivationPath;
+ }
+ public setPath(derivationPath: string) {
+ this._derivationPath = derivationPath;
+ }
+ public setPathIndex(pathIndex: number) {
+ this._derivationPathIndex = pathIndex;
+ }
+ public async handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, result?: any) => void,
+ ) {
+ let accounts;
+ let txParams;
+ switch (payload.method) {
+ case 'eth_coinbase':
+ try {
+ accounts = await this.getAccountsAsync();
+ end(null, accounts[0]);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_accounts':
- try {
- accounts = await this.getAccountsAsync();
- end(null, accounts);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_accounts':
+ try {
+ accounts = await this.getAccountsAsync();
+ end(null, accounts);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_sendTransaction':
- txParams = payload.params[0];
- try {
- LedgerSubprovider._validateSender(txParams.from);
- const result = await this._sendTransactionAsync(txParams);
- end(null, result);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_sendTransaction':
+ txParams = payload.params[0];
+ try {
+ LedgerSubprovider._validateSender(txParams.from);
+ const result = await this._sendTransactionAsync(txParams);
+ end(null, result);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_signTransaction':
- txParams = payload.params[0];
- try {
- const result = await this._signTransactionWithoutSendingAsync(txParams);
- end(null, result);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_signTransaction':
+ txParams = payload.params[0];
+ try {
+ const result = await this._signTransactionWithoutSendingAsync(txParams);
+ end(null, result);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_sign':
- case 'personal_sign':
- const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0];
- try {
- if (_.isUndefined(data)) {
- throw new Error(LedgerSubproviderErrors.DataMissingForSignPersonalMessage);
- }
- assert.isHexString('data', data);
- const ecSignatureHex = await this.signPersonalMessageAsync(data);
- end(null, ecSignatureHex);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_sign':
+ case 'personal_sign':
+ const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0];
+ try {
+ if (_.isUndefined(data)) {
+ throw new Error(LedgerSubproviderErrors.DataMissingForSignPersonalMessage);
+ }
+ assert.isHexString('data', data);
+ const ecSignatureHex = await this.signPersonalMessageAsync(data);
+ end(null, ecSignatureHex);
+ } catch (err) {
+ end(err);
+ }
+ return;
- default:
- next();
- return;
- }
- }
- public async getAccountsAsync(): Promise<string[]> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ default:
+ next();
+ return;
+ }
+ }
+ public async getAccountsAsync(): Promise<string[]> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
- let ledgerResponse;
- try {
- ledgerResponse = await this._ledgerClientIfExists.getAddress_async(
- this._derivationPath,
- this._shouldAlwaysAskForConfirmation,
- SHOULD_GET_CHAIN_CODE,
- );
- } finally {
- await this._destroyLedgerClientAsync();
- }
+ let ledgerResponse;
+ try {
+ ledgerResponse = await this._ledgerClientIfExists.getAddress_async(
+ this._derivationPath,
+ this._shouldAlwaysAskForConfirmation,
+ SHOULD_GET_CHAIN_CODE,
+ );
+ } finally {
+ await this._destroyLedgerClientAsync();
+ }
- const hdKey = new HDNode();
- hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
- hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
+ const hdKey = new HDNode();
+ hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
+ hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
- const accounts = [];
- for (let i = 0; i < NUM_ADDRESSES_TO_FETCH; i++) {
- const derivedHDNode = hdKey.derive(`m/${i + this._derivationPathIndex}`);
- const derivedPublicKey = derivedHDNode.publicKey;
- const shouldSanitizePublicKey = true;
- const ethereumAddressUnprefixed = ethUtil
- .publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
- .toString('hex');
- const ethereumAddressPrefixed = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
- accounts.push(ethereumAddressPrefixed.toLowerCase());
- }
- return accounts;
- }
- public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ const accounts = [];
+ for (let i = 0; i < NUM_ADDRESSES_TO_FETCH; i++) {
+ const derivedHDNode = hdKey.derive(`m/${i + this._derivationPathIndex}`);
+ const derivedPublicKey = derivedHDNode.publicKey;
+ const shouldSanitizePublicKey = true;
+ const ethereumAddressUnprefixed = ethUtil
+ .publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
+ .toString('hex');
+ const ethereumAddressPrefixed = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
+ accounts.push(ethereumAddressPrefixed.toLowerCase());
+ }
+ return accounts;
+ }
+ public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
- const tx = new EthereumTx(txParams);
+ const tx = new EthereumTx(txParams);
- // Set the EIP155 bits
- tx.raw[6] = Buffer.from([this._networkId]); // v
- tx.raw[7] = Buffer.from([]); // r
- tx.raw[8] = Buffer.from([]); // s
+ // Set the EIP155 bits
+ tx.raw[6] = Buffer.from([this._networkId]); // v
+ tx.raw[7] = Buffer.from([]); // r
+ tx.raw[8] = Buffer.from([]); // s
- const txHex = tx.serialize().toString('hex');
- try {
- const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signTransaction_async(derivationPath, txHex);
- // Store signature in transaction
- tx.r = Buffer.from(result.r, 'hex');
- tx.s = Buffer.from(result.s, 'hex');
- tx.v = Buffer.from(result.v, 'hex');
+ const txHex = tx.serialize().toString('hex');
+ try {
+ const derivationPath = this._getDerivationPath();
+ const result = await this._ledgerClientIfExists.signTransaction_async(derivationPath, txHex);
+ // Store signature in transaction
+ tx.r = Buffer.from(result.r, 'hex');
+ tx.s = Buffer.from(result.s, 'hex');
+ tx.v = Buffer.from(result.v, 'hex');
- // EIP155: v should be chain_id * 2 + {35, 36}
- const signedChainId = Math.floor((tx.v[0] - 35) / 2);
- if (signedChainId !== this._networkId) {
- await this._destroyLedgerClientAsync();
- const err = new Error(LedgerSubproviderErrors.TooOldLedgerFirmware);
- throw err;
- }
+ // EIP155: v should be chain_id * 2 + {35, 36}
+ const signedChainId = Math.floor((tx.v[0] - 35) / 2);
+ if (signedChainId !== this._networkId) {
+ await this._destroyLedgerClientAsync();
+ const err = new Error(LedgerSubproviderErrors.TooOldLedgerFirmware);
+ throw err;
+ }
- const signedTxHex = `0x${tx.serialize().toString('hex')}`;
- await this._destroyLedgerClientAsync();
- return signedTxHex;
- } catch (err) {
- await this._destroyLedgerClientAsync();
- throw err;
- }
- }
- public async signPersonalMessageAsync(data: string): Promise<string> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
- try {
- const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signPersonalMessage_async(
- derivationPath,
- ethUtil.stripHexPrefix(data),
- );
- const v = result.v - 27;
- let vHex = v.toString(16);
- if (vHex.length < 2) {
- vHex = `0${v}`;
- }
- const signature = `0x${result.r}${result.s}${vHex}`;
- await this._destroyLedgerClientAsync();
- return signature;
- } catch (err) {
- await this._destroyLedgerClientAsync();
- throw err;
- }
- }
- private _getDerivationPath() {
- const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`;
- return derivationPath;
- }
- private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> {
- await this._connectionLock.wait();
- if (!_.isUndefined(this._ledgerClientIfExists)) {
- this._connectionLock.signal();
- throw new Error(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
- }
- const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync();
- this._connectionLock.signal();
- return ledgerEthereumClient;
- }
- private async _destroyLedgerClientAsync() {
- await this._connectionLock.wait();
- if (_.isUndefined(this._ledgerClientIfExists)) {
- this._connectionLock.signal();
- return;
- }
- await this._ledgerClientIfExists.comm.close_async();
- this._ledgerClientIfExists = undefined;
- this._connectionLock.signal();
- }
- private async _sendTransactionAsync(txParams: PartialTxParams): Promise<string> {
- await this._nonceLock.wait();
- try {
- // fill in the extras
- const filledParams = await this._populateMissingTxParamsAsync(txParams);
- // sign it
- const signedTx = await this.signTransactionAsync(filledParams);
- // emit a submit
- const payload = {
- method: 'eth_sendRawTransaction',
- params: [signedTx],
- };
- const result = await this.emitPayloadAsync(payload);
- this._nonceLock.signal();
- return result.result;
- } catch (err) {
- this._nonceLock.signal();
- throw err;
- }
- }
- private async _signTransactionWithoutSendingAsync(txParams: PartialTxParams): Promise<ResponseWithTxParams> {
- await this._nonceLock.wait();
- try {
- // fill in the extras
- const filledParams = await this._populateMissingTxParamsAsync(txParams);
- // sign it
- const signedTx = await this.signTransactionAsync(filledParams);
+ const signedTxHex = `0x${tx.serialize().toString('hex')}`;
+ await this._destroyLedgerClientAsync();
+ return signedTxHex;
+ } catch (err) {
+ await this._destroyLedgerClientAsync();
+ throw err;
+ }
+ }
+ public async signPersonalMessageAsync(data: string): Promise<string> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ try {
+ const derivationPath = this._getDerivationPath();
+ const result = await this._ledgerClientIfExists.signPersonalMessage_async(
+ derivationPath,
+ ethUtil.stripHexPrefix(data),
+ );
+ const v = result.v - 27;
+ let vHex = v.toString(16);
+ if (vHex.length < 2) {
+ vHex = `0${v}`;
+ }
+ const signature = `0x${result.r}${result.s}${vHex}`;
+ await this._destroyLedgerClientAsync();
+ return signature;
+ } catch (err) {
+ await this._destroyLedgerClientAsync();
+ throw err;
+ }
+ }
+ private _getDerivationPath() {
+ const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`;
+ return derivationPath;
+ }
+ private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> {
+ await this._connectionLock.wait();
+ if (!_.isUndefined(this._ledgerClientIfExists)) {
+ this._connectionLock.signal();
+ throw new Error(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
+ }
+ const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync();
+ this._connectionLock.signal();
+ return ledgerEthereumClient;
+ }
+ private async _destroyLedgerClientAsync() {
+ await this._connectionLock.wait();
+ if (_.isUndefined(this._ledgerClientIfExists)) {
+ this._connectionLock.signal();
+ return;
+ }
+ await this._ledgerClientIfExists.comm.close_async();
+ this._ledgerClientIfExists = undefined;
+ this._connectionLock.signal();
+ }
+ private async _sendTransactionAsync(txParams: PartialTxParams): Promise<string> {
+ await this._nonceLock.wait();
+ try {
+ // fill in the extras
+ const filledParams = await this._populateMissingTxParamsAsync(txParams);
+ // sign it
+ const signedTx = await this.signTransactionAsync(filledParams);
+ // emit a submit
+ const payload = {
+ method: 'eth_sendRawTransaction',
+ params: [signedTx],
+ };
+ const result = await this.emitPayloadAsync(payload);
+ this._nonceLock.signal();
+ return result.result;
+ } catch (err) {
+ this._nonceLock.signal();
+ throw err;
+ }
+ }
+ private async _signTransactionWithoutSendingAsync(txParams: PartialTxParams): Promise<ResponseWithTxParams> {
+ await this._nonceLock.wait();
+ try {
+ // fill in the extras
+ const filledParams = await this._populateMissingTxParamsAsync(txParams);
+ // sign it
+ const signedTx = await this.signTransactionAsync(filledParams);
- this._nonceLock.signal();
- const result = {
- raw: signedTx,
- tx: txParams,
- };
- return result;
- } catch (err) {
- this._nonceLock.signal();
- throw err;
- }
- }
- private async _populateMissingTxParamsAsync(txParams: PartialTxParams): Promise<PartialTxParams> {
- if (_.isUndefined(txParams.gasPrice)) {
- const gasPriceResult = await this.emitPayloadAsync({
- method: 'eth_gasPrice',
- params: [],
- });
- const gasPrice = gasPriceResult.result.toString();
- txParams.gasPrice = gasPrice;
- }
- if (_.isUndefined(txParams.nonce)) {
- const nonceResult = await this.emitPayloadAsync({
- method: 'eth_getTransactionCount',
- params: [txParams.from, 'pending'],
- });
- const nonce = nonceResult.result;
- txParams.nonce = nonce;
- }
- if (_.isUndefined(txParams.gas)) {
- const gasResult = await this.emitPayloadAsync({
- method: 'eth_estimateGas',
- params: [txParams],
- });
- const gas = gasResult.result.toString();
- txParams.gas = gas;
- }
- return txParams;
- }
+ this._nonceLock.signal();
+ const result = {
+ raw: signedTx,
+ tx: txParams,
+ };
+ return result;
+ } catch (err) {
+ this._nonceLock.signal();
+ throw err;
+ }
+ }
+ private async _populateMissingTxParamsAsync(txParams: PartialTxParams): Promise<PartialTxParams> {
+ if (_.isUndefined(txParams.gasPrice)) {
+ const gasPriceResult = await this.emitPayloadAsync({
+ method: 'eth_gasPrice',
+ params: [],
+ });
+ const gasPrice = gasPriceResult.result.toString();
+ txParams.gasPrice = gasPrice;
+ }
+ if (_.isUndefined(txParams.nonce)) {
+ const nonceResult = await this.emitPayloadAsync({
+ method: 'eth_getTransactionCount',
+ params: [txParams.from, 'pending'],
+ });
+ const nonce = nonceResult.result;
+ txParams.nonce = nonce;
+ }
+ if (_.isUndefined(txParams.gas)) {
+ const gasResult = await this.emitPayloadAsync({
+ method: 'eth_estimateGas',
+ params: [txParams],
+ });
+ const gas = gasResult.result.toString();
+ txParams.gas = gas;
+ }
+ return txParams;
+ }
}
diff --git a/packages/subproviders/src/subproviders/redundant_rpc.ts b/packages/subproviders/src/subproviders/redundant_rpc.ts
index a3cb463a8..92d7ae8ae 100644
--- a/packages/subproviders/src/subproviders/redundant_rpc.ts
+++ b/packages/subproviders/src/subproviders/redundant_rpc.ts
@@ -7,46 +7,46 @@ import { JSONRPCPayload } from '../types';
import { Subprovider } from './subprovider';
export class RedundantRPCSubprovider extends Subprovider {
- private _rpcs: RpcSubprovider[];
- private static async _firstSuccessAsync(
- rpcs: RpcSubprovider[],
- payload: JSONRPCPayload,
- next: () => void,
- ): Promise<any> {
- let lastErr: Error | undefined;
- for (const rpc of rpcs) {
- try {
- const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next);
- return data;
- } catch (err) {
- lastErr = err;
- continue;
- }
- }
- if (!_.isUndefined(lastErr)) {
- throw lastErr;
- }
- }
- constructor(endpoints: string[]) {
- super();
- this._rpcs = _.map(endpoints, endpoint => {
- return new RpcSubprovider({
- rpcUrl: endpoint,
- });
- });
- }
- // tslint:disable-next-line:async-suffix
- public async handleRequest(
- payload: JSONRPCPayload,
- next: () => void,
- end: (err: Error | null, data?: any) => void,
- ): Promise<void> {
- const rpcsCopy = this._rpcs.slice();
- try {
- const data = await RedundantRPCSubprovider._firstSuccessAsync(rpcsCopy, payload, next);
- end(null, data);
- } catch (err) {
- end(err);
- }
- }
+ private _rpcs: RpcSubprovider[];
+ private static async _firstSuccessAsync(
+ rpcs: RpcSubprovider[],
+ payload: JSONRPCPayload,
+ next: () => void,
+ ): Promise<any> {
+ let lastErr: Error | undefined;
+ for (const rpc of rpcs) {
+ try {
+ const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next);
+ return data;
+ } catch (err) {
+ lastErr = err;
+ continue;
+ }
+ }
+ if (!_.isUndefined(lastErr)) {
+ throw lastErr;
+ }
+ }
+ constructor(endpoints: string[]) {
+ super();
+ this._rpcs = _.map(endpoints, endpoint => {
+ return new RpcSubprovider({
+ rpcUrl: endpoint,
+ });
+ });
+ }
+ // tslint:disable-next-line:async-suffix
+ public async handleRequest(
+ payload: JSONRPCPayload,
+ next: () => void,
+ end: (err: Error | null, data?: any) => void,
+ ): Promise<void> {
+ const rpcsCopy = this._rpcs.slice();
+ try {
+ const data = await RedundantRPCSubprovider._firstSuccessAsync(rpcsCopy, payload, next);
+ end(null, data);
+ } catch (err) {
+ end(err);
+ }
+ }
}
diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts
index 6435c9f65..b6bb16918 100644
--- a/packages/subproviders/src/subproviders/subprovider.ts
+++ b/packages/subproviders/src/subproviders/subprovider.ts
@@ -8,33 +8,33 @@ import { JSONRPCPayload } from '../types';
* Altered version of: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class Subprovider {
- private _engine: any;
- // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
- private static _getRandomId() {
- const extraDigits = 3;
- // 13 time digits
- const datePart = new Date().getTime() * Math.pow(10, extraDigits);
- // 3 random digits
- const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits));
- // 16 digits
- return datePart + extraPart;
- }
- private static _createFinalPayload(payload: JSONRPCPayload): Web3.JSONRPCRequestPayload {
- const finalPayload = {
- // defaults
- id: Subprovider._getRandomId(),
- jsonrpc: '2.0',
- params: [],
- ...payload,
- };
- return finalPayload;
- }
- public setEngine(engine: any): void {
- this._engine = engine;
- }
- public async emitPayloadAsync(payload: JSONRPCPayload): Promise<any> {
- const finalPayload = Subprovider._createFinalPayload(payload);
- const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload);
- return response;
- }
+ private _engine: any;
+ // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
+ private static _getRandomId() {
+ const extraDigits = 3;
+ // 13 time digits
+ const datePart = new Date().getTime() * Math.pow(10, extraDigits);
+ // 3 random digits
+ const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits));
+ // 16 digits
+ return datePart + extraPart;
+ }
+ private static _createFinalPayload(payload: JSONRPCPayload): Web3.JSONRPCRequestPayload {
+ const finalPayload = {
+ // defaults
+ id: Subprovider._getRandomId(),
+ jsonrpc: '2.0',
+ params: [],
+ ...payload,
+ };
+ return finalPayload;
+ }
+ public setEngine(engine: any): void {
+ this._engine = engine;
+ }
+ public async emitPayloadAsync(payload: JSONRPCPayload): Promise<any> {
+ const finalPayload = Subprovider._createFinalPayload(payload);
+ const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload);
+ return response;
+ }
}
diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts
index 3db8be943..ec1eedd34 100644
--- a/packages/subproviders/src/types.ts
+++ b/packages/subproviders/src/types.ts
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
export interface LedgerCommunicationClient {
- close_async: () => Promise<void>;
+ close_async: () => Promise<void>;
}
/*
@@ -10,28 +10,28 @@ export interface LedgerCommunicationClient {
* NodeJs and Browser communication are supported.
*/
export interface LedgerEthereumClient {
- // shouldGetChainCode is defined as `true` instead of `boolean` because other types rely on the assumption
- // that we get back the chain code and we don't have dependent types to express it properly
- getAddress_async: (
- derivationPath: string,
- askForDeviceConfirmation: boolean,
- shouldGetChainCode: true,
- ) => Promise<LedgerGetAddressResult>;
- signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<ECSignature>;
- signTransaction_async: (derivationPath: string, txHex: string) => Promise<ECSignatureString>;
- comm: LedgerCommunicationClient;
+ // shouldGetChainCode is defined as `true` instead of `boolean` because other types rely on the assumption
+ // that we get back the chain code and we don't have dependent types to express it properly
+ getAddress_async: (
+ derivationPath: string,
+ askForDeviceConfirmation: boolean,
+ shouldGetChainCode: true,
+ ) => Promise<LedgerGetAddressResult>;
+ signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<ECSignature>;
+ signTransaction_async: (derivationPath: string, txHex: string) => Promise<ECSignatureString>;
+ comm: LedgerCommunicationClient;
}
export interface ECSignatureString {
- v: string;
- r: string;
- s: string;
+ v: string;
+ r: string;
+ s: string;
}
export interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClient>;
@@ -43,10 +43,10 @@ export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClien
* accountFetchingConfigs: configs related to fetching accounts from a Ledger
*/
export interface LedgerSubproviderConfigs {
- networkId: number;
- ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
- derivationPath?: string;
- accountFetchingConfigs?: AccountFetchingConfigs;
+ networkId: number;
+ ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
+ derivationPath?: string;
+ accountFetchingConfigs?: AccountFetchingConfigs;
}
/*
@@ -55,60 +55,60 @@ export interface LedgerSubproviderConfigs {
* before fetching their addresses
*/
export interface AccountFetchingConfigs {
- numAddressesToReturn?: number;
- shouldAskForOnDeviceConfirmation?: boolean;
+ numAddressesToReturn?: number;
+ shouldAskForOnDeviceConfirmation?: boolean;
}
export interface SignatureData {
- hash: string;
- r: string;
- s: string;
- v: number;
+ hash: string;
+ r: string;
+ s: string;
+ v: number;
}
export interface LedgerGetAddressResult {
- address: string;
- publicKey: string;
- chainCode: string;
+ address: string;
+ publicKey: string;
+ chainCode: string;
}
export interface LedgerWalletSubprovider {
- getPath: () => string;
- setPath: (path: string) => void;
- setPathIndex: (pathIndex: number) => void;
+ getPath: () => string;
+ setPath: (path: string) => void;
+ setPathIndex: (pathIndex: number) => void;
}
export interface PartialTxParams {
- nonce: string;
- gasPrice?: string;
- gas: string;
- to: string;
- from?: string;
- value?: string;
- data?: string;
- chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
+ nonce: string;
+ gasPrice?: string;
+ gas: string;
+ to: string;
+ from?: string;
+ value?: string;
+ data?: string;
+ chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
}
export type DoneCallback = (err?: Error) => void;
export interface JSONRPCPayload {
- params: any[];
- method: string;
+ params: any[];
+ method: string;
}
export interface LedgerCommunication {
- close_async: () => Promise<void>;
+ close_async: () => Promise<void>;
}
export interface ResponseWithTxParams {
- raw: string;
- tx: PartialTxParams;
+ raw: string;
+ tx: PartialTxParams;
}
export enum LedgerSubproviderErrors {
- TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
- FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
- DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
- SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
- MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED',
+ TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
+ FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
+ DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
+ SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
+ MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED',
}
diff --git a/packages/subproviders/test/chai_setup.ts b/packages/subproviders/test/chai_setup.ts
index a281bab6c..941a141d5 100644
--- a/packages/subproviders/test/chai_setup.ts
+++ b/packages/subproviders/test/chai_setup.ts
@@ -3,9 +3,9 @@ import chaiAsPromised = require('chai-as-promised');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
- configure() {
- chai.config.includeStack = true;
- chai.use(dirtyChai);
- chai.use(chaiAsPromised);
- },
+ configure() {
+ chai.config.includeStack = true;
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
};
diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts
index 628b532d7..6c1de46aa 100644
--- a/packages/subproviders/test/integration/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts
@@ -17,168 +17,168 @@ const expect = chai.expect;
const TEST_RPC_ACCOUNT_0 = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
describe('LedgerSubprovider', () => {
- let ledgerSubprovider: LedgerSubprovider;
- const networkId: number = 42;
- before(async () => {
- ledgerSubprovider = new LedgerSubprovider({
- networkId,
- ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
- });
- });
- describe('direct method calls', () => {
- it('returns a list of accounts', async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
- expect(accounts[0]).to.not.be.an('undefined');
- expect(accounts.length).to.be.equal(10);
- });
- it('signs a personal message', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
- expect(ecSignatureHex.length).to.be.equal(132);
- expect(ecSignatureHex.substr(0, 2)).to.be.equal('0x');
- });
- it('signs a transaction', async () => {
- const tx = {
- nonce: '0x00',
- gas: '0x2710',
- to: '0x0000000000000000000000000000000000000000',
- value: '0x00',
- chainId: 3,
- };
- const txHex = await ledgerSubprovider.signTransactionAsync(tx);
- expect(txHex).to.be.equal(
- '0xf85f8080822710940000000000000000000000000000000000000000808077a088a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98ba0019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
- );
- });
- });
- describe('calls through a provider', () => {
- let defaultProvider: Web3ProviderEngine;
- let ledgerProvider: Web3ProviderEngine;
- before(() => {
- ledgerProvider = new Web3ProviderEngine();
- ledgerProvider.addProvider(ledgerSubprovider);
- const httpProvider = new RpcSubprovider({
- rpcUrl: 'http://localhost:8545',
- });
- ledgerProvider.addProvider(httpProvider);
- ledgerProvider.start();
+ let ledgerSubprovider: LedgerSubprovider;
+ const networkId: number = 42;
+ before(async () => {
+ ledgerSubprovider = new LedgerSubprovider({
+ networkId,
+ ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
+ });
+ });
+ describe('direct method calls', () => {
+ it('returns a list of accounts', async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ expect(accounts[0]).to.not.be.an('undefined');
+ expect(accounts.length).to.be.equal(10);
+ });
+ it('signs a personal message', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
+ expect(ecSignatureHex.length).to.be.equal(132);
+ expect(ecSignatureHex.substr(0, 2)).to.be.equal('0x');
+ });
+ it('signs a transaction', async () => {
+ const tx = {
+ nonce: '0x00',
+ gas: '0x2710',
+ to: '0x0000000000000000000000000000000000000000',
+ value: '0x00',
+ chainId: 3,
+ };
+ const txHex = await ledgerSubprovider.signTransactionAsync(tx);
+ expect(txHex).to.be.equal(
+ '0xf85f8080822710940000000000000000000000000000000000000000808077a088a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98ba0019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
+ );
+ });
+ });
+ describe('calls through a provider', () => {
+ let defaultProvider: Web3ProviderEngine;
+ let ledgerProvider: Web3ProviderEngine;
+ before(() => {
+ ledgerProvider = new Web3ProviderEngine();
+ ledgerProvider.addProvider(ledgerSubprovider);
+ const httpProvider = new RpcSubprovider({
+ rpcUrl: 'http://localhost:8545',
+ });
+ ledgerProvider.addProvider(httpProvider);
+ ledgerProvider.start();
- defaultProvider = new Web3ProviderEngine();
- defaultProvider.addProvider(httpProvider);
- defaultProvider.start();
- });
- it('returns a list of accounts', (done: DoneCallback) => {
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- });
- it('signs a personal message with eth_sign', (done: DoneCallback) => {
- (async () => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const accounts = await ledgerSubprovider.getAccountsAsync();
- const signer = accounts[0];
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: [signer, messageHex],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(132);
- expect(response.result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- it('signs a personal message with personal_sign', (done: DoneCallback) => {
- (async () => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const accounts = await ledgerSubprovider.getAccountsAsync();
- const signer = accounts[0];
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [messageHex, signer],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(132);
- expect(response.result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- it('signs a transaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0x00',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_signTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.raw.length).to.be.equal(206);
- expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- });
- it('signs and sends a transaction', (done: DoneCallback) => {
- (async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
+ defaultProvider = new Web3ProviderEngine();
+ defaultProvider.addProvider(httpProvider);
+ defaultProvider.start();
+ });
+ it('returns a list of accounts', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with eth_sign', (done: DoneCallback) => {
+ (async () => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ const signer = accounts[0];
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: [signer, messageHex],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(132);
+ expect(response.result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ it('signs a personal message with personal_sign', (done: DoneCallback) => {
+ (async () => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ const signer = accounts[0];
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [messageHex, signer],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(132);
+ expect(response.result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ it('signs a transaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0x00',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.raw.length).to.be.equal(206);
+ expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ });
+ it('signs and sends a transaction', (done: DoneCallback) => {
+ (async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
- // Give first account on Ledger sufficient ETH to complete tx send
- let tx = {
- to: accounts[0],
- from: TEST_RPC_ACCOUNT_0,
- value: '0x8ac7230489e80000', // 10 ETH
- };
- let payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- await promisify(defaultProvider.sendAsync, defaultProvider)(payload);
+ // Give first account on Ledger sufficient ETH to complete tx send
+ let tx = {
+ to: accounts[0],
+ from: TEST_RPC_ACCOUNT_0,
+ value: '0x8ac7230489e80000', // 10 ETH
+ };
+ let payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ await promisify(defaultProvider.sendAsync, defaultProvider)(payload);
- // Send transaction from Ledger
- tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- from: accounts[0],
- value: '0xde0b6b3a7640000',
- };
- payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- const result = response.result.result;
- expect(result.length).to.be.equal(66);
- expect(result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- });
+ // Send transaction from Ledger
+ tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ from: accounts[0],
+ value: '0xde0b6b3a7640000',
+ };
+ payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ const result = response.result.result;
+ expect(result.length).to.be.equal(66);
+ expect(result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ });
});
diff --git a/packages/subproviders/test/unit/ledger_subprovider_test.ts b/packages/subproviders/test/unit/ledger_subprovider_test.ts
index 1c70dd3a6..101b6b906 100644
--- a/packages/subproviders/test/unit/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/unit/ledger_subprovider_test.ts
@@ -15,230 +15,230 @@ const expect = chai.expect;
const FAKE_ADDRESS = '0xb088a3bc93f71b4de97b9de773e9647645983688';
describe('LedgerSubprovider', () => {
- const networkId: number = 42;
- let ledgerSubprovider: LedgerSubprovider;
- before(async () => {
- const ledgerEthereumClientFactoryAsync = async () => {
- // tslint:disable:no-object-literal-type-assertion
- const ledgerEthClient = {
- getAddress_async: async () => {
- const publicKey =
- '04f428290f4c5ed6a198f71b8205f488141dbb3f0840c923bbfa798ecbee6370986c03b5575d94d506772fb48a6a44e345e4ebd4f028a6f609c44b655d6d3e71a1';
- const chainCode = 'ac055a5537c0c7e9e02d14a197cad6b857836da2a12043b46912a37d959b5ae8';
- const address = '0xBa388BA5e5EEF2c6cE42d831c2B3A28D3c99bdB1';
- return {
- publicKey,
- address,
- chainCode,
- };
- },
- signPersonalMessage_async: async () => {
- const ecSignature = {
- v: 28,
- r: 'a6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae49148',
- s: '0652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d0',
- };
- return ecSignature;
- },
- signTransaction_async: async (derivationPath: string, txHex: string) => {
- const ecSignature = {
- v: '77',
- r: '88a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98b',
- s: '019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
- };
- return ecSignature;
- },
- comm: {
- close_async: _.noop,
- } as LedgerCommunicationClient,
- };
- // tslint:enable:no-object-literal-type-assertion
- return ledgerEthClient;
- };
- ledgerSubprovider = new LedgerSubprovider({
- networkId,
- ledgerEthereumClientFactoryAsync,
- });
- });
- describe('direct method calls', () => {
- describe('success cases', () => {
- it('returns a list of accounts', async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
- expect(accounts[0]).to.be.equal(FAKE_ADDRESS);
- expect(accounts.length).to.be.equal(10);
- });
- it('signs a personal message', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
- expect(ecSignatureHex).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- });
- });
- describe('failure cases', () => {
- it('cannot open multiple simultaneous connections to the Ledger device', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- return expect(
- Promise.all([
- ledgerSubprovider.getAccountsAsync(),
- ledgerSubprovider.signPersonalMessageAsync(data),
- ]),
- ).to.be.rejectedWith(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
- });
- });
- });
- describe('calls through a provider', () => {
- let provider: Web3ProviderEngine;
- before(() => {
- provider = new Web3ProviderEngine();
- provider.addProvider(ledgerSubprovider);
- const httpProvider = new RpcSubprovider({
- rpcUrl: 'http://localhost:8545',
- });
- provider.addProvider(httpProvider);
- provider.start();
- });
- describe('success cases', () => {
- it('returns a list of accounts', (done: DoneCallback) => {
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- expect(response.result[0]).to.be.equal(FAKE_ADDRESS);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a personal message with eth_sign', (done: DoneCallback) => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: ['0x0000000000000000000000000000000000000000', messageHex],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a personal message with personal_sign', (done: DoneCallback) => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [messageHex, '0x0000000000000000000000000000000000000000'],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a transaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0x00',
- gasPrice: '0x00',
- nonce: '0x00',
- gas: '0x00',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_signTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.raw.length).to.be.equal(192);
- expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- });
- describe('failure cases', () => {
- it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: ['0x0000000000000000000000000000000000000000', nonHexMessage],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [nonHexMessage, '0x0000000000000000000000000000000000000000'],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0xde0b6b3a7640000',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `from` param invalid address when calling eth_sendTransaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- from: '0xIncorrectEthereumAddress',
- value: '0xde0b6b3a7640000',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- });
- });
+ const networkId: number = 42;
+ let ledgerSubprovider: LedgerSubprovider;
+ before(async () => {
+ const ledgerEthereumClientFactoryAsync = async () => {
+ // tslint:disable:no-object-literal-type-assertion
+ const ledgerEthClient = {
+ getAddress_async: async () => {
+ const publicKey =
+ '04f428290f4c5ed6a198f71b8205f488141dbb3f0840c923bbfa798ecbee6370986c03b5575d94d506772fb48a6a44e345e4ebd4f028a6f609c44b655d6d3e71a1';
+ const chainCode = 'ac055a5537c0c7e9e02d14a197cad6b857836da2a12043b46912a37d959b5ae8';
+ const address = '0xBa388BA5e5EEF2c6cE42d831c2B3A28D3c99bdB1';
+ return {
+ publicKey,
+ address,
+ chainCode,
+ };
+ },
+ signPersonalMessage_async: async () => {
+ const ecSignature = {
+ v: 28,
+ r: 'a6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae49148',
+ s: '0652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d0',
+ };
+ return ecSignature;
+ },
+ signTransaction_async: async (derivationPath: string, txHex: string) => {
+ const ecSignature = {
+ v: '77',
+ r: '88a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98b',
+ s: '019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
+ };
+ return ecSignature;
+ },
+ comm: {
+ close_async: _.noop,
+ } as LedgerCommunicationClient,
+ };
+ // tslint:enable:no-object-literal-type-assertion
+ return ledgerEthClient;
+ };
+ ledgerSubprovider = new LedgerSubprovider({
+ networkId,
+ ledgerEthereumClientFactoryAsync,
+ });
+ });
+ describe('direct method calls', () => {
+ describe('success cases', () => {
+ it('returns a list of accounts', async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ expect(accounts[0]).to.be.equal(FAKE_ADDRESS);
+ expect(accounts.length).to.be.equal(10);
+ });
+ it('signs a personal message', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
+ expect(ecSignatureHex).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ });
+ });
+ describe('failure cases', () => {
+ it('cannot open multiple simultaneous connections to the Ledger device', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ return expect(
+ Promise.all([
+ ledgerSubprovider.getAccountsAsync(),
+ ledgerSubprovider.signPersonalMessageAsync(data),
+ ]),
+ ).to.be.rejectedWith(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
+ });
+ });
+ });
+ describe('calls through a provider', () => {
+ let provider: Web3ProviderEngine;
+ before(() => {
+ provider = new Web3ProviderEngine();
+ provider.addProvider(ledgerSubprovider);
+ const httpProvider = new RpcSubprovider({
+ rpcUrl: 'http://localhost:8545',
+ });
+ provider.addProvider(httpProvider);
+ provider.start();
+ });
+ describe('success cases', () => {
+ it('returns a list of accounts', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ expect(response.result[0]).to.be.equal(FAKE_ADDRESS);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with eth_sign', (done: DoneCallback) => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: ['0x0000000000000000000000000000000000000000', messageHex],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with personal_sign', (done: DoneCallback) => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [messageHex, '0x0000000000000000000000000000000000000000'],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a transaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0x00',
+ gasPrice: '0x00',
+ nonce: '0x00',
+ gas: '0x00',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.raw.length).to.be.equal(192);
+ expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ });
+ describe('failure cases', () => {
+ it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
+ const nonHexMessage = 'hello world';
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: ['0x0000000000000000000000000000000000000000', nonHexMessage],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => {
+ const nonHexMessage = 'hello world';
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [nonHexMessage, '0x0000000000000000000000000000000000000000'],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0xde0b6b3a7640000',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `from` param invalid address when calling eth_sendTransaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ from: '0xIncorrectEthereumAddress',
+ value: '0xde0b6b3a7640000',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ });
+ });
});
diff --git a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
index c3170745c..defb741dd 100644
--- a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
+++ b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
@@ -12,45 +12,45 @@ const expect = chai.expect;
chaiSetup.configure();
describe('RedundantRpcSubprovider', () => {
- let provider: Web3ProviderEngine;
- it('succeeds when supplied a healthy endpoint', (done: DoneCallback) => {
- provider = new Web3ProviderEngine();
- const endpoints = ['http://localhost:8545'];
- const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
- provider.addProvider(redundantSubprovider);
- provider.start();
+ let provider: Web3ProviderEngine;
+ it('succeeds when supplied a healthy endpoint', (done: DoneCallback) => {
+ provider = new Web3ProviderEngine();
+ const endpoints = ['http://localhost:8545'];
+ const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
+ provider.addProvider(redundantSubprovider);
+ provider.start();
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('succeeds when supplied at least one healthy endpoint', (done: DoneCallback) => {
- provider = new Web3ProviderEngine();
- const endpoints = ['http://does-not-exist:3000', 'http://localhost:8545'];
- const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
- provider.addProvider(redundantSubprovider);
- provider.start();
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('succeeds when supplied at least one healthy endpoint', (done: DoneCallback) => {
+ provider = new Web3ProviderEngine();
+ const endpoints = ['http://does-not-exist:3000', 'http://localhost:8545'];
+ const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
+ provider.addProvider(redundantSubprovider);
+ provider.start();
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- provider.sendAsync(payload, callback);
- });
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
});
diff --git a/packages/subproviders/test/utils/report_callback_errors.ts b/packages/subproviders/test/utils/report_callback_errors.ts
index 8a8f4d966..e6aadfa84 100644
--- a/packages/subproviders/test/utils/report_callback_errors.ts
+++ b/packages/subproviders/test/utils/report_callback_errors.ts
@@ -1,14 +1,14 @@
import { DoneCallback } from '../../src/types';
export const reportCallbackErrors = (done: DoneCallback) => {
- return (f: (...args: any[]) => void) => {
- const wrapped = async (...args: any[]) => {
- try {
- f(...args);
- } catch (err) {
- done(err);
- }
- };
- return wrapped;
- };
+ return (f: (...args: any[]) => void) => {
+ const wrapped = async (...args: any[]) => {
+ try {
+ f(...args);
+ } catch (err) {
+ done(err);
+ }
+ };
+ return wrapped;
+ };
};
diff --git a/packages/subproviders/tsconfig.json b/packages/subproviders/tsconfig.json
index 9a65a0a97..4d92e88a5 100644
--- a/packages/subproviders/tsconfig.json
+++ b/packages/subproviders/tsconfig.json
@@ -1,15 +1,15 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/subproviders/tslint.json b/packages/subproviders/tslint.json
index ffaefe83a..e63054bfc 100644
--- a/packages/subproviders/tslint.json
+++ b/packages/subproviders/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}