aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2017-11-13 09:26:21 +0800
committerGitHub <noreply@github.com>2017-11-13 09:26:21 +0800
commit5aef16c2aacd279a8e688b4e735526bff7e4970f (patch)
treea85a712d3a478199a09ac6aebac6e43d2e3f3c3a
parent6becf22a2f752ef7c34ce1b423efd51773cc5fde (diff)
parente512e38efbbec030add83ce2bc50f0d17862bdd6 (diff)
downloaddexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar.gz
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar.bz2
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar.lz
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar.xz
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.tar.zst
dexon-sol-tools-5aef16c2aacd279a8e688b4e735526bff7e4970f.zip
Merge pull request #211 from 0xProject/feature/orderWatcherLocalStateStore
Order watcher local state store
-rw-r--r--package.json2
-rw-r--r--src/0x.ts3
-rw-r--r--src/order_watcher/event_watcher.ts15
-rw-r--r--src/order_watcher/order_state_watcher.ts108
-rw-r--r--src/stores/balance_proxy_allowance_lazy_store.ts82
-rw-r--r--src/stores/order_filled_cancelled_lazy_store.ts61
-rw-r--r--src/types.ts6
-rw-r--r--src/utils/exchange_transfer_simulator.ts76
-rw-r--r--src/utils/order_state_utils.ts53
-rw-r--r--test/event_watcher_test.ts2
-rw-r--r--test/exchange_transfer_simulator_test.ts18
-rw-r--r--test/order_state_watcher_test.ts73
-rw-r--r--yarn.lock4
13 files changed, 283 insertions, 220 deletions
diff --git a/package.json b/package.json
index e7e21bdce..9faaeb8c1 100644
--- a/package.json
+++ b/package.json
@@ -62,7 +62,7 @@
"chai-as-promised": "^7.1.0",
"chai-as-promised-typescript-typings": "0.0.3",
"chai-bignumber": "^2.0.1",
- "chai-typescript-typings": "^0.0.0",
+ "chai-typescript-typings": "^0.0.1",
"copyfiles": "^1.2.0",
"coveralls": "^3.0.0",
"dirty-chai": "^2.0.1",
diff --git a/src/0x.ts b/src/0x.ts
index a1841eaa8..fe765bbbe 100644
--- a/src/0x.ts
+++ b/src/0x.ts
@@ -205,9 +205,8 @@ export class ZeroEx {
const etherTokenContractAddressIfExists = _.isUndefined(config) ? undefined : config.etherTokenContractAddress;
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.token, etherTokenContractAddressIfExists);
const orderWatcherConfig = _.isUndefined(config) ? undefined : config.orderWatcherConfig;
- const orderStateUtils = new OrderStateUtils(this.token, this.exchange);
this.orderStateWatcher = new OrderStateWatcher(
- this._web3Wrapper, this._abiDecoder, orderStateUtils, orderWatcherConfig,
+ this._web3Wrapper, this._abiDecoder, this.token, this.exchange, orderWatcherConfig,
);
}
/**
diff --git a/src/order_watcher/event_watcher.ts b/src/order_watcher/event_watcher.ts
index c9e72281c..81529a98c 100644
--- a/src/order_watcher/event_watcher.ts
+++ b/src/order_watcher/event_watcher.ts
@@ -28,10 +28,8 @@ export class EventWatcher {
private _pollingIntervalMs: number;
private _intervalIdIfExists?: NodeJS.Timer;
private _lastEvents: Web3.LogEntry[] = [];
- private _numConfirmations: number;
- constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number, numConfirmations: number) {
+ constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number) {
this._web3Wrapper = web3Wrapper;
- this._numConfirmations = numConfirmations;
this._pollingIntervalMs = _.isUndefined(pollingIntervalMs) ?
DEFAULT_EVENT_POLLING_INTERVAL :
pollingIntervalMs;
@@ -67,16 +65,9 @@ export class EventWatcher {
this._lastEvents = pendingEvents;
}
private async _getEventsAsync(): Promise<Web3.LogEntry[]> {
- let latestBlock: BlockParamLiteral|number;
- if (this._numConfirmations === 0) {
- latestBlock = BlockParamLiteral.Pending;
- } else {
- const currentBlock = await this._web3Wrapper.getBlockNumberAsync();
- latestBlock = currentBlock - this._numConfirmations;
- }
const eventFilter = {
- fromBlock: latestBlock,
- toBlock: latestBlock,
+ fromBlock: BlockParamLiteral.Pending,
+ toBlock: BlockParamLiteral.Pending,
};
const events = await this._web3Wrapper.getLogsAsync(eventFilter);
return events;
diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts
index 4866f8409..139f13fdf 100644
--- a/src/order_watcher/order_state_watcher.ts
+++ b/src/order_watcher/order_state_watcher.ts
@@ -1,6 +1,5 @@
import * as _ from 'lodash';
import {schemas} from '0x-json-schemas';
-import * as ethUtil from 'ethereumjs-util';
import {ZeroEx} from '../0x';
import {EventWatcher} from './event_watcher';
import {assert} from '../utils/assert';
@@ -15,13 +14,22 @@ import {
Web3Provider,
BlockParamLiteral,
LogWithDecodedArgs,
+ ContractEventArgs,
OnOrderStateChangeCallback,
OrderStateWatcherConfig,
+ ApprovalContractEventArgs,
+ TransferContractEventArgs,
+ LogFillContractEventArgs,
+ LogCancelContractEventArgs,
ExchangeEvents,
TokenEvents,
ZeroExError,
} from '../types';
import {Web3Wrapper} from '../web3_wrapper';
+import {TokenWrapper} from '../contract_wrappers/token_wrapper';
+import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper';
+import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store';
+import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store';
const DEFAULT_NUM_CONFIRMATIONS = 0;
@@ -44,26 +52,26 @@ interface OrderByOrderHash {
export class OrderStateWatcher {
private _orderByOrderHash: OrderByOrderHash = {};
private _dependentOrderHashes: DependentOrderHashes = {};
- private _web3Wrapper: Web3Wrapper;
private _callbackIfExistsAsync?: OnOrderStateChangeCallback;
private _eventWatcher: EventWatcher;
+ private _web3Wrapper: Web3Wrapper;
private _abiDecoder: AbiDecoder;
private _orderStateUtils: OrderStateUtils;
- private _numConfirmations: number;
+ private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
constructor(
- web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, orderStateUtils: OrderStateUtils,
+ web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper,
config?: OrderStateWatcherConfig,
) {
+ this._abiDecoder = abiDecoder;
this._web3Wrapper = web3Wrapper;
- const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.pollingIntervalMs;
- this._numConfirmations = _.isUndefined(config) ?
- DEFAULT_NUM_CONFIRMATIONS
- : config.numConfirmations;
- this._eventWatcher = new EventWatcher(
- this._web3Wrapper, eventPollingIntervalMs, this._numConfirmations,
+ const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
+ this._eventWatcher = new EventWatcher(web3Wrapper, eventPollingIntervalMs);
+ this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(token);
+ this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
+ this._orderStateUtils = new OrderStateUtils(
+ this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore,
);
- this._abiDecoder = abiDecoder;
- this._orderStateUtils = orderStateUtils;
}
/**
* Add an order to the orderStateWatcher. Before the order is added, it's
@@ -108,6 +116,11 @@ export class OrderStateWatcher {
* Ends an orderStateWatcher subscription.
*/
public unsubscribe(): void {
+ if (_.isUndefined(this._callbackIfExistsAsync)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ this._balanceAndProxyAllowanceLazyStore.deleteAll();
+ this._orderFilledCancelledLazyStore.deleteAll();
delete this._callbackIfExistsAsync;
this._eventWatcher.unsubscribe();
}
@@ -117,45 +130,68 @@ export class OrderStateWatcher {
if (!isLogDecoded) {
return; // noop
}
- // Unfortunately blockNumber is returned as a hex-encoded string, so we
- // convert it to a number here.
- const blockNumberBuff = ethUtil.toBuffer(maybeDecodedLog.blockNumber);
- const blockNumber = ethUtil.bufferToInt(blockNumberBuff);
-
- const decodedLog = maybeDecodedLog as LogWithDecodedArgs<any>;
+ const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>;
let makerToken: string;
let makerAddress: string;
let orderHashesSet: Set<string>;
switch (decodedLog.event) {
case TokenEvents.Approval:
+ {
+ // Invalidate cache
+ const args = decodedLog.args as ApprovalContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
+ // Revalidate orders
makerToken = decodedLog.address;
- makerAddress = decodedLog.args._owner;
+ makerAddress = args._owner;
orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]);
if (!_.isUndefined(orderHashesSet)) {
const orderHashes = Array.from(orderHashesSet);
- await this._emitRevalidateOrdersAsync(orderHashes, blockNumber);
+ await this._emitRevalidateOrdersAsync(orderHashes);
}
break;
-
+ }
case TokenEvents.Transfer:
+ {
+ // Invalidate cache
+ const args = decodedLog.args as TransferContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
+ // Revalidate orders
makerToken = decodedLog.address;
- makerAddress = decodedLog.args._from;
+ makerAddress = args._from;
orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]);
if (!_.isUndefined(orderHashesSet)) {
const orderHashes = Array.from(orderHashesSet);
- await this._emitRevalidateOrdersAsync(orderHashes, blockNumber);
+ await this._emitRevalidateOrdersAsync(orderHashes);
}
break;
-
+ }
case ExchangeEvents.LogFill:
+ {
+ // Invalidate cache
+ const args = decodedLog.args as LogFillContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
+ const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
+ if (isOrderWatched) {
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ break;
+ }
case ExchangeEvents.LogCancel:
- const orderHash = decodedLog.args.orderHash;
+ {
+ // Invalidate cache
+ const args = decodedLog.args as LogCancelContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
if (isOrderWatched) {
- await this._emitRevalidateOrdersAsync([orderHash], blockNumber);
+ await this._emitRevalidateOrdersAsync([orderHash]);
}
break;
-
+ }
case ExchangeEvents.LogError:
return; // noop
@@ -163,22 +199,16 @@ export class OrderStateWatcher {
throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event);
}
}
- private async _emitRevalidateOrdersAsync(orderHashes: string[], blockNumber: number): Promise<void> {
- const defaultBlock = this._numConfirmations === 0 ?
- BlockParamLiteral.Pending :
- blockNumber;
- const methodOpts = {
- defaultBlock,
- };
-
+ private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> {
for (const orderHash of orderHashes) {
const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder;
- const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder, methodOpts);
- if (!_.isUndefined(this._callbackIfExistsAsync)) {
- await this._callbackIfExistsAsync(orderState);
- } else {
+ // Most of these calls will never reach the network because the data is fetched from stores
+ // and only updated when cache is invalidated
+ const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder);
+ if (_.isUndefined(this._callbackIfExistsAsync)) {
break; // Unsubscribe was called
}
+ await this._callbackIfExistsAsync(orderState);
}
}
private addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string) {
diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts
new file mode 100644
index 000000000..c83e61606
--- /dev/null
+++ b/src/stores/balance_proxy_allowance_lazy_store.ts
@@ -0,0 +1,82 @@
+import * as _ from 'lodash';
+import * as Web3 from 'web3';
+import {BigNumber} from 'bignumber.js';
+import {TokenWrapper} from '../contract_wrappers/token_wrapper';
+import {BlockParamLiteral} from '../types';
+
+/**
+ * Copy on read store for balances/proxyAllowances of tokens/accounts
+ */
+export class BalanceAndProxyAllowanceLazyStore {
+ private token: TokenWrapper;
+ private balance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber,
+ },
+ };
+ private proxyAllowance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber,
+ },
+ };
+ constructor(token: TokenWrapper) {
+ this.token = token;
+ this.balance = {};
+ this.proxyAllowance = {};
+ }
+ public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const balance = await this.token.getBalanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setBalance(tokenAddress, userAddress, balance);
+ }
+ const cachedBalance = this.balance[tokenAddress][userAddress];
+ return cachedBalance;
+ }
+ public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
+ if (_.isUndefined(this.balance[tokenAddress])) {
+ this.balance[tokenAddress] = {};
+ }
+ this.balance[tokenAddress][userAddress] = balance;
+ }
+ public deleteBalance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this.balance[tokenAddress])) {
+ delete this.balance[tokenAddress][userAddress];
+ if (_.isEmpty(this.balance[tokenAddress])) {
+ delete this.balance[tokenAddress];
+ }
+ }
+ }
+ public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (_.isUndefined(this.proxyAllowance[tokenAddress]) ||
+ _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
+ }
+ const cachedProxyAllowance = this.proxyAllowance[tokenAddress][userAddress];
+ return cachedProxyAllowance;
+ }
+ public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
+ if (_.isUndefined(this.proxyAllowance[tokenAddress])) {
+ this.proxyAllowance[tokenAddress] = {};
+ }
+ this.proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
+ }
+ public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this.proxyAllowance[tokenAddress])) {
+ delete this.proxyAllowance[tokenAddress][userAddress];
+ if (_.isEmpty(this.proxyAllowance[tokenAddress])) {
+ delete this.proxyAllowance[tokenAddress];
+ }
+ }
+ }
+ public deleteAll(): void {
+ this.balance = {};
+ this.proxyAllowance = {};
+ }
+}
diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts
new file mode 100644
index 000000000..9d74da096
--- /dev/null
+++ b/src/stores/order_filled_cancelled_lazy_store.ts
@@ -0,0 +1,61 @@
+import * as _ from 'lodash';
+import * as Web3 from 'web3';
+import {BigNumber} from 'bignumber.js';
+import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper';
+import {BlockParamLiteral} from '../types';
+
+/**
+ * Copy on read store for filled/cancelled taker amounts
+ */
+export class OrderFilledCancelledLazyStore {
+ private exchange: ExchangeWrapper;
+ private filledTakerAmount: {
+ [orderHash: string]: BigNumber,
+ };
+ private cancelledTakerAmount: {
+ [orderHash: string]: BigNumber,
+ };
+ constructor(exchange: ExchangeWrapper) {
+ this.exchange = exchange;
+ this.filledTakerAmount = {};
+ this.cancelledTakerAmount = {};
+ }
+ public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this.filledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const filledTakerAmount = await this.exchange.getFilledTakerAmountAsync(orderHash, methodOpts);
+ this.setFilledTakerAmount(orderHash, filledTakerAmount);
+ }
+ const cachedFilled = this.filledTakerAmount[orderHash];
+ return cachedFilled;
+ }
+ public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
+ this.filledTakerAmount[orderHash] = filledTakerAmount;
+ }
+ public deleteFilledTakerAmount(orderHash: string): void {
+ delete this.filledTakerAmount[orderHash];
+ }
+ public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this.cancelledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const cancelledTakerAmount = await this.exchange.getCanceledTakerAmountAsync(orderHash, methodOpts);
+ this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
+ }
+ const cachedCancelled = this.cancelledTakerAmount[orderHash];
+ return cachedCancelled;
+ }
+ public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
+ this.cancelledTakerAmount[orderHash] = cancelledTakerAmount;
+ }
+ public deleteCancelledTakerAmount(orderHash: string): void {
+ delete this.cancelledTakerAmount[orderHash];
+ }
+ public deleteAll(): void {
+ this.filledTakerAmount = {};
+ this.cancelledTakerAmount = {};
+ }
+}
diff --git a/src/types.ts b/src/types.ts
index a366fc31e..fcfbdc92b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -397,12 +397,10 @@ export interface JSONRPCPayload {
}
/*
- * pollingIntervalMs: How often to poll the Ethereum node for new events.
- * numConfirmations: How many confirmed blocks deep you wish to listen for events at.
+ * eventPollingIntervalMs: How often to poll the Ethereum node for new events
*/
export interface OrderStateWatcherConfig {
- pollingIntervalMs?: number;
- numConfirmations: number;
+ eventPollingIntervalMs?: number;
}
/*
diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts
index 89b23c8ab..308ef06db 100644
--- a/src/utils/exchange_transfer_simulator.ts
+++ b/src/utils/exchange_transfer_simulator.ts
@@ -1,7 +1,8 @@
import * as _ from 'lodash';
import BigNumber from 'bignumber.js';
-import {ExchangeContractErrs, TradeSide, TransferType} from '../types';
+import {ExchangeContractErrs, TradeSide, TransferType, BlockParamLiteral} from '../types';
import {TokenWrapper} from '../contract_wrappers/token_wrapper';
+import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store';
enum FailureReason {
Balance = 'balance',
@@ -31,58 +32,13 @@ const ERR_MSG_MAPPING = {
},
};
-/**
- * Copy on read store for balances/proxyAllowances of tokens/accounts touched in trades
- */
-export class BalanceAndProxyAllowanceLazyStore {
- protected _token: TokenWrapper;
- private _balance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber,
- },
- };
- private _proxyAllowance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber,
- },
- };
+export class ExchangeTransferSimulator {
+ private store: BalanceAndProxyAllowanceLazyStore;
+ private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
constructor(token: TokenWrapper) {
- this._token = token;
- this._balance = {};
- this._proxyAllowance = {};
- }
- protected async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
- const balance = await this._token.getBalanceAsync(tokenAddress, userAddress);
- this.setBalance(tokenAddress, userAddress, balance);
- }
- const cachedBalance = this._balance[tokenAddress][userAddress];
- return cachedBalance;
- }
- protected setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
- if (_.isUndefined(this._balance[tokenAddress])) {
- this._balance[tokenAddress] = {};
- }
- this._balance[tokenAddress][userAddress] = balance;
+ this.store = new BalanceAndProxyAllowanceLazyStore(token);
+ this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
}
- protected async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (_.isUndefined(this._proxyAllowance[tokenAddress]) ||
- _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])) {
- const proxyAllowance = await this._token.getProxyAllowanceAsync(tokenAddress, userAddress);
- this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
- }
- const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
- return cachedProxyAllowance;
- }
- protected setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
- if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
- this._proxyAllowance[tokenAddress] = {};
- }
- this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
- }
-}
-
-export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore {
/**
* Simulates transferFrom call performed by a proxy
* @param tokenAddress Address of the token to be transferred
@@ -95,8 +51,8 @@ export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore
public async transferFromAsync(tokenAddress: string, from: string, to: string,
amountInBaseUnits: BigNumber, tradeSide: TradeSide,
transferType: TransferType): Promise<void> {
- const balance = await this.getBalanceAsync(tokenAddress, from);
- const proxyAllowance = await this.getProxyAllowanceAsync(tokenAddress, from);
+ const balance = await this.store.getBalanceAsync(tokenAddress, from);
+ const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, from);
if (proxyAllowance.lessThan(amountInBaseUnits)) {
this.throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
}
@@ -109,20 +65,20 @@ export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore
}
private async decreaseProxyAllowanceAsync(tokenAddress: string, userAddress: string,
amountInBaseUnits: BigNumber): Promise<void> {
- const proxyAllowance = await this.getProxyAllowanceAsync(tokenAddress, userAddress);
- if (!proxyAllowance.eq(this._token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
- this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
+ const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, userAddress);
+ if (!proxyAllowance.eq(this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
+ this.store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
}
}
private async increaseBalanceAsync(tokenAddress: string, userAddress: string,
amountInBaseUnits: BigNumber): Promise<void> {
- const balance = await this.getBalanceAsync(tokenAddress, userAddress);
- this.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
+ const balance = await this.store.getBalanceAsync(tokenAddress, userAddress);
+ this.store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
}
private async decreaseBalanceAsync(tokenAddress: string, userAddress: string,
amountInBaseUnits: BigNumber): Promise<void> {
- const balance = await this.getBalanceAsync(tokenAddress, userAddress);
- this.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
+ const balance = await this.store.getBalanceAsync(tokenAddress, userAddress);
+ this.store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
}
private throwValidationError(failureReason: FailureReason, tradeSide: TradeSide,
transferType: TransferType): Promise<never> {
diff --git a/src/utils/order_state_utils.ts b/src/utils/order_state_utils.ts
index 36a4b68d6..f82601cae 100644
--- a/src/utils/order_state_utils.ts
+++ b/src/utils/order_state_utils.ts
@@ -1,4 +1,5 @@
import * as _ from 'lodash';
+import * as Web3 from 'web3';
import BigNumber from 'bignumber.js';
import {
ExchangeContractErrs,
@@ -14,16 +15,19 @@ import {TokenWrapper} from '../contract_wrappers/token_wrapper';
import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper';
import {utils} from '../utils/utils';
import {constants} from '../utils/constants';
+import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store';
+import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store';
export class OrderStateUtils {
- private tokenWrapper: TokenWrapper;
- private exchangeWrapper: ExchangeWrapper;
- constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) {
- this.tokenWrapper = tokenWrapper;
- this.exchangeWrapper = exchangeWrapper;
+ private balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ constructor(balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
+ orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore) {
+ this.balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
+ this.orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
}
- public async getOrderStateAsync(signedOrder: SignedOrder, methodOpts?: MethodOpts): Promise<OrderState> {
- const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder, methodOpts);
+ public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
+ const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
const orderHash = ZeroEx.getOrderHashHex(signedOrder);
try {
this.validateIfOrderIsValid(signedOrder, orderRelevantState);
@@ -42,26 +46,31 @@ export class OrderStateUtils {
return orderState;
}
}
- public async getOrderRelevantStateAsync(
- signedOrder: SignedOrder, methodOpts?: MethodOpts): Promise<OrderRelevantState> {
- const zrxTokenAddress = await this.exchangeWrapper.getZRXTokenAddressAsync();
+ public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
+ // HACK: We access the private property here but otherwise the interface will be less nice.
+ // If we pass it from the instantiator - there is no opportunity to get it there
+ // because JS doesn't support async constructors.
+ // Moreover - it's cached under the hood so it's equivalent to an async constructor.
+ const exchange = (this.orderFilledCancelledLazyStore as any).exchange as ExchangeWrapper;
+ const zrxTokenAddress = await exchange.getZRXTokenAddressAsync();
const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const makerBalance = await this.tokenWrapper.getBalanceAsync(
- signedOrder.makerTokenAddress, signedOrder.maker, methodOpts,
+ const makerBalance = await this.balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ signedOrder.makerTokenAddress, signedOrder.maker,
);
- const makerProxyAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
- signedOrder.makerTokenAddress, signedOrder.maker, methodOpts,
+ const makerProxyAllowance = await this.balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ signedOrder.makerTokenAddress, signedOrder.maker,
);
- const makerFeeBalance = await this.tokenWrapper.getBalanceAsync(
- zrxTokenAddress, signedOrder.maker, methodOpts,
+ const makerFeeBalance = await this.balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ zrxTokenAddress, signedOrder.maker,
);
- const makerFeeProxyAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
- zrxTokenAddress, signedOrder.maker, methodOpts,
+ const makerFeeProxyAllowance = await this.balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ zrxTokenAddress, signedOrder.maker,
);
- const filledTakerTokenAmount = await this.exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts);
- const canceledTakerTokenAmount = await this.exchangeWrapper.getCanceledTakerAmountAsync(orderHash, methodOpts);
- const unavailableTakerTokenAmount =
- await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash, methodOpts);
+ const filledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
+ const canceledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
+ orderHash,
+ );
+ const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
const totalMakerTokenAmount = signedOrder.makerTokenAmount;
const totalTakerTokenAmount = signedOrder.takerTokenAmount;
const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
diff --git a/test/event_watcher_test.ts b/test/event_watcher_test.ts
index 98dab93b5..b4164fe63 100644
--- a/test/event_watcher_test.ts
+++ b/test/event_watcher_test.ts
@@ -58,7 +58,7 @@ describe('EventWatcher', () => {
web3 = web3Factory.create();
const pollingIntervalMs = 10;
web3Wrapper = new Web3Wrapper(web3.currentProvider);
- eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs, numConfirmations);
+ eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs);
});
afterEach(() => {
// clean up any stubs after the test has completed
diff --git a/test/exchange_transfer_simulator_test.ts b/test/exchange_transfer_simulator_test.ts
index 3373ebf03..99cb7fb4f 100644
--- a/test/exchange_transfer_simulator_test.ts
+++ b/test/exchange_transfer_simulator_test.ts
@@ -59,11 +59,10 @@ describe('ExchangeTransferSimulator', () => {
await exchangeTransferSimulator.transferFromAsync(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade,
);
- const senderBalance = await (exchangeTransferSimulator as any).getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await (exchangeTransferSimulator as any).getBalanceAsync(
- exampleTokenAddress, recipient);
- const senderProxyAllowance = await (exchangeTransferSimulator as any).getProxyAllowanceAsync(
- exampleTokenAddress, sender);
+ const store = (exchangeTransferSimulator as any).store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
expect(senderBalance).to.be.bignumber.equal(0);
expect(recipientBalance).to.be.bignumber.equal(transferAmount);
expect(senderProxyAllowance).to.be.bignumber.equal(0);
@@ -76,11 +75,10 @@ describe('ExchangeTransferSimulator', () => {
await exchangeTransferSimulator.transferFromAsync(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade,
);
- const senderBalance = await (exchangeTransferSimulator as any).getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await (exchangeTransferSimulator as any).getBalanceAsync(
- exampleTokenAddress, recipient);
- const senderProxyAllowance = await (exchangeTransferSimulator as any).getProxyAllowanceAsync(
- exampleTokenAddress, sender);
+ const store = (exchangeTransferSimulator as any).store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
expect(senderBalance).to.be.bignumber.equal(0);
expect(recipientBalance).to.be.bignumber.equal(transferAmount);
expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts
index 41f938584..c8a4a8064 100644
--- a/test/order_state_watcher_test.ts
+++ b/test/order_state_watcher_test.ts
@@ -15,6 +15,7 @@ import {
ZeroExConfig,
OrderState,
SignedOrder,
+ ZeroExError,
OrderStateValid,
OrderStateInvalid,
ExchangeContractErrs,
@@ -92,14 +93,10 @@ describe('OrderStateWatcher', () => {
afterEach(async () => {
zeroEx.orderStateWatcher.unsubscribe();
});
- it('should fail when trying to subscribe twice', (done: DoneCallback) => {
+ it('should fail when trying to subscribe twice', async () => {
zeroEx.orderStateWatcher.subscribe(_.noop);
- try {
- zeroEx.orderStateWatcher.subscribe(_.noop);
- done(new Error('Expected the second subscription to fail'));
- } catch (err) {
- done();
- }
+ expect(() => zeroEx.orderStateWatcher.subscribe(_.noop))
+ .to.throw(ZeroExError.SubscriptionAlreadyPresent);
});
});
describe('tests with cleanup', async () => {
@@ -355,67 +352,5 @@ describe('OrderStateWatcher', () => {
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
})().catch(done);
});
- describe('check numConfirmations behavior', () => {
- before(() => {
- const configs: ZeroExConfig = {
- orderWatcherConfig: {
- numConfirmations: 1,
- },
- };
- zeroEx = new ZeroEx(web3.currentProvider, configs);
- });
- it('should emit orderState when watching at 1 confirmation deep and event is one block deep',
- (done: DoneCallback) => {
- (async () => {
- fillScenarios = new FillScenarios(
- zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress,
- );
-
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address, takerToken.address, maker, taker, fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
- done();
- });
- zeroEx.orderStateWatcher.subscribe(callback);
-
- const anyRecipient = taker;
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
- blockchainLifecycle.mineABlock();
- })().catch(done);
- });
- it('shouldn\'t emit orderState when watching at 1 confirmation deep and event is in mempool',
- (done: DoneCallback) => {
- (async () => {
- fillScenarios = new FillScenarios(
- zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress,
- );
-
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address, takerToken.address, maker, taker, fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportCallbackErrors(done)((orderState: OrderState) => {
- throw new Error('OrderState callback fired when it shouldn\'t have');
- });
- zeroEx.orderStateWatcher.subscribe(callback);
-
- const anyRecipient = taker;
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
- setTimeout(() => {
- done();
- }, TIMEOUT_MS);
- })().catch(done);
- });
- });
});
});
diff --git a/yarn.lock b/yarn.lock
index 55f3f7d1b..864b87712 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1041,6 +1041,10 @@ chai-typescript-typings@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/chai-typescript-typings/-/chai-typescript-typings-0.0.0.tgz#52e076d72cf29129c94ab1dba6e33ce3828a0724"
+chai-typescript-typings@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/chai-typescript-typings/-/chai-typescript-typings-0.0.1.tgz#433dee303b0b2978ad0dd03129df0a5afb791274"
+
chai@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.0.2.tgz#2f7327c4de6f385dd7787999e2ab02697a32b83b"