aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/subproviders/src/index.ts2
-rw-r--r--packages/subproviders/src/subproviders/empty_wallet_subprovider.ts11
-rw-r--r--packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts23
-rw-r--r--packages/subproviders/src/subproviders/ganache.ts15
-rw-r--r--packages/subproviders/src/subproviders/injected_web3.ts22
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts189
-rw-r--r--packages/subproviders/src/subproviders/nonce_tracker.ts16
-rw-r--r--packages/subproviders/src/subproviders/redundant_rpc.ts13
-rw-r--r--packages/subproviders/src/subproviders/subprovider.ts23
9 files changed, 199 insertions, 115 deletions
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index 0dc053286..d88792fd0 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -24,7 +24,7 @@ export {
/**
* A factory method for creating a LedgerEthereumClient usable in a browser context.
- * @return LedgerEthereumClient A browser client
+ * @return LedgerEthereumClient A browser client for the LedgerSubprovider
*/
export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerEthereumClient> {
const ledgerConnection = await TransportU2F.create();
diff --git a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts b/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts
index d5d03a4ac..38972b6cf 100644
--- a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts
+++ b/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts
@@ -4,15 +4,14 @@ import { Callback, ErrorCallback } from '../types';
import { Subprovider } from './subprovider';
-/*
- * This class implements the web3-provider-engine subprovider interface and returns
- * that the provider has no addresses when queried.
- * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
+ * It intercepts the `eth_accounts` JSON RPC requests and never returns any addresses when queried.
*/
export class EmptyWalletSubprovider extends Subprovider {
// This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
switch (payload.method) {
case 'eth_accounts':
end(null, []);
diff --git a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts b/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts
index faec2d9e5..8241e58f2 100644
--- a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts
+++ b/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts
@@ -4,23 +4,28 @@ import { Callback, ErrorCallback } from '../types';
import { Subprovider } from './subprovider';
-/*
- * This class implements the web3-provider-engine subprovider interface and returns
- * the constant gas estimate when queried.
- * HACK: We need this so that our tests don't use testrpc gas estimation which sometimes kills the node.
- * Source: https://github.com/trufflesuite/ganache-cli/issues/417
- * Source: https://github.com/trufflesuite/ganache-cli/issues/437
- * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
+// HACK: We need this so that our tests don't use testrpc gas estimation which sometimes kills the node.
+// Source: https://github.com/trufflesuite/ganache-cli/issues/417
+// Source: https://github.com/trufflesuite/ganache-cli/issues/437
+// Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
+
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
+ * It intercepts the `eth_estimateGas` JSON RPC call and always returns a constant gas amount when queried.
*/
export class FakeGasEstimateSubprovider extends Subprovider {
private _constantGasAmount: number;
+ /**
+ * Instantiates an instance of the FakeGasEstimateSubprovider
+ * @param constantGasAmount The constant gas amount you want returned
+ */
constructor(constantGasAmount: number) {
super();
this._constantGasAmount = constantGasAmount;
}
// This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
switch (payload.method) {
case 'eth_estimateGas':
end(null, this._constantGasAmount);
diff --git a/packages/subproviders/src/subproviders/ganache.ts b/packages/subproviders/src/subproviders/ganache.ts
index 6ff939674..22ac5e1e2 100644
--- a/packages/subproviders/src/subproviders/ganache.ts
+++ b/packages/subproviders/src/subproviders/ganache.ts
@@ -5,20 +5,23 @@ import { Callback, ErrorCallback } from '../types';
import { Subprovider } from './subprovider';
-/*
- * This class implements the web3-provider-engine subprovider interface and returns
- * the provider connected to a in-process ganache.
- * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
+ * It intercepts all JSON RPC requests and relays them to an in-process ganache instance.
*/
export class GanacheSubprovider extends Subprovider {
private _ganacheProvider: Web3.Provider;
+ /**
+ * Instantiates a GanacheSubprovider
+ * @param opts The desired opts with which to instantiate the Ganache provider
+ */
constructor(opts: any) {
super();
this._ganacheProvider = Ganache.provider(opts);
}
// This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
this._ganacheProvider.sendAsync(payload, (err: Error | null, result: any) => {
end(err, result && result.result);
});
diff --git a/packages/subproviders/src/subproviders/injected_web3.ts b/packages/subproviders/src/subproviders/injected_web3.ts
index ec7304cb7..3e3744d5c 100644
--- a/packages/subproviders/src/subproviders/injected_web3.ts
+++ b/packages/subproviders/src/subproviders/injected_web3.ts
@@ -5,19 +5,25 @@ import { Callback, ErrorCallback } from '../types';
import { Subprovider } from './subprovider';
-/*
- * This class implements the web3-provider-engine subprovider interface and forwards
- * requests involving user accounts (getAccounts, sendTransaction, etc...) to the injected
- * provider instance in their browser.
- * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine)
+ * subprovider interface. It forwards JSON RPC requests involving user accounts (getAccounts,
+ * sendTransaction, etc...) to the provider instance supplied at instantiation. All other requests
+ * are passed onwards for subsequent subproviders to handle.
*/
export class InjectedWeb3Subprovider extends Subprovider {
private _injectedWeb3: Web3;
- constructor(subprovider: Web3.Provider) {
+ /**
+ * Instantiates a new InjectedWeb3Subprovider
+ * @param provider Web3 provider that should handle all user account related requests
+ */
+ constructor(provider: Web3.Provider) {
super();
- this._injectedWeb3 = new Web3(subprovider);
+ this._injectedWeb3 = new Web3(provider);
}
- public handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
+ // This method needs to be here to satisfy the interface but linter wants it to be static.
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private handleRequest(payload: Web3.JSONRPCRequestPayload, next: Callback, end: ErrorCallback) {
switch (payload.method) {
case 'web3_clientVersion':
this._injectedWeb3.version.getNode(end);
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 95217c84d..75c73c4de 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -24,6 +24,11 @@ const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10;
const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
const SHOULD_GET_CHAIN_CODE = true;
+/**
+ * Subprovider for interfacing with a user's [Ledger Nano S](https://www.ledgerwallet.com/products/ledger-nano-s).
+ * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and
+ * re-routes them to a Ledger device plugged into the users computer.
+ */
export class LedgerSubprovider extends Subprovider {
private _nonceLock = new Lock();
private _connectionLock = new Lock();
@@ -38,6 +43,11 @@ export class LedgerSubprovider extends Subprovider {
throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
}
}
+ /**
+ * Instantiates a LedgerSubprovider
+ * @param config Several available configurations
+ * @return LedgerSubprovider instance
+ */
constructor(config: LedgerSubproviderConfigs) {
super();
this._networkId = config.networkId;
@@ -50,84 +60,38 @@ export class LedgerSubprovider extends Subprovider {
: ASK_FOR_ON_DEVICE_CONFIRMATION;
this._derivationPathIndex = 0;
}
+ /**
+ * Retrieve the set derivation path
+ * @returns derivation path
+ */
public getPath(): string {
return this._derivationPath;
}
+ /**
+ * Set a desired derivation path when computing the available user addresses
+ * @param derivationPath The desired derivation path (e.g `44'/60'/0'`)
+ */
public setPath(derivationPath: string) {
this._derivationPath = derivationPath;
}
+ /**
+ * Set the final derivation path index. If a user wishes to sign a message with the
+ * 6th address in a derivation path, before calling `signPersonalMessageAsync`, you must
+ * call this method with pathIndex `6`.
+ * @param pathIndex Desired derivation path index
+ */
public setPathIndex(pathIndex: number) {
this._derivationPathIndex = pathIndex;
}
- // Required to implement this public interface which doesn't conform to our linting rule.
- // tslint:disable-next-line:async-suffix
- public async handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: Callback,
- 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_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_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;
- }
- }
+ /**
+ * Retrieve a users Ledger accounts. The accounts are derived from the derivationPath,
+ * master public key and chainCode. Because of this, you can request as many accounts
+ * as you wish and it only requires a single request to the Ledger device. This method
+ * is automatically called when issuing a `eth_accounts` JSON RPC request via your providerEngine
+ * instance.
+ * @param numberOfAccounts Number of accounts to retrieve (default: 10)
+ * @return An array of accounts
+ */
public async getAccountsAsync(numberOfAccounts: number = DEFAULT_NUM_ADDRESSES_TO_FETCH): Promise<string[]> {
this._ledgerClientIfExists = await this._createLedgerClientAsync();
@@ -159,6 +123,14 @@ export class LedgerSubprovider extends Subprovider {
}
return accounts;
}
+ /**
+ * Sign a transaction with the Ledger. If you've added the LedgerSubprovider to your
+ * app's provider, you can simply send an `eth_sendTransaction` JSON RPC request, and
+ * this method will be called auto-magically. If you are not using this via a ProviderEngine
+ * instance, you can call it directly.
+ * @param txParams Parameters of the transaction to sign
+ * @return Signed transaction hex string
+ */
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
this._ledgerClientIfExists = await this._createLedgerClientAsync();
@@ -194,6 +166,16 @@ export class LedgerSubprovider extends Subprovider {
throw err;
}
}
+ /**
+ * Sign a personal Ethereum signed message. The signing address will be to one
+ * retrieved given a derivationPath and pathIndex set on the subprovider.
+ * The Ledger adds the Ethereum signed message prefix on-device. If you've added
+ * the LedgerSubprovider to your app's provider, you can simply send an `eth_sign`
+ * or `personal_sign` JSON RPC request, and this method will be called auto-magically.
+ * If you are not using this via a ProviderEngine instance, you can call it directly.
+ * @param data Message to sign
+ * @return Signature hex string (order: rsv)
+ */
public async signPersonalMessageAsync(data: string): Promise<string> {
this._ledgerClientIfExists = await this._createLedgerClientAsync();
try {
@@ -215,6 +197,75 @@ export class LedgerSubprovider extends Subprovider {
throw err;
}
}
+ // Required to implement this public interface which doesn't conform to our linting rule.
+ // tslint:disable-next-line:async-suffix underscore-private-and-protected
+ private async handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: Callback,
+ 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_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_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;
+ }
+ }
private _getDerivationPath() {
const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`;
return derivationPath;
diff --git a/packages/subproviders/src/subproviders/nonce_tracker.ts b/packages/subproviders/src/subproviders/nonce_tracker.ts
index 82eab4a9a..b51ba4ac8 100644
--- a/packages/subproviders/src/subproviders/nonce_tracker.ts
+++ b/packages/subproviders/src/subproviders/nonce_tracker.ts
@@ -10,13 +10,13 @@ import { Callback, ErrorCallback, NextCallback, NonceSubproviderErrors } from '.
import { Subprovider } from './subprovider';
-// We do not export this since this is not our error, and we do not throw this error
const NONCE_TOO_LOW_ERROR_MESSAGE = 'Transaction nonce is too low';
-/*
- This class is heavily inspiried by the Web3ProviderEngine NonceSubprovider
- We have added the additional feature of clearing any nonce balues when an error message
- describes a nonce value being too low.
-*/
+
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
+ * It is heavily inspired by the [NonceSubprovider](https://github.com/MetaMask/provider-engine/blob/master/subproviders/nonce-tracker.js).
+ * We added the additional feature of clearing the cached nonce value when a `nonce value too low` error occurs.
+ */
export class NonceTrackerSubprovider extends Subprovider {
private _nonceCache: { [address: string]: string } = {};
private static _reconstructTransaction(payload: Web3.JSONRPCRequestPayload): EthereumTx {
@@ -47,8 +47,8 @@ export class NonceTrackerSubprovider extends Subprovider {
}
}
// Required to implement this public interface which doesn't conform to our linting rule.
- // tslint:disable-next-line:async-suffix
- public async handleRequest(
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private async handleRequest(
payload: Web3.JSONRPCRequestPayload,
next: NextCallback,
end: ErrorCallback,
diff --git a/packages/subproviders/src/subproviders/redundant_rpc.ts b/packages/subproviders/src/subproviders/redundant_rpc.ts
index dacd1c2c5..2b84d223b 100644
--- a/packages/subproviders/src/subproviders/redundant_rpc.ts
+++ b/packages/subproviders/src/subproviders/redundant_rpc.ts
@@ -7,6 +7,11 @@ import { Callback } from '../types';
import { Subprovider } from './subprovider';
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
+ * It attempts to handle each JSON RPC request by sequentially attempting to receive a valid response from one of a
+ * set of JSON RPC endpoints.
+ */
export class RedundantRPCSubprovider extends Subprovider {
private _rpcs: RpcSubprovider[];
private static async _firstSuccessAsync(
@@ -28,6 +33,10 @@ export class RedundantRPCSubprovider extends Subprovider {
throw lastErr;
}
}
+ /**
+ * Instantiates a new RedundantRPCSubprovider
+ * @param endpoints JSON RPC endpoints to attempt. Attempts are made in the order of the endpoints.
+ */
constructor(endpoints: string[]) {
super();
this._rpcs = _.map(endpoints, endpoint => {
@@ -37,8 +46,8 @@ export class RedundantRPCSubprovider extends Subprovider {
});
}
// Required to implement this public interface which doesn't conform to our linting rule.
- // tslint:disable-next-line:async-suffix
- public async handleRequest(
+ // tslint:disable-next-line:prefer-function-over-method underscore-private-and-protected
+ private async handleRequest(
payload: Web3.JSONRPCRequestPayload,
next: Callback,
end: (err: Error | null, data?: any) => void,
diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts
index 4a224fc4e..0b30c6397 100644
--- a/packages/subproviders/src/subproviders/subprovider.ts
+++ b/packages/subproviders/src/subproviders/subprovider.ts
@@ -2,10 +2,9 @@ import promisify = require('es6-promisify');
import * as Web3 from 'web3';
import { JSONRPCRequestPayloadWithMethod } from '../types';
-/*
- * A version of the base class Subprovider found in providerEngine
+/**
+ * A altered version of the base class Subprovider found in [web3-provider-engine](https://github.com/MetaMask/provider-engine).
* This one has an async/await `emitPayloadAsync` and also defined types.
- * Altered version of: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class Subprovider {
private _engine: any;
@@ -31,9 +30,13 @@ export class Subprovider {
};
return finalPayload;
}
- public setEngine(engine: any): void {
- this._engine = engine;
- }
+ /**
+ * Emits a JSON RPC payload that will then be handled by the ProviderEngine instance
+ * this subprovider is a part of. The payload will cascade down the subprovider middleware
+ * stack until finding the responsible entity for handling the request.
+ * @param payload JSON RPC payload
+ * @returns JSON RPC response payload
+ */
public async emitPayloadAsync(
payload: Partial<JSONRPCRequestPayloadWithMethod>,
): Promise<Web3.JSONRPCResponsePayload> {
@@ -41,4 +44,12 @@ export class Subprovider {
const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload);
return response;
}
+ /**
+ * Set's the subprovider's engine to the ProviderEngine it is added to.
+ * This is only called within the ProviderEngine source code
+ */
+ // tslint:disable-next-line:underscore-private-and-protected
+ private setEngine(engine: any): void {
+ this._engine = engine;
+ }
}