aboutsummaryrefslogtreecommitdiffstats
path: root/packages/subproviders
diff options
context:
space:
mode:
Diffstat (limited to 'packages/subproviders')
-rw-r--r--packages/subproviders/CHANGELOG.md4
-rw-r--r--packages/subproviders/README.md28
-rw-r--r--packages/subproviders/package.json6
-rw-r--r--packages/subproviders/src/globals.d.ts54
-rw-r--r--packages/subproviders/src/index.ts21
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts8
-rw-r--r--packages/subproviders/src/types.ts10
-rw-r--r--packages/subproviders/test/integration/ledger_subprovider_test.ts23
-rw-r--r--packages/subproviders/test/unit/ledger_subprovider_test.ts10
9 files changed, 104 insertions, 60 deletions
diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md
index 7e1e006e3..8e7321d4a 100644
--- a/packages/subproviders/CHANGELOG.md
+++ b/packages/subproviders/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## v0.7.0 - _TBD_
+
+ * Updated legerco packages. Removed node-hid package as a dependency and make it an optional dependency. It is still used in integration tests but is causing problems for users on Linux distros. (#437)
+
## v0.6.0 - _March 4, 2018_
* Move web3 types from being a devDep to a dep since one cannot use this package without it (#429)
diff --git a/packages/subproviders/README.md b/packages/subproviders/README.md
index 67a6a92d2..4614342b2 100644
--- a/packages/subproviders/README.md
+++ b/packages/subproviders/README.md
@@ -42,6 +42,28 @@ const accounts = await ledgerSubprovider.getAccountsAsync();
A subprovider that enables your dApp to send signing requests to a user's Ledger Nano S hardware wallet. These can be requests to sign transactions or messages.
+Ledger Nano (and this library) by default uses a derivation path of `44'/60'/0'`. This is different to TestRPC which by default uses `m/44'/60'/0'/0`. This is a configuration option in the Ledger Subprovider package.
+
+##### Ledger Nano S + Node-hid (usb)
+
+By default, node-hid transport support is an optional dependency. This is due to the requirement of native usb developer packages on the host system. If these aren't installed the entire `npm install` fails. We also no longer export node-hid transport client factories. To re-create this see our integration tests or follow the example below:
+
+```typescript
+import Eth from '@ledgerhq/hw-app-eth';
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
+async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> {
+ const ledgerConnection = await TransportNodeHid.create();
+ const ledgerEthClient = new Eth(ledgerConnection);
+ return ledgerEthClient;
+}
+
+// Create a LedgerSubprovider with the node-hid transport
+ledgerSubprovider = new LedgerSubprovider({
+ networkId,
+ ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
+});
+```
+
#### Redundant RPC subprovider
A subprovider which attempts to send an RPC call to a list of RPC endpoints sequentially, until one of them returns a successful response.
@@ -104,10 +126,12 @@ yarn run test:unit
In order to run the integration tests, make sure you have a Ledger Nano S available.
+* Setup your Ledger with the development mnemonic seed: `concert load couple harbor equip island argue ramp clarify fence smart topic`
* Plug it into your computer
* Unlock the device
* Open the on-device Ethereum app
-* Make sure "browser support" is disabled
+* Make sure "browser support" and "contract data" are disabled
+* Start [TestRPC](https://github.com/trufflesuite/ganache-cli) locally at port `8545`
Then run:
@@ -115,6 +139,8 @@ Then run:
yarn test:integration
```
+**Note:** We assume a derivation path of `m/44'/60'/0'/0` which is already configured in the tests. With this setup and derivation path, your first account should be `0x5409ed021d9299bf6814279a6a1411a7e866a631`, exactly like TestRPC.
+
#### All tests
```bash
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index ec0642bce..a3e865d24 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -21,12 +21,13 @@
"@0xproject/assert": "^0.1.0",
"@0xproject/types": "^0.3.0",
"@0xproject/utils": "^0.4.0",
+ "@ledgerhq/hw-app-eth": "^4.3.0",
+ "@ledgerhq/hw-transport-u2f": "^4.3.0",
"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",
@@ -53,5 +54,8 @@
"types-ethereumjs-util": "0xProject/types-ethereumjs-util",
"typescript": "2.7.1",
"webpack": "^3.1.0"
+ },
+ "optionalDependencies": {
+ "@ledgerhq/hw-transport-node-hid": "^4.3.0"
}
}
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts
index 6f344dcd3..d59ee9e67 100644
--- a/packages/subproviders/src/globals.d.ts
+++ b/packages/subproviders/src/globals.d.ts
@@ -32,32 +32,38 @@ interface ECSignature {
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(
+
+interface LedgerTransport {
+ close(): Promise<void>;
+}
+
+declare module '@ledgerhq/hw-app-eth' {
+ class Eth {
+ public transport: LedgerTransport;
+ constructor(transport: LedgerTransport);
+ public getAddress(
path: string,
- display?: boolean,
- chaincode?: boolean,
+ boolDisplay?: boolean,
+ boolChaincode?: 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>;
+ public signTransaction(path: string, rawTxHex: string): Promise<ECSignatureString>;
+ public getAppConfiguration(): Promise<{ arbitraryDataEnabled: number; version: string }>;
+ public signPersonalMessage(path: string, messageHex: string): Promise<ECSignature>;
+ }
+ export default Eth;
+}
+
+declare module '@ledgerhq/hw-transport-u2f' {
+ export default class TransportU2F implements LedgerTransport {
+ public static create(): Promise<LedgerTransport>;
+ public close(): Promise<void>;
+ }
+}
+
+declare module '@ledgerhq/hw-transport-node-hid' {
+ export default class TransportNodeHid implements LedgerTransport {
+ public static create(): Promise<LedgerTransport>;
+ public close(): Promise<void>;
}
}
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index 4da405ec0..e22b6f5f3 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -1,8 +1,5 @@
-import {
- comm_node as LedgerNodeCommunication,
- comm_u2f as LedgerBrowserCommunication,
- eth as LedgerEthereumClientFn,
-} from 'ledgerco';
+import Eth from '@ledgerhq/hw-app-eth';
+import TransportU2F from '@ledgerhq/hw-transport-u2f';
import { LedgerEthereumClient } from './types';
@@ -19,17 +16,7 @@ export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient, NonceS
* @return LedgerEthereumClient A browser client
*/
export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerBrowserCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
- return ledgerEthClient;
-}
-
-/**
- * A factory for creating a LedgerEthereumClient usable in a Node.js context.
- * @return LedgerEthereumClient A Node.js client
- */
-export async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerNodeCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
+ const ledgerConnection = await TransportU2F.create();
+ const ledgerEthClient = new Eth(ledgerConnection);
return ledgerEthClient;
}
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 85cdf0efc..0a84caae3 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -134,7 +134,7 @@ export class LedgerSubprovider extends Subprovider {
let ledgerResponse;
try {
- ledgerResponse = await this._ledgerClientIfExists.getAddress_async(
+ ledgerResponse = await this._ledgerClientIfExists.getAddress(
this._derivationPath,
this._shouldAlwaysAskForConfirmation,
SHOULD_GET_CHAIN_CODE,
@@ -173,7 +173,7 @@ export class LedgerSubprovider extends Subprovider {
const txHex = tx.serialize().toString('hex');
try {
const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signTransaction_async(derivationPath, txHex);
+ const result = await this._ledgerClientIfExists.signTransaction(derivationPath, txHex);
// Store signature in transaction
tx.r = Buffer.from(result.r, 'hex');
tx.s = Buffer.from(result.s, 'hex');
@@ -199,7 +199,7 @@ export class LedgerSubprovider extends Subprovider {
this._ledgerClientIfExists = await this._createLedgerClientAsync();
try {
const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signPersonalMessage_async(
+ const result = await this._ledgerClientIfExists.signPersonalMessage(
derivationPath,
ethUtil.stripHexPrefix(data),
);
@@ -236,7 +236,7 @@ export class LedgerSubprovider extends Subprovider {
this._connectionLock.signal();
return;
}
- await this._ledgerClientIfExists.comm.close_async();
+ await this._ledgerClientIfExists.transport.close();
this._ledgerClientIfExists = undefined;
this._connectionLock.signal();
}
diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts
index 65b7f6c8f..f49ac6107 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: () => Promise<void>;
}
/*
@@ -12,14 +12,14 @@ export interface LedgerCommunicationClient {
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: (
+ getAddress: (
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;
+ signTransaction: (derivationPath: string, rawTxHex: string) => Promise<ECSignatureString>;
+ signPersonalMessage: (derivationPath: string, messageHex: string) => Promise<ECSignature>;
+ transport: LedgerCommunicationClient;
}
export interface ECSignatureString {
diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts
index b052a76d2..a94cfbe3a 100644
--- a/packages/subproviders/test/integration/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts
@@ -1,3 +1,7 @@
+import Eth from '@ledgerhq/hw-app-eth';
+// HACK: This depdency is optional and tslint skips optional depdencies
+// tslint:disable-next-line:no-implicit-dependencies
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
import * as chai from 'chai';
import promisify = require('es6-promisify');
import * as ethUtils from 'ethereumjs-util';
@@ -6,14 +10,21 @@ import Web3 = require('web3');
import Web3ProviderEngine = require('web3-provider-engine');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
-import { ledgerEthereumNodeJsClientFactoryAsync, LedgerSubprovider } from '../../src';
-import { DoneCallback } from '../../src/types';
+import { LedgerSubprovider } from '../../src';
+import { DoneCallback, LedgerEthereumClient } from '../../src/types';
import { chaiSetup } from '../chai_setup';
import { reportCallbackErrors } from '../utils/report_callback_errors';
chaiSetup.configure();
const expect = chai.expect;
+async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> {
+ const ledgerConnection = await TransportNodeHid.create();
+ const ledgerEthClient = new Eth(ledgerConnection);
+ return ledgerEthClient;
+}
+
+const TESTRPC_DERIVATION_PATH = `m/44'/60'/0'/0`;
const TEST_RPC_ACCOUNT_0 = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
describe('LedgerSubprovider', () => {
@@ -23,6 +34,7 @@ describe('LedgerSubprovider', () => {
ledgerSubprovider = new LedgerSubprovider({
networkId,
ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
+ derivationPath: TESTRPC_DERIVATION_PATH,
});
});
describe('direct method calls', () => {
@@ -31,6 +43,10 @@ describe('LedgerSubprovider', () => {
expect(accounts[0]).to.not.be.an('undefined');
expect(accounts.length).to.be.equal(10);
});
+ it('returns the expected first account from a ledger set up with the test mnemonic', async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ expect(accounts[0]).to.be.equal(TEST_RPC_ACCOUNT_0);
+ });
it('returns requested number of accounts', async () => {
const numberOfAccounts = 20;
const accounts = await ledgerSubprovider.getAccountsAsync(numberOfAccounts);
@@ -50,10 +66,11 @@ describe('LedgerSubprovider', () => {
to: '0x0000000000000000000000000000000000000000',
value: '0x00',
chainId: 3,
+ from: TEST_RPC_ACCOUNT_0,
};
const txHex = await ledgerSubprovider.signTransactionAsync(tx);
expect(txHex).to.be.equal(
- '0xf85f8080822710940000000000000000000000000000000000000000808077a088a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98ba0019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
+ '0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178',
);
});
});
diff --git a/packages/subproviders/test/unit/ledger_subprovider_test.ts b/packages/subproviders/test/unit/ledger_subprovider_test.ts
index 0d301bce9..4c0803a29 100644
--- a/packages/subproviders/test/unit/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/unit/ledger_subprovider_test.ts
@@ -21,7 +21,7 @@ describe('LedgerSubprovider', () => {
const ledgerEthereumClientFactoryAsync = async () => {
// tslint:disable:no-object-literal-type-assertion
const ledgerEthClient = {
- getAddress_async: async () => {
+ getAddress: async () => {
const publicKey =
'04f428290f4c5ed6a198f71b8205f488141dbb3f0840c923bbfa798ecbee6370986c03b5575d94d506772fb48a6a44e345e4ebd4f028a6f609c44b655d6d3e71a1';
const chainCode = 'ac055a5537c0c7e9e02d14a197cad6b857836da2a12043b46912a37d959b5ae8';
@@ -32,7 +32,7 @@ describe('LedgerSubprovider', () => {
chainCode,
};
},
- signPersonalMessage_async: async () => {
+ signPersonalMessage: async () => {
const ecSignature = {
v: 28,
r: 'a6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae49148',
@@ -40,7 +40,7 @@ describe('LedgerSubprovider', () => {
};
return ecSignature;
},
- signTransaction_async: async (derivationPath: string, txHex: string) => {
+ signTransaction: async (derivationPath: string, txHex: string) => {
const ecSignature = {
v: '77',
r: '88a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98b',
@@ -48,8 +48,8 @@ describe('LedgerSubprovider', () => {
};
return ecSignature;
},
- comm: {
- close_async: _.noop,
+ transport: {
+ close: _.noop,
} as LedgerCommunicationClient,
};
// tslint:enable:no-object-literal-type-assertion