aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJacob Evans <dekz@dekz.net>2018-02-07 07:29:19 +0800
committerGitHub <noreply@github.com>2018-02-07 07:29:19 +0800
commit548246c05f128d23d701d9b738ce53deaf783f63 (patch)
tree2dd525ed3624352160c1de2747b7eb7932e28820 /packages
parent629653425da973df0ac5d21d11da5a61c3a31e09 (diff)
parent4be8eca3fe6a81b468776f5413f25ec726e4e752 (diff)
downloaddexon-0x-contracts-548246c05f128d23d701d9b738ce53deaf783f63.tar.gz
dexon-0x-contracts-548246c05f128d23d701d9b738ce53deaf783f63.tar.zst
dexon-0x-contracts-548246c05f128d23d701d9b738ce53deaf783f63.zip
Merge pull request #355 from 0xProject/feature/subproviders/nonce-tracker
Nonce tracker subprovider
Diffstat (limited to 'packages')
-rw-r--r--packages/0x.js/src/contract_wrappers/contract_wrapper.ts3
-rw-r--r--packages/0x.js/src/contract_wrappers/exchange_wrapper.ts3
-rw-r--r--packages/0x.js/src/index.ts10
-rw-r--r--packages/0x.js/src/order_watcher/event_watcher.ts4
-rw-r--r--packages/0x.js/src/order_watcher/order_state_watcher.ts3
-rw-r--r--packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts2
-rw-r--r--packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts2
-rw-r--r--packages/0x.js/src/types.ts14
-rw-r--r--packages/0x.js/src/utils/exchange_transfer_simulator.ts3
-rw-r--r--packages/0x.js/test/exchange_transfer_simulator_test.ts3
-rw-r--r--packages/0x.js/test/exchange_wrapper_test.ts3
-rw-r--r--packages/0x.js/test/order_validation_test.ts3
-rw-r--r--packages/subproviders/CHANGELOG.md5
-rw-r--r--packages/subproviders/package.json1
-rw-r--r--packages/subproviders/src/globals.d.ts20
-rw-r--r--packages/subproviders/src/index.ts3
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts2
-rw-r--r--packages/subproviders/src/subproviders/nonce_tracker.ts103
-rw-r--r--packages/subproviders/src/subproviders/redundant_rpc.ts1
-rw-r--r--packages/subproviders/src/types.ts8
-rw-r--r--packages/subproviders/test/unit/nonce_tracker_subprovider_test.ts151
-rw-r--r--packages/testnet-faucets/Dockerfile5
-rw-r--r--packages/testnet-faucets/package.json1
-rw-r--r--packages/testnet-faucets/src/ts/global.d.ts15
-rw-r--r--packages/testnet-faucets/src/ts/handler.ts4
-rw-r--r--packages/types/CHANGELOG.md2
-rw-r--r--packages/types/src/index.ts10
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 4310f8d4a..7934ecb66 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 5a6230bfe..7c61d123f 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",
"express": "^4.15.2",
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 bf5b3e81e..121112fd3 100644
--- a/packages/testnet-faucets/src/ts/handler.ts
+++ b/packages/testnet-faucets/src/ts/handler.ts
@@ -1,9 +1,9 @@
+import { NonceTrackerSubprovider } from '@0xproject/subproviders';
import { addressUtils } from '@0xproject/utils';
import * as express from 'express';
import * as _ from 'lodash';
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 { EtherRequestQueue } from './ether_request_queue';
@@ -96,7 +96,7 @@ export class Handler {
// tslint:disable-next-line:prefer-function-over-method
private _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;