From 13299158d1e22d1af1cd36434fc403a74743ecb1 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 4 Mar 2018 19:05:26 -0800 Subject: Add sol-cover implementation --- packages/subproviders/CHANGELOG.md | 3 +++ packages/subproviders/package.json | 2 ++ packages/subproviders/src/globals.d.ts | 15 ++++++++++++ packages/subproviders/src/index.ts | 3 +++ .../src/subproviders/empty_wallet_subprovider.ts | 17 ++++++------- .../subproviders/fake_gas_estimate_subprovider.ts | 18 +++++++------- packages/subproviders/src/subproviders/ganache.ts | 28 ++++++++++++++++++++++ .../subproviders/src/subproviders/injected_web3.ts | 14 ++++------- packages/subproviders/src/subproviders/ledger.ts | 4 ++-- .../subproviders/src/subproviders/nonce_tracker.ts | 24 +++++++++++-------- .../subproviders/src/subproviders/redundant_rpc.ts | 7 +++--- .../subproviders/src/subproviders/subprovider.ts | 12 ++++++---- packages/subproviders/src/types.ts | 9 +++---- .../test/unit/nonce_tracker_subprovider_test.ts | 24 ++++++++++--------- 14 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 packages/subproviders/src/subproviders/ganache.ts (limited to 'packages/subproviders') diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 9ab7e6594..8c859f5a5 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -3,6 +3,9 @@ ## v0.7.0 - _March 8, 2018_ * Updated legerco packages. Removed node-hid package as a dependency and make it an optional dependency. It is still used in integration tests but is causing problems for users on Linux distros. (#437) + * Export `GanacheSubprovider` and `Subprovider` (#426) + * Make all subproviders to derive from `Subprovider` (#426) + * Add types for `NextCallback`, `OnNextCompleted` (#426) ## v0.6.0 - _March 4, 2018_ diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index c52484f82..259265a53 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -23,10 +23,12 @@ "@0xproject/utils": "^0.4.1", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "^4.3.0", + "@0xproject/deployer": "^0.1.0", "bn.js": "^4.11.8", "es6-promisify": "^5.0.0", "ethereumjs-tx": "^1.3.3", "ethereumjs-util": "^5.1.1", + "ganache-core": "^2.0.2", "hdkey": "^0.7.1", "lodash": "^4.17.4", "semaphore-async-await": "^1.5.1", diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts index d59ee9e67..fc0cdf038 100644 --- a/packages/subproviders/src/globals.d.ts +++ b/packages/subproviders/src/globals.d.ts @@ -134,3 +134,18 @@ declare module 'hdkey' { } export = HDNode; } + +// hdkey declarations +declare module 'ganache-core' { + import * as Web3 from 'web3'; + export interface GanacheOpts { + verbose: boolean; + logger: { + log(msg: string): void; + }; + port: number; + networkId: number; + mnemonic: string; + } + export function provider(opts: GanacheOpts): Web3.Provider; +} diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index e22b6f5f3..cafb50fe5 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -2,12 +2,15 @@ import Eth from '@ledgerhq/hw-app-eth'; import TransportU2F from '@ledgerhq/hw-transport-u2f'; import { LedgerEthereumClient } from './types'; +export { Callback, NextCallback } from './types'; export { EmptyWalletSubprovider } from './subproviders/empty_wallet_subprovider'; export { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_subprovider'; export { InjectedWeb3Subprovider } from './subproviders/injected_web3'; export { RedundantRPCSubprovider } from './subproviders/redundant_rpc'; export { LedgerSubprovider } from './subproviders/ledger'; +export { GanacheSubprovider } from './subproviders/ganache'; +export { Subprovider } from './subproviders/subprovider'; export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient, NonceSubproviderErrors } from './types'; diff --git a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts b/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts index 8c1fdfdb2..f5983dd9b 100644 --- a/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts +++ b/packages/subproviders/src/subproviders/empty_wallet_subprovider.ts @@ -1,14 +1,20 @@ -import { JSONRPCPayload } from '@0xproject/types'; +import * as Web3 from 'web3'; + +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 */ -export class EmptyWalletSubprovider { +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: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) { + public handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: () => void, + end: (err: Error | null, result: any) => void, + ) { switch (payload.method) { case 'eth_accounts': end(null, []); @@ -19,9 +25,4 @@ export class EmptyWalletSubprovider { return; } } - // Required to implement this method despite not needing it for this subprovider - // tslint:disable-next-line:prefer-function-over-method - public setEngine(engine: any) { - // noop - } } diff --git a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts b/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts index b455a0ed7..2421dcd02 100644 --- a/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts +++ b/packages/subproviders/src/subproviders/fake_gas_estimate_subprovider.ts @@ -1,4 +1,6 @@ -import { JSONRPCPayload } from '@0xproject/types'; +import * as Web3 from 'web3'; + +import { Subprovider } from './subprovider'; /* * This class implements the web3-provider-engine subprovider interface and returns @@ -8,14 +10,19 @@ import { JSONRPCPayload } from '@0xproject/types'; * Source: https://github.com/trufflesuite/ganache-cli/issues/437 * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ -export class FakeGasEstimateSubprovider { +export class FakeGasEstimateSubprovider extends Subprovider { private _constantGasAmount: number; 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: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) { + public handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: () => void, + end: (err: Error | null, result: any) => void, + ) { switch (payload.method) { case 'eth_estimateGas': end(null, this._constantGasAmount); @@ -26,9 +33,4 @@ export class FakeGasEstimateSubprovider { return; } } - // Required to implement this method despite not needing it for this subprovider - // tslint:disable-next-line:prefer-function-over-method - public setEngine(engine: any) { - // noop - } } diff --git a/packages/subproviders/src/subproviders/ganache.ts b/packages/subproviders/src/subproviders/ganache.ts new file mode 100644 index 000000000..a979aecf4 --- /dev/null +++ b/packages/subproviders/src/subproviders/ganache.ts @@ -0,0 +1,28 @@ +import * as Ganache from 'ganache-core'; +import * as Web3 from 'web3'; + +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 + */ +export class GanacheSubprovider extends Subprovider { + private _ganacheProvider: Web3.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: () => void, + end: (err: Error | null, result: any) => void, + ) { + 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 0d70180c4..6d4e2b27b 100644 --- a/packages/subproviders/src/subproviders/injected_web3.ts +++ b/packages/subproviders/src/subproviders/injected_web3.ts @@ -1,5 +1,7 @@ import * as _ from 'lodash'; -import Web3 = require('web3'); +import * as Web3 from 'web3'; + +import { Subprovider } from './subprovider'; /* * This class implements the web3-provider-engine subprovider interface and forwards @@ -7,9 +9,10 @@ import Web3 = require('web3'); * provider instance in their browser. * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ -export class InjectedWeb3Subprovider { +export class InjectedWeb3Subprovider extends Subprovider { private _injectedWeb3: Web3; constructor(subprovider: Web3.Provider) { + super(); this._injectedWeb3 = new Web3(subprovider); } public handleRequest( @@ -40,11 +43,4 @@ export class InjectedWeb3Subprovider { return; } } - // Required to implement this method despite not needing it for this subprovider - // The engine argument type should be Web3ProviderEngine, but we've decided to keep it as type any - // to remove the provider engine depdency given this method is a noop - // tslint:disable-next-line:prefer-function-over-method - public setEngine(engine: any) { - // noop - } } diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 0a84caae3..03f7ef493 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -5,7 +5,7 @@ import ethUtil = require('ethereumjs-util'); import HDNode = require('hdkey'); import * as _ from 'lodash'; import Semaphore from 'semaphore-async-await'; -import Web3 = require('web3'); +import * as Web3 from 'web3'; import { LedgerEthereumClient, @@ -147,7 +147,7 @@ export class LedgerSubprovider extends Subprovider { hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex'); hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex'); - const accounts = []; + const accounts: string[] = []; for (let i = 0; i < numberOfAccounts; i++) { const derivedHDNode = hdKey.derive(`m/${i + this._derivationPathIndex}`); const derivedPublicKey = derivedHDNode.publicKey; diff --git a/packages/subproviders/src/subproviders/nonce_tracker.ts b/packages/subproviders/src/subproviders/nonce_tracker.ts index d967d40f2..82eab4a9a 100644 --- a/packages/subproviders/src/subproviders/nonce_tracker.ts +++ b/packages/subproviders/src/subproviders/nonce_tracker.ts @@ -1,12 +1,12 @@ import * as _ from 'lodash'; +import { BlockParamLiteral } from '@0xproject/types'; import EthereumTx = require('ethereumjs-tx'); import ethUtil = require('ethereumjs-util'); +import * as Web3 from 'web3'; import providerEngineUtils = require('web3-provider-engine/util/rpc-cache-utils'); -import { BlockParamLiteral } from '@0xproject/types'; - -import { ErrorCallback, JSONRPCPayload, NonceSubproviderErrors, OptionalNextCallback } from '../types'; +import { Callback, ErrorCallback, NextCallback, NonceSubproviderErrors } from '../types'; import { Subprovider } from './subprovider'; @@ -19,7 +19,7 @@ const NONCE_TOO_LOW_ERROR_MESSAGE = 'Transaction nonce is too low'; */ export class NonceTrackerSubprovider extends Subprovider { private _nonceCache: { [address: string]: string } = {}; - private static _reconstructTransaction(payload: JSONRPCPayload): EthereumTx { + private static _reconstructTransaction(payload: Web3.JSONRPCRequestPayload): EthereumTx { const raw = payload.params[0]; if (_.isUndefined(raw)) { throw new Error(NonceSubproviderErrors.EmptyParametersFound); @@ -28,7 +28,7 @@ export class NonceTrackerSubprovider extends Subprovider { const transaction = new EthereumTx(rawData); return transaction; } - private static _determineAddress(payload: JSONRPCPayload): string { + private static _determineAddress(payload: Web3.JSONRPCRequestPayload): string { let address: string; switch (payload.method) { case 'eth_getTransactionCount': @@ -48,7 +48,11 @@ 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(payload: JSONRPCPayload, next: OptionalNextCallback, end: ErrorCallback): Promise { + public async handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: NextCallback, + end: ErrorCallback, + ): Promise { switch (payload.method) { case 'eth_getTransactionCount': const requestDefaultBlock = providerEngineUtils.blockTagForPayload(payload); @@ -58,7 +62,7 @@ export class NonceTrackerSubprovider extends Subprovider { if (!_.isUndefined(cachedResult)) { return end(null, cachedResult); } else { - return next((requestError: Error | null, requestResult: any, cb: any) => { + return next((requestError: Error | null, requestResult: any, cb: Callback) => { if (_.isNull(requestError)) { this._nonceCache[address] = requestResult as string; } @@ -69,7 +73,7 @@ export class NonceTrackerSubprovider extends Subprovider { return next(); } case 'eth_sendRawTransaction': - return next((sendTransactionError: Error | null, txResult: any, cb: any) => { + return next((sendTransactionError: Error | null, txResult: any, cb: Callback) => { if (_.isNull(sendTransactionError)) { this._handleSuccessfulTransaction(payload); } else { @@ -81,7 +85,7 @@ export class NonceTrackerSubprovider extends Subprovider { return next(); } } - private _handleSuccessfulTransaction(payload: JSONRPCPayload): void { + private _handleSuccessfulTransaction(payload: Web3.JSONRPCRequestPayload): void { const address = NonceTrackerSubprovider._determineAddress(payload); const transaction = NonceTrackerSubprovider._reconstructTransaction(payload); // Increment the nonce from the previous successfully submitted transaction @@ -94,7 +98,7 @@ export class NonceTrackerSubprovider extends Subprovider { const nextPrefixedHexNonce = `0x${nextHexNonce}`; this._nonceCache[address] = nextPrefixedHexNonce; } - private _handleSendTransactionError(payload: JSONRPCPayload, err: Error): void { + private _handleSendTransactionError(payload: Web3.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/redundant_rpc.ts b/packages/subproviders/src/subproviders/redundant_rpc.ts index 5a94f93d7..0df2f91f4 100644 --- a/packages/subproviders/src/subproviders/redundant_rpc.ts +++ b/packages/subproviders/src/subproviders/redundant_rpc.ts @@ -1,16 +1,15 @@ import { promisify } from '@0xproject/utils'; import * as _ from 'lodash'; +import * as Web3 from 'web3'; import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); -import { JSONRPCPayload } from '../types'; - import { Subprovider } from './subprovider'; export class RedundantRPCSubprovider extends Subprovider { private _rpcs: RpcSubprovider[]; private static async _firstSuccessAsync( rpcs: RpcSubprovider[], - payload: JSONRPCPayload, + payload: Web3.JSONRPCRequestPayload, next: () => void, ): Promise { let lastErr: Error | undefined; @@ -38,7 +37,7 @@ 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( - payload: JSONRPCPayload, + payload: Web3.JSONRPCRequestPayload, next: () => void, end: (err: Error | null, data?: any) => void, ): Promise { diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts index 6435c9f65..d4e0de67a 100644 --- a/packages/subproviders/src/subproviders/subprovider.ts +++ b/packages/subproviders/src/subproviders/subprovider.ts @@ -1,7 +1,5 @@ import promisify = require('es6-promisify'); -import Web3 = require('web3'); - -import { JSONRPCPayload } from '../types'; +import * as Web3 from 'web3'; /* * A version of the base class Subprovider found in providerEngine * This one has an async/await `emitPayloadAsync` and also defined types. @@ -19,7 +17,9 @@ export class Subprovider { // 16 digits return datePart + extraPart; } - private static _createFinalPayload(payload: JSONRPCPayload): Web3.JSONRPCRequestPayload { + private static _createFinalPayload( + payload: Partial & { method: string }, + ): Web3.JSONRPCRequestPayload { const finalPayload = { // defaults id: Subprovider._getRandomId(), @@ -32,7 +32,9 @@ export class Subprovider { public setEngine(engine: any): void { this._engine = engine; } - public async emitPayloadAsync(payload: JSONRPCPayload): Promise { + public async emitPayloadAsync( + payload: Partial & { method: string }, + ): Promise { const finalPayload = Subprovider._createFinalPayload(payload); const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload); return response; diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index f49ac6107..543da5947 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -91,11 +91,6 @@ export interface PartialTxParams { export type DoneCallback = (err?: Error) => void; -export interface JSONRPCPayload { - params: any[]; - method: string; -} - export interface LedgerCommunication { close_async: () => Promise; } @@ -118,5 +113,7 @@ export enum NonceSubproviderErrors { CannotDetermineAddressFromPayload = 'CANNOT_DETERMINE_ADDRESS_FROM_PAYLOAD', } -export type OptionalNextCallback = (callback?: (err: Error | null, result: any, cb: any) => void) => void; 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; diff --git a/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts b/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts index e98d9023c..14176c145 100644 --- a/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts +++ b/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts @@ -1,9 +1,11 @@ import * as chai from 'chai'; +import * as fs from 'fs'; import * as _ from 'lodash'; +import * as path from 'path'; import Web3ProviderEngine = require('web3-provider-engine'); import FixtureSubprovider = require('web3-provider-engine/subproviders/fixture'); -import promisify = require('es6-promisify'); +import { promisify } from '@0xproject/utils'; import EthereumTx = require('ethereumjs-tx'); import { NonceTrackerSubprovider } from '../../src'; @@ -62,9 +64,9 @@ describe('NonceTrackerSubprovider', () => { const payload = { ...getTransactionCountPayload, params: ['0x0', 'pending'] }; - const response = await promisify(provider.sendAsync, provider)(payload); + const response = await promisify(provider.sendAsync, provider)(payload); expect(response.result).to.be.eq('0x00'); - const secondResponse = await promisify(provider.sendAsync, provider)(payload); + const secondResponse = await promisify(provider.sendAsync, provider)(payload); expect(secondResponse.result).to.be.eq('0x00'); }); it('does not cache the result for latest transaction count', async () => { @@ -76,9 +78,9 @@ describe('NonceTrackerSubprovider', () => { const payload = { ...getTransactionCountPayload, params: ['0x0', 'latest'] }; - const response = await promisify(provider.sendAsync, provider)(payload); + const response = await promisify(provider.sendAsync, provider)(payload); expect(response.result).to.be.eq('0x00'); - const secondResponse = await promisify(provider.sendAsync, provider)(payload); + const secondResponse = await promisify(provider.sendAsync, provider)(payload); expect(secondResponse.result).to.be.eq('0x99'); }); it('clears the cache on a Nonce Too Low Error', async () => { @@ -105,14 +107,14 @@ describe('NonceTrackerSubprovider', () => { params: [transaction.serialize()], }; - const response = await promisify(provider.sendAsync, provider)(noncePayload); + const response = await promisify(provider.sendAsync, provider)(noncePayload); expect(response.result).to.be.eq('0x00'); - const secondResponse = await promisify(provider.sendAsync, provider)(noncePayload); + const secondResponse = await promisify(provider.sendAsync, provider)(noncePayload); expect(secondResponse.result).to.be.eq('0x00'); try { await promisify(provider.sendAsync, provider)(txPayload); } catch (err) { - const thirdResponse = await promisify(provider.sendAsync, provider)(noncePayload); + const thirdResponse = await promisify(provider.sendAsync, provider)(noncePayload); expect(thirdResponse.result).to.be.eq('0x99'); } }); @@ -140,12 +142,12 @@ describe('NonceTrackerSubprovider', () => { params: [transaction.serialize()], }; - const response = await promisify(provider.sendAsync, provider)(noncePayload); + const response = await promisify(provider.sendAsync, provider)(noncePayload); expect(response.result).to.be.eq('0x00'); - const secondResponse = await promisify(provider.sendAsync, provider)(noncePayload); + const secondResponse = await promisify(provider.sendAsync, provider)(noncePayload); expect(secondResponse.result).to.be.eq('0x00'); await promisify(provider.sendAsync, provider)(txPayload); - const thirdResponse = await promisify(provider.sendAsync, provider)(noncePayload); + const thirdResponse = await promisify(provider.sendAsync, provider)(noncePayload); expect(thirdResponse.result).to.be.eq('0x01'); }); }); -- cgit v1.2.3