diff options
Diffstat (limited to 'packages/0x.js/src/contract_wrappers/contract_wrapper.ts')
-rw-r--r-- | packages/0x.js/src/contract_wrappers/contract_wrapper.ts | 121 |
1 files changed, 69 insertions, 52 deletions
diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts index d56b8632d..27551c01d 100644 --- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts @@ -1,11 +1,13 @@ -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import {Block, BlockAndLogStreamer} from 'ethereumjs-blockstream'; +import { intervalUtils } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream'; import * as _ from 'lodash'; import * as Web3 from 'web3'; import { Artifact, BlockParamLiteral, + BlockRange, ContractEventArgs, ContractEvents, EventCallback, @@ -13,15 +15,15 @@ import { InternalZeroExError, LogWithDecodedArgs, RawLog, - SubscriptionOpts, ZeroExError, } from '../types'; -import {AbiDecoder} from '../utils/abi_decoder'; -import {constants} from '../utils/constants'; -import {filterUtils} from '../utils/filter_utils'; -import {intervalUtils} from '../utils/interval_utils'; +import { AbiDecoder } from '../utils/abi_decoder'; +import { constants } from '../utils/constants'; +import { filterUtils } from '../utils/filter_utils'; -const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {[contractName: string]: ZeroExError} = { +const CONTRACT_NAME_TO_NOT_FOUND_ERROR: { + [contractName: string]: ZeroExError; +} = { ZRX: ZeroExError.ZRXContractDoesNotExist, EtherToken: ZeroExError.EtherTokenContractDoesNotExist, Token: ZeroExError.TokenContractDoesNotExist, @@ -34,26 +36,25 @@ export class ContractWrapper { protected _web3Wrapper: Web3Wrapper; private _networkId: number; private _abiDecoder?: AbiDecoder; - private _blockAndLogStreamer: BlockAndLogStreamer|undefined; + private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined; private _blockAndLogStreamInterval: NodeJS.Timer; - private _filters: {[filterToken: string]: Web3.FilterObject}; - private _filterCallbacks: {[filterToken: string]: EventCallback<ContractEventArgs>}; - private _onLogAddedSubscriptionToken: string|undefined; - private _onLogRemovedSubscriptionToken: string|undefined; + private _filters: { [filterToken: string]: Web3.FilterObject }; + private _filterCallbacks: { + [filterToken: string]: EventCallback<ContractEventArgs>; + }; + private _onLogAddedSubscriptionToken: string | undefined; + private _onLogRemovedSubscriptionToken: string | undefined; constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) { this._web3Wrapper = web3Wrapper; this._networkId = networkId; this._abiDecoder = abiDecoder; this._filters = {}; this._filterCallbacks = {}; - this._blockAndLogStreamer = undefined; + this._blockAndLogStreamerIfExists = undefined; this._onLogAddedSubscriptionToken = undefined; this._onLogRemovedSubscriptionToken = undefined; } - /** - * Cancels all existing subscriptions - */ - public unsubscribeAll(): void { + protected unsubscribeAll(): void { const filterTokens = _.keys(this._filterCallbacks); _.each(filterTokens, filterToken => { this._unsubscribe(filterToken); @@ -74,27 +75,36 @@ export class ContractWrapper { } } protected _subscribe<ArgsType extends ContractEventArgs>( - address: string, eventName: ContractEvents, indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi, - callback: EventCallback<ArgsType>): string { + address: string, + eventName: ContractEvents, + indexFilterValues: IndexedFilterValues, + abi: Web3.ContractAbi, + callback: EventCallback<ArgsType>, + ): string { const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi); - if (_.isUndefined(this._blockAndLogStreamer)) { + if (_.isUndefined(this._blockAndLogStreamerIfExists)) { this._startBlockAndLogStream(); } const filterToken = filterUtils.generateUUID(); this._filters[filterToken] = filter; - this._filterCallbacks[filterToken] = callback; + this._filterCallbacks[filterToken] = callback as EventCallback<ContractEventArgs>; return filterToken; } protected async _getLogsAsync<ArgsType extends ContractEventArgs>( - address: string, eventName: ContractEvents, subscriptionOpts: SubscriptionOpts, - indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi): Promise<Array<LogWithDecodedArgs<ArgsType>>> { - const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, subscriptionOpts); + address: string, + eventName: ContractEvents, + blockRange: BlockRange, + indexFilterValues: IndexedFilterValues, + abi: Web3.ContractAbi, + ): Promise<Array<LogWithDecodedArgs<ArgsType>>> { + const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange); const logs = await this._web3Wrapper.getLogsAsync(filter); const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this)); return logsWithDecodedArguments; } protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( - log: Web3.LogEntry): LogWithDecodedArgs<ArgsType>|RawLog { + log: Web3.LogEntry, + ): LogWithDecodedArgs<ArgsType> | RawLog { if (_.isUndefined(this._abiDecoder)) { throw new Error(InternalZeroExError.NoAbiDecoder); } @@ -102,7 +112,8 @@ export class ContractWrapper { return logWithDecodedArgs; } protected async _instantiateContractIfExistsAsync( - artifact: Artifact, addressIfExists?: string, + artifact: Artifact, + addressIfExists?: string, ): Promise<Web3.ContractInstance> { let contractAddress: string; if (_.isUndefined(addressIfExists)) { @@ -117,9 +128,7 @@ export class ContractWrapper { if (!doesContractExist) { throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]); } - const contractInstance = this._web3Wrapper.getContractInstance( - artifact.abi, contractAddress, - ); + const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress); return contractInstance; } protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string { @@ -146,45 +155,53 @@ export class ContractWrapper { }); } private _startBlockAndLogStream(): void { - this._blockAndLogStreamer = new BlockAndLogStreamer( + if (!_.isUndefined(this._blockAndLogStreamerIfExists)) { + throw new Error(ZeroExError.SubscriptionAlreadyPresent); + } + this._blockAndLogStreamerIfExists = new BlockAndLogStreamer( this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper), this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper), ); const catchAllLogFilter = {}; - this._blockAndLogStreamer.addLogFilter(catchAllLogFilter); + this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval( - this._reconcileBlockAsync.bind(this), constants.DEFAULT_BLOCK_POLLING_INTERVAL, + this._reconcileBlockAsync.bind(this), + constants.DEFAULT_BLOCK_POLLING_INTERVAL, + this._onReconcileBlockError.bind(this), ); let isRemoved = false; - this._onLogAddedSubscriptionToken = this._blockAndLogStreamer.subscribeToOnLogAdded( + this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded( this._onLogStateChanged.bind(this, isRemoved), ); isRemoved = true; - this._onLogRemovedSubscriptionToken = this._blockAndLogStreamer.subscribeToOnLogRemoved( + this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved( this._onLogStateChanged.bind(this, isRemoved), ); } + private _onReconcileBlockError(err: Error): void { + const filterTokens = _.keys(this._filterCallbacks); + _.each(filterTokens, filterToken => { + this._unsubscribe(filterToken, err); + }); + } + private _setNetworkId(networkId: number): void { + this._networkId = networkId; + } private _stopBlockAndLogStream(): void { - (this._blockAndLogStreamer as BlockAndLogStreamer).unsubscribeFromOnLogAdded( - this._onLogAddedSubscriptionToken as string); - (this._blockAndLogStreamer as BlockAndLogStreamer).unsubscribeFromOnLogRemoved( - this._onLogRemovedSubscriptionToken as string); + if (_.isUndefined(this._blockAndLogStreamerIfExists)) { + throw new Error(ZeroExError.SubscriptionNotFound); + } + this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string); + this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string); intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamInterval); - delete this._blockAndLogStreamer; + delete this._blockAndLogStreamerIfExists; } private async _reconcileBlockAsync(): Promise<void> { - try { - const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest); - // We need to coerce to Block type cause Web3.Block includes types for mempool blocks - if (!_.isUndefined(this._blockAndLogStreamer)) { - // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined - await this._blockAndLogStreamer.reconcileNewBlock(latestBlock as any as Block); - } - } catch (err) { - const filterTokens = _.keys(this._filterCallbacks); - _.each(filterTokens, filterToken => { - this._unsubscribe(filterToken, err); - }); + const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest); + // We need to coerce to Block type cause Web3.Block includes types for mempool blocks + if (!_.isUndefined(this._blockAndLogStreamerIfExists)) { + // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined + await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block); } } } |