From ddbcf5f4706a9d75cd5fd4af508f3c07a78f2886 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 11:11:32 -0500 Subject: Refactor out BalanceAndProxyAllowanceLazyStore --- src/stores/balance_proxy_allowance_lazy_store.ts | 54 +++++++++++++++++++++++ src/utils/exchange_transfer_simulator.ts | 55 +----------------------- 2 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 src/stores/balance_proxy_allowance_lazy_store.ts 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..e8178b9a9 --- /dev/null +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -0,0 +1,54 @@ +import * as _ from 'lodash'; +import {BigNumber} from 'bignumber.js'; +import {TokenWrapper} from '../contract_wrappers/token_wrapper'; + +/** + * Copy on read store for balances/proxyAllowances of tokens/accounts + */ +export class BalanceAndProxyAllowanceLazyStore { + protected 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 = {}; + } + protected async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { + 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; + } + protected async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { + 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; + } +} diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index 89b23c8ab..6812759fc 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -1,7 +1,7 @@ import * as _ from 'lodash'; import BigNumber from 'bignumber.js'; import {ExchangeContractErrs, TradeSide, TransferType} from '../types'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; +import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; enum FailureReason { Balance = 'balance', @@ -31,57 +31,6 @@ 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, - }, - }; - constructor(token: TokenWrapper) { - this._token = token; - this._balance = {}; - this._proxyAllowance = {}; - } - protected async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { - 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; - } - protected async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { - 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 @@ -110,7 +59,7 @@ export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore private async decreaseProxyAllowanceAsync(tokenAddress: string, userAddress: string, amountInBaseUnits: BigNumber): Promise { const proxyAllowance = await this.getProxyAllowanceAsync(tokenAddress, userAddress); - if (!proxyAllowance.eq(this._token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + if (!proxyAllowance.eq(this.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); } } -- cgit v1.2.3 From 742660591f66b1bccd194803a1bf1e43a60a3b31 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 11:15:08 -0500 Subject: Make a store an instance variable of exchange transfer simulator and stop inheriting it --- src/stores/balance_proxy_allowance_lazy_store.ts | 10 ++++----- src/utils/exchange_transfer_simulator.ts | 27 +++++++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index e8178b9a9..7f392cb82 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -6,7 +6,7 @@ import {TokenWrapper} from '../contract_wrappers/token_wrapper'; * Copy on read store for balances/proxyAllowances of tokens/accounts */ export class BalanceAndProxyAllowanceLazyStore { - protected token: TokenWrapper; + private token: TokenWrapper; private balance: { [tokenAddress: string]: { [userAddress: string]: BigNumber, @@ -22,7 +22,7 @@ export class BalanceAndProxyAllowanceLazyStore { this.balance = {}; this.proxyAllowance = {}; } - protected async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { + public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) { const balance = await this.token.getBalanceAsync(tokenAddress, userAddress); this.setBalance(tokenAddress, userAddress, balance); @@ -30,13 +30,13 @@ export class BalanceAndProxyAllowanceLazyStore { const cachedBalance = this.balance[tokenAddress][userAddress]; return cachedBalance; } - protected setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void { + public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void { if (_.isUndefined(this.balance[tokenAddress])) { this.balance[tokenAddress] = {}; } this.balance[tokenAddress][userAddress] = balance; } - protected async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { + public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress); @@ -45,7 +45,7 @@ export class BalanceAndProxyAllowanceLazyStore { const cachedProxyAllowance = this.proxyAllowance[tokenAddress][userAddress]; return cachedProxyAllowance; } - protected setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void { + public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void { if (_.isUndefined(this.proxyAllowance[tokenAddress])) { this.proxyAllowance[tokenAddress] = {}; } diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index 6812759fc..ecdec0be0 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -1,6 +1,7 @@ import * as _ from 'lodash'; import BigNumber from 'bignumber.js'; import {ExchangeContractErrs, TradeSide, TransferType} from '../types'; +import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; enum FailureReason { @@ -31,7 +32,13 @@ const ERR_MSG_MAPPING = { }, }; -export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore { +export class ExchangeTransferSimulator { + private store: BalanceAndProxyAllowanceLazyStore; + private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; + constructor(token: TokenWrapper) { + this.store = new BalanceAndProxyAllowanceLazyStore(token); + this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + } /** * Simulates transferFrom call performed by a proxy * @param tokenAddress Address of the token to be transferred @@ -44,8 +51,8 @@ export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore public async transferFromAsync(tokenAddress: string, from: string, to: string, amountInBaseUnits: BigNumber, tradeSide: TradeSide, transferType: TransferType): Promise { - 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); } @@ -58,20 +65,20 @@ export class ExchangeTransferSimulator extends BalanceAndProxyAllowanceLazyStore } private async decreaseProxyAllowanceAsync(tokenAddress: string, userAddress: string, amountInBaseUnits: BigNumber): Promise { - 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 { - 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 { - 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 { -- cgit v1.2.3 From f163e6d8cc53dbbfd65168c977aa53c5ab9b3c08 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 11:22:05 -0500 Subject: Fix tests --- test/exchange_transfer_simulator_test.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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); -- cgit v1.2.3 From 6edae865168dc86a5a3b39558158d22a4ff3bd99 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 12:12:30 -0500 Subject: Make store configurable by blockParam --- src/stores/balance_proxy_allowance_lazy_store.ts | 15 ++++++++++++--- src/utils/exchange_transfer_simulator.ts | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index 7f392cb82..2580fd449 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -1,4 +1,5 @@ import * as _ from 'lodash'; +import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; @@ -7,6 +8,7 @@ import {TokenWrapper} from '../contract_wrappers/token_wrapper'; */ export class BalanceAndProxyAllowanceLazyStore { private token: TokenWrapper; + private defaultBlock: Web3.BlockParam; private balance: { [tokenAddress: string]: { [userAddress: string]: BigNumber, @@ -17,14 +19,18 @@ export class BalanceAndProxyAllowanceLazyStore { [userAddress: string]: BigNumber, }, }; - constructor(token: TokenWrapper) { + constructor(token: TokenWrapper, defaultBlock: Web3.BlockParam) { this.token = token; + this.defaultBlock = defaultBlock; this.balance = {}; this.proxyAllowance = {}; } public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) { - const balance = await this.token.getBalanceAsync(tokenAddress, userAddress); + const methodOpts = { + defaultBlock: this.defaultBlock, + }; + const balance = await this.token.getBalanceAsync(tokenAddress, userAddress, methodOpts); this.setBalance(tokenAddress, userAddress, balance); } const cachedBalance = this.balance[tokenAddress][userAddress]; @@ -39,7 +45,10 @@ export class BalanceAndProxyAllowanceLazyStore { public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { - const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress); + const methodOpts = { + defaultBlock: this.defaultBlock, + }; + const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts); this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance); } const cachedProxyAllowance = this.proxyAllowance[tokenAddress][userAddress]; diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index ecdec0be0..56bd48f17 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -1,6 +1,6 @@ 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'; @@ -36,7 +36,7 @@ export class ExchangeTransferSimulator { private store: BalanceAndProxyAllowanceLazyStore; private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; constructor(token: TokenWrapper) { - this.store = new BalanceAndProxyAllowanceLazyStore(token); + this.store = new BalanceAndProxyAllowanceLazyStore(token, BlockParamLiteral.Latest); this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; } /** -- cgit v1.2.3 From dcda8fe538a6db82bed4f036ef63bad5882e681a Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 12:12:47 -0500 Subject: Add store for order filled/cancelled state --- src/stores/order_filled_cancelled_lazy_store.ts | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/stores/order_filled_cancelled_lazy_store.ts 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..beb8fe6d7 --- /dev/null +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -0,0 +1,52 @@ +import * as _ from 'lodash'; +import * as Web3 from 'web3'; +import {BigNumber} from 'bignumber.js'; +import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; + +/** + * Copy on read store for filled/cancelled taker amounts + */ +export class OrderFilledCancelledLazyStore { + private exchange: ExchangeWrapper; + private defaultBlock: Web3.BlockParam; + private filledTakerAmount: { + [orderHash: string]: BigNumber, + }; + private cancelledTakerAmount: { + [orderHash: string]: BigNumber, + }; + constructor(exchange: ExchangeWrapper, defaultBlock: Web3.BlockParam) { + this.exchange = exchange; + this.defaultBlock = defaultBlock; + this.filledTakerAmount = {}; + this.cancelledTakerAmount = {}; + } + public async getFilledTakerAmountAsync(orderHash: string): Promise { + if (_.isUndefined(this.filledTakerAmount[orderHash])) { + const methodOpts = { + defaultBlock: this.defaultBlock, + }; + 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 async getCancelledTakerAmountAsync(orderHash: string): Promise { + if (_.isUndefined(this.cancelledTakerAmount[orderHash])) { + const methodOpts = { + defaultBlock: this.defaultBlock, + }; + 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; + } +} -- cgit v1.2.3 From 75b390cf93e0903b1859f02a3f3508bc16a4565a Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 12:22:19 -0500 Subject: Add functions to clear stores cache --- src/stores/balance_proxy_allowance_lazy_store.ts | 10 ++++++++++ src/stores/order_filled_cancelled_lazy_store.ts | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index 2580fd449..92a6aaa51 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -42,6 +42,11 @@ export class BalanceAndProxyAllowanceLazyStore { } this.balance[tokenAddress][userAddress] = balance; } + public deleteBalance(tokenAddress: string, userAddress: string): void { + if (!_.isUndefined(this.balance[tokenAddress])) { + delete this.balance[tokenAddress][userAddress]; + } + } public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { @@ -60,4 +65,9 @@ export class BalanceAndProxyAllowanceLazyStore { } this.proxyAllowance[tokenAddress][userAddress] = proxyAllowance; } + public deleteProxyAllowance(tokenAddress: string, userAddress: string): void { + if (!_.isUndefined(this.proxyAllowance[tokenAddress])) { + delete this.proxyAllowance[tokenAddress][userAddress]; + } + } } diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts index beb8fe6d7..cb4c786f3 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -35,6 +35,9 @@ export class OrderFilledCancelledLazyStore { 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 { if (_.isUndefined(this.cancelledTakerAmount[orderHash])) { const methodOpts = { @@ -49,4 +52,7 @@ export class OrderFilledCancelledLazyStore { public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void { this.cancelledTakerAmount[orderHash] = cancelledTakerAmount; } + public deleteCancelledTakerAmount(orderHash: string): void { + delete this.cancelledTakerAmount[orderHash]; + } } -- cgit v1.2.3 From 4921f61e76d2e6f1b2c8718766a7e93e11ad4671 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:03:04 -0500 Subject: Add LatestBlockNumberNotSet internal error --- src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.ts b/src/types.ts index a366fc31e..8aaa666c3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,7 @@ export enum ZeroExError { export enum InternalZeroExError { NoAbiDecoder = 'NO_ABI_DECODER', ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY', + LatestBlockNumberNotSet = 'LATEST_BLOCK_NUMBER_NOT_SET', } /** -- cgit v1.2.3 From 70436fa535f51eca5c1b5951e1218e72f9a767e0 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:03:53 -0500 Subject: Make stores accept numConfirmations and blockStore instead of defaultBlock --- src/stores/balance_proxy_allowance_lazy_store.ts | 15 ++++++++++----- src/stores/order_filled_cancelled_lazy_store.ts | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index 92a6aaa51..a2b8fcf67 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -2,13 +2,15 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; +import {BlockStore} from './block_store'; /** * Copy on read store for balances/proxyAllowances of tokens/accounts */ export class BalanceAndProxyAllowanceLazyStore { private token: TokenWrapper; - private defaultBlock: Web3.BlockParam; + private numConfirmations: number; + private blockStore: BlockStore; private balance: { [tokenAddress: string]: { [userAddress: string]: BigNumber, @@ -19,16 +21,18 @@ export class BalanceAndProxyAllowanceLazyStore { [userAddress: string]: BigNumber, }, }; - constructor(token: TokenWrapper, defaultBlock: Web3.BlockParam) { + constructor(token: TokenWrapper, blockStore: BlockStore, numConfirmations: number) { this.token = token; - this.defaultBlock = defaultBlock; + this.numConfirmations = numConfirmations; + this.blockStore = blockStore; this.balance = {}; this.proxyAllowance = {}; } public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) { + const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); const methodOpts = { - defaultBlock: this.defaultBlock, + defaultBlock, }; const balance = await this.token.getBalanceAsync(tokenAddress, userAddress, methodOpts); this.setBalance(tokenAddress, userAddress, balance); @@ -50,8 +54,9 @@ export class BalanceAndProxyAllowanceLazyStore { public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { + const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); const methodOpts = { - defaultBlock: this.defaultBlock, + defaultBlock, }; const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts); this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance); diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts index cb4c786f3..978de0b18 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -2,29 +2,33 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; +import {BlockStore} from './block_store'; /** * Copy on read store for filled/cancelled taker amounts */ export class OrderFilledCancelledLazyStore { private exchange: ExchangeWrapper; - private defaultBlock: Web3.BlockParam; + private numConfirmations: number; + private blockStore: BlockStore; private filledTakerAmount: { [orderHash: string]: BigNumber, }; private cancelledTakerAmount: { [orderHash: string]: BigNumber, }; - constructor(exchange: ExchangeWrapper, defaultBlock: Web3.BlockParam) { + constructor(exchange: ExchangeWrapper, blockStore: BlockStore, numConfirmations: number) { this.exchange = exchange; - this.defaultBlock = defaultBlock; + this.numConfirmations = numConfirmations; + this.blockStore = blockStore; this.filledTakerAmount = {}; this.cancelledTakerAmount = {}; } public async getFilledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.filledTakerAmount[orderHash])) { + const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); const methodOpts = { - defaultBlock: this.defaultBlock, + defaultBlock, }; const filledTakerAmount = await this.exchange.getFilledTakerAmountAsync(orderHash, methodOpts); this.setFilledTakerAmount(orderHash, filledTakerAmount); @@ -40,8 +44,9 @@ export class OrderFilledCancelledLazyStore { } public async getCancelledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.cancelledTakerAmount[orderHash])) { + const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); const methodOpts = { - defaultBlock: this.defaultBlock, + defaultBlock, }; const cancelledTakerAmount = await this.exchange.getCanceledTakerAmountAsync(orderHash, methodOpts); this.setCancelledTakerAmount(orderHash, cancelledTakerAmount); -- cgit v1.2.3 From 473ce8b61750103843173518bac599186405f7b3 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:04:14 -0500 Subject: Add initial incomplete BlockStore implementation --- src/stores/block_store.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/stores/block_store.ts diff --git a/src/stores/block_store.ts b/src/stores/block_store.ts new file mode 100644 index 000000000..d1b4d3c54 --- /dev/null +++ b/src/stores/block_store.ts @@ -0,0 +1,38 @@ +import * as _ from 'lodash'; +import * as Web3 from 'web3'; +import {BigNumber} from 'bignumber.js'; +import {BlockParamLiteral, InternalZeroExError} from '../types'; +import {Web3Wrapper} from '../web3_wrapper'; + +/** + * Store for a current latest block number + */ +export class BlockStore { + private web3Wrapper?: Web3Wrapper; + private latestBlockNumber?: number; + constructor(web3Wrapper?: Web3Wrapper) { + this.web3Wrapper = web3Wrapper; + // TODO start a subscription + } + public getBlockNumberWithNConfirmations(numConfirmations: number): Web3.BlockParam { + let blockNumber; + if (numConfirmations === 0) { + blockNumber = BlockParamLiteral.Pending; + } else if (numConfirmations === 1) { + // HACK: We special-case `numConfirmations === 1` so that we can use this block store without actually + // setting `latestBlockNumber` when block number is latest (in order validation) whhich allows us to omit + // an async call in a constructor of `ExchangeTransferSimulator` + blockNumber = BlockParamLiteral.Latest; + } else { + if (_.isUndefined(this.latestBlockNumber)) { + throw new Error(InternalZeroExError.LatestBlockNumberNotSet); + } + // Latest block already has 1 confirmation + blockNumber = this.latestBlockNumber - numConfirmations + 1; + } + return blockNumber; + } + public setLatestBlock(latestBlockNumber: number): void { + this.latestBlockNumber = latestBlockNumber; + } +} -- cgit v1.2.3 From ffcc4877638262b29c8a88535e9535da07428396 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:05:24 -0500 Subject: Create fake blockStore for exchange transfer simulator --- src/utils/exchange_transfer_simulator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index 56bd48f17..cd46397ed 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -3,6 +3,7 @@ import BigNumber from 'bignumber.js'; import {ExchangeContractErrs, TradeSide, TransferType, BlockParamLiteral} from '../types'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; +import {BlockStore} from '../stores/block_store'; enum FailureReason { Balance = 'balance', @@ -36,7 +37,9 @@ export class ExchangeTransferSimulator { private store: BalanceAndProxyAllowanceLazyStore; private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; constructor(token: TokenWrapper) { - this.store = new BalanceAndProxyAllowanceLazyStore(token, BlockParamLiteral.Latest); + const blockStore = new BlockStore(); + const latestBlockConfirmationNumber = 1; + this.store = new BalanceAndProxyAllowanceLazyStore(token, blockStore, latestBlockConfirmationNumber); this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; } /** -- cgit v1.2.3 From e72ba39c41a86cd17a3115426ad579efbfe11ddc Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:07:05 -0500 Subject: Make orderStateUtils operate on stores --- src/order_watcher/order_state_watcher.ts | 9 +----- src/utils/order_state_utils.ts | 53 +++++++++++++++++++------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 4866f8409..ec7e69b72 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -164,16 +164,9 @@ export class OrderStateWatcher { } } private async _emitRevalidateOrdersAsync(orderHashes: string[], blockNumber: number): Promise { - const defaultBlock = this._numConfirmations === 0 ? - BlockParamLiteral.Pending : - blockNumber; - const methodOpts = { - defaultBlock, - }; - for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; - const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder, methodOpts); + const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder); if (!_.isUndefined(this._callbackIfExistsAsync)) { await this._callbackIfExistsAsync(orderState); } else { 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 { - const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder, methodOpts); + public async getOrderStateAsync(signedOrder: SignedOrder): Promise { + 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 { - const zrxTokenAddress = await this.exchangeWrapper.getZRXTokenAddressAsync(); + public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise { + // 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); -- cgit v1.2.3 From 9d3fe1258a53d9e2169b3d733f64d3931fefcce0 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 15:09:22 -0500 Subject: Create stores in orderStateWatcher --- src/0x.ts | 3 +-- src/order_watcher/order_state_watcher.ts | 38 +++++++++++++++++++------------- 2 files changed, 24 insertions(+), 17 deletions(-) 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/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index ec7e69b72..8e0b46eb0 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'; @@ -22,6 +21,11 @@ import { ZeroExError, } from '../types'; import {Web3Wrapper} from '../web3_wrapper'; +import {BlockStore} from '../stores/block_store'; +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 +48,35 @@ interface OrderByOrderHash { export class OrderStateWatcher { private _orderByOrderHash: OrderByOrderHash = {}; private _dependentOrderHashes: DependentOrderHashes = {}; - private _web3Wrapper: Web3Wrapper; private _callbackIfExistsAsync?: OnOrderStateChangeCallback; private _eventWatcher: EventWatcher; 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._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, + web3Wrapper, eventPollingIntervalMs, this._numConfirmations, ); this._abiDecoder = abiDecoder; - this._orderStateUtils = orderStateUtils; + const blockStore = new BlockStore(web3Wrapper); + this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( + token, blockStore, this._numConfirmations, + ); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( + exchange, blockStore, this._numConfirmations, + ); + this._orderStateUtils = new OrderStateUtils( + this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, + ); } /** * Add an order to the orderStateWatcher. Before the order is added, it's @@ -117,11 +130,6 @@ 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; let makerToken: string; let makerAddress: string; @@ -133,7 +141,7 @@ export class OrderStateWatcher { orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]); if (!_.isUndefined(orderHashesSet)) { const orderHashes = Array.from(orderHashesSet); - await this._emitRevalidateOrdersAsync(orderHashes, blockNumber); + await this._emitRevalidateOrdersAsync(orderHashes); } break; @@ -143,7 +151,7 @@ export class OrderStateWatcher { orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]); if (!_.isUndefined(orderHashesSet)) { const orderHashes = Array.from(orderHashesSet); - await this._emitRevalidateOrdersAsync(orderHashes, blockNumber); + await this._emitRevalidateOrdersAsync(orderHashes); } break; @@ -152,7 +160,7 @@ export class OrderStateWatcher { const orderHash = decodedLog.args.orderHash; const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); if (isOrderWatched) { - await this._emitRevalidateOrdersAsync([orderHash], blockNumber); + await this._emitRevalidateOrdersAsync([orderHash]); } break; @@ -163,7 +171,7 @@ export class OrderStateWatcher { throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event); } } - private async _emitRevalidateOrdersAsync(orderHashes: string[], blockNumber: number): Promise { + private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder); -- cgit v1.2.3 From 44c15fc1ef955a4e188708590eb45eebfea60f12 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 16:31:35 -0500 Subject: Add more errors --- src/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types.ts b/src/types.ts index 8aaa666c3..10faa5cf3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,12 +18,14 @@ export enum ZeroExError { SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT', TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT', + FailedToFetchLatestBlock = 'FAILED_TO_FETCH_LATEST_BLOCK', } export enum InternalZeroExError { NoAbiDecoder = 'NO_ABI_DECODER', ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY', LatestBlockNumberNotSet = 'LATEST_BLOCK_NUMBER_NOT_SET', + Web3WrapperRequiredToStartBlockStore = 'WEB3_WRAPPER_REQUIRED_TO_START_BLOCK_STORE', } /** -- cgit v1.2.3 From 61e7b735dcae37195e4b306cab3e4c50cd9c3ba5 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 16:31:48 -0500 Subject: Adjust tests to new interface --- test/order_state_watcher_test.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index 41f938584..880ef0fc3 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) => { - zeroEx.orderStateWatcher.subscribe(_.noop); - try { - zeroEx.orderStateWatcher.subscribe(_.noop); - done(new Error('Expected the second subscription to fail')); - } catch (err) { - done(); - } + it('should fail when trying to subscribe twice', async () => { + await zeroEx.orderStateWatcher.subscribeAsync(_.noop); + return expect(zeroEx.orderStateWatcher.subscribeAsync(_.noop)) + .to.be.rejectedWith(ZeroExError.SubscriptionAlreadyPresent); }); }); describe('tests with cleanup', async () => { @@ -122,7 +119,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); done(); }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); })().catch(done); }); @@ -161,7 +158,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); done(); }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); const anyRecipient = taker; const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance); @@ -186,7 +183,7 @@ describe('OrderStateWatcher', () => { done(); } }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( @@ -223,7 +220,7 @@ describe('OrderStateWatcher', () => { done(); } }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, @@ -324,7 +321,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); done(); }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); @@ -351,7 +348,7 @@ describe('OrderStateWatcher', () => { expect(orderRelevantState.canceledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits); done(); }); - zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.orderStateWatcher.subscribeAsync(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits); })().catch(done); }); -- cgit v1.2.3 From 6bcd9adb9e41ccc45ef5e117df243526bbd281ec Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 16:32:23 -0500 Subject: Make subscribe function async and make blockStore operational --- src/order_watcher/order_state_watcher.ts | 51 +++++++++++++++++++++----------- src/stores/block_store.ts | 29 +++++++++++++++--- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 8e0b46eb0..affb350a3 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -50,11 +50,15 @@ export class OrderStateWatcher { private _dependentOrderHashes: DependentOrderHashes = {}; private _callbackIfExistsAsync?: OnOrderStateChangeCallback; private _eventWatcher: EventWatcher; + private _web3Wrapper: Web3Wrapper; + private _token: TokenWrapper; + private _exchange: ExchangeWrapper; private _abiDecoder: AbiDecoder; - private _orderStateUtils: OrderStateUtils; + private _orderStateUtils?: OrderStateUtils; + private _blockStore?: BlockStore; private _numConfirmations: number; - private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; - private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; + private _orderFilledCancelledLazyStore?: OrderFilledCancelledLazyStore; + private _balanceAndProxyAllowanceLazyStore?: BalanceAndProxyAllowanceLazyStore; constructor( web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, config?: OrderStateWatcherConfig, @@ -67,16 +71,8 @@ export class OrderStateWatcher { web3Wrapper, eventPollingIntervalMs, this._numConfirmations, ); this._abiDecoder = abiDecoder; - const blockStore = new BlockStore(web3Wrapper); - this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - token, blockStore, this._numConfirmations, - ); - this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( - exchange, blockStore, this._numConfirmations, - ); - this._orderStateUtils = new OrderStateUtils( - this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, - ); + this._token = token; + this._exchange = exchange; } /** * Add an order to the orderStateWatcher. Before the order is added, it's @@ -109,11 +105,22 @@ export class OrderStateWatcher { * @param callback Receives the orderHash of the order that should be re-validated, together * with all the order-relevant blockchain state needed to re-validate the order. */ - public subscribe(callback: OnOrderStateChangeCallback): void { + public async subscribeAsync(callback: OnOrderStateChangeCallback): Promise { assert.isFunction('callback', callback); if (!_.isUndefined(this._callbackIfExistsAsync)) { throw new Error(ZeroExError.SubscriptionAlreadyPresent); } + this._blockStore = new BlockStore(this._web3Wrapper); + await this._blockStore.startAsync(); + this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( + this._token, this._blockStore, this._numConfirmations, + ); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( + this._exchange, this._blockStore, this._numConfirmations, + ); + this._orderStateUtils = new OrderStateUtils( + this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, + ); this._callbackIfExistsAsync = callback; this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); } @@ -121,7 +128,15 @@ export class OrderStateWatcher { * Ends an orderStateWatcher subscription. */ public unsubscribe(): void { + if (_.isUndefined(this._blockStore)) { + throw new Error(ZeroExError.SubscriptionNotFound); + } + this._blockStore.stop(); + delete this._blockStore; delete this._callbackIfExistsAsync; + delete this._balanceAndProxyAllowanceLazyStore; + delete this._orderFilledCancelledLazyStore; + delete this._orderStateUtils; this._eventWatcher.unsubscribe(); } private async _onEventWatcherCallbackAsync(log: LogEvent): Promise { @@ -174,12 +189,14 @@ export class OrderStateWatcher { private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; + if (_.isUndefined(this._orderStateUtils)) { + break; // Unsubscribe was called + } const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder); - if (!_.isUndefined(this._callbackIfExistsAsync)) { - await this._callbackIfExistsAsync(orderState); - } else { + if (_.isUndefined(this._callbackIfExistsAsync)) { break; // Unsubscribe was called } + await this._callbackIfExistsAsync(orderState); } } private addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string) { diff --git a/src/stores/block_store.ts b/src/stores/block_store.ts index d1b4d3c54..70798a999 100644 --- a/src/stores/block_store.ts +++ b/src/stores/block_store.ts @@ -1,8 +1,11 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; -import {BlockParamLiteral, InternalZeroExError} from '../types'; +import {BlockParamLiteral, InternalZeroExError, ZeroExError} from '../types'; import {Web3Wrapper} from '../web3_wrapper'; +import {intervalUtils} from '../utils/interval_utils'; + +const POLLING_INTERVAL_MS = 500; /** * Store for a current latest block number @@ -10,9 +13,9 @@ import {Web3Wrapper} from '../web3_wrapper'; export class BlockStore { private web3Wrapper?: Web3Wrapper; private latestBlockNumber?: number; + private intervalId?: NodeJS.Timer; constructor(web3Wrapper?: Web3Wrapper) { this.web3Wrapper = web3Wrapper; - // TODO start a subscription } public getBlockNumberWithNConfirmations(numConfirmations: number): Web3.BlockParam { let blockNumber; @@ -32,7 +35,25 @@ export class BlockStore { } return blockNumber; } - public setLatestBlock(latestBlockNumber: number): void { - this.latestBlockNumber = latestBlockNumber; + public async startAsync(): Promise { + await this.updateLatestBlockAsync(); + this.intervalId = intervalUtils.setAsyncExcludingInterval( + this.updateLatestBlockAsync.bind(this), POLLING_INTERVAL_MS, + ); + } + public stop(): void { + if (!_.isUndefined(this.intervalId)) { + intervalUtils.clearAsyncExcludingInterval(this.intervalId); + } + } + private async updateLatestBlockAsync(): Promise { + if (_.isUndefined(this.web3Wrapper)) { + throw new Error(InternalZeroExError.Web3WrapperRequiredToStartBlockStore); + } + const block = await this.web3Wrapper.getBlockAsync(BlockParamLiteral.Latest); + if (_.isNull(block.number)) { + throw new Error(ZeroExError.FailedToFetchLatestBlock); + } + this.latestBlockNumber = block.number; } } -- cgit v1.2.3 From 81ce4a02290cc38e6dc30873c18b0f9c8c9c4db3 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 17:30:16 -0500 Subject: Add more configs for order watcher --- src/types.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index 10faa5cf3..555fef855 100644 --- a/src/types.ts +++ b/src/types.ts @@ -400,12 +400,14 @@ export interface JSONRPCPayload { } /* - * pollingIntervalMs: How often to poll the Ethereum node for new events. + * eventPollingIntervalMs: How often to poll the Ethereum node for new events + * blockPollingIntervalMs: How often to poll the Ethereum node for new blocks * numConfirmations: How many confirmed blocks deep you wish to listen for events at. */ export interface OrderStateWatcherConfig { - pollingIntervalMs?: number; - numConfirmations: number; + eventPollingIntervalMs?: number; + blockPollingIntervalMs?: number; + numConfirmations?: number; } /* -- cgit v1.2.3 From 009f81fe4f8dd8ed6b671d309553a6d04edd90ca Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 10 Nov 2017 17:30:55 -0500 Subject: Clear store cache on events --- src/order_watcher/order_state_watcher.ts | 101 ++++++++++++++++++++++--------- src/stores/block_store.ts | 8 ++- 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index affb350a3..c4914fc64 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -14,8 +14,13 @@ import { Web3Provider, BlockParamLiteral, LogWithDecodedArgs, + ContractEventArgs, OnOrderStateChangeCallback, OrderStateWatcherConfig, + ApprovalContractEventArgs, + TransferContractEventArgs, + LogFillContractEventArgs, + LogCancelContractEventArgs, ExchangeEvents, TokenEvents, ZeroExError, @@ -54,25 +59,46 @@ export class OrderStateWatcher { private _token: TokenWrapper; private _exchange: ExchangeWrapper; private _abiDecoder: AbiDecoder; - private _orderStateUtils?: OrderStateUtils; - private _blockStore?: BlockStore; + private _orderStateUtils: OrderStateUtils; + private _blockStore: BlockStore; private _numConfirmations: number; - private _orderFilledCancelledLazyStore?: OrderFilledCancelledLazyStore; - private _balanceAndProxyAllowanceLazyStore?: BalanceAndProxyAllowanceLazyStore; + private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; + private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; constructor( web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, config?: OrderStateWatcherConfig, ) { +<<<<<<< HEAD const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.pollingIntervalMs; this._numConfirmations = _.isUndefined(config) ? DEFAULT_NUM_CONFIRMATIONS : config.numConfirmations; - this._eventWatcher = new EventWatcher( - web3Wrapper, eventPollingIntervalMs, this._numConfirmations, - ); +======= + this._orders = {}; this._abiDecoder = abiDecoder; this._token = token; this._exchange = exchange; + this._web3Wrapper = web3Wrapper; + this._dependentOrderHashes = {}; + const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; + const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; + this._numConfirmations = (_.isUndefined(config) || _.isUndefined(config.numConfirmations)) ? + DEFAULT_NUM_CONFIRMATIONS : + config.numConfirmations; +>>>>>>> Clear store cache on events + this._eventWatcher = new EventWatcher( + web3Wrapper, eventPollingIntervalMs, this._numConfirmations, + ); + this._blockStore = new BlockStore(this._web3Wrapper, blockPollingIntervalMs); + this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( + this._token, this._blockStore, this._numConfirmations, + ); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( + this._exchange, this._blockStore, this._numConfirmations, + ); + this._orderStateUtils = new OrderStateUtils( + this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, + ); } /** * Add an order to the orderStateWatcher. Before the order is added, it's @@ -110,17 +136,7 @@ export class OrderStateWatcher { if (!_.isUndefined(this._callbackIfExistsAsync)) { throw new Error(ZeroExError.SubscriptionAlreadyPresent); } - this._blockStore = new BlockStore(this._web3Wrapper); await this._blockStore.startAsync(); - this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._token, this._blockStore, this._numConfirmations, - ); - this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( - this._exchange, this._blockStore, this._numConfirmations, - ); - this._orderStateUtils = new OrderStateUtils( - this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, - ); this._callbackIfExistsAsync = callback; this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); } @@ -128,15 +144,11 @@ export class OrderStateWatcher { * Ends an orderStateWatcher subscription. */ public unsubscribe(): void { - if (_.isUndefined(this._blockStore)) { + if (_.isUndefined(this._callbackIfExistsAsync)) { throw new Error(ZeroExError.SubscriptionNotFound); } this._blockStore.stop(); - delete this._blockStore; delete this._callbackIfExistsAsync; - delete this._balanceAndProxyAllowanceLazyStore; - delete this._orderFilledCancelledLazyStore; - delete this._orderStateUtils; this._eventWatcher.unsubscribe(); } private async _onEventWatcherCallbackAsync(log: LogEvent): Promise { @@ -145,40 +157,69 @@ export class OrderStateWatcher { if (!isLogDecoded) { return; // noop } - const decodedLog = maybeDecodedLog as LogWithDecodedArgs; + const decodedLog = maybeDecodedLog as LogWithDecodedArgs; let makerToken: string; let makerAddress: string; let orderHashesSet: Set; switch (decodedLog.event) { case TokenEvents.Approval: + { + // Invalidate cache + const args = decodedLog.args as ApprovalContractEventArgs; + this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner); 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); } 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); } break; - + } case ExchangeEvents.LogFill: + { + // Invalidate cache + const args = decodedLog.args as LogFillContractEventArgs; + this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); + this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); + // Revalidate orders + const orderHash = args.orderHash; + const isOrderWatched = !_.isUndefined(this._orders[orderHash]); + if (isOrderWatched) { + await this._emitRevalidateOrdersAsync([orderHash]); + } + break; + } case ExchangeEvents.LogCancel: - const orderHash = decodedLog.args.orderHash; - const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); + { + // Invalidate cache + const args = decodedLog.args as LogCancelContractEventArgs; + this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); + this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); + // Revalidate orders + const orderHash = args.orderHash; + const isOrderWatched = !_.isUndefined(this._orders[orderHash]); if (isOrderWatched) { await this._emitRevalidateOrdersAsync([orderHash]); } break; - + } case ExchangeEvents.LogError: return; // noop diff --git a/src/stores/block_store.ts b/src/stores/block_store.ts index 70798a999..d1f6c329a 100644 --- a/src/stores/block_store.ts +++ b/src/stores/block_store.ts @@ -5,7 +5,7 @@ import {BlockParamLiteral, InternalZeroExError, ZeroExError} from '../types'; import {Web3Wrapper} from '../web3_wrapper'; import {intervalUtils} from '../utils/interval_utils'; -const POLLING_INTERVAL_MS = 500; +const DEFAULT_BLOCK_POLLING_INTERVAL_MS = 500; /** * Store for a current latest block number @@ -14,8 +14,10 @@ export class BlockStore { private web3Wrapper?: Web3Wrapper; private latestBlockNumber?: number; private intervalId?: NodeJS.Timer; - constructor(web3Wrapper?: Web3Wrapper) { + private blockPollingIntervalMs: number; + constructor(web3Wrapper?: Web3Wrapper, blockPollingIntervalMs?: number) { this.web3Wrapper = web3Wrapper; + this.blockPollingIntervalMs = blockPollingIntervalMs || DEFAULT_BLOCK_POLLING_INTERVAL_MS; } public getBlockNumberWithNConfirmations(numConfirmations: number): Web3.BlockParam { let blockNumber; @@ -38,7 +40,7 @@ export class BlockStore { public async startAsync(): Promise { await this.updateLatestBlockAsync(); this.intervalId = intervalUtils.setAsyncExcludingInterval( - this.updateLatestBlockAsync.bind(this), POLLING_INTERVAL_MS, + this.updateLatestBlockAsync.bind(this), this.blockPollingIntervalMs, ); } public stop(): void { -- cgit v1.2.3 From 53c918cc785ee3769103acc3e15aa9275c9383d9 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 12:09:14 -0500 Subject: Clear cache on unsubscribe --- src/order_watcher/order_state_watcher.ts | 2 ++ src/stores/balance_proxy_allowance_lazy_store.ts | 4 ++++ src/stores/order_filled_cancelled_lazy_store.ts | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index c4914fc64..a19d6be9c 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -148,6 +148,8 @@ export class OrderStateWatcher { throw new Error(ZeroExError.SubscriptionNotFound); } this._blockStore.stop(); + this._balanceAndProxyAllowanceLazyStore.deleteAll(); + this._orderFilledCancelledLazyStore.deleteAll(); delete this._callbackIfExistsAsync; this._eventWatcher.unsubscribe(); } diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index a2b8fcf67..ac21c0818 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -75,4 +75,8 @@ export class BalanceAndProxyAllowanceLazyStore { delete this.proxyAllowance[tokenAddress][userAddress]; } } + 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 index 978de0b18..5b0dab35c 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -60,4 +60,8 @@ export class OrderFilledCancelledLazyStore { public deleteCancelledTakerAmount(orderHash: string): void { delete this.cancelledTakerAmount[orderHash]; } + public deleteAll(): void { + this.filledTakerAmount = {}; + this.cancelledTakerAmount = {}; + } } -- cgit v1.2.3 From bcad937003fa925878d34e5fa83e5a89369d0957 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 12:26:57 -0500 Subject: Fix last merge conflicts --- src/order_watcher/order_state_watcher.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index a19d6be9c..59de1cc2f 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -68,13 +68,7 @@ export class OrderStateWatcher { web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, config?: OrderStateWatcherConfig, ) { -<<<<<<< HEAD - const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.pollingIntervalMs; - this._numConfirmations = _.isUndefined(config) ? - DEFAULT_NUM_CONFIRMATIONS - : config.numConfirmations; -======= - this._orders = {}; + this._orderByOrderHash = {}; this._abiDecoder = abiDecoder; this._token = token; this._exchange = exchange; @@ -82,10 +76,6 @@ export class OrderStateWatcher { this._dependentOrderHashes = {}; const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; - this._numConfirmations = (_.isUndefined(config) || _.isUndefined(config.numConfirmations)) ? - DEFAULT_NUM_CONFIRMATIONS : - config.numConfirmations; ->>>>>>> Clear store cache on events this._eventWatcher = new EventWatcher( web3Wrapper, eventPollingIntervalMs, this._numConfirmations, ); @@ -202,7 +192,7 @@ export class OrderStateWatcher { this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); // Revalidate orders const orderHash = args.orderHash; - const isOrderWatched = !_.isUndefined(this._orders[orderHash]); + const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); if (isOrderWatched) { await this._emitRevalidateOrdersAsync([orderHash]); } @@ -216,7 +206,7 @@ export class OrderStateWatcher { this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); // Revalidate orders const orderHash = args.orderHash; - const isOrderWatched = !_.isUndefined(this._orders[orderHash]); + const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); if (isOrderWatched) { await this._emitRevalidateOrdersAsync([orderHash]); } -- cgit v1.2.3 From f5608d2c94bcee05a76ef102f235f5e860820567 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 12:53:03 -0500 Subject: Pass blockStore to eventWatcher --- src/order_watcher/event_watcher.ts | 18 ++++++++---------- src/order_watcher/order_state_watcher.ts | 13 ++++++++----- test/event_watcher_test.ts | 7 ++++++- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/order_watcher/event_watcher.ts b/src/order_watcher/event_watcher.ts index c9e72281c..5303bb651 100644 --- a/src/order_watcher/event_watcher.ts +++ b/src/order_watcher/event_watcher.ts @@ -11,6 +11,7 @@ import {AbiDecoder} from '../utils/abi_decoder'; import {intervalUtils} from '../utils/interval_utils'; import {assert} from '../utils/assert'; import {utils} from '../utils/utils'; +import {BlockStore} from '../stores/block_store'; const DEFAULT_EVENT_POLLING_INTERVAL = 200; @@ -29,8 +30,11 @@ export class EventWatcher { private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: Web3.LogEntry[] = []; private _numConfirmations: number; - constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number, numConfirmations: number) { + private _blockStore: BlockStore; + constructor(web3Wrapper: Web3Wrapper, blockStore: BlockStore, pollingIntervalMs: undefined|number, + numConfirmations: number) { this._web3Wrapper = web3Wrapper; + this._blockStore = blockStore; this._numConfirmations = numConfirmations; this._pollingIntervalMs = _.isUndefined(pollingIntervalMs) ? DEFAULT_EVENT_POLLING_INTERVAL : @@ -67,16 +71,10 @@ export class EventWatcher { this._lastEvents = pendingEvents; } private async _getEventsAsync(): Promise { - let latestBlock: BlockParamLiteral|number; - if (this._numConfirmations === 0) { - latestBlock = BlockParamLiteral.Pending; - } else { - const currentBlock = await this._web3Wrapper.getBlockNumberAsync(); - latestBlock = currentBlock - this._numConfirmations; - } + const blockNumber = this._blockStore.getBlockNumberWithNConfirmations(this._numConfirmations); const eventFilter = { - fromBlock: latestBlock, - toBlock: latestBlock, + fromBlock: blockNumber, + toBlock: blockNumber, }; 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 59de1cc2f..685d67455 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -61,7 +61,6 @@ export class OrderStateWatcher { private _abiDecoder: AbiDecoder; private _orderStateUtils: OrderStateUtils; private _blockStore: BlockStore; - private _numConfirmations: number; private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; constructor( @@ -76,15 +75,18 @@ export class OrderStateWatcher { this._dependentOrderHashes = {}; const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; + const numConfirmations = (_.isUndefined(config) || _.isUndefined(config.numConfirmations)) ? + DEFAULT_NUM_CONFIRMATIONS : + config.numConfirmations; + this._blockStore = new BlockStore(this._web3Wrapper, blockPollingIntervalMs); this._eventWatcher = new EventWatcher( - web3Wrapper, eventPollingIntervalMs, this._numConfirmations, + web3Wrapper, this._blockStore, eventPollingIntervalMs, numConfirmations, ); - this._blockStore = new BlockStore(this._web3Wrapper, blockPollingIntervalMs); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._token, this._blockStore, this._numConfirmations, + this._token, this._blockStore, numConfirmations, ); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( - this._exchange, this._blockStore, this._numConfirmations, + this._exchange, this._blockStore, numConfirmations, ); this._orderStateUtils = new OrderStateUtils( this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, @@ -159,6 +161,7 @@ export class OrderStateWatcher { // Invalidate cache const args = decodedLog.args as ApprovalContractEventArgs; this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner); + // Revalidate orders makerToken = decodedLog.address; makerAddress = args._owner; orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]); diff --git a/test/event_watcher_test.ts b/test/event_watcher_test.ts index 98dab93b5..0a1e7eb63 100644 --- a/test/event_watcher_test.ts +++ b/test/event_watcher_test.ts @@ -8,6 +8,7 @@ import {chaiSetup} from './utils/chai_setup'; import {web3Factory} from './utils/web3_factory'; import {Web3Wrapper} from '../src/web3_wrapper'; import {EventWatcher} from '../src/order_watcher/event_watcher'; +import {BlockStore} from '../src/stores/block_store'; import { ZeroEx, LogEvent, @@ -23,6 +24,7 @@ describe('EventWatcher', () => { let stubs: Sinon.SinonStub[] = []; let eventWatcher: EventWatcher; let web3Wrapper: Web3Wrapper; + let blockStore: BlockStore; const numConfirmations = 0; const logA: Web3.LogEntry = { address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5', @@ -58,12 +60,15 @@ describe('EventWatcher', () => { web3 = web3Factory.create(); const pollingIntervalMs = 10; web3Wrapper = new Web3Wrapper(web3.currentProvider); - eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs, numConfirmations); + blockStore = new BlockStore(); + await blockStore.startAsync(); + eventWatcher = new EventWatcher(web3Wrapper, blockStore, pollingIntervalMs, numConfirmations); }); afterEach(() => { // clean up any stubs after the test has completed _.each(stubs, s => s.restore()); stubs = []; + blockStore.stop(); eventWatcher.unsubscribe(); }); it('correctly emits initial log events', (done: DoneCallback) => { -- cgit v1.2.3 From d4dc428124a210cc6b8e1c50527a08e902bfadd0 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 12:55:51 -0500 Subject: Remove tautology check --- src/order_watcher/order_state_watcher.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 685d67455..e6250cef5 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -225,9 +225,6 @@ export class OrderStateWatcher { private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; - if (_.isUndefined(this._orderStateUtils)) { - break; // Unsubscribe was called - } const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder); if (_.isUndefined(this._callbackIfExistsAsync)) { break; // Unsubscribe was called -- cgit v1.2.3 From a9ae555b88cc36ff2cbd92fdd37a339702860c01 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 13:06:25 -0500 Subject: Store number of confirmations in a blockStore --- src/order_watcher/event_watcher.ts | 7 ++----- src/order_watcher/order_state_watcher.ts | 8 ++++---- src/stores/balance_proxy_allowance_lazy_store.ts | 8 +++----- src/stores/block_store.ts | 15 ++++++++++----- src/stores/order_filled_cancelled_lazy_store.ts | 8 +++----- src/utils/exchange_transfer_simulator.ts | 4 ++-- test/event_watcher_test.ts | 6 ++---- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/order_watcher/event_watcher.ts b/src/order_watcher/event_watcher.ts index 5303bb651..7e27d5e79 100644 --- a/src/order_watcher/event_watcher.ts +++ b/src/order_watcher/event_watcher.ts @@ -29,13 +29,10 @@ export class EventWatcher { private _pollingIntervalMs: number; private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: Web3.LogEntry[] = []; - private _numConfirmations: number; private _blockStore: BlockStore; - constructor(web3Wrapper: Web3Wrapper, blockStore: BlockStore, pollingIntervalMs: undefined|number, - numConfirmations: number) { + constructor(web3Wrapper: Web3Wrapper, blockStore: BlockStore, pollingIntervalMs: undefined|number) { this._web3Wrapper = web3Wrapper; this._blockStore = blockStore; - this._numConfirmations = numConfirmations; this._pollingIntervalMs = _.isUndefined(pollingIntervalMs) ? DEFAULT_EVENT_POLLING_INTERVAL : pollingIntervalMs; @@ -71,7 +68,7 @@ export class EventWatcher { this._lastEvents = pendingEvents; } private async _getEventsAsync(): Promise { - const blockNumber = this._blockStore.getBlockNumberWithNConfirmations(this._numConfirmations); + const blockNumber = this._blockStore.getBlockNumber(); const eventFilter = { fromBlock: blockNumber, toBlock: blockNumber, diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index e6250cef5..0f5b5c30c 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -78,15 +78,15 @@ export class OrderStateWatcher { const numConfirmations = (_.isUndefined(config) || _.isUndefined(config.numConfirmations)) ? DEFAULT_NUM_CONFIRMATIONS : config.numConfirmations; - this._blockStore = new BlockStore(this._web3Wrapper, blockPollingIntervalMs); + this._blockStore = new BlockStore(numConfirmations, this._web3Wrapper, blockPollingIntervalMs); this._eventWatcher = new EventWatcher( - web3Wrapper, this._blockStore, eventPollingIntervalMs, numConfirmations, + web3Wrapper, this._blockStore, eventPollingIntervalMs, ); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._token, this._blockStore, numConfirmations, + this._token, this._blockStore, ); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( - this._exchange, this._blockStore, numConfirmations, + this._exchange, this._blockStore, ); this._orderStateUtils = new OrderStateUtils( this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index ac21c0818..88152e145 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -9,7 +9,6 @@ import {BlockStore} from './block_store'; */ export class BalanceAndProxyAllowanceLazyStore { private token: TokenWrapper; - private numConfirmations: number; private blockStore: BlockStore; private balance: { [tokenAddress: string]: { @@ -21,16 +20,15 @@ export class BalanceAndProxyAllowanceLazyStore { [userAddress: string]: BigNumber, }, }; - constructor(token: TokenWrapper, blockStore: BlockStore, numConfirmations: number) { + constructor(token: TokenWrapper, blockStore: BlockStore) { this.token = token; - this.numConfirmations = numConfirmations; this.blockStore = blockStore; this.balance = {}; this.proxyAllowance = {}; } public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) { - const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); + const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { defaultBlock, }; @@ -54,7 +52,7 @@ export class BalanceAndProxyAllowanceLazyStore { public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { - const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); + const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { defaultBlock, }; diff --git a/src/stores/block_store.ts b/src/stores/block_store.ts index d1f6c329a..ae9c232bb 100644 --- a/src/stores/block_store.ts +++ b/src/stores/block_store.ts @@ -15,15 +15,17 @@ export class BlockStore { private latestBlockNumber?: number; private intervalId?: NodeJS.Timer; private blockPollingIntervalMs: number; - constructor(web3Wrapper?: Web3Wrapper, blockPollingIntervalMs?: number) { + private numConfirmations: number; + constructor(numConfirmations: number, web3Wrapper?: Web3Wrapper, blockPollingIntervalMs?: number) { + this.numConfirmations = numConfirmations; this.web3Wrapper = web3Wrapper; this.blockPollingIntervalMs = blockPollingIntervalMs || DEFAULT_BLOCK_POLLING_INTERVAL_MS; } - public getBlockNumberWithNConfirmations(numConfirmations: number): Web3.BlockParam { + public getBlockNumber(): Web3.BlockParam { let blockNumber; - if (numConfirmations === 0) { + if (this.numConfirmations === 0) { blockNumber = BlockParamLiteral.Pending; - } else if (numConfirmations === 1) { + } else if (this.numConfirmations === 1) { // HACK: We special-case `numConfirmations === 1` so that we can use this block store without actually // setting `latestBlockNumber` when block number is latest (in order validation) whhich allows us to omit // an async call in a constructor of `ExchangeTransferSimulator` @@ -33,11 +35,14 @@ export class BlockStore { throw new Error(InternalZeroExError.LatestBlockNumberNotSet); } // Latest block already has 1 confirmation - blockNumber = this.latestBlockNumber - numConfirmations + 1; + blockNumber = this.latestBlockNumber - this.numConfirmations + 1; } return blockNumber; } public async startAsync(): Promise { + if (this.numConfirmations === 0 || this.numConfirmations === 1) { + return; // no-op + } await this.updateLatestBlockAsync(); this.intervalId = intervalUtils.setAsyncExcludingInterval( this.updateLatestBlockAsync.bind(this), this.blockPollingIntervalMs, diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts index 5b0dab35c..104a8240e 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -9,7 +9,6 @@ import {BlockStore} from './block_store'; */ export class OrderFilledCancelledLazyStore { private exchange: ExchangeWrapper; - private numConfirmations: number; private blockStore: BlockStore; private filledTakerAmount: { [orderHash: string]: BigNumber, @@ -17,16 +16,15 @@ export class OrderFilledCancelledLazyStore { private cancelledTakerAmount: { [orderHash: string]: BigNumber, }; - constructor(exchange: ExchangeWrapper, blockStore: BlockStore, numConfirmations: number) { + constructor(exchange: ExchangeWrapper, blockStore: BlockStore) { this.exchange = exchange; - this.numConfirmations = numConfirmations; this.blockStore = blockStore; this.filledTakerAmount = {}; this.cancelledTakerAmount = {}; } public async getFilledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.filledTakerAmount[orderHash])) { - const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); + const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { defaultBlock, }; @@ -44,7 +42,7 @@ export class OrderFilledCancelledLazyStore { } public async getCancelledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.cancelledTakerAmount[orderHash])) { - const defaultBlock = this.blockStore.getBlockNumberWithNConfirmations(this.numConfirmations); + const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { defaultBlock, }; diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index cd46397ed..475dcf953 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -37,9 +37,9 @@ export class ExchangeTransferSimulator { private store: BalanceAndProxyAllowanceLazyStore; private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; constructor(token: TokenWrapper) { - const blockStore = new BlockStore(); const latestBlockConfirmationNumber = 1; - this.store = new BalanceAndProxyAllowanceLazyStore(token, blockStore, latestBlockConfirmationNumber); + const blockStore = new BlockStore(latestBlockConfirmationNumber); + this.store = new BalanceAndProxyAllowanceLazyStore(token, blockStore); this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; } /** diff --git a/test/event_watcher_test.ts b/test/event_watcher_test.ts index 0a1e7eb63..2673f978b 100644 --- a/test/event_watcher_test.ts +++ b/test/event_watcher_test.ts @@ -60,15 +60,13 @@ describe('EventWatcher', () => { web3 = web3Factory.create(); const pollingIntervalMs = 10; web3Wrapper = new Web3Wrapper(web3.currentProvider); - blockStore = new BlockStore(); - await blockStore.startAsync(); - eventWatcher = new EventWatcher(web3Wrapper, blockStore, pollingIntervalMs, numConfirmations); + blockStore = new BlockStore(numConfirmations); + eventWatcher = new EventWatcher(web3Wrapper, blockStore, pollingIntervalMs); }); afterEach(() => { // clean up any stubs after the test has completed _.each(stubs, s => s.restore()); stubs = []; - blockStore.stop(); eventWatcher.unsubscribe(); }); it('correctly emits initial log events', (done: DoneCallback) => { -- cgit v1.2.3 From 22cd6989a0217f2a49e59ce64bcc69b2c238aba4 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 15:00:48 -0500 Subject: Add a comment --- src/order_watcher/order_state_watcher.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 0f5b5c30c..ab80f231f 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -225,6 +225,8 @@ export class OrderStateWatcher { private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; + // Most of those 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 -- cgit v1.2.3 From 84c965d459b948eb03cb8a70a66663bd7b35f463 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:10:47 -0500 Subject: Remove blockStore and default to numConfirmations === 0 --- src/order_watcher/event_watcher.ts | 10 ++-- src/order_watcher/order_state_watcher.ts | 20 ++----- src/stores/balance_proxy_allowance_lazy_store.ts | 12 ++--- src/stores/block_store.ts | 66 ------------------------ src/stores/order_filled_cancelled_lazy_store.ts | 12 ++--- src/types.ts | 2 - src/utils/exchange_transfer_simulator.ts | 5 +- test/event_watcher_test.ts | 5 +- test/order_state_watcher_test.ts | 16 +++--- 9 files changed, 25 insertions(+), 123 deletions(-) delete mode 100644 src/stores/block_store.ts diff --git a/src/order_watcher/event_watcher.ts b/src/order_watcher/event_watcher.ts index 7e27d5e79..81529a98c 100644 --- a/src/order_watcher/event_watcher.ts +++ b/src/order_watcher/event_watcher.ts @@ -11,7 +11,6 @@ import {AbiDecoder} from '../utils/abi_decoder'; import {intervalUtils} from '../utils/interval_utils'; import {assert} from '../utils/assert'; import {utils} from '../utils/utils'; -import {BlockStore} from '../stores/block_store'; const DEFAULT_EVENT_POLLING_INTERVAL = 200; @@ -29,10 +28,8 @@ export class EventWatcher { private _pollingIntervalMs: number; private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: Web3.LogEntry[] = []; - private _blockStore: BlockStore; - constructor(web3Wrapper: Web3Wrapper, blockStore: BlockStore, pollingIntervalMs: undefined|number) { + constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number) { this._web3Wrapper = web3Wrapper; - this._blockStore = blockStore; this._pollingIntervalMs = _.isUndefined(pollingIntervalMs) ? DEFAULT_EVENT_POLLING_INTERVAL : pollingIntervalMs; @@ -68,10 +65,9 @@ export class EventWatcher { this._lastEvents = pendingEvents; } private async _getEventsAsync(): Promise { - const blockNumber = this._blockStore.getBlockNumber(); const eventFilter = { - fromBlock: blockNumber, - toBlock: blockNumber, + 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 ab80f231f..c40131bcb 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -26,7 +26,6 @@ import { ZeroExError, } from '../types'; import {Web3Wrapper} from '../web3_wrapper'; -import {BlockStore} from '../stores/block_store'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store'; @@ -60,7 +59,6 @@ export class OrderStateWatcher { private _exchange: ExchangeWrapper; private _abiDecoder: AbiDecoder; private _orderStateUtils: OrderStateUtils; - private _blockStore: BlockStore; private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; constructor( @@ -75,19 +73,11 @@ export class OrderStateWatcher { this._dependentOrderHashes = {}; const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; - const numConfirmations = (_.isUndefined(config) || _.isUndefined(config.numConfirmations)) ? - DEFAULT_NUM_CONFIRMATIONS : - config.numConfirmations; - this._blockStore = new BlockStore(numConfirmations, this._web3Wrapper, blockPollingIntervalMs); - this._eventWatcher = new EventWatcher( - web3Wrapper, this._blockStore, eventPollingIntervalMs, - ); + this._eventWatcher = new EventWatcher(web3Wrapper, eventPollingIntervalMs); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._token, this._blockStore, - ); - this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore( - this._exchange, this._blockStore, + this._token, ); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(this._exchange); this._orderStateUtils = new OrderStateUtils( this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, ); @@ -123,12 +113,11 @@ export class OrderStateWatcher { * @param callback Receives the orderHash of the order that should be re-validated, together * with all the order-relevant blockchain state needed to re-validate the order. */ - public async subscribeAsync(callback: OnOrderStateChangeCallback): Promise { + public subscribe(callback: OnOrderStateChangeCallback): void { assert.isFunction('callback', callback); if (!_.isUndefined(this._callbackIfExistsAsync)) { throw new Error(ZeroExError.SubscriptionAlreadyPresent); } - await this._blockStore.startAsync(); this._callbackIfExistsAsync = callback; this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); } @@ -139,7 +128,6 @@ export class OrderStateWatcher { if (_.isUndefined(this._callbackIfExistsAsync)) { throw new Error(ZeroExError.SubscriptionNotFound); } - this._blockStore.stop(); this._balanceAndProxyAllowanceLazyStore.deleteAll(); this._orderFilledCancelledLazyStore.deleteAll(); delete this._callbackIfExistsAsync; diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index 88152e145..5c54fbb3b 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -2,14 +2,13 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; -import {BlockStore} from './block_store'; +import {BlockParamLiteral} from '../types'; /** * Copy on read store for balances/proxyAllowances of tokens/accounts */ export class BalanceAndProxyAllowanceLazyStore { private token: TokenWrapper; - private blockStore: BlockStore; private balance: { [tokenAddress: string]: { [userAddress: string]: BigNumber, @@ -20,17 +19,15 @@ export class BalanceAndProxyAllowanceLazyStore { [userAddress: string]: BigNumber, }, }; - constructor(token: TokenWrapper, blockStore: BlockStore) { + constructor(token: TokenWrapper) { this.token = token; - this.blockStore = blockStore; this.balance = {}; this.proxyAllowance = {}; } public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.balance[tokenAddress]) || _.isUndefined(this.balance[tokenAddress][userAddress])) { - const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { - defaultBlock, + defaultBlock: BlockParamLiteral.Pending, }; const balance = await this.token.getBalanceAsync(tokenAddress, userAddress, methodOpts); this.setBalance(tokenAddress, userAddress, balance); @@ -52,9 +49,8 @@ export class BalanceAndProxyAllowanceLazyStore { public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { if (_.isUndefined(this.proxyAllowance[tokenAddress]) || _.isUndefined(this.proxyAllowance[tokenAddress][userAddress])) { - const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { - defaultBlock, + defaultBlock: BlockParamLiteral.Pending, }; const proxyAllowance = await this.token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts); this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance); diff --git a/src/stores/block_store.ts b/src/stores/block_store.ts deleted file mode 100644 index ae9c232bb..000000000 --- a/src/stores/block_store.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as _ from 'lodash'; -import * as Web3 from 'web3'; -import {BigNumber} from 'bignumber.js'; -import {BlockParamLiteral, InternalZeroExError, ZeroExError} from '../types'; -import {Web3Wrapper} from '../web3_wrapper'; -import {intervalUtils} from '../utils/interval_utils'; - -const DEFAULT_BLOCK_POLLING_INTERVAL_MS = 500; - -/** - * Store for a current latest block number - */ -export class BlockStore { - private web3Wrapper?: Web3Wrapper; - private latestBlockNumber?: number; - private intervalId?: NodeJS.Timer; - private blockPollingIntervalMs: number; - private numConfirmations: number; - constructor(numConfirmations: number, web3Wrapper?: Web3Wrapper, blockPollingIntervalMs?: number) { - this.numConfirmations = numConfirmations; - this.web3Wrapper = web3Wrapper; - this.blockPollingIntervalMs = blockPollingIntervalMs || DEFAULT_BLOCK_POLLING_INTERVAL_MS; - } - public getBlockNumber(): Web3.BlockParam { - let blockNumber; - if (this.numConfirmations === 0) { - blockNumber = BlockParamLiteral.Pending; - } else if (this.numConfirmations === 1) { - // HACK: We special-case `numConfirmations === 1` so that we can use this block store without actually - // setting `latestBlockNumber` when block number is latest (in order validation) whhich allows us to omit - // an async call in a constructor of `ExchangeTransferSimulator` - blockNumber = BlockParamLiteral.Latest; - } else { - if (_.isUndefined(this.latestBlockNumber)) { - throw new Error(InternalZeroExError.LatestBlockNumberNotSet); - } - // Latest block already has 1 confirmation - blockNumber = this.latestBlockNumber - this.numConfirmations + 1; - } - return blockNumber; - } - public async startAsync(): Promise { - if (this.numConfirmations === 0 || this.numConfirmations === 1) { - return; // no-op - } - await this.updateLatestBlockAsync(); - this.intervalId = intervalUtils.setAsyncExcludingInterval( - this.updateLatestBlockAsync.bind(this), this.blockPollingIntervalMs, - ); - } - public stop(): void { - if (!_.isUndefined(this.intervalId)) { - intervalUtils.clearAsyncExcludingInterval(this.intervalId); - } - } - private async updateLatestBlockAsync(): Promise { - if (_.isUndefined(this.web3Wrapper)) { - throw new Error(InternalZeroExError.Web3WrapperRequiredToStartBlockStore); - } - const block = await this.web3Wrapper.getBlockAsync(BlockParamLiteral.Latest); - if (_.isNull(block.number)) { - throw new Error(ZeroExError.FailedToFetchLatestBlock); - } - this.latestBlockNumber = block.number; - } -} diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts index 104a8240e..39adff4d1 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -2,31 +2,28 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import {BigNumber} from 'bignumber.js'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {BlockStore} from './block_store'; +import {BlockParamLiteral} from '../types'; /** * Copy on read store for filled/cancelled taker amounts */ export class OrderFilledCancelledLazyStore { private exchange: ExchangeWrapper; - private blockStore: BlockStore; private filledTakerAmount: { [orderHash: string]: BigNumber, }; private cancelledTakerAmount: { [orderHash: string]: BigNumber, }; - constructor(exchange: ExchangeWrapper, blockStore: BlockStore) { + constructor(exchange: ExchangeWrapper) { this.exchange = exchange; - this.blockStore = blockStore; this.filledTakerAmount = {}; this.cancelledTakerAmount = {}; } public async getFilledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.filledTakerAmount[orderHash])) { - const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { - defaultBlock, + defaultBlock: BlockParamLiteral.Pending, }; const filledTakerAmount = await this.exchange.getFilledTakerAmountAsync(orderHash, methodOpts); this.setFilledTakerAmount(orderHash, filledTakerAmount); @@ -42,9 +39,8 @@ export class OrderFilledCancelledLazyStore { } public async getCancelledTakerAmountAsync(orderHash: string): Promise { if (_.isUndefined(this.cancelledTakerAmount[orderHash])) { - const defaultBlock = this.blockStore.getBlockNumber(); const methodOpts = { - defaultBlock, + defaultBlock: BlockParamLiteral.Pending, }; const cancelledTakerAmount = await this.exchange.getCanceledTakerAmountAsync(orderHash, methodOpts); this.setCancelledTakerAmount(orderHash, cancelledTakerAmount); diff --git a/src/types.ts b/src/types.ts index 555fef855..b34dbfb4e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -402,12 +402,10 @@ export interface JSONRPCPayload { /* * eventPollingIntervalMs: How often to poll the Ethereum node for new events * blockPollingIntervalMs: How often to poll the Ethereum node for new blocks - * numConfirmations: How many confirmed blocks deep you wish to listen for events at. */ export interface OrderStateWatcherConfig { eventPollingIntervalMs?: number; blockPollingIntervalMs?: number; - numConfirmations?: number; } /* diff --git a/src/utils/exchange_transfer_simulator.ts b/src/utils/exchange_transfer_simulator.ts index 475dcf953..308ef06db 100644 --- a/src/utils/exchange_transfer_simulator.ts +++ b/src/utils/exchange_transfer_simulator.ts @@ -3,7 +3,6 @@ import BigNumber from 'bignumber.js'; import {ExchangeContractErrs, TradeSide, TransferType, BlockParamLiteral} from '../types'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; -import {BlockStore} from '../stores/block_store'; enum FailureReason { Balance = 'balance', @@ -37,9 +36,7 @@ export class ExchangeTransferSimulator { private store: BalanceAndProxyAllowanceLazyStore; private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; constructor(token: TokenWrapper) { - const latestBlockConfirmationNumber = 1; - const blockStore = new BlockStore(latestBlockConfirmationNumber); - this.store = new BalanceAndProxyAllowanceLazyStore(token, blockStore); + this.store = new BalanceAndProxyAllowanceLazyStore(token); this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; } /** diff --git a/test/event_watcher_test.ts b/test/event_watcher_test.ts index 2673f978b..b4164fe63 100644 --- a/test/event_watcher_test.ts +++ b/test/event_watcher_test.ts @@ -8,7 +8,6 @@ import {chaiSetup} from './utils/chai_setup'; import {web3Factory} from './utils/web3_factory'; import {Web3Wrapper} from '../src/web3_wrapper'; import {EventWatcher} from '../src/order_watcher/event_watcher'; -import {BlockStore} from '../src/stores/block_store'; import { ZeroEx, LogEvent, @@ -24,7 +23,6 @@ describe('EventWatcher', () => { let stubs: Sinon.SinonStub[] = []; let eventWatcher: EventWatcher; let web3Wrapper: Web3Wrapper; - let blockStore: BlockStore; const numConfirmations = 0; const logA: Web3.LogEntry = { address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5', @@ -60,8 +58,7 @@ describe('EventWatcher', () => { web3 = web3Factory.create(); const pollingIntervalMs = 10; web3Wrapper = new Web3Wrapper(web3.currentProvider); - blockStore = new BlockStore(numConfirmations); - eventWatcher = new EventWatcher(web3Wrapper, blockStore, pollingIntervalMs); + eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs); }); afterEach(() => { // clean up any stubs after the test has completed diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index 880ef0fc3..76ecd5dd4 100644 --- a/test/order_state_watcher_test.ts +++ b/test/order_state_watcher_test.ts @@ -94,8 +94,8 @@ describe('OrderStateWatcher', () => { zeroEx.orderStateWatcher.unsubscribe(); }); it('should fail when trying to subscribe twice', async () => { - await zeroEx.orderStateWatcher.subscribeAsync(_.noop); - return expect(zeroEx.orderStateWatcher.subscribeAsync(_.noop)) + await zeroEx.orderStateWatcher.subscribe(_.noop); + return expect(zeroEx.orderStateWatcher.subscribe(_.noop)) .to.be.rejectedWith(ZeroExError.SubscriptionAlreadyPresent); }); }); @@ -119,7 +119,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); done(); }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + await zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); })().catch(done); }); @@ -158,7 +158,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); done(); }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + 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); @@ -183,7 +183,7 @@ describe('OrderStateWatcher', () => { done(); } }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + zeroEx.orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( @@ -220,7 +220,7 @@ describe('OrderStateWatcher', () => { done(); } }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + zeroEx.orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, @@ -321,7 +321,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); done(); }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + zeroEx.orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); @@ -348,7 +348,7 @@ describe('OrderStateWatcher', () => { expect(orderRelevantState.canceledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits); done(); }); - await zeroEx.orderStateWatcher.subscribeAsync(callback); + zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits); })().catch(done); }); -- cgit v1.2.3 From d52825a5b18b0ac7591ca2f32a5062e949e4a65a Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:52:22 -0500 Subject: Fix tests --- test/order_state_watcher_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index 76ecd5dd4..df007588e 100644 --- a/test/order_state_watcher_test.ts +++ b/test/order_state_watcher_test.ts @@ -96,7 +96,7 @@ describe('OrderStateWatcher', () => { it('should fail when trying to subscribe twice', async () => { await zeroEx.orderStateWatcher.subscribe(_.noop); return expect(zeroEx.orderStateWatcher.subscribe(_.noop)) - .to.be.rejectedWith(ZeroExError.SubscriptionAlreadyPresent); + .to.be.eventually.rejectedWith(ZeroExError.SubscriptionAlreadyPresent); }); }); describe('tests with cleanup', async () => { -- cgit v1.2.3 From 3204c077d1f2095ceda3f2cd91735e3b7db66df2 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:53:21 -0500 Subject: Remove redundant instance variables --- src/order_watcher/order_state_watcher.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index c40131bcb..3e8fd7e3a 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -55,8 +55,6 @@ export class OrderStateWatcher { private _callbackIfExistsAsync?: OnOrderStateChangeCallback; private _eventWatcher: EventWatcher; private _web3Wrapper: Web3Wrapper; - private _token: TokenWrapper; - private _exchange: ExchangeWrapper; private _abiDecoder: AbiDecoder; private _orderStateUtils: OrderStateUtils; private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; @@ -67,17 +65,13 @@ export class OrderStateWatcher { ) { this._orderByOrderHash = {}; this._abiDecoder = abiDecoder; - this._token = token; - this._exchange = exchange; this._web3Wrapper = web3Wrapper; this._dependentOrderHashes = {}; const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; this._eventWatcher = new EventWatcher(web3Wrapper, eventPollingIntervalMs); - this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._token, - ); - this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(this._exchange); + this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(token); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); this._orderStateUtils = new OrderStateUtils( this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, ); -- cgit v1.2.3 From a5876978838baa9a66c2dd9714dd2810839aa55b Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:54:12 -0500 Subject: Remove duplicate operations --- src/order_watcher/order_state_watcher.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 3e8fd7e3a..3c09a2743 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -174,7 +174,6 @@ export class OrderStateWatcher { // Invalidate cache const args = decodedLog.args as LogFillContractEventArgs; this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); - this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); // Revalidate orders const orderHash = args.orderHash; const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); @@ -188,7 +187,6 @@ export class OrderStateWatcher { // Invalidate cache const args = decodedLog.args as LogCancelContractEventArgs; this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); - this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); // Revalidate orders const orderHash = args.orderHash; const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); -- cgit v1.2.3 From fdb82d5dd4ea14e35ee03cf9abca6105fa1e0595 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:54:31 -0500 Subject: Fix a typo --- src/order_watcher/order_state_watcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 3c09a2743..b69540716 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -205,7 +205,7 @@ export class OrderStateWatcher { private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; - // Most of those calls will never reach the network because the data is fetched from stores + // 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)) { -- cgit v1.2.3 From 7b50a6490db820b4e7f7b972ae0dcb602e5118b6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:55:42 -0500 Subject: Don't store empty objects --- src/stores/balance_proxy_allowance_lazy_store.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stores/balance_proxy_allowance_lazy_store.ts b/src/stores/balance_proxy_allowance_lazy_store.ts index 5c54fbb3b..c83e61606 100644 --- a/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/src/stores/balance_proxy_allowance_lazy_store.ts @@ -44,6 +44,9 @@ export class BalanceAndProxyAllowanceLazyStore { 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 { @@ -67,6 +70,9 @@ export class BalanceAndProxyAllowanceLazyStore { 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 { -- cgit v1.2.3 From 610298a25d5a5d1bb7a6baa407f169a44e7695bf Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 18:56:09 -0500 Subject: Remove redundant spaces --- src/stores/order_filled_cancelled_lazy_store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/order_filled_cancelled_lazy_store.ts b/src/stores/order_filled_cancelled_lazy_store.ts index 39adff4d1..9d74da096 100644 --- a/src/stores/order_filled_cancelled_lazy_store.ts +++ b/src/stores/order_filled_cancelled_lazy_store.ts @@ -5,7 +5,7 @@ import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; import {BlockParamLiteral} from '../types'; /** - * Copy on read store for filled/cancelled taker amounts + * Copy on read store for filled/cancelled taker amounts */ export class OrderFilledCancelledLazyStore { private exchange: ExchangeWrapper; -- cgit v1.2.3 From d73fb5a23c0e23eaec90aee0b563c06fa8f380d6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 19:24:58 -0500 Subject: Fix tests --- test/order_state_watcher_test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index df007588e..aa387f495 100644 --- a/test/order_state_watcher_test.ts +++ b/test/order_state_watcher_test.ts @@ -94,9 +94,9 @@ describe('OrderStateWatcher', () => { zeroEx.orderStateWatcher.unsubscribe(); }); it('should fail when trying to subscribe twice', async () => { - await zeroEx.orderStateWatcher.subscribe(_.noop); - return expect(zeroEx.orderStateWatcher.subscribe(_.noop)) - .to.be.eventually.rejectedWith(ZeroExError.SubscriptionAlreadyPresent); + zeroEx.orderStateWatcher.subscribe(_.noop); + expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)) + .to.throw(); }); }); describe('tests with cleanup', async () => { @@ -119,7 +119,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); done(); }); - await zeroEx.orderStateWatcher.subscribe(callback); + zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); })().catch(done); }); -- cgit v1.2.3 From 7ea0b138bc7a61037636eb5b4d2ad9b995a27f79 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 20:02:54 -0500 Subject: Remove unused code --- package.json | 2 +- src/order_watcher/order_state_watcher.ts | 3 --- src/types.ts | 5 ----- test/order_state_watcher_test.ts | 2 +- yarn.lock | 4 ++++ 5 files changed, 6 insertions(+), 10 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/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index b69540716..139f13fdf 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -63,12 +63,9 @@ export class OrderStateWatcher { web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, config?: OrderStateWatcherConfig, ) { - this._orderByOrderHash = {}; this._abiDecoder = abiDecoder; this._web3Wrapper = web3Wrapper; - this._dependentOrderHashes = {}; const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; - const blockPollingIntervalMs = _.isUndefined(config) ? undefined : config.blockPollingIntervalMs; this._eventWatcher = new EventWatcher(web3Wrapper, eventPollingIntervalMs); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(token); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); diff --git a/src/types.ts b/src/types.ts index b34dbfb4e..fcfbdc92b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,14 +18,11 @@ export enum ZeroExError { SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT', TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT', - FailedToFetchLatestBlock = 'FAILED_TO_FETCH_LATEST_BLOCK', } export enum InternalZeroExError { NoAbiDecoder = 'NO_ABI_DECODER', ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY', - LatestBlockNumberNotSet = 'LATEST_BLOCK_NUMBER_NOT_SET', - Web3WrapperRequiredToStartBlockStore = 'WEB3_WRAPPER_REQUIRED_TO_START_BLOCK_STORE', } /** @@ -401,11 +398,9 @@ export interface JSONRPCPayload { /* * eventPollingIntervalMs: How often to poll the Ethereum node for new events - * blockPollingIntervalMs: How often to poll the Ethereum node for new blocks */ export interface OrderStateWatcherConfig { eventPollingIntervalMs?: number; - blockPollingIntervalMs?: number; } /* diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index aa387f495..2b1aa68b7 100644 --- a/test/order_state_watcher_test.ts +++ b/test/order_state_watcher_test.ts @@ -96,7 +96,7 @@ describe('OrderStateWatcher', () => { it('should fail when trying to subscribe twice', async () => { zeroEx.orderStateWatcher.subscribe(_.noop); expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)) - .to.throw(); + .to.throw(ZeroExError.SubscriptionAlreadyPresent); }); }); describe('tests with cleanup', async () => { 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" -- cgit v1.2.3 From e512e38efbbec030add83ce2bc50f0d17862bdd6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sun, 12 Nov 2017 20:21:24 -0500 Subject: Remove old tests --- test/order_state_watcher_test.ts | 62 ---------------------------------------- 1 file changed, 62 deletions(-) diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts index 2b1aa68b7..c8a4a8064 100644 --- a/test/order_state_watcher_test.ts +++ b/test/order_state_watcher_test.ts @@ -352,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); - }); - }); }); }); -- cgit v1.2.3