diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-02-07 07:36:00 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-02-07 07:36:00 +0800 |
commit | 36a49966eb4d5b78192cb5fa76c98755ef092f8b (patch) | |
tree | 7100b22aa1c4ac824bd054fefdae53e9df0aab9a /packages | |
parent | f818d06b43ae9279c4ee022685e3bcb9f27ba2fd (diff) | |
parent | 548246c05f128d23d701d9b738ce53deaf783f63 (diff) | |
download | dexon-0x-contracts-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.gz dexon-0x-contracts-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.zst dexon-0x-contracts-36a49966eb4d5b78192cb5fa76c98755ef092f8b.zip |
Merge branch 'development' into feature/testnet-faucets/order-dispenser
* development:
Attribute the origins of NonceTracker
Move BlockParamLiteral to shared types package
Fixes
Move BlockParam and BlockParamLiteral to shared types
Rename called to something more readable
Newline prettier/lint
Yarn.lock
Refactor tests for reuse of the fixture subprovider
Remove re-fetch of transaction count on error
Disable linter for multiple class declarations
Remove double declaration
Enable CIRCLECI and declare web3
Test faucets to use new NonceTracker
Update changelog
Readability and prettier
Prettify
Nonce tracker subprovider Caches the nonce when a request to getTransactionCount is made and increments the pending nonce after successful transactions
Diffstat (limited to 'packages')
27 files changed, 351 insertions, 33 deletions
diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts index d913e8d9b..b313273b5 100644 --- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts @@ -1,4 +1,4 @@ -import { LogWithDecodedArgs, RawLog } from '@0xproject/types'; +import { BlockParamLiteral, LogWithDecodedArgs, RawLog } from '@0xproject/types'; import { AbiDecoder, intervalUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream'; @@ -7,7 +7,6 @@ import * as Web3 from 'web3'; import { Artifact, - BlockParamLiteral, BlockRange, ContractEventArgs, ContractEvents, diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 63c0d073a..e0c85505c 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { DecodedLogArgs, LogWithDecodedArgs } from '@0xproject/types'; +import { BlockParamLiteral, DecodedLogArgs, LogWithDecodedArgs } from '@0xproject/types'; import { AbiDecoder, BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; @@ -7,7 +7,6 @@ import * as Web3 from 'web3'; import { artifacts } from '../artifacts'; import { - BlockParamLiteral, BlockRange, ECSignature, EventCallback, diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 41e67e177..c3c8854da 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -2,7 +2,6 @@ export { ZeroEx } from './0x'; export { Order, - BlockParamLiteral, SignedOrder, ECSignature, ZeroExError, @@ -14,7 +13,6 @@ export { TokenEvents, IndexedFilterValues, BlockRange, - BlockParam, OrderCancellationRequest, OrderFillRequest, LogErrorContractEventArgs, @@ -44,6 +42,12 @@ export { OrderState, } from './types'; -export { ContractEventArg, LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from '@0xproject/types'; +export { + BlockParamLiteral, + BlockParam, + ContractEventArg, + LogWithDecodedArgs, + TransactionReceiptWithDecodedLogs, +} from '@0xproject/types'; export { TransactionReceipt } from '@0xproject/types'; diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts index 5d05bfb60..e67b93251 100644 --- a/packages/0x.js/src/order_watcher/event_watcher.ts +++ b/packages/0x.js/src/order_watcher/event_watcher.ts @@ -3,7 +3,9 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as Web3 from 'web3'; -import { BlockParamLiteral, EventWatcherCallback, ZeroExError } from '../types'; +import { BlockParamLiteral } from '@0xproject/types'; + +import { EventWatcherCallback, ZeroExError } from '../types'; import { assert } from '../utils/assert'; const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200; diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts index 1ad1a90b1..a9b3eba68 100644 --- a/packages/0x.js/src/order_watcher/order_state_watcher.ts +++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { LogWithDecodedArgs } from '@0xproject/types'; +import { BlockParamLiteral, LogWithDecodedArgs } from '@0xproject/types'; import { AbiDecoder, intervalUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; @@ -11,7 +11,6 @@ import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allow import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store'; import { ApprovalContractEventArgs, - BlockParamLiteral, ContractEventArgs, DepositContractEventArgs, EtherTokenEvents, diff --git a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts index 33feea105..ede1319fe 100644 --- a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts @@ -1,8 +1,8 @@ +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { TokenWrapper } from '../contract_wrappers/token_wrapper'; -import { BlockParamLiteral } from '../types'; /** * Copy on read store for balances/proxyAllowances of tokens/accounts diff --git a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts index e22364c09..0a0d93406 100644 --- a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts +++ b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts @@ -1,8 +1,8 @@ +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; -import { BlockParamLiteral } from '../types'; /** * Copy on read store for filled/cancelled taker amounts diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index a0deb91c9..a2d1b9eb4 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -1,5 +1,7 @@ -import { ContractEventArg, LogWithDecodedArgs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; + +import { BlockParam, BlockParamLiteral, ContractEventArg, LogWithDecodedArgs } from '@0xproject/types'; + import * as Web3 from 'web3'; export enum ZeroExError { @@ -219,16 +221,6 @@ export interface IndexedFilterValues { [index: string]: ContractEventArg; } -// Earliest is omitted by design. It is simply an alias for the `0` constant and -// is thus not very helpful. Moreover, this type is used in places that only accept -// `latest` or `pending`. -export enum BlockParamLiteral { - Latest = 'latest', - Pending = 'pending', -} - -export type BlockParam = BlockParamLiteral | number; - export interface BlockRange { fromBlock: BlockParam; toBlock: BlockParam; diff --git a/packages/0x.js/src/utils/exchange_transfer_simulator.ts b/packages/0x.js/src/utils/exchange_transfer_simulator.ts index 662cd210c..9a920c643 100644 --- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts +++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts @@ -1,9 +1,10 @@ +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { TokenWrapper } from '../contract_wrappers/token_wrapper'; import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; -import { BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType } from '../types'; +import { ExchangeContractErrs, TradeSide, TransferType } from '../types'; enum FailureReason { Balance = 'balance', diff --git a/packages/0x.js/test/exchange_transfer_simulator_test.ts b/packages/0x.js/test/exchange_transfer_simulator_test.ts index e85a1640f..ba0ee9059 100644 --- a/packages/0x.js/test/exchange_transfer_simulator_test.ts +++ b/packages/0x.js/test/exchange_transfer_simulator_test.ts @@ -1,9 +1,10 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { ExchangeContractErrs, Token, ZeroEx } from '../src'; -import { BlockParamLiteral, TradeSide, TransferType } from '../src/types'; +import { TradeSide, TransferType } from '../src/types'; import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; import { chaiSetup } from './utils/chai_setup'; diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index 044298601..325426438 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; @@ -18,7 +19,7 @@ import { Token, ZeroEx, } from '../src'; -import { BlockParamLiteral, DoneCallback } from '../src/types'; +import { DoneCallback } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts index 934e2e51f..b3dc42396 100644 --- a/packages/0x.js/test/order_validation_test.ts +++ b/packages/0x.js/test/order_validation_test.ts @@ -1,11 +1,12 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; +import { BlockParamLiteral } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as Sinon from 'sinon'; import * as Web3 from 'web3'; import { ExchangeContractErrs, SignedOrder, Token, ZeroEx, ZeroExError } from '../src'; -import { BlockParamLiteral, TradeSide, TransferType } from '../src/types'; +import { TradeSide, TransferType } from '../src/types'; import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; import { OrderValidationUtils } from '../src/utils/order_validation_utils'; diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 6ee8e2455..908272406 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -1,8 +1,9 @@ # CHANGELOG -## v0.4.0 - _Feburary 02, 2018_ +## v0.4.1 - _Febuary 2, 2018_ - * InjectedWeb3Subprovider accepts a Provider in the constructor, previously it was a Web3 object. + * Added NonceTrackerSubprovider (#355) + * InjectedWeb3Subprovider accepts a Provider in the constructor, previously it was a Web3 object (#363) ## v0.3.5 - _January 28, 2018_ diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 9137f3c77..76f7ab089 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -33,6 +33,7 @@ }, "devDependencies": { "@0xproject/tslint-config": "^0.4.7", + "@0xproject/types": "^0.1.9", "@0xproject/utils": "^0.3.0", "@types/lodash": "^4.14.86", "@types/mocha": "^2.2.42", diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts index 53457fa24..6f344dcd3 100644 --- a/packages/subproviders/src/globals.d.ts +++ b/packages/subproviders/src/globals.d.ts @@ -13,7 +13,9 @@ declare module 'ethereumjs-tx' { public r: Buffer; public s: Buffer; public v: Buffer; + public nonce: Buffer; public serialize(): Buffer; + public getSenderAddress(): Buffer; constructor(txParams: any); } export = EthereumTx; @@ -97,6 +99,24 @@ declare module 'web3-provider-engine' { } export = Web3ProviderEngine; } +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 * as Web3 from 'web3'; + class FixtureSubprovider { + constructor(staticResponses: any); + public handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: () => void, + end: (err: Error | null, data?: any) => void, + ): void; + } + export = FixtureSubprovider; +} // hdkey declarations declare module 'hdkey' { diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index 720c4362f..67d52ee25 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -9,7 +9,8 @@ import { LedgerEthereumClient } from './types'; export { InjectedWeb3Subprovider } from './subproviders/injected_web3'; export { RedundantRPCSubprovider } from './subproviders/redundant_rpc'; export { LedgerSubprovider } from './subproviders/ledger'; -export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient } from './types'; +export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; +export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient, NonceSubproviderErrors } from './types'; /** * A factory method for creating a LedgerEthereumClient usable in a browser context. diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 7267a793e..5966a88bb 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -60,6 +60,8 @@ export class LedgerSubprovider extends Subprovider { public setPathIndex(pathIndex: number) { this._derivationPathIndex = pathIndex; } + // Required to implement this public interface which doesn't conform to our linting rule. + // tslint:disable-next-line:async-suffix public async handleRequest( payload: Web3.JSONRPCRequestPayload, next: () => void, diff --git a/packages/subproviders/src/subproviders/nonce_tracker.ts b/packages/subproviders/src/subproviders/nonce_tracker.ts new file mode 100644 index 000000000..d967d40f2 --- /dev/null +++ b/packages/subproviders/src/subproviders/nonce_tracker.ts @@ -0,0 +1,103 @@ +import * as _ from 'lodash'; + +import EthereumTx = require('ethereumjs-tx'); +import ethUtil = require('ethereumjs-util'); +import providerEngineUtils = require('web3-provider-engine/util/rpc-cache-utils'); + +import { BlockParamLiteral } from '@0xproject/types'; + +import { ErrorCallback, JSONRPCPayload, NonceSubproviderErrors, OptionalNextCallback } from '../types'; + +import { Subprovider } from './subprovider'; + +// We do not export this since this is not our error, and we do not throw this error +const NONCE_TOO_LOW_ERROR_MESSAGE = 'Transaction nonce is too low'; +/* + This class is heavily inspiried by the Web3ProviderEngine NonceSubprovider + We have added the additional feature of clearing any nonce balues when an error message + describes a nonce value being too low. +*/ +export class NonceTrackerSubprovider extends Subprovider { + private _nonceCache: { [address: string]: string } = {}; + private static _reconstructTransaction(payload: JSONRPCPayload): 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: JSONRPCPayload): 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); + } + } + // 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<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: any) => { + if (_.isNull(requestError)) { + this._nonceCache[address] = requestResult as string; + } + cb(); + }); + } + } else { + return next(); + } + case 'eth_sendRawTransaction': + return next((sendTransactionError: Error | null, txResult: any, cb: any) => { + if (_.isNull(sendTransactionError)) { + this._handleSuccessfulTransaction(payload); + } else { + this._handleSendTransactionError(payload, sendTransactionError); + } + cb(); + }); + default: + return next(); + } + } + private _handleSuccessfulTransaction(payload: JSONRPCPayload): 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++; + let nextHexNonce = nonce.toString(16); + if (nextHexNonce.length % 2) { + nextHexNonce = `0${nextHexNonce}`; + } + const nextPrefixedHexNonce = `0x${nextHexNonce}`; + this._nonceCache[address] = nextPrefixedHexNonce; + } + private _handleSendTransactionError(payload: JSONRPCPayload, 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 a3cb463a8..5a94f93d7 100644 --- a/packages/subproviders/src/subproviders/redundant_rpc.ts +++ b/packages/subproviders/src/subproviders/redundant_rpc.ts @@ -35,6 +35,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, diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index 3db8be943..65b7f6c8f 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -112,3 +112,11 @@ export enum LedgerSubproviderErrors { SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED', } + +export enum NonceSubproviderErrors { + EmptyParametersFound = 'EMPTY_PARAMETERS_FOUND', + 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; diff --git a/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts b/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts new file mode 100644 index 000000000..e98d9023c --- /dev/null +++ b/packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts @@ -0,0 +1,151 @@ +import * as chai from 'chai'; +import * as _ from 'lodash'; +import Web3ProviderEngine = require('web3-provider-engine'); +import FixtureSubprovider = require('web3-provider-engine/subproviders/fixture'); + +import promisify = require('es6-promisify'); +import EthereumTx = require('ethereumjs-tx'); + +import { NonceTrackerSubprovider } from '../../src'; +import { chaiSetup } from '../chai_setup'; + +const expect = chai.expect; +chaiSetup.configure(); + +describe('NonceTrackerSubprovider', () => { + let provider: Web3ProviderEngine; + const getTransactionCountPayload = { + jsonrpc: '2.0', + method: 'eth_getTransactionCount', + params: ['0x0', 'pending'], + id: 1, + }; + const sendTransactionPayload = { + jsonrpc: '2.0', + method: 'eth_sendRawTransaction', + params: [], + id: 1, + }; + const txParams = [ + '0x', + '0x09184e72a000', + '0x2710', + '0x0000000000000000000000000000000000000000', + '0x', + '0x7f7465737432000000000000000000000000000000000000000000000000000000600057', + '0x1c', + '0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab', + '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13', + ]; + function createFixtureSubprovider() { + let isFirstGetTransactionCount = true; + const fixedBlockNumberAndTransactionCountProvider = new FixtureSubprovider({ + eth_getBlockByNumber: '0x01', + eth_getTransactionCount: (data: any, next: any, end: any) => { + // For testing caching we return different results on the second call + if (isFirstGetTransactionCount) { + isFirstGetTransactionCount = false; + end(null, '0x00'); + } else { + end(null, '0x99'); + } + }, + }); + return fixedBlockNumberAndTransactionCountProvider; + } + it('successfully caches the transaction count', async () => { + provider = new Web3ProviderEngine(); + const nonceTrackerSubprovider = new NonceTrackerSubprovider(); + provider.addProvider(nonceTrackerSubprovider); + provider.addProvider(createFixtureSubprovider()); + provider.start(); + + const payload = { ...getTransactionCountPayload, params: ['0x0', 'pending'] }; + + const response = await promisify(provider.sendAsync, provider)(payload); + expect(response.result).to.be.eq('0x00'); + 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 () => { + provider = new Web3ProviderEngine(); + const nonceTrackerSubprovider = new NonceTrackerSubprovider(); + provider.addProvider(nonceTrackerSubprovider); + provider.addProvider(createFixtureSubprovider()); + provider.start(); + + const payload = { ...getTransactionCountPayload, params: ['0x0', 'latest'] }; + + const response = await promisify(provider.sendAsync, provider)(payload); + expect(response.result).to.be.eq('0x00'); + 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 () => { + provider = new Web3ProviderEngine(); + const nonceTrackerSubprovider = new NonceTrackerSubprovider(); + provider.addProvider(nonceTrackerSubprovider); + provider.addProvider(createFixtureSubprovider()); + provider.addProvider( + new FixtureSubprovider({ + eth_sendRawTransaction: (data: any, next: any, end: any) => { + end(new Error('Transaction nonce is too low')); + }, + }), + ); + provider.start(); + + const noncePayload = { + ...getTransactionCountPayload, + params: ['0x1f36f546477cda21bf2296c50976f2740247906f', 'pending'], + }; + const transaction = new EthereumTx(txParams); + const txPayload = { + ...sendTransactionPayload, + params: [transaction.serialize()], + }; + + const response = await promisify(provider.sendAsync, provider)(noncePayload); + expect(response.result).to.be.eq('0x00'); + 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); + expect(thirdResponse.result).to.be.eq('0x99'); + } + }); + it('increments the used nonce when a transaction successfully submits', async () => { + provider = new Web3ProviderEngine(); + const nonceTrackerSubprovider = new NonceTrackerSubprovider(); + provider.addProvider(nonceTrackerSubprovider); + provider.addProvider(createFixtureSubprovider()); + provider.addProvider( + new FixtureSubprovider({ + eth_sendRawTransaction: (data: any, next: any, end: any) => { + end(null); + }, + }), + ); + provider.start(); + + const noncePayload = { + ...getTransactionCountPayload, + params: ['0x1f36f546477cda21bf2296c50976f2740247906f', 'pending'], + }; + const transaction = new EthereumTx(txParams); + const txPayload = { + ...sendTransactionPayload, + params: [transaction.serialize()], + }; + + const response = await promisify(provider.sendAsync, provider)(noncePayload); + expect(response.result).to.be.eq('0x00'); + 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); + expect(thirdResponse.result).to.be.eq('0x01'); + }); +}); diff --git a/packages/testnet-faucets/Dockerfile b/packages/testnet-faucets/Dockerfile index 6d6ddc192..bd1dcc4a4 100644 --- a/packages/testnet-faucets/Dockerfile +++ b/packages/testnet-faucets/Dockerfile @@ -2,6 +2,11 @@ FROM node WORKDIR /src +# Ledger Provider (in the Subproviders package) requires node-hid at dependency install time +# which compiles and expects certain USB developer library packages to be present +RUN apt-get -qq update && apt-get install -y libhidapi-dev libusb-1.0-0-dev +# Our fork of ledgerco disables requiring node-hid at run time if CIRCLECI is set to true +ENV CIRCLECI=true COPY package.json . RUN npm i RUN npm install forever -g diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 9efacae0a..53c2f75e9 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -17,6 +17,7 @@ "dependencies": { "0x.js": "^0.32.0", "@0xproject/utils": "^0.3.0", + "@0xproject/subproviders": "^0.3.5", "body-parser": "^1.17.1", "ethereumjs-tx": "^1.3.3", "ethereumjs-util": "^5.1.1", diff --git a/packages/testnet-faucets/src/ts/global.d.ts b/packages/testnet-faucets/src/ts/global.d.ts index 97cd35680..41a2f3a8a 100644 --- a/packages/testnet-faucets/src/ts/global.d.ts +++ b/packages/testnet-faucets/src/ts/global.d.ts @@ -1,5 +1,4 @@ declare module 'rollbar'; -declare module 'web3-provider-engine'; declare module 'web3-provider-engine/subproviders/rpc'; declare module 'web3-provider-engine/subproviders/nonce-tracker'; declare module 'web3-provider-engine/subproviders/hooked-wallet'; @@ -24,3 +23,17 @@ declare module 'ethereumjs-tx' { } export = EthereumTx; } + +/* tslint:disable */ +declare module 'web3-provider-engine' { + class Web3ProviderEngine { + public on(event: string, handler: () => void): void; + public send(payload: any): void; + public sendAsync(payload: any, callback: (error: any, response: any) => void): void; + public addProvider(provider: any): void; + public start(): void; + public stop(): void; + } + export = Web3ProviderEngine; +} +/* tslint:enable */ diff --git a/packages/testnet-faucets/src/ts/handler.ts b/packages/testnet-faucets/src/ts/handler.ts index e9ced3437..d96b90802 100644 --- a/packages/testnet-faucets/src/ts/handler.ts +++ b/packages/testnet-faucets/src/ts/handler.ts @@ -1,4 +1,5 @@ import { Order, SignedOrder, ZeroEx } from '0x.js'; +import { NonceTrackerSubprovider } from '@0xproject/subproviders'; import { BigNumber } from '@0xproject/utils'; import * as express from 'express'; import * as _ from 'lodash'; @@ -11,7 +12,6 @@ import * as Web3 from 'web3'; (global as any).XMLHttpRequest = undefined; import ProviderEngine = require('web3-provider-engine'); import HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet'); -import NonceSubprovider = require('web3-provider-engine/subproviders/nonce-tracker'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import { configs } from './configs'; @@ -61,7 +61,7 @@ export class Handler { } private static _createProviderEngine(rpcUrl: string) { const engine = new ProviderEngine(); - engine.addProvider(new NonceSubprovider()); + engine.addProvider(new NonceTrackerSubprovider()); engine.addProvider(new HookedWalletSubprovider(idManagement)); engine.addProvider( new RpcSubprovider({ diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 899482c4c..e75d0ddcb 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,3 +1,5 @@ # CHANGELOG ## vx.x.x + + * Added BlockLiteralParam and BlockParam, refactored out of 0x.js types. (#355) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 7b53b52c4..cb17936f7 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -56,3 +56,13 @@ export enum SolidityTypes { export interface TransactionReceiptWithDecodedLogs extends TransactionReceipt { logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>; } + +// Earliest is omitted by design. It is simply an alias for the `0` constant and +// is thus not very helpful. Moreover, this type is used in places that only accept +// `latest` or `pending`. +export enum BlockParamLiteral { + Latest = 'latest', + Pending = 'pending', +} + +export type BlockParam = BlockParamLiteral | number; |