aboutsummaryrefslogtreecommitdiffstats
path: root/packages/subproviders/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/subproviders/src')
-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
7 files changed, 512 insertions, 512 deletions
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts
index ed4fbc145..53457fa24 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 ecba186df..720c4362f 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 dc1b8b24c..bd29acb22 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 00649873b..7267a793e 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 92d7ae8ae..a3cb463a8 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 b6bb16918..6435c9f65 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 ec1eedd34..3db8be943 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',
}