diff options
author | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
---|---|---|
committer | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
commit | 35703539d0f2b4ddb3b11d0de8c9634af59ab71f (patch) | |
tree | ae3731221dbbb3a6fa40060a8d916cfd3f738289 /packages/subproviders/src | |
parent | 92a1fde5b1ecd81b07cdb5bf0c9c1cd3544799db (diff) | |
download | dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.gz dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.zst dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.zip |
Deploy @dexon-foundation/0x.jsstable
Diffstat (limited to 'packages/subproviders/src')
19 files changed, 0 insertions, 1756 deletions
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts deleted file mode 100644 index 3cbf84e37..000000000 --- a/packages/subproviders/src/globals.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -declare module '*.json' { - const json: any; - /* tslint:disable */ - export default json; - /* tslint:enable */ -} -declare module 'web3-provider-engine/util/rpc-cache-utils' { - class ProviderEngineRpcUtils { - public static blockTagForPayload(payload: any): string | null; - } - export = ProviderEngineRpcUtils; -} -declare module 'web3-provider-engine/subproviders/fixture' { - import { JSONRPCRequestPayload, JSONRPCResponsePayload } from 'ethereum-types'; - class FixtureSubprovider { - constructor(staticResponses: any); - public handleRequest( - payload: JSONRPCRequestPayload, - next: () => void, - end: (err: Error | null, data?: JSONRPCResponsePayload) => void, - ): void; - } - export = FixtureSubprovider; -} diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts deleted file mode 100644 index 050027f96..000000000 --- a/packages/subproviders/src/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import Eth from '@ledgerhq/hw-app-eth'; -import TransportU2F from '@ledgerhq/hw-transport-u2f'; -export import Web3ProviderEngine = require('web3-provider-engine'); - -import { LedgerEthereumClient } from './types'; - -/** - * A factory method for creating a LedgerEthereumClient usable in a browser context. - * @return LedgerEthereumClient A browser client for the LedgerSubprovider - */ -export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerEthereumClient> { - const ledgerConnection = await TransportU2F.create(); - const ledgerEthClient = new Eth(ledgerConnection); - return ledgerEthClient; -} - -export { prependSubprovider } from './utils/subprovider_utils'; - -export { EmptyWalletSubprovider } from './subproviders/empty_wallet_subprovider'; -export { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_subprovider'; -export { SignerSubprovider } from './subproviders/signer'; -export { RedundantSubprovider } from './subproviders/redundant_subprovider'; -export { LedgerSubprovider } from './subproviders/ledger'; -export { RPCSubprovider } from './subproviders/rpc_subprovider'; -export { GanacheSubprovider } from './subproviders/ganache'; -export { Subprovider } from './subproviders/subprovider'; -export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; -export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet'; -export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet'; -export { MetamaskSubprovider } from './subproviders/metamask_subprovider'; -export { EthLightwalletSubprovider } from './subproviders/eth_lightwallet_subprovider'; - -export { - Callback, - ErrorCallback, - NextCallback, - LedgerCommunicationClient, - LedgerEthereumClient, - NonceSubproviderErrors, - LedgerSubproviderConfigs, - PartialTxParams, - JSONRPCRequestPayloadWithMethod, - ECSignatureString, - AccountFetchingConfigs, - LedgerEthereumClientFactoryAsync, - OnNextCompleted, - MnemonicWalletSubproviderConfigs, - LedgerGetAddressResult, -} from './types'; - -export { ECSignature, EIP712Object, EIP712ObjectValue, EIP712TypedData, EIP712Types, EIP712Parameter } from '@0x/types'; - -export { - JSONRPCRequestPayload, - Provider, - JSONRPCResponsePayload, - JSONRPCErrorCallback, - JSONRPCResponseError, -} from 'ethereum-types'; diff --git a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts deleted file mode 100644 index e9d104074..000000000 --- a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { assert } from '@0x/assert'; -import { addressUtils } from '@0x/utils'; -import { JSONRPCRequestPayload, JSONRPCResponsePayload } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { Callback, ErrorCallback, PartialTxParams, WalletSubproviderErrors } from '../types'; - -import { Subprovider } from './subprovider'; - -export abstract class BaseWalletSubprovider extends Subprovider { - protected static _validateTxParams(txParams: PartialTxParams): void { - if (!_.isUndefined(txParams.to)) { - assert.isETHAddressHex('to', txParams.to); - } - assert.isHexString('nonce', txParams.nonce); - } - private static _validateSender(sender: string): void { - if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) { - throw new Error(WalletSubproviderErrors.SenderInvalidOrNotSupplied); - } - } - - public abstract async getAccountsAsync(): Promise<string[]>; - public abstract async signTransactionAsync(txParams: PartialTxParams): Promise<string>; - public abstract async signPersonalMessageAsync(data: string, address: string): Promise<string>; - public abstract async signTypedDataAsync(address: string, typedData: any): Promise<string>; - - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { - let accounts; - let txParams; - let address; - let typedData; - 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 { - BaseWalletSubprovider._validateSender(txParams.from); - const filledParams = await this._populateMissingTxParamsAsync(txParams); - const signedTx = await this.signTransactionAsync(filledParams); - const response = await this._emitSendTransactionAsync(signedTx); - end(null, response.result); - } catch (err) { - end(err); - } - return; - - case 'eth_signTransaction': - txParams = payload.params[0]; - try { - const filledParams = await this._populateMissingTxParamsAsync(txParams); - const signedTx = await this.signTransactionAsync(filledParams); - const result = { - raw: signedTx, - tx: 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]; - address = payload.method === 'eth_sign' ? payload.params[0] : payload.params[1]; - try { - const ecSignatureHex = await this.signPersonalMessageAsync(data, address); - end(null, ecSignatureHex); - } catch (err) { - end(err); - } - return; - case 'eth_signTypedData': - [address, typedData] = payload.params; - try { - const signature = await this.signTypedDataAsync(address, typedData); - end(null, signature); - } catch (err) { - end(err); - } - return; - - default: - next(); - return; - } - } - private async _emitSendTransactionAsync(signedTx: string): Promise<JSONRPCResponsePayload> { - const payload = { - method: 'eth_sendRawTransaction', - params: [signedTx], - }; - const result = await this.emitPayloadAsync(payload); - return result; - } - private async _populateMissingTxParamsAsync(partialTxParams: PartialTxParams): Promise<PartialTxParams> { - let txParams = partialTxParams; - if (_.isUndefined(partialTxParams.gasPrice)) { - const gasPriceResult = await this.emitPayloadAsync({ - method: 'eth_gasPrice', - params: [], - }); - const gasPrice = gasPriceResult.result.toString(); - txParams = { ...txParams, gasPrice }; - } - if (_.isUndefined(partialTxParams.nonce)) { - const nonceResult = await this.emitPayloadAsync({ - method: 'eth_getTransactionCount', - params: [partialTxParams.from, 'pending'], - }); - const nonce = nonceResult.result; - txParams = { ...txParams, nonce }; - } - if (_.isUndefined(partialTxParams.gas)) { - const gasResult = await this.emitPayloadAsync({ - method: 'eth_estimateGas', - params: [partialTxParams], - }); - const gas = gasResult.result.toString(); - txParams = { ...txParams, gas }; - } - return txParams; - } -} diff --git a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts b/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts deleted file mode 100644 index 4268c67bd..000000000 --- a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { JSONRPCRequestPayload } from 'ethereum-types'; - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -/** - * 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 conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { - switch (payload.method) { - case 'eth_accounts': - end(null, []); - return; - - default: - next(); - return; - } - } -} diff --git a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts deleted file mode 100644 index 15cd713af..000000000 --- a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { EIP712TypedData } from '@0x/types'; -import * as lightwallet from 'eth-lightwallet'; - -import { PartialTxParams } from '../types'; - -import { BaseWalletSubprovider } from './base_wallet_subprovider'; -import { PrivateKeyWalletSubprovider } from './private_key_wallet'; - -/* - * This class implements the web3-provider-engine subprovider interface and forwards - * requests involving user accounts and signing operations to eth-lightwallet - * - * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js - */ -export class EthLightwalletSubprovider extends BaseWalletSubprovider { - private readonly _keystore: lightwallet.keystore; - private readonly _pwDerivedKey: Uint8Array; - /** - * Instantiate an EthLightwalletSubprovider - * @param keystore The EthLightWallet keystore you wish to use - * @param pwDerivedKey The password derived key to use - * @return EthLightwalletSubprovider instance - */ - constructor(keystore: lightwallet.keystore, pwDerivedKey: Uint8Array) { - super(); - this._keystore = keystore; - this._pwDerivedKey = pwDerivedKey; - } - /** - * Retrieve the accounts associated with the eth-lightwallet instance. - * This method is implicitly called when issuing a `eth_accounts` JSON RPC request - * via your providerEngine instance. - * - * @return An array of accounts - */ - public async getAccountsAsync(): Promise<string[]> { - const accounts = this._keystore.getAddresses(); - return accounts; - } - /** - * Signs a transaction with the account specificed by the `from` field in txParams. - * If you've added this Subprovider 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> { - // Lightwallet loses the chain id information when hex encoding the transaction - // this results in a different signature on certain networks. PrivateKeyWallet - // respects this as it uses the parameters passed in - let privateKey = this._keystore.exportPrivateKey(txParams.from, this._pwDerivedKey); - const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); - privateKey = ''; - const privateKeySignature = await privateKeyWallet.signTransactionAsync(txParams); - return privateKeySignature; - } - /** - * Sign a personal Ethereum signed message. The signing account will be the account - * associated with the provided address. - * If you've added this Subprovider to your app's provider, you can simply send an `eth_sign` - * or `personal_sign` JSON RPC request, and this method will be called auto-magically. - * If you are not using this via a ProviderEngine instance, you can call it directly. - * @param data Hex string message to sign - * @param address Address of the account to sign with - * @return Signature hex string (order: rsv) - */ - public async signPersonalMessageAsync(data: string, address: string): Promise<string> { - let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); - const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); - privateKey = ''; - const result = privateKeyWallet.signPersonalMessageAsync(data, address); - return result; - } - /** - * Sign an EIP712 Typed Data message. The signing address will associated with the provided address. - * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData` - * 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 address Address of the account to sign with - * @param data the typed data object - * @return Signature hex string (order: rsv) - */ - public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { - let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); - const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); - privateKey = ''; - const result = privateKeyWallet.signTypedDataAsync(address, typedData); - return result; - } -} diff --git a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts b/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts deleted file mode 100644 index bc1b34aba..000000000 --- a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { JSONRPCRequestPayload } from 'ethereum-types'; - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -// 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 readonly _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 conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { - switch (payload.method) { - case 'eth_estimateGas': - end(null, this._constantGasAmount); - return; - - default: - next(); - return; - } - } -} diff --git a/packages/subproviders/src/subproviders/ganache.ts b/packages/subproviders/src/subproviders/ganache.ts deleted file mode 100644 index 2b8544f8b..000000000 --- a/packages/subproviders/src/subproviders/ganache.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; -import * as Ganache from 'ganache-core'; - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -/** - * 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 readonly _ganacheProvider: Provider; - /** - * Instantiates a GanacheSubprovider - * @param opts The desired opts with which to instantiate the Ganache provider - */ - constructor(opts: Ganache.GanacheOpts) { - super(); - this._ganacheProvider = Ganache.provider(opts); - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param _next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, _next: Callback, end: ErrorCallback): Promise<void> { - this._ganacheProvider.sendAsync(payload, (err: Error | null, result: any) => { - end(err, result && result.result); - }); - } -} diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts deleted file mode 100644 index b5ca10ce1..000000000 --- a/packages/subproviders/src/subproviders/ledger.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { assert } from '@0x/assert'; -import { addressUtils } from '@0x/utils'; -import EthereumTx = require('ethereumjs-tx'); -import ethUtil = require('ethereumjs-util'); -import HDNode = require('hdkey'); -import * as _ from 'lodash'; -import { Lock } from 'semaphore-async-await'; - -import { - DerivedHDKeyInfo, - LedgerEthereumClient, - LedgerEthereumClientFactoryAsync, - LedgerSubproviderConfigs, - LedgerSubproviderErrors, - PartialTxParams, - WalletSubproviderErrors, -} from '../types'; -import { walletUtils } from '../utils/wallet_utils'; - -import { BaseWalletSubprovider } from './base_wallet_subprovider'; - -const DEFAULT_BASE_DERIVATION_PATH = `44'/60'/0'`; -const ASK_FOR_ON_DEVICE_CONFIRMATION = false; -const SHOULD_GET_CHAIN_CODE = true; -const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10; -const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; - -/** - * Subprovider for interfacing with a user's [Ledger Nano S](https://www.ledgerwallet.com/products/ledger-nano-s). - * 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 BaseWalletSubprovider { - // tslint:disable-next-line:no-unused-variable - private readonly _connectionLock = new Lock(); - private readonly _networkId: number; - private _baseDerivationPath: string; - private readonly _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync; - private _ledgerClientIfExists?: LedgerEthereumClient; - private readonly _shouldAlwaysAskForConfirmation: boolean; - private readonly _addressSearchLimit: number; - /** - * Instantiates a LedgerSubprovider. Defaults to derivationPath set to `44'/60'/0'`. - * TestRPC/Ganache defaults to `m/44'/60'/0'/0`, so set this in the configs if desired. - * @param config Several available configurations - * @return LedgerSubprovider instance - */ - constructor(config: LedgerSubproviderConfigs) { - super(); - this._networkId = config.networkId; - this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync; - this._baseDerivationPath = config.baseDerivationPath || DEFAULT_BASE_DERIVATION_PATH; - this._shouldAlwaysAskForConfirmation = - !_.isUndefined(config.accountFetchingConfigs) && - !_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation) - ? config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation - : ASK_FOR_ON_DEVICE_CONFIRMATION; - this._addressSearchLimit = - !_.isUndefined(config.accountFetchingConfigs) && - !_.isUndefined(config.accountFetchingConfigs.addressSearchLimit) - ? config.accountFetchingConfigs.addressSearchLimit - : DEFAULT_ADDRESS_SEARCH_LIMIT; - } - /** - * Retrieve the set derivation path - * @returns derivation path - */ - public getPath(): string { - return this._baseDerivationPath; - } - /** - * Set a desired derivation path when computing the available user addresses - * @param basDerivationPath The desired derivation path (e.g `44'/60'/0'`) - */ - public setPath(basDerivationPath: string): void { - this._baseDerivationPath = basDerivationPath; - } - /** - * Retrieve a users Ledger accounts. The accounts are derived from the derivationPath, - * master public key and chain code. 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[]> { - const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync(); - const derivedKeyInfos = walletUtils.calculateDerivedHDKeyInfos(initialDerivedKeyInfo, numberOfAccounts); - const accounts = _.map(derivedKeyInfos, k => k.address); - return accounts; - } - /** - * Signs a transaction on the Ledger with the account specificed by the `from` field in txParams. - * 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> { - LedgerSubprovider._validateTxParams(txParams); - if (_.isUndefined(txParams.from) || !addressUtils.isAddress(txParams.from)) { - throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid); - } - const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync(); - const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, txParams.from); - - this._ledgerClientIfExists = await this._createLedgerClientAsync(); - - const tx = new EthereumTx(txParams); - - // Set the EIP155 bits - const vIndex = 6; - tx.raw[vIndex] = Buffer.from([this._networkId]); // v - const rIndex = 7; - tx.raw[rIndex] = Buffer.from([]); // r - const sIndex = 8; - tx.raw[sIndex] = Buffer.from([]); // s - - const txHex = tx.serialize().toString('hex'); - try { - const fullDerivationPath = derivedKeyInfo.derivationPath; - const result = await this._ledgerClientIfExists.signTransaction(fullDerivationPath, 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 eip55Constant = 35; - const signedChainId = Math.floor((tx.v[0] - eip55Constant) / 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; - } - } - /** - * Sign a personal Ethereum signed message. The signing account will be the account - * associated with the provided address. - * 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 Hex string message to sign - * @param address Address of the account to sign with - * @return Signature hex string (order: rsv) - */ - public async signPersonalMessageAsync(data: string, address: string): Promise<string> { - if (_.isUndefined(data)) { - throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); - } - assert.isHexString('data', data); - assert.isETHAddressHex('address', address); - const initialDerivedKeyInfo = await this._initialDerivedKeyInfoAsync(); - const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, address); - - this._ledgerClientIfExists = await this._createLedgerClientAsync(); - try { - const fullDerivationPath = derivedKeyInfo.derivationPath; - const result = await this._ledgerClientIfExists.signPersonalMessage( - fullDerivationPath, - ethUtil.stripHexPrefix(data), - ); - const lowestValidV = 27; - const v = result.v - lowestValidV; - const hexBase = 16; - let vHex = v.toString(hexBase); - 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; - } - } - /** - * eth_signTypedData is currently not supported on Ledger devices. - * @param address Address of the account to sign with - * @param data the typed data object - * @return Signature hex string (order: rsv) - */ - // tslint:disable-next-line:prefer-function-over-method - public async signTypedDataAsync(address: string, typedData: any): Promise<string> { - throw new Error(WalletSubproviderErrors.MethodNotSupported); - } - private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> { - await this._connectionLock.acquire(); - if (!_.isUndefined(this._ledgerClientIfExists)) { - this._connectionLock.release(); - throw new Error(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed); - } - const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync(); - this._connectionLock.release(); - return ledgerEthereumClient; - } - private async _destroyLedgerClientAsync(): Promise<void> { - await this._connectionLock.acquire(); - if (_.isUndefined(this._ledgerClientIfExists)) { - this._connectionLock.release(); - return; - } - await this._ledgerClientIfExists.transport.close(); - this._ledgerClientIfExists = undefined; - this._connectionLock.release(); - } - private async _initialDerivedKeyInfoAsync(): Promise<DerivedHDKeyInfo> { - this._ledgerClientIfExists = await this._createLedgerClientAsync(); - - const parentKeyDerivationPath = `m/${this._baseDerivationPath}`; - let ledgerResponse; - try { - ledgerResponse = await this._ledgerClientIfExists.getAddress( - parentKeyDerivationPath, - 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 address = walletUtils.addressOfHDKey(hdKey); - const initialDerivedKeyInfo = { - hdKey, - address, - derivationPath: parentKeyDerivationPath, - baseDerivationPath: this._baseDerivationPath, - }; - return initialDerivedKeyInfo; - } - private _findDerivedKeyInfoForAddress(initalHDKey: DerivedHDKeyInfo, address: string): DerivedHDKeyInfo { - const matchedDerivedKeyInfo = walletUtils.findDerivedKeyInfoForAddressIfExists( - address, - initalHDKey, - this._addressSearchLimit, - ); - if (_.isUndefined(matchedDerivedKeyInfo)) { - throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`); - } - return matchedDerivedKeyInfo; - } -} diff --git a/packages/subproviders/src/subproviders/metamask_subprovider.ts b/packages/subproviders/src/subproviders/metamask_subprovider.ts deleted file mode 100644 index ba207d4cc..000000000 --- a/packages/subproviders/src/subproviders/metamask_subprovider.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { marshaller, Web3Wrapper } from '@0x/web3-wrapper'; -import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -/** - * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) - * subprovider interface and the provider sendAsync interface. - * It handles inconsistencies with Metamask implementations of various JSON RPC methods. - * It forwards JSON RPC requests involving the domain of a signer (getAccounts, - * sendTransaction, signMessage etc...) to the provider instance supplied at instantiation. All other requests - * are passed onwards for subsequent subproviders to handle. - */ -export class MetamaskSubprovider extends Subprovider { - private readonly _web3Wrapper: Web3Wrapper; - private readonly _provider: Provider; - /** - * Instantiates a new MetamaskSubprovider - * @param provider Web3 provider that should handle all user account related requests - */ - constructor(provider: Provider) { - super(); - this._web3Wrapper = new Web3Wrapper(provider); - this._provider = provider; - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { - let message; - let address; - switch (payload.method) { - case 'web3_clientVersion': - try { - const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); - end(null, nodeVersion); - } catch (err) { - end(err); - } - return; - case 'eth_accounts': - try { - const accounts = await this._web3Wrapper.getAvailableAddressesAsync(); - end(null, accounts); - } catch (err) { - end(err); - } - return; - case 'eth_sendTransaction': - const [txParams] = payload.params; - try { - const txData = marshaller.unmarshalTxData(txParams); - const txHash = await this._web3Wrapper.sendTransactionAsync(txData); - end(null, txHash); - } catch (err) { - end(err); - } - return; - case 'eth_sign': - [address, message] = payload.params; - try { - // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec - // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - const signature = await this._web3Wrapper.signMessageAsync(address, prefixedMsgHex); - signature ? end(null, signature) : end(new Error('Error performing eth_sign'), null); - } catch (err) { - end(err); - } - return; - case 'eth_signTypedData': - case 'eth_signTypedData_v3': - [address, message] = payload.params; - try { - // Metamask supports multiple versions and has namespaced signTypedData to v3 for an indeterminate period of time. - // eth_signTypedData is mapped to an older implementation before the spec was finalized. - // Source: https://github.com/MetaMask/metamask-extension/blob/c49d854b55b3efd34c7fd0414b76f7feaa2eec7c/app/scripts/metamask-controller.js#L1262 - // and expects message to be serialised as JSON - const messageJSON = JSON.stringify(message); - const signature = await this._web3Wrapper.sendRawPayloadAsync<string>({ - method: 'eth_signTypedData_v3', - params: [address, messageJSON], - }); - signature ? end(null, signature) : end(new Error('Error performing eth_signTypedData'), null); - } catch (err) { - end(err); - } - return; - default: - next(); - return; - } - } - /** - * This method conforms to the provider sendAsync interface. - * Allowing the MetamaskSubprovider to be used as a generic provider (outside of Web3ProviderEngine) with the - * addition of wrapping the inconsistent Metamask behaviour - * @param payload JSON RPC payload - * @return The contents nested under the result key of the response body - */ - public sendAsync(payload: JSONRPCRequestPayload, callback: ErrorCallback): void { - void this.handleRequest( - payload, - // handleRequest has decided to not handle this, so fall through to the provider - () => { - const sendAsync = this._provider.sendAsync.bind(this._provider); - sendAsync(payload, callback); - }, - // handleRequest has called end and will handle this - (err, data) => { - err ? callback(err) : callback(null, { ...payload, result: data }); - }, - ); - } -} diff --git a/packages/subproviders/src/subproviders/mnemonic_wallet.ts b/packages/subproviders/src/subproviders/mnemonic_wallet.ts deleted file mode 100644 index 140e3d515..000000000 --- a/packages/subproviders/src/subproviders/mnemonic_wallet.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { assert } from '@0x/assert'; -import { EIP712TypedData } from '@0x/types'; -import { addressUtils } from '@0x/utils'; -import * as bip39 from 'bip39'; -import HDNode = require('hdkey'); -import * as _ from 'lodash'; - -import { DerivedHDKeyInfo, MnemonicWalletSubproviderConfigs, PartialTxParams, WalletSubproviderErrors } from '../types'; -import { walletUtils } from '../utils/wallet_utils'; - -import { BaseWalletSubprovider } from './base_wallet_subprovider'; -import { PrivateKeyWalletSubprovider } from './private_key_wallet'; - -const DEFAULT_BASE_DERIVATION_PATH = `44'/60'/0'/0`; -const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10; -const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; - -/** - * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. - * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and handles - * all requests with accounts derived from the supplied mnemonic. - */ -export class MnemonicWalletSubprovider extends BaseWalletSubprovider { - private readonly _addressSearchLimit: number; - private _baseDerivationPath: string; - private _derivedKeyInfo: DerivedHDKeyInfo; - private readonly _mnemonic: string; - - /** - * Instantiates a MnemonicWalletSubprovider. Defaults to baseDerivationPath set to `44'/60'/0'/0`. - * This is the default in TestRPC/Ganache, it can be overridden if desired. - * @param config Configuration for the mnemonic wallet, must contain the mnemonic - * @return MnemonicWalletSubprovider instance - */ - constructor(config: MnemonicWalletSubproviderConfigs) { - assert.isString('mnemonic', config.mnemonic); - const baseDerivationPath = config.baseDerivationPath || DEFAULT_BASE_DERIVATION_PATH; - assert.isString('baseDerivationPath', baseDerivationPath); - const addressSearchLimit = config.addressSearchLimit || DEFAULT_ADDRESS_SEARCH_LIMIT; - assert.isNumber('addressSearchLimit', addressSearchLimit); - super(); - - this._mnemonic = config.mnemonic; - this._baseDerivationPath = baseDerivationPath; - this._addressSearchLimit = addressSearchLimit; - this._derivedKeyInfo = this._initialDerivedKeyInfo(this._baseDerivationPath); - } - /** - * Retrieve the set derivation path - * @returns derivation path - */ - public getPath(): string { - return this._baseDerivationPath; - } - /** - * Set a desired derivation path when computing the available user addresses - * @param baseDerivationPath The desired derivation path (e.g `44'/60'/0'`) - */ - public setPath(baseDerivationPath: string): void { - this._baseDerivationPath = baseDerivationPath; - this._derivedKeyInfo = this._initialDerivedKeyInfo(this._baseDerivationPath); - } - /** - * Retrieve the accounts associated with the mnemonic. - * This method is implicitly 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[]> { - const derivedKeys = walletUtils.calculateDerivedHDKeyInfos(this._derivedKeyInfo, numberOfAccounts); - const accounts = _.map(derivedKeys, k => k.address); - return accounts; - } - - /** - * Signs a transaction with the account specificed by the `from` field in txParams. - * If you've added this Subprovider 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> { - if (_.isUndefined(txParams.from) || !addressUtils.isAddress(txParams.from)) { - throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid); - } - const privateKeyWallet = this._privateKeyWalletForAddress(txParams.from); - const signedTx = privateKeyWallet.signTransactionAsync(txParams); - return signedTx; - } - /** - * Sign a personal Ethereum signed message. The signing account will be the account - * associated with the provided address. If you've added the MnemonicWalletSubprovider 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 Hex string message to sign - * @param address Address of the account to sign with - * @return Signature hex string (order: rsv) - */ - public async signPersonalMessageAsync(data: string, address: string): Promise<string> { - if (_.isUndefined(data)) { - throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); - } - assert.isHexString('data', data); - assert.isETHAddressHex('address', address); - const privateKeyWallet = this._privateKeyWalletForAddress(address); - const sig = await privateKeyWallet.signPersonalMessageAsync(data, address); - return sig; - } - /** - * Sign an EIP712 Typed Data message. The signing account will be the account - * associated with the provided address. If you've added this MnemonicWalletSubprovider to - * your app's provider, you can simply send an `eth_signTypedData` 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 address Address of the account to sign with - * @param data the typed data object - * @return Signature hex string (order: rsv) - */ - public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { - if (_.isUndefined(typedData)) { - throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); - } - assert.isETHAddressHex('address', address); - const privateKeyWallet = this._privateKeyWalletForAddress(address); - const sig = await privateKeyWallet.signTypedDataAsync(address, typedData); - return sig; - } - private _privateKeyWalletForAddress(address: string): PrivateKeyWalletSubprovider { - const derivedKeyInfo = this._findDerivedKeyInfoForAddress(address); - const privateKeyHex = derivedKeyInfo.hdKey.privateKey.toString('hex'); - const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKeyHex); - return privateKeyWallet; - } - private _findDerivedKeyInfoForAddress(address: string): DerivedHDKeyInfo { - const matchedDerivedKeyInfo = walletUtils.findDerivedKeyInfoForAddressIfExists( - address, - this._derivedKeyInfo, - this._addressSearchLimit, - ); - if (_.isUndefined(matchedDerivedKeyInfo)) { - throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`); - } - return matchedDerivedKeyInfo; - } - private _initialDerivedKeyInfo(baseDerivationPath: string): DerivedHDKeyInfo { - const seed = bip39.mnemonicToSeed(this._mnemonic); - const hdKey = HDNode.fromMasterSeed(seed); - // Walk down to base derivation level (i.e m/44'/60'/0') and create an initial key at that level - // all children will then be walked relative (i.e m/0) - const parentKeyDerivationPath = `m/${baseDerivationPath}`; - const parentHDKeyAtDerivationPath = hdKey.derive(parentKeyDerivationPath); - const address = walletUtils.addressOfHDKey(parentHDKeyAtDerivationPath); - const derivedKeyInfo = { - address, - baseDerivationPath, - derivationPath: parentKeyDerivationPath, - hdKey: parentHDKeyAtDerivationPath, - }; - return derivedKeyInfo; - } -} diff --git a/packages/subproviders/src/subproviders/nonce_tracker.ts b/packages/subproviders/src/subproviders/nonce_tracker.ts deleted file mode 100644 index eea722aee..000000000 --- a/packages/subproviders/src/subproviders/nonce_tracker.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as _ from 'lodash'; - -import { BlockParamLiteral, JSONRPCRequestPayload } from 'ethereum-types'; -import EthereumTx = require('ethereumjs-tx'); -import ethUtil = require('ethereumjs-util'); -import providerEngineUtils = require('web3-provider-engine/util/rpc-cache-utils'); - -import { Callback, ErrorCallback, NextCallback, NonceSubproviderErrors } from '../types'; - -import { Subprovider } from './subprovider'; - -const NONCE_TOO_LOW_ERROR_MESSAGE = 'Transaction nonce is 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 readonly _nonceCache: { [address: string]: string } = {}; - private static _reconstructTransaction(payload: JSONRPCRequestPayload): EthereumTx { - const raw = payload.params[0]; - if (_.isUndefined(raw)) { - throw new Error(NonceSubproviderErrors.EmptyParametersFound); - } - const rawData = ethUtil.toBuffer(raw); - const transaction = new EthereumTx(rawData); - return transaction; - } - private static _determineAddress(payload: JSONRPCRequestPayload): string { - let address: string; - switch (payload.method) { - case 'eth_getTransactionCount': - address = payload.params[0].toLowerCase(); - return address; - case 'eth_sendRawTransaction': - const transaction = NonceTrackerSubprovider._reconstructTransaction(payload); - const addressRaw = transaction - .getSenderAddress() - .toString('hex') - .toLowerCase(); - address = `0x${addressRaw}`; - return address; - default: - throw new Error(NonceSubproviderErrors.CannotDetermineAddressFromPayload); - } - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: NextCallback, end: ErrorCallback): Promise<void> { - switch (payload.method) { - case 'eth_getTransactionCount': - const requestDefaultBlock = providerEngineUtils.blockTagForPayload(payload); - if (requestDefaultBlock === BlockParamLiteral.Pending) { - const address = NonceTrackerSubprovider._determineAddress(payload); - const cachedResult = this._nonceCache[address]; - if (!_.isUndefined(cachedResult)) { - return end(null, cachedResult); - } else { - return next((requestError: Error | null, requestResult: any, cb: Callback) => { - if (_.isNull(requestError)) { - this._nonceCache[address] = requestResult as string; - } - cb(); - }); - } - } else { - return next(); - } - case 'eth_sendRawTransaction': - return next((sendTransactionError: Error | null, _txResult: any, cb: Callback) => { - if (_.isNull(sendTransactionError)) { - this._handleSuccessfulTransaction(payload); - } else { - this._handleSendTransactionError(payload, sendTransactionError); - } - cb(); - }); - default: - return next(); - } - } - private _handleSuccessfulTransaction(payload: JSONRPCRequestPayload): void { - const address = NonceTrackerSubprovider._determineAddress(payload); - const transaction = NonceTrackerSubprovider._reconstructTransaction(payload); - // Increment the nonce from the previous successfully submitted transaction - let nonce = ethUtil.bufferToInt(transaction.nonce); - nonce++; - const hexBase = 16; - let nextHexNonce = nonce.toString(hexBase); - if (nextHexNonce.length % 2) { - nextHexNonce = `0${nextHexNonce}`; - } - const nextPrefixedHexNonce = `0x${nextHexNonce}`; - this._nonceCache[address] = nextPrefixedHexNonce; - } - private _handleSendTransactionError(payload: JSONRPCRequestPayload, err: Error): void { - const address = NonceTrackerSubprovider._determineAddress(payload); - if (this._nonceCache[address] && _.includes(err.message, NONCE_TOO_LOW_ERROR_MESSAGE)) { - delete this._nonceCache[address]; - } - } -} diff --git a/packages/subproviders/src/subproviders/private_key_wallet.ts b/packages/subproviders/src/subproviders/private_key_wallet.ts deleted file mode 100644 index dca7e6810..000000000 --- a/packages/subproviders/src/subproviders/private_key_wallet.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { assert } from '@0x/assert'; -import { EIP712TypedData } from '@0x/types'; -import { signTypedDataUtils } from '@0x/utils'; -import EthereumTx = require('ethereumjs-tx'); -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { PartialTxParams, WalletSubproviderErrors } from '../types'; - -import { BaseWalletSubprovider } from './base_wallet_subprovider'; - -/** - * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. - * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and handles - * all requests with the supplied Ethereum private key. - */ -export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider { - private readonly _address: string; - private readonly _privateKeyBuffer: Buffer; - /** - * Instantiates a PrivateKeyWalletSubprovider. - * @param privateKey The corresponding private key to an Ethereum address - * @return PrivateKeyWalletSubprovider instance - */ - constructor(privateKey: string) { - assert.isString('privateKey', privateKey); - super(); - this._privateKeyBuffer = Buffer.from(privateKey, 'hex'); - this._address = `0x${ethUtil.privateToAddress(this._privateKeyBuffer).toString('hex')}`; - } - /** - * Retrieve the account associated with the supplied private key. - * This method is implicitly called when issuing a `eth_accounts` JSON RPC request - * via your providerEngine instance. - * @return An array of accounts - */ - public async getAccountsAsync(): Promise<string[]> { - return [this._address]; - } - /** - * Sign a transaction with the private key. If you've added this Subprovider 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> { - PrivateKeyWalletSubprovider._validateTxParams(txParams); - if (!_.isUndefined(txParams.from) && txParams.from !== this._address) { - throw new Error( - `Requested to sign transaction with address: ${txParams.from}, instantiated with address: ${ - this._address - }`, - ); - } - const tx = new EthereumTx(txParams); - tx.sign(this._privateKeyBuffer); - const rawTx = `0x${tx.serialize().toString('hex')}`; - return rawTx; - } - /** - * Sign a personal Ethereum signed message. The signing address will be calculated from the private key. - * The address must be provided it must match the address calculated from the private key. - * If you've added this Subprovider to your app's provider, you can simply send an `eth_sign` - * or `personal_sign` JSON RPC request, and this method will be called auto-magically. - * If you are not using this via a ProviderEngine instance, you can call it directly. - * @param data Hex string message to sign - * @param address Address of the account to sign with - * @return Signature hex string (order: rsv) - */ - public async signPersonalMessageAsync(data: string, address: string): Promise<string> { - if (_.isUndefined(data)) { - throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); - } - assert.isHexString('data', data); - assert.isETHAddressHex('address', address); - if (address !== this._address) { - throw new Error( - `Requested to sign message with address: ${address}, instantiated with address: ${this._address}`, - ); - } - const dataBuff = ethUtil.toBuffer(data); - const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); - const sig = ethUtil.ecsign(msgHashBuff, this._privateKeyBuffer); - const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); - return rpcSig; - } - /** - * Sign an EIP712 Typed Data message. The signing address will be calculated from the private key. - * The address must be provided it must match the address calculated from the private key. - * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData` - * 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 address Address of the account to sign with - * @param data the typed data object - * @return Signature hex string (order: rsv) - */ - public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { - if (_.isUndefined(typedData)) { - throw new Error(WalletSubproviderErrors.DataMissingForSignTypedData); - } - assert.isETHAddressHex('address', address); - if (address !== this._address) { - throw new Error( - `Requested to sign message with address: ${address}, instantiated with address: ${this._address}`, - ); - } - const dataBuff = signTypedDataUtils.generateTypedDataHash(typedData); - const sig = ethUtil.ecsign(dataBuff, this._privateKeyBuffer); - const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); - return rpcSig; - } -} diff --git a/packages/subproviders/src/subproviders/redundant_subprovider.ts b/packages/subproviders/src/subproviders/redundant_subprovider.ts deleted file mode 100644 index 58312f203..000000000 --- a/packages/subproviders/src/subproviders/redundant_subprovider.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { promisify } from '@0x/utils'; -import { JSONRPCRequestPayload } from 'ethereum-types'; -import * as _ from 'lodash'; - -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 RedundantSubprovider extends Subprovider { - private readonly _subproviders: Subprovider[]; - private static async _firstSuccessAsync( - subproviders: Subprovider[], - payload: JSONRPCRequestPayload, - next: Callback, - ): Promise<any> { - let lastErr: Error | undefined; - for (const subprovider of subproviders) { - try { - const data = await promisify(subprovider.handleRequest.bind(subprovider))(payload, next); - return data; - } catch (err) { - lastErr = err; - continue; - } - } - if (!_.isUndefined(lastErr)) { - throw lastErr; - } - } - /** - * Instantiates a new RedundantSubprovider - * @param subproviders Subproviders to attempt the request with - */ - constructor(subproviders: Subprovider[]) { - super(); - this._subproviders = subproviders; - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:async-suffix - public async handleRequest( - payload: JSONRPCRequestPayload, - next: Callback, - end: (err: Error | null, data?: any) => void, - ): Promise<void> { - const subprovidersCopy = this._subproviders.slice(); - try { - const data = await RedundantSubprovider._firstSuccessAsync(subprovidersCopy, payload, next); - end(null, data); - } catch (err) { - end(err); - } - } -} diff --git a/packages/subproviders/src/subproviders/rpc_subprovider.ts b/packages/subproviders/src/subproviders/rpc_subprovider.ts deleted file mode 100644 index 437518e12..000000000 --- a/packages/subproviders/src/subproviders/rpc_subprovider.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { assert } from '@0x/assert'; -import { StatusCodes } from '@0x/types'; -import { fetchAsync } from '@0x/utils'; -import { JSONRPCRequestPayload } from 'ethereum-types'; -import JsonRpcError = require('json-rpc-error'); - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -/** - * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. - * It forwards on JSON RPC requests to the supplied `rpcUrl` endpoint - */ -export class RPCSubprovider extends Subprovider { - private readonly _rpcUrl: string; - private readonly _requestTimeoutMs: number; - /** - * @param rpcUrl URL to the backing Ethereum node to which JSON RPC requests should be sent - * @param requestTimeoutMs Amount of miliseconds to wait before timing out the JSON RPC request - */ - constructor(rpcUrl: string, requestTimeoutMs: number = 20000) { - super(); - assert.isString('rpcUrl', rpcUrl); - assert.isNumber('requestTimeoutMs', requestTimeoutMs); - this._rpcUrl = rpcUrl; - this._requestTimeoutMs = requestTimeoutMs; - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param _next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, _next: Callback, end: ErrorCallback): Promise<void> { - const finalPayload = Subprovider._createFinalPayload(payload); - const headers = new Headers({ - Accept: 'application/json', - 'Content-Type': 'application/json', - }); - - let response; - try { - response = await fetchAsync( - this._rpcUrl, - { - method: 'POST', - headers, - body: JSON.stringify(finalPayload), - }, - this._requestTimeoutMs, - ); - } catch (err) { - end(new JsonRpcError.InternalError(err)); - return; - } - - const text = await response.text(); - if (!response.ok) { - const statusCode = response.status; - switch (statusCode) { - case StatusCodes.MethodNotAllowed: - end(new JsonRpcError.MethodNotFound()); - return; - case StatusCodes.GatewayTimeout: - const errMsg = - 'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.'; - const err = new Error(errMsg); - end(new JsonRpcError.InternalError(err)); - return; - default: - end(new JsonRpcError.InternalError(text)); - return; - } - } - - let data; - try { - data = JSON.parse(text); - } catch (err) { - end(new JsonRpcError.InternalError(err)); - return; - } - - if (data.error) { - end(data.error); - return; - } - end(null, data.result); - } -} diff --git a/packages/subproviders/src/subproviders/signer.ts b/packages/subproviders/src/subproviders/signer.ts deleted file mode 100644 index 9bd5cbdf1..000000000 --- a/packages/subproviders/src/subproviders/signer.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { marshaller, Web3Wrapper } from '@0x/web3-wrapper'; -import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; - -import { Callback, ErrorCallback } from '../types'; - -import { Subprovider } from './subprovider'; - -/** - * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) - * subprovider interface. It forwards JSON RPC requests involving the domain of a signer (getAccounts, - * sendTransaction, signMessage etc...) to the provider instance supplied at instantiation. All other requests - * are passed onwards for subsequent subproviders to handle. - */ -export class SignerSubprovider extends Subprovider { - private readonly _web3Wrapper: Web3Wrapper; - /** - * Instantiates a new SignerSubprovider. - * @param provider Web3 provider that should handle all user account related requests - */ - constructor(provider: Provider) { - super(); - this._web3Wrapper = new Web3Wrapper(provider); - } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:prefer-function-over-method async-suffix - public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { - let message; - let address; - switch (payload.method) { - case 'web3_clientVersion': - try { - const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); - end(null, nodeVersion); - } catch (err) { - end(err); - } - return; - case 'eth_accounts': - try { - const accounts = await this._web3Wrapper.getAvailableAddressesAsync(); - end(null, accounts); - } catch (err) { - end(err); - } - return; - case 'eth_sendTransaction': - const [txParams] = payload.params; - try { - const txData = marshaller.unmarshalTxData(txParams); - const txHash = await this._web3Wrapper.sendTransactionAsync(txData); - end(null, txHash); - } catch (err) { - end(err); - } - return; - case 'eth_sign': - [address, message] = payload.params; - try { - const signature = await this._web3Wrapper.signMessageAsync(address, message); - end(null, signature); - } catch (err) { - end(err); - } - return; - case 'eth_signTypedData': - [address, message] = payload.params; - try { - const signature = await this._web3Wrapper.signTypedDataAsync(address, message); - end(null, signature); - } catch (err) { - end(err); - } - return; - default: - next(); - return; - } - } -} diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts deleted file mode 100644 index cd6780e0c..000000000 --- a/packages/subproviders/src/subproviders/subprovider.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { promisify } from '@0x/utils'; -import { JSONRPCRequestPayload, JSONRPCResponsePayload, Provider } from 'ethereum-types'; - -import { Callback, ErrorCallback, JSONRPCRequestPayloadWithMethod } from '../types'; -/** - * 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. - */ -export abstract class Subprovider { - // tslint:disable-next-line:underscore-private-and-protected - private engine!: Provider; - protected static _createFinalPayload( - payload: Partial<JSONRPCRequestPayloadWithMethod>, - ): Partial<JSONRPCRequestPayloadWithMethod> { - const finalPayload = { - // defaults - id: Subprovider._getRandomId(), - jsonrpc: '2.0', - params: [], - ...payload, - }; - return finalPayload; - } - // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js - private static _getRandomId(): number { - const extraDigits = 3; - const baseTen = 10; - // 13 time digits - const datePart = new Date().getTime() * Math.pow(baseTen, extraDigits); - // 3 random digits - const extraPart = Math.floor(Math.random() * Math.pow(baseTen, extraDigits)); - // 16 digits - return datePart + extraPart; - } - /** - * @param payload JSON RPC request payload - * @param next A callback to pass the request to the next subprovider in the stack - * @param end A callback called once the subprovider is done handling the request - */ - // tslint:disable-next-line:async-suffix - public abstract async handleRequest( - payload: JSONRPCRequestPayload, - next: Callback, - end: ErrorCallback, - ): Promise<void>; - - /** - * 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<JSONRPCResponsePayload> { - const finalPayload = Subprovider._createFinalPayload(payload); - // Promisify does the binding internally and `this` is supplied as a second argument - // tslint:disable-next-line:no-unbound-method - const response = await promisify<JSONRPCResponsePayload>(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, do not call - * directly. - * @param engine The ProviderEngine this subprovider is added to - */ - public setEngine(engine: Provider): void { - this.engine = engine; - } -} diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts deleted file mode 100644 index ed3aea176..000000000 --- a/packages/subproviders/src/types.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { ECSignature } from '@0x/types'; -import { JSONRPCRequestPayload } from 'ethereum-types'; -import HDNode = require('hdkey'); - -export interface LedgerCommunicationClient { - close: () => Promise<void>; -} - -/** - * Elliptic Curve signature - * The LedgerEthereumClient sends Ethereum-specific requests to the Ledger Nano S - * It uses an internal LedgerCommunicationClient to relay these requests. Currently - * 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: ( - derivationPath: string, - askForDeviceConfirmation: boolean, - shouldGetChainCode: true, - ) => Promise<LedgerGetAddressResult>; - signTransaction: (derivationPath: string, rawTxHex: string) => Promise<ECSignatureString>; - signPersonalMessage: (derivationPath: string, messageHex: string) => Promise<ECSignature>; - transport: LedgerCommunicationClient; -} - -export interface ECSignatureString { - v: string; - r: string; - s: string; -} - -export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClient>; - -/** - * networkId: The ethereum networkId to set as the chainId from EIP155 - * ledgerConnectionType: Environment in which you wish to connect to Ledger (nodejs or browser) - * derivationPath: Initial derivation path to use e.g 44'/60'/0' - * accountFetchingConfigs: configs related to fetching accounts from a Ledger - */ -export interface LedgerSubproviderConfigs { - networkId: number; - ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync; - baseDerivationPath?: string; - accountFetchingConfigs?: AccountFetchingConfigs; -} - -/** - * addressSearchLimit: The maximum number of addresses to search through, defaults to 1000 - * numAddressesToReturn: Number of addresses to return from 'eth_accounts' call - * shouldAskForOnDeviceConfirmation: Whether you wish to prompt the user on their Ledger - * before fetching their addresses - */ -export interface AccountFetchingConfigs { - addressSearchLimit?: number; - numAddressesToReturn?: number; - shouldAskForOnDeviceConfirmation?: boolean; -} - -/** - * mnemonic: The string mnemonic seed - * addressSearchLimit: The maximum number of addresses to search through, defaults to 1000 - * baseDerivationPath: The base derivation path (e.g 44'/60'/0'/0) - */ -export interface MnemonicWalletSubproviderConfigs { - mnemonic: string; - addressSearchLimit?: number; - baseDerivationPath?: string; -} - -export interface SignatureData { - hash: string; - r: string; - s: string; - v: number; -} - -export interface LedgerGetAddressResult { - address: string; - publicKey: string; - chainCode: string; -} - -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 -} - -export type DoneCallback = (err?: Error) => void; - -export interface LedgerCommunication { - close_async: () => Promise<void>; -} - -export interface ResponseWithTxParams { - raw: string; - tx: PartialTxParams; -} - -export enum WalletSubproviderErrors { - AddressNotFound = 'ADDRESS_NOT_FOUND', - DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE', - DataMissingForSignTypedData = 'DATA_MISSING_FOR_SIGN_TYPED_DATA', - SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', - FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID', - MethodNotSupported = 'METHOD_NOT_SUPPORTED', -} -export enum LedgerSubproviderErrors { - TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE', - MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED', -} - -export enum NonceSubproviderErrors { - EmptyParametersFound = 'EMPTY_PARAMETERS_FOUND', - CannotDetermineAddressFromPayload = 'CANNOT_DETERMINE_ADDRESS_FROM_PAYLOAD', -} -export interface DerivedHDKeyInfo { - address: string; - baseDerivationPath: string; - derivationPath: string; - hdKey: HDNode; -} - -export type ErrorCallback = (err: Error | null, data?: any) => void; -export type Callback = () => void; -export type OnNextCompleted = (err: Error | null, result: any, cb: Callback) => void; -export type NextCallback = (callback?: OnNextCompleted) => void; - -export interface JSONRPCRequestPayloadWithMethod extends JSONRPCRequestPayload { - method: string; -} diff --git a/packages/subproviders/src/utils/subprovider_utils.ts b/packages/subproviders/src/utils/subprovider_utils.ts deleted file mode 100644 index beda408ab..000000000 --- a/packages/subproviders/src/utils/subprovider_utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Web3ProviderEngine = require('web3-provider-engine'); - -import { Subprovider } from '../subproviders/subprovider'; - -/** - * Prepends a subprovider to a provider - * @param provider Given provider - * @param subprovider Subprovider to prepend - */ -export function prependSubprovider(provider: Web3ProviderEngine, subprovider: Subprovider): void { - subprovider.setEngine(provider); - // HACK: We use implementation details of provider engine here - // https://github.com/MetaMask/provider-engine/blob/master/index.js#L68 - (provider as any)._providers = [subprovider, ...(provider as any)._providers]; -} diff --git a/packages/subproviders/src/utils/wallet_utils.ts b/packages/subproviders/src/utils/wallet_utils.ts deleted file mode 100644 index 7027ca8a0..000000000 --- a/packages/subproviders/src/utils/wallet_utils.ts +++ /dev/null @@ -1,79 +0,0 @@ -import ethUtil = require('ethereumjs-util'); -import HDNode = require('hdkey'); - -import { DerivedHDKeyInfo } from '../types'; - -const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; - -class DerivedHDKeyInfoIterator implements IterableIterator<DerivedHDKeyInfo> { - private readonly _parentDerivedKeyInfo: DerivedHDKeyInfo; - private readonly _searchLimit: number; - private _index: number; - - constructor(initialDerivedKey: DerivedHDKeyInfo, searchLimit: number = DEFAULT_ADDRESS_SEARCH_LIMIT) { - this._searchLimit = searchLimit; - this._parentDerivedKeyInfo = initialDerivedKey; - this._index = 0; - } - - public next(): IteratorResult<DerivedHDKeyInfo> { - const baseDerivationPath = this._parentDerivedKeyInfo.baseDerivationPath; - const derivationIndex = this._index; - const fullDerivationPath = `m/${baseDerivationPath}/${derivationIndex}`; - const path = `m/${derivationIndex}`; - const hdKey = this._parentDerivedKeyInfo.hdKey.derive(path); - const address = walletUtils.addressOfHDKey(hdKey); - const derivedKey: DerivedHDKeyInfo = { - address, - hdKey, - baseDerivationPath, - derivationPath: fullDerivationPath, - }; - const isDone = this._index === this._searchLimit; - this._index++; - return { - done: isDone, - value: derivedKey, - }; - } - - public [Symbol.iterator](): IterableIterator<DerivedHDKeyInfo> { - return this; - } -} - -export const walletUtils = { - calculateDerivedHDKeyInfos(parentDerivedKeyInfo: DerivedHDKeyInfo, numberOfKeys: number): DerivedHDKeyInfo[] { - const derivedKeys: DerivedHDKeyInfo[] = []; - const derivedKeyIterator = new DerivedHDKeyInfoIterator(parentDerivedKeyInfo, numberOfKeys); - for (const key of derivedKeyIterator) { - derivedKeys.push(key); - } - return derivedKeys; - }, - findDerivedKeyInfoForAddressIfExists( - address: string, - parentDerivedKeyInfo: DerivedHDKeyInfo, - searchLimit: number, - ): DerivedHDKeyInfo | undefined { - const lowercaseAddress = address.toLowerCase(); - let matchedKey: DerivedHDKeyInfo | undefined; - const derivedKeyIterator = new DerivedHDKeyInfoIterator(parentDerivedKeyInfo, searchLimit); - for (const key of derivedKeyIterator) { - if (key.address === lowercaseAddress) { - matchedKey = key; - break; - } - } - return matchedKey; - }, - addressOfHDKey(hdKey: HDNode): string { - const shouldSanitizePublicKey = true; - const derivedPublicKey = hdKey.publicKey; - const ethereumAddressUnprefixed = ethUtil - .publicToAddress(derivedPublicKey, shouldSanitizePublicKey) - .toString('hex'); - const address = ethUtil.addHexPrefix(ethereumAddressUnprefixed).toLowerCase(); - return address; - }, -}; |