diff options
Diffstat (limited to 'src/order_watcher')
-rw-r--r-- | src/order_watcher/event_watcher.ts | 15 | ||||
-rw-r--r-- | src/order_watcher/order_state_watcher.ts | 108 |
2 files changed, 72 insertions, 51 deletions
diff --git a/src/order_watcher/event_watcher.ts b/src/order_watcher/event_watcher.ts index c9e72281c..81529a98c 100644 --- a/src/order_watcher/event_watcher.ts +++ b/src/order_watcher/event_watcher.ts @@ -28,10 +28,8 @@ export class EventWatcher { private _pollingIntervalMs: number; private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: Web3.LogEntry[] = []; - private _numConfirmations: number; - constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number, numConfirmations: number) { + constructor(web3Wrapper: Web3Wrapper, pollingIntervalMs: undefined|number) { this._web3Wrapper = web3Wrapper; - this._numConfirmations = numConfirmations; this._pollingIntervalMs = _.isUndefined(pollingIntervalMs) ? DEFAULT_EVENT_POLLING_INTERVAL : pollingIntervalMs; @@ -67,16 +65,9 @@ export class EventWatcher { this._lastEvents = pendingEvents; } private async _getEventsAsync(): Promise<Web3.LogEntry[]> { - let latestBlock: BlockParamLiteral|number; - if (this._numConfirmations === 0) { - latestBlock = BlockParamLiteral.Pending; - } else { - const currentBlock = await this._web3Wrapper.getBlockNumberAsync(); - latestBlock = currentBlock - this._numConfirmations; - } const eventFilter = { - fromBlock: latestBlock, - toBlock: latestBlock, + fromBlock: BlockParamLiteral.Pending, + toBlock: BlockParamLiteral.Pending, }; const events = await this._web3Wrapper.getLogsAsync(eventFilter); return events; diff --git a/src/order_watcher/order_state_watcher.ts b/src/order_watcher/order_state_watcher.ts index 4866f8409..139f13fdf 100644 --- a/src/order_watcher/order_state_watcher.ts +++ b/src/order_watcher/order_state_watcher.ts @@ -1,6 +1,5 @@ import * as _ from 'lodash'; import {schemas} from '0x-json-schemas'; -import * as ethUtil from 'ethereumjs-util'; import {ZeroEx} from '../0x'; import {EventWatcher} from './event_watcher'; import {assert} from '../utils/assert'; @@ -15,13 +14,22 @@ import { Web3Provider, BlockParamLiteral, LogWithDecodedArgs, + ContractEventArgs, OnOrderStateChangeCallback, OrderStateWatcherConfig, + ApprovalContractEventArgs, + TransferContractEventArgs, + LogFillContractEventArgs, + LogCancelContractEventArgs, ExchangeEvents, TokenEvents, ZeroExError, } from '../types'; import {Web3Wrapper} from '../web3_wrapper'; +import {TokenWrapper} from '../contract_wrappers/token_wrapper'; +import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; +import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store'; +import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; const DEFAULT_NUM_CONFIRMATIONS = 0; @@ -44,26 +52,26 @@ interface OrderByOrderHash { export class OrderStateWatcher { private _orderByOrderHash: OrderByOrderHash = {}; private _dependentOrderHashes: DependentOrderHashes = {}; - private _web3Wrapper: Web3Wrapper; private _callbackIfExistsAsync?: OnOrderStateChangeCallback; private _eventWatcher: EventWatcher; + private _web3Wrapper: Web3Wrapper; private _abiDecoder: AbiDecoder; private _orderStateUtils: OrderStateUtils; - private _numConfirmations: number; + private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; + private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; constructor( - web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, orderStateUtils: OrderStateUtils, + web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, config?: OrderStateWatcherConfig, ) { + this._abiDecoder = abiDecoder; this._web3Wrapper = web3Wrapper; - const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.pollingIntervalMs; - this._numConfirmations = _.isUndefined(config) ? - DEFAULT_NUM_CONFIRMATIONS - : config.numConfirmations; - this._eventWatcher = new EventWatcher( - this._web3Wrapper, eventPollingIntervalMs, this._numConfirmations, + const eventPollingIntervalMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; + this._eventWatcher = new EventWatcher(web3Wrapper, eventPollingIntervalMs); + this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(token); + this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); + this._orderStateUtils = new OrderStateUtils( + this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, ); - this._abiDecoder = abiDecoder; - this._orderStateUtils = orderStateUtils; } /** * Add an order to the orderStateWatcher. Before the order is added, it's @@ -108,6 +116,11 @@ export class OrderStateWatcher { * Ends an orderStateWatcher subscription. */ public unsubscribe(): void { + if (_.isUndefined(this._callbackIfExistsAsync)) { + throw new Error(ZeroExError.SubscriptionNotFound); + } + this._balanceAndProxyAllowanceLazyStore.deleteAll(); + this._orderFilledCancelledLazyStore.deleteAll(); delete this._callbackIfExistsAsync; this._eventWatcher.unsubscribe(); } @@ -117,45 +130,68 @@ export class OrderStateWatcher { if (!isLogDecoded) { return; // noop } - // Unfortunately blockNumber is returned as a hex-encoded string, so we - // convert it to a number here. - const blockNumberBuff = ethUtil.toBuffer(maybeDecodedLog.blockNumber); - const blockNumber = ethUtil.bufferToInt(blockNumberBuff); - - const decodedLog = maybeDecodedLog as LogWithDecodedArgs<any>; + const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>; let makerToken: string; let makerAddress: string; let orderHashesSet: Set<string>; switch (decodedLog.event) { case TokenEvents.Approval: + { + // Invalidate cache + const args = decodedLog.args as ApprovalContractEventArgs; + this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner); + // Revalidate orders makerToken = decodedLog.address; - makerAddress = decodedLog.args._owner; + makerAddress = args._owner; orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]); if (!_.isUndefined(orderHashesSet)) { const orderHashes = Array.from(orderHashesSet); - await this._emitRevalidateOrdersAsync(orderHashes, blockNumber); + await this._emitRevalidateOrdersAsync(orderHashes); } break; - + } case TokenEvents.Transfer: + { + // Invalidate cache + const args = decodedLog.args as TransferContractEventArgs; + this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from); + this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to); + // Revalidate orders makerToken = decodedLog.address; - makerAddress = decodedLog.args._from; + makerAddress = args._from; orderHashesSet = _.get(this._dependentOrderHashes, [makerAddress, makerToken]); if (!_.isUndefined(orderHashesSet)) { const orderHashes = Array.from(orderHashesSet); - await this._emitRevalidateOrdersAsync(orderHashes, blockNumber); + await this._emitRevalidateOrdersAsync(orderHashes); } break; - + } case ExchangeEvents.LogFill: + { + // Invalidate cache + const args = decodedLog.args as LogFillContractEventArgs; + this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); + // Revalidate orders + const orderHash = args.orderHash; + const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); + if (isOrderWatched) { + await this._emitRevalidateOrdersAsync([orderHash]); + } + break; + } case ExchangeEvents.LogCancel: - const orderHash = decodedLog.args.orderHash; + { + // Invalidate cache + const args = decodedLog.args as LogCancelContractEventArgs; + this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); + // Revalidate orders + const orderHash = args.orderHash; const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); if (isOrderWatched) { - await this._emitRevalidateOrdersAsync([orderHash], blockNumber); + await this._emitRevalidateOrdersAsync([orderHash]); } break; - + } case ExchangeEvents.LogError: return; // noop @@ -163,22 +199,16 @@ export class OrderStateWatcher { throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event); } } - private async _emitRevalidateOrdersAsync(orderHashes: string[], blockNumber: number): Promise<void> { - const defaultBlock = this._numConfirmations === 0 ? - BlockParamLiteral.Pending : - blockNumber; - const methodOpts = { - defaultBlock, - }; - + private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> { for (const orderHash of orderHashes) { const signedOrder = this._orderByOrderHash[orderHash] as SignedOrder; - const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder, methodOpts); - if (!_.isUndefined(this._callbackIfExistsAsync)) { - await this._callbackIfExistsAsync(orderState); - } else { + // Most of these calls will never reach the network because the data is fetched from stores + // and only updated when cache is invalidated + const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder); + if (_.isUndefined(this._callbackIfExistsAsync)) { break; // Unsubscribe was called } + await this._callbackIfExistsAsync(orderState); } } private addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string) { |