aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/order-watcher/src/artifacts.ts19
-rw-r--r--packages/order-watcher/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts74
-rw-r--r--packages/order-watcher/src/fetchers/order_filled_cancelled_fetcher.ts27
-rw-r--r--packages/order-watcher/src/index.ts3
-rw-r--r--packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts54
-rw-r--r--packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts230
-rw-r--r--packages/order-watcher/src/order_watcher/event_watcher.ts14
-rw-r--r--packages/order-watcher/src/order_watcher/expiration_watcher.ts12
-rw-r--r--packages/order-watcher/src/order_watcher/order_watcher.ts429
-rw-r--r--packages/order-watcher/src/types.ts16
-rw-r--r--packages/order-watcher/src/utils/assert.ts12
-rw-r--r--packages/order-watcher/test/expiration_watcher_test.ts110
-rw-r--r--packages/order-watcher/test/global_hooks.ts6
-rw-r--r--packages/order-watcher/test/order_watcher_test.ts401
-rw-r--r--packages/order-watcher/test/utils/token_utils.ts34
-rw-r--r--packages/order-watcher/test/utils/web3_wrapper.ts2
16 files changed, 893 insertions, 550 deletions
diff --git a/packages/order-watcher/src/artifacts.ts b/packages/order-watcher/src/artifacts.ts
index 13587984c..4732fb2b5 100644
--- a/packages/order-watcher/src/artifacts.ts
+++ b/packages/order-watcher/src/artifacts.ts
@@ -1,18 +1,13 @@
import { Artifact } from '@0xproject/types';
-import * as DummyToken from './compact_artifacts/DummyToken.json';
-import * as EtherToken from './compact_artifacts/EtherToken.json';
+import * as ERC20Token from './compact_artifacts/ERC20Token.json';
+import * as ERC721Token from './compact_artifacts/ERC721Token.json';
import * as Exchange from './compact_artifacts/Exchange.json';
-import * as Token from './compact_artifacts/Token.json';
-import * as TokenRegistry from './compact_artifacts/TokenRegistry.json';
-import * as TokenTransferProxy from './compact_artifacts/TokenTransferProxy.json';
-import * as ZRX from './compact_artifacts/ZRX.json';
+import * as WETH9 from './compact_artifacts/WETH9.json';
+
export const artifacts = {
- ZRX: (ZRX as any) as Artifact,
- DummyToken: (DummyToken as any) as Artifact,
- Token: (Token as any) as Artifact,
+ ERC20Token: (ERC20Token as any) as Artifact,
+ ERC721Token: (ERC721Token as any) as Artifact,
Exchange: (Exchange as any) as Artifact,
- EtherToken: (EtherToken as any) as Artifact,
- TokenRegistry: (TokenRegistry as any) as Artifact,
- TokenTransferProxy: (TokenTransferProxy as any) as Artifact,
+ EtherToken: (WETH9 as any) as Artifact,
};
diff --git a/packages/order-watcher/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/order-watcher/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
new file mode 100644
index 000000000..b1c013928
--- /dev/null
+++ b/packages/order-watcher/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
@@ -0,0 +1,74 @@
+// tslint:disable:no-unnecessary-type-assertion
+import { BlockParamLiteral, ERC20TokenWrapper, ERC721TokenWrapper } from '@0xproject/contract-wrappers';
+import { AbstractBalanceAndProxyAllowanceFetcher, assetProxyUtils } from '@0xproject/order-utils';
+import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+
+export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
+ private readonly _erc20Token: ERC20TokenWrapper;
+ private readonly _erc721Token: ERC721TokenWrapper;
+ private readonly _stateLayer: BlockParamLiteral;
+ constructor(erc20Token: ERC20TokenWrapper, erc721Token: ERC721TokenWrapper, stateLayer: BlockParamLiteral) {
+ this._erc20Token = erc20Token;
+ this._erc721Token = erc721Token;
+ this._stateLayer = stateLayer;
+ }
+ public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
+ const decodedAssetData = assetProxyUtils.decodeAssetData(assetData);
+ if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
+ const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
+ const balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, {
+ defaultBlock: this._stateLayer,
+ });
+ return balance;
+ } else {
+ const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
+ const tokenOwner = await this._erc721Token.getOwnerOfAsync(
+ decodedERC721AssetData.tokenAddress,
+ decodedERC721AssetData.tokenId,
+ {
+ defaultBlock: this._stateLayer,
+ },
+ );
+ const balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
+ return balance;
+ }
+ }
+ public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
+ const decodedAssetData = assetProxyUtils.decodeAssetData(assetData);
+ if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
+ const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
+ const proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(
+ decodedERC20AssetData.tokenAddress,
+ userAddress,
+ {
+ defaultBlock: this._stateLayer,
+ },
+ );
+ return proxyAllowance;
+ } else {
+ const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
+
+ const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync(
+ decodedERC721AssetData.tokenAddress,
+ userAddress,
+ {
+ defaultBlock: this._stateLayer,
+ },
+ );
+ if (isApprovedForAll) {
+ return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ } else {
+ const isApproved = await this._erc721Token.isProxyApprovedAsync(
+ decodedERC721AssetData.tokenAddress,
+ decodedERC721AssetData.tokenId,
+ {
+ defaultBlock: this._stateLayer,
+ },
+ );
+ const proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
+ return proxyAllowance;
+ }
+ }
+ }
+}
diff --git a/packages/order-watcher/src/fetchers/order_filled_cancelled_fetcher.ts b/packages/order-watcher/src/fetchers/order_filled_cancelled_fetcher.ts
new file mode 100644
index 000000000..bfad1a48c
--- /dev/null
+++ b/packages/order-watcher/src/fetchers/order_filled_cancelled_fetcher.ts
@@ -0,0 +1,27 @@
+// tslint:disable:no-unnecessary-type-assertion
+import { BlockParamLiteral, ExchangeWrapper } from '@0xproject/contract-wrappers';
+import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
+import { BigNumber } from '@0xproject/utils';
+
+export class OrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
+ private readonly _exchange: ExchangeWrapper;
+ private readonly _stateLayer: BlockParamLiteral;
+ constructor(exchange: ExchangeWrapper, stateLayer: BlockParamLiteral) {
+ this._exchange = exchange;
+ this._stateLayer = stateLayer;
+ }
+ public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ const filledTakerAmount = this._exchange.getFilledTakerAssetAmountAsync(orderHash, {
+ defaultBlock: this._stateLayer,
+ });
+ return filledTakerAmount;
+ }
+ public async isOrderCancelledAsync(orderHash: string): Promise<boolean> {
+ const isCancelled = await this._exchange.isCancelledAsync(orderHash);
+ return isCancelled;
+ }
+ public getZRXAssetData(): string {
+ const zrxAssetData = this._exchange.getZRXAssetData();
+ return zrxAssetData;
+ }
+}
diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts
index 390003b1d..5f84554c8 100644
--- a/packages/order-watcher/src/index.ts
+++ b/packages/order-watcher/src/index.ts
@@ -4,4 +4,5 @@ export { OrderStateValid, OrderStateInvalid, OrderState } from '@0xproject/types
export { OnOrderStateChangeCallback, OrderWatcherConfig } from './types';
-export { BlockParamLiteral, BlockParam, Order, Provider, SignedOrder } from '@0xproject/types';
+export { Order, SignedOrder } from '@0xproject/types';
+export { BlockParamLiteral, BlockParam, Provider } from 'ethereum-types';
diff --git a/packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts b/packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts
new file mode 100644
index 000000000..e13663c7a
--- /dev/null
+++ b/packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts
@@ -0,0 +1,54 @@
+import { AbiDecoder } from '@0xproject/utils';
+import { ContractAbi, DecodedLogArgs, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types';
+
+const TOKEN_TYPE_COLLISION = `Token can't be marked as ERC20 and ERC721 at the same time`;
+
+/**
+ * ERC20 and ERC721 have some events with different args but colliding signature.
+ * For exmaple:
+ * Transfer(_from address, _to address, _value uint256)
+ * Transfer(_from address, _to address, _tokenId uint256)
+ * Both have the signature:
+ * Transfer(address,address,uint256)
+ *
+ * In order to correctly decode those events we need to know the token type by address in advance.
+ * You can pass it by calling `this.addERC20Token(address)` or `this.addERC721Token(address)`
+ */
+export class CollisionResistanceAbiDecoder {
+ private readonly _erc20AbiDecoder: AbiDecoder;
+ private readonly _erc721AbiDecoder: AbiDecoder;
+ private readonly _restAbiDecoder: AbiDecoder;
+ private readonly _knownERC20Tokens = new Set();
+ private readonly _knownERC721Tokens = new Set();
+ constructor(erc20Abi: ContractAbi, erc721Abi: ContractAbi, abis: ContractAbi[]) {
+ this._erc20AbiDecoder = new AbiDecoder([erc20Abi]);
+ this._erc721AbiDecoder = new AbiDecoder([erc721Abi]);
+ this._restAbiDecoder = new AbiDecoder(abis);
+ }
+ public tryToDecodeLogOrNoop<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
+ if (this._knownERC20Tokens.has(log.address)) {
+ const maybeDecodedERC20Log = this._erc20AbiDecoder.tryToDecodeLogOrNoop(log);
+ return maybeDecodedERC20Log;
+ } else if (this._knownERC721Tokens.has(log.address)) {
+ const maybeDecodedERC721Log = this._erc721AbiDecoder.tryToDecodeLogOrNoop(log);
+ return maybeDecodedERC721Log;
+ } else {
+ const maybeDecodedLog = this._restAbiDecoder.tryToDecodeLogOrNoop(log);
+ return maybeDecodedLog;
+ }
+ }
+ // Hints the ABI decoder that a particular token address is ERC20 and events from it should be decoded as ERC20 events
+ public addERC20Token(address: string): void {
+ if (this._knownERC721Tokens.has(address)) {
+ throw new Error(TOKEN_TYPE_COLLISION);
+ }
+ this._knownERC20Tokens.add(address);
+ }
+ // Hints the ABI decoder that a particular token address is ERC721 and events from it should be decoded as ERC721 events
+ public addERC721Token(address: string): void {
+ if (this._knownERC20Tokens.has(address)) {
+ throw new Error(TOKEN_TYPE_COLLISION);
+ }
+ this._knownERC721Tokens.add(address);
+ }
+}
diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts
new file mode 100644
index 000000000..ae7d5078c
--- /dev/null
+++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts
@@ -0,0 +1,230 @@
+// tslint:disable:no-unnecessary-type-assertion
+import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
+import { AssetProxyId, ERC20AssetData, ERC721AssetData, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as _ from 'lodash';
+
+export interface OrderHashesByMakerAddress {
+ [makerAddress: string]: Set<string>;
+}
+
+export interface OrderHashesByERC20ByMakerAddress {
+ [makerAddress: string]: {
+ [erc20TokenAddress: string]: Set<string>;
+ };
+}
+
+export interface OrderHashesByERC721AddressByTokenIdByMakerAddress {
+ [makerAddress: string]: {
+ [erc721TokenAddress: string]: {
+ // Ideally erc721TokenId should be a BigNumber, but it's not a valid index type so we just convert it to a string before using it as an index
+ [erc721TokenId: string]: Set<string>;
+ };
+ };
+}
+
+/**
+ */
+export class DependentOrderHashesTracker {
+ private readonly _zrxTokenAddress: string;
+ // `_orderHashesByMakerAddress` is redundant and could be generated from
+ // `_orderHashesByERC20ByMakerAddress` and `_orderHashesByERC721AddressByTokenIdByMakerAddress`
+ // on the fly by merging all the entries together but it's more complex and computationally heavy.
+ // We might change that in future if we're move memory-constrained.
+ private readonly _orderHashesByMakerAddress: OrderHashesByMakerAddress = {};
+ private readonly _orderHashesByERC20ByMakerAddress: OrderHashesByERC20ByMakerAddress = {};
+ private readonly _orderHashesByERC721AddressByTokenIdByMakerAddress: OrderHashesByERC721AddressByTokenIdByMakerAddress = {};
+ constructor(zrxTokenAddress: string) {
+ this._zrxTokenAddress = zrxTokenAddress;
+ }
+ public getDependentOrderHashesByERC721ByMaker(makerAddress: string, tokenAddress: string): string[] {
+ const orderHashSets = _.values(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[makerAddress][tokenAddress],
+ );
+ const orderHashList = _.reduce(
+ orderHashSets,
+ (accumulator, orderHashSet) => [...accumulator, ...orderHashSet],
+ [] as string[],
+ );
+ const uniqueOrderHashList = _.uniq(orderHashList);
+ return uniqueOrderHashList;
+ }
+ public getDependentOrderHashesByMaker(makerAddress: string): string[] {
+ const dependentOrderHashes = Array.from(this._orderHashesByMakerAddress[makerAddress]);
+ return dependentOrderHashes;
+ }
+ public getDependentOrderHashesByAssetDataByMaker(makerAddress: string, assetData: string): string[] {
+ const decodedAssetData = assetProxyUtils.decodeAssetData(assetData);
+ const dependentOrderHashes =
+ decodedAssetData.assetProxyId === AssetProxyId.ERC20
+ ? this._getDependentOrderHashesByERC20AssetData(makerAddress, assetData)
+ : this._getDependentOrderHashesByERC721AssetData(makerAddress, assetData);
+ return dependentOrderHashes;
+ }
+ public addToDependentOrderHashes(signedOrder: SignedOrder): void {
+ const decodedMakerAssetData = assetProxyUtils.decodeAssetData(signedOrder.makerAssetData);
+ if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) {
+ this._addToERC20DependentOrderHashes(signedOrder, (decodedMakerAssetData as ERC20AssetData).tokenAddress);
+ } else {
+ this._addToERC721DependentOrderHashes(
+ signedOrder,
+ (decodedMakerAssetData as ERC721AssetData).tokenAddress,
+ (decodedMakerAssetData as ERC721AssetData).tokenId,
+ );
+ }
+ this._addToERC20DependentOrderHashes(signedOrder, this._zrxTokenAddress);
+ this._addToMakerDependentOrderHashes(signedOrder);
+ }
+ public removeFromDependentOrderHashes(signedOrder: SignedOrder): void {
+ const decodedMakerAssetData = assetProxyUtils.decodeAssetData(signedOrder.makerAssetData);
+ if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) {
+ this._removeFromERC20DependentOrderhashes(
+ signedOrder,
+ (decodedMakerAssetData as ERC20AssetData).tokenAddress,
+ );
+ } else {
+ this._removeFromERC721DependentOrderhashes(
+ signedOrder,
+ (decodedMakerAssetData as ERC721AssetData).tokenAddress,
+ (decodedMakerAssetData as ERC721AssetData).tokenId,
+ );
+ }
+ this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress);
+ this._removeFromMakerDependentOrderhashes(signedOrder);
+ }
+ private _getDependentOrderHashesByERC20AssetData(makerAddress: string, erc20AssetData: string): string[] {
+ const tokenAddress = assetProxyUtils.decodeERC20AssetData(erc20AssetData).tokenAddress;
+ let dependentOrderHashes: string[] = [];
+ if (
+ !_.isUndefined(this._orderHashesByERC20ByMakerAddress[makerAddress]) &&
+ !_.isUndefined(this._orderHashesByERC20ByMakerAddress[makerAddress][tokenAddress])
+ ) {
+ dependentOrderHashes = Array.from(this._orderHashesByERC20ByMakerAddress[makerAddress][tokenAddress]);
+ }
+ return dependentOrderHashes;
+ }
+ private _getDependentOrderHashesByERC721AssetData(makerAddress: string, erc721AssetData: string): string[] {
+ const tokenAddress = assetProxyUtils.decodeERC721AssetData(erc721AssetData).tokenAddress;
+ const tokenId = assetProxyUtils.decodeERC721AssetData(erc721AssetData).tokenId;
+ let dependentOrderHashes: string[] = [];
+ if (
+ !_.isUndefined(this._orderHashesByERC721AddressByTokenIdByMakerAddress[makerAddress]) &&
+ !_.isUndefined(this._orderHashesByERC721AddressByTokenIdByMakerAddress[makerAddress][tokenAddress]) &&
+ !_.isUndefined(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[makerAddress][tokenAddress][tokenId.toString()],
+ )
+ ) {
+ dependentOrderHashes = Array.from(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[makerAddress][tokenAddress][tokenId.toString()],
+ );
+ }
+ return dependentOrderHashes;
+ }
+ private _addToERC20DependentOrderHashes(signedOrder: SignedOrder, erc20TokenAddress: string): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ if (_.isUndefined(this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress])) {
+ this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress] = {};
+ }
+ if (_.isUndefined(this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress])) {
+ this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress] = new Set();
+ }
+ this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress].add(orderHash);
+ }
+ private _addToERC721DependentOrderHashes(
+ signedOrder: SignedOrder,
+ erc721TokenAddress: string,
+ tokenId: BigNumber,
+ ): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ if (_.isUndefined(this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress])) {
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress] = {};
+ }
+
+ if (
+ _.isUndefined(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress],
+ )
+ ) {
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress] = {};
+ }
+
+ if (
+ _.isUndefined(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress][
+ tokenId.toString()
+ ],
+ )
+ ) {
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress][
+ tokenId.toString()
+ ] = new Set();
+ }
+
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress][
+ tokenId.toString()
+ ].add(orderHash);
+ }
+ private _addToMakerDependentOrderHashes(signedOrder: SignedOrder): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ if (_.isUndefined(this._orderHashesByMakerAddress[signedOrder.makerAddress])) {
+ this._orderHashesByMakerAddress[signedOrder.makerAddress] = new Set();
+ }
+ this._orderHashesByMakerAddress[signedOrder.makerAddress].add(orderHash);
+ }
+ private _removeFromERC20DependentOrderhashes(signedOrder: SignedOrder, erc20TokenAddress: string): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress].delete(orderHash);
+
+ if (_.isEmpty(this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress])) {
+ delete this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][erc20TokenAddress];
+ }
+
+ if (_.isEmpty(this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress])) {
+ delete this._orderHashesByERC20ByMakerAddress[signedOrder.makerAddress];
+ }
+ }
+ private _removeFromERC721DependentOrderhashes(
+ signedOrder: SignedOrder,
+ erc721TokenAddress: string,
+ tokenId: BigNumber,
+ ): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress][
+ tokenId.toString()
+ ].delete(orderHash);
+
+ if (
+ _.isEmpty(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress][
+ tokenId.toString()
+ ],
+ )
+ ) {
+ delete this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][
+ erc721TokenAddress
+ ][tokenId.toString()];
+ }
+
+ if (
+ _.isEmpty(
+ this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][erc721TokenAddress],
+ )
+ ) {
+ delete this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress][
+ erc721TokenAddress
+ ];
+ }
+
+ if (_.isEmpty(this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress])) {
+ delete this._orderHashesByERC721AddressByTokenIdByMakerAddress[signedOrder.makerAddress];
+ }
+ }
+ private _removeFromMakerDependentOrderhashes(signedOrder: SignedOrder): void {
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ this._orderHashesByMakerAddress[signedOrder.makerAddress].delete(orderHash);
+
+ if (_.isEmpty(this._orderHashesByMakerAddress[signedOrder.makerAddress])) {
+ delete this._orderHashesByMakerAddress[signedOrder.makerAddress];
+ }
+ }
+}
diff --git a/packages/order-watcher/src/order_watcher/event_watcher.ts b/packages/order-watcher/src/order_watcher/event_watcher.ts
index 08ecf81cb..68c043dfe 100644
--- a/packages/order-watcher/src/order_watcher/event_watcher.ts
+++ b/packages/order-watcher/src/order_watcher/event_watcher.ts
@@ -1,6 +1,6 @@
-import { BlockParamLiteral, LogEntry } from '@0xproject/types';
import { intervalUtils, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { BlockParamLiteral, LogEntry, Provider } from 'ethereum-types';
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash';
@@ -19,22 +19,22 @@ enum LogEventState {
* depth.
*/
export class EventWatcher {
- private _web3Wrapper: Web3Wrapper;
+ private readonly _web3Wrapper: Web3Wrapper;
+ private readonly _pollingIntervalMs: number;
+ private readonly _stateLayer: BlockParamLiteral;
+ private readonly _isVerbose: boolean;
private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined;
private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer;
private _onLogAddedSubscriptionToken: string | undefined;
private _onLogRemovedSubscriptionToken: string | undefined;
- private _pollingIntervalMs: number;
- private _stateLayer: BlockParamLiteral;
- private _isVerbose: boolean;
constructor(
- web3Wrapper: Web3Wrapper,
+ provider: Provider,
pollingIntervalIfExistsMs: undefined | number,
stateLayer: BlockParamLiteral = BlockParamLiteral.Latest,
isVerbose: boolean,
) {
this._isVerbose = isVerbose;
- this._web3Wrapper = web3Wrapper;
+ this._web3Wrapper = new Web3Wrapper(provider);
this._stateLayer = stateLayer;
this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
? DEFAULT_EVENT_POLLING_INTERVAL_MS
diff --git a/packages/order-watcher/src/order_watcher/expiration_watcher.ts b/packages/order-watcher/src/order_watcher/expiration_watcher.ts
index 31fda7dca..c4c94a015 100644
--- a/packages/order-watcher/src/order_watcher/expiration_watcher.ts
+++ b/packages/order-watcher/src/order_watcher/expiration_watcher.ts
@@ -13,10 +13,10 @@ const DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS = 50;
* It stores them in a min heap by expiration time and checks for expired ones every `orderExpirationCheckingIntervalMs`
*/
export class ExpirationWatcher {
- private _orderHashByExpirationRBTree: RBTree<string>;
- private _expiration: { [orderHash: string]: BigNumber } = {};
- private _orderExpirationCheckingIntervalMs: number;
- private _expirationMarginMs: number;
+ private readonly _orderHashByExpirationRBTree: RBTree<string>;
+ private readonly _expiration: { [orderHash: string]: BigNumber } = {};
+ private readonly _orderExpirationCheckingIntervalMs: number;
+ private readonly _expirationMarginMs: number;
private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
this._orderExpirationCheckingIntervalMs =
@@ -68,8 +68,8 @@ export class ExpirationWatcher {
private _pruneExpiredOrders(callback: (orderHash: string) => void): void {
const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
while (true) {
- const hasTrakedOrders = this._orderHashByExpirationRBTree.size === 0;
- if (hasTrakedOrders) {
+ const hasNoTrackedOrders = this._orderHashByExpirationRBTree.size === 0;
+ if (hasNoTrackedOrders) {
break;
}
const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min();
diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts
index b09ba8d9d..af479f32d 100644
--- a/packages/order-watcher/src/order_watcher/order_watcher.ts
+++ b/packages/order-watcher/src/order_watcher/order_watcher.ts
@@ -1,55 +1,53 @@
+// tslint:disable:no-unnecessary-type-assertion
import {
- BalanceAndProxyAllowanceLazyStore,
ContractWrappers,
- OrderFilledCancelledLazyStore,
+ ERC20TokenApprovalEventArgs,
+ ERC20TokenEventArgs,
+ ERC20TokenEvents,
+ ERC20TokenTransferEventArgs,
+ ERC721TokenApprovalEventArgs,
+ ERC721TokenApprovalForAllEventArgs,
+ ERC721TokenEventArgs,
+ ERC721TokenEvents,
+ ERC721TokenTransferEventArgs,
+ ExchangeCancelEventArgs,
+ ExchangeCancelUpToEventArgs,
+ ExchangeEventArgs,
+ ExchangeEvents,
+ ExchangeFillEventArgs,
+ WETH9DepositEventArgs,
+ WETH9EventArgs,
+ WETH9Events,
+ WETH9WithdrawalEventArgs,
} from '@0xproject/contract-wrappers';
import { schemas } from '@0xproject/json-schemas';
-import { getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils';
import {
- BlockParamLiteral,
- ExchangeContractErrs,
- LogEntryEvent,
- LogWithDecodedArgs,
- OrderState,
- Provider,
- SignedOrder,
-} from '@0xproject/types';
+ assetProxyUtils,
+ BalanceAndProxyAllowanceLazyStore,
+ OrderFilledCancelledLazyStore,
+ orderHashUtils,
+ OrderStateUtils,
+} from '@0xproject/order-utils';
+import { ExchangeContractErrs, OrderState, SignedOrder } from '@0xproject/types';
import { errorUtils, intervalUtils } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { BlockParamLiteral, LogEntryEvent, LogWithDecodedArgs, Provider } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts } from '../artifacts';
-import {
- EtherTokenDepositEventArgs,
- EtherTokenEventArgs,
- EtherTokenEvents,
- EtherTokenWithdrawalEventArgs,
-} from '../generated_contract_wrappers/ether_token';
-import {
- ExchangeEventArgs,
- ExchangeEvents,
- ExchangeLogCancelEventArgs,
- ExchangeLogFillEventArgs,
-} from '../generated_contract_wrappers/exchange';
-import {
- TokenApprovalEventArgs,
- TokenEventArgs,
- TokenEvents,
- TokenTransferEventArgs,
-} from '../generated_contract_wrappers/token';
+import { AssetBalanceAndProxyAllowanceFetcher } from '../fetchers/asset_balance_and_proxy_allowance_fetcher';
+import { OrderFilledCancelledFetcher } from '../fetchers/order_filled_cancelled_fetcher';
+import { orderWatcherPartialConfigSchema } from '../schemas/order_watcher_partial_config_schema';
import { OnOrderStateChangeCallback, OrderWatcherConfig, OrderWatcherError } from '../types';
import { assert } from '../utils/assert';
+import { CollisionResistanceAbiDecoder } from './collision_resistant_abi_decoder';
+import { DependentOrderHashesTracker } from './dependent_order_hashes_tracker';
import { EventWatcher } from './event_watcher';
import { ExpirationWatcher } from './expiration_watcher';
-type ContractEventArgs = EtherTokenEventArgs | ExchangeEventArgs | TokenEventArgs;
+const MILLISECONDS_IN_A_SECOND = 1000;
-interface DependentOrderHashes {
- [makerAddress: string]: {
- [makerToken: string]: Set<string>;
- };
-}
+type ContractEventArgs = WETH9EventArgs | ExchangeEventArgs | ERC20TokenEventArgs | ERC721TokenEventArgs;
interface OrderByOrderHash {
[orderHash: string]: SignedOrder;
@@ -59,8 +57,15 @@ interface OrderStateByOrderHash {
[orderHash: string]: OrderState;
}
-// tslint:disable-next-line:custom-no-magic-numbers
-const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
+const DEFAULT_ORDER_WATCHER_CONFIG: OrderWatcherConfig = {
+ stateLayer: BlockParamLiteral.Latest,
+ orderExpirationCheckingIntervalMs: 50,
+ eventPollingIntervalMs: 200,
+ expirationMarginMs: 0,
+ // tslint:disable-next-line:custom-no-magic-numbers
+ cleanupJobIntervalMs: 1000 * 60 * 60, // 1h
+ isVerbose: true,
+};
/**
* This class includes all the functionality related to watching a set of orders
@@ -69,56 +74,68 @@ const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
* the order should be deemed invalid.
*/
export class OrderWatcher {
- private _contractWrappers: ContractWrappers;
- private _orderStateByOrderHashCache: OrderStateByOrderHash = {};
- private _orderByOrderHash: OrderByOrderHash = {};
- private _dependentOrderHashes: DependentOrderHashes = {};
- private _callbackIfExists?: OnOrderStateChangeCallback;
- private _eventWatcher: EventWatcher;
- private _web3Wrapper: Web3Wrapper;
- private _expirationWatcher: ExpirationWatcher;
- private _orderStateUtils: OrderStateUtils;
- private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
- private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
- private _cleanupJobInterval: number;
+ private readonly _dependentOrderHashesTracker: DependentOrderHashesTracker;
+ private readonly _orderStateByOrderHashCache: OrderStateByOrderHash = {};
+ private readonly _orderByOrderHash: OrderByOrderHash = {};
+ private readonly _eventWatcher: EventWatcher;
+ private readonly _provider: Provider;
+ private readonly _collisionResistantAbiDecoder: CollisionResistanceAbiDecoder;
+ private readonly _expirationWatcher: ExpirationWatcher;
+ private readonly _orderStateUtils: OrderStateUtils;
+ private readonly _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private readonly _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private readonly _cleanupJobInterval: number;
private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
- constructor(provider: Provider, networkId: number, config?: OrderWatcherConfig) {
- this._web3Wrapper = new Web3Wrapper(provider);
- const artifactJSONs = _.values(artifacts);
- const abiArrays = _.map(artifactJSONs, artifact => artifact.abi);
- _.forEach(abiArrays, abi => {
- this._web3Wrapper.abiDecoder.addABI(abi);
- });
- this._contractWrappers = new ContractWrappers(provider, { networkId });
- const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
- const stateLayer =
- _.isUndefined(config) || _.isUndefined(config.stateLayer) ? BlockParamLiteral.Latest : config.stateLayer;
- const isVerbose = !_.isUndefined(config) && !_.isUndefined(config.isVerbose) ? config.isVerbose : false;
- this._eventWatcher = new EventWatcher(this._web3Wrapper, pollingIntervalIfExistsMs, stateLayer, isVerbose);
- this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._contractWrappers.token,
- stateLayer,
+ private _callbackIfExists?: OnOrderStateChangeCallback;
+ constructor(
+ provider: Provider,
+ networkId: number,
+ partialConfig: Partial<OrderWatcherConfig> = DEFAULT_ORDER_WATCHER_CONFIG,
+ ) {
+ assert.isWeb3Provider('provider', provider);
+ assert.isNumber('networkId', networkId);
+ assert.doesConformToSchema('partialConfig', partialConfig, orderWatcherPartialConfigSchema);
+ const config = {
+ ...DEFAULT_ORDER_WATCHER_CONFIG,
+ ...partialConfig,
+ };
+
+ this._provider = provider;
+ this._collisionResistantAbiDecoder = new CollisionResistanceAbiDecoder(
+ artifacts.ERC20Token.abi,
+ artifacts.ERC721Token.abi,
+ [artifacts.EtherToken.abi, artifacts.Exchange.abi],
);
- this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(
- this._contractWrappers.exchange,
- stateLayer,
+ const contractWrappers = new ContractWrappers(provider, { networkId });
+ this._eventWatcher = new EventWatcher(
+ provider,
+ config.eventPollingIntervalMs,
+ config.stateLayer,
+ config.isVerbose,
);
- this._orderStateUtils = new OrderStateUtils(
- this._balanceAndProxyAllowanceLazyStore,
- this._orderFilledCancelledLazyStore,
+ const balanceAndProxyAllowanceFetcher = new AssetBalanceAndProxyAllowanceFetcher(
+ contractWrappers.erc20Token,
+ contractWrappers.erc721Token,
+ config.stateLayer,
+ );
+ this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
+ balanceAndProxyAllowanceFetcher,
);
- const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
- ? undefined
- : config.orderExpirationCheckingIntervalMs;
+ const orderFilledCancelledFetcher = new OrderFilledCancelledFetcher(
+ contractWrappers.exchange,
+ config.stateLayer,
+ );
+ this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(orderFilledCancelledFetcher);
+ this._orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher);
const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
this._expirationWatcher = new ExpirationWatcher(
expirationMarginIfExistsMs,
- orderExpirationCheckingIntervalMsIfExists,
+ config.orderExpirationCheckingIntervalMs,
);
- this._cleanupJobInterval =
- _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
- ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
- : config.cleanupJobIntervalMs;
+ this._cleanupJobInterval = config.cleanupJobIntervalMs;
+ const zrxTokenAddress = assetProxyUtils.decodeERC20AssetData(orderFilledCancelledFetcher.getZRXAssetData())
+ .tokenAddress;
+ this._dependentOrderHashesTracker = new DependentOrderHashesTracker(zrxTokenAddress);
}
/**
* Add an order to the orderWatcher. Before the order is added, it's
@@ -127,13 +144,14 @@ export class OrderWatcher {
*/
public addOrder(signedOrder: SignedOrder): void {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const orderHash = getOrderHashHex(signedOrder);
- assert.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker);
- this._orderByOrderHash[orderHash] = signedOrder;
- this._addToDependentOrderHashes(signedOrder, orderHash);
- const milisecondsInASecond = 1000;
- const expirationUnixTimestampMs = signedOrder.expirationUnixTimestampSec.times(milisecondsInASecond);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ assert.isValidSignatureAsync(this._provider, orderHash, signedOrder.signature, signedOrder.makerAddress);
+
+ const expirationUnixTimestampMs = signedOrder.expirationTimeSeconds.times(MILLISECONDS_IN_A_SECOND);
this._expirationWatcher.addOrder(orderHash, expirationUnixTimestampMs);
+
+ this._orderByOrderHash[orderHash] = signedOrder;
+ this._dependentOrderHashesTracker.addToDependentOrderHashes(signedOrder);
}
/**
* Removes an order from the orderWatcher
@@ -145,16 +163,10 @@ export class OrderWatcher {
if (_.isUndefined(signedOrder)) {
return; // noop
}
+ this._dependentOrderHashesTracker.removeFromDependentOrderHashes(signedOrder);
delete this._orderByOrderHash[orderHash];
- delete this._orderStateByOrderHashCache[orderHash];
- const zrxTokenAddress = this._orderFilledCancelledLazyStore.getZRXTokenAddress();
-
- this._removeFromDependentOrderHashes(signedOrder.maker, zrxTokenAddress, orderHash);
- if (zrxTokenAddress !== signedOrder.makerTokenAddress) {
- this._removeFromDependentOrderHashes(signedOrder.maker, signedOrder.makerTokenAddress, orderHash);
- }
-
this._expirationWatcher.removeOrder(orderHash);
+ delete this._orderStateByOrderHashCache[orderHash];
}
/**
* Starts an orderWatcher subscription. The callback will be called every time a watched order's
@@ -203,21 +215,27 @@ export class OrderWatcher {
const signedOrder = this._orderByOrderHash[orderHash];
this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(orderHash);
+ this._orderFilledCancelledLazyStore.deleteIsCancelled(orderHash);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.takerTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerAssetData, signedOrder.makerAddress);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(
+ signedOrder.makerAssetData,
+ signedOrder.makerAddress,
+ );
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerAssetData, signedOrder.takerAddress);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(
+ signedOrder.takerAssetData,
+ signedOrder.takerAddress,
+ );
- const zrxTokenAddress = this._getZRXTokenAddress();
+ const zrxAssetData = this._orderFilledCancelledLazyStore.getZRXAssetData();
if (!signedOrder.makerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.makerAddress);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.makerAddress);
}
if (!signedOrder.takerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.takerAddress);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.takerAddress);
}
}
private _onOrderExpired(orderHash: string): void {
@@ -240,89 +258,122 @@ export class OrderWatcher {
}
return;
}
- const log = logIfExists as LogEntryEvent; // At this moment we are sure that no error occured and log is defined.
- const maybeDecodedLog = this._web3Wrapper.abiDecoder.tryToDecodeLogOrNoop<ContractEventArgs>(log);
+ const maybeDecodedLog = this._collisionResistantAbiDecoder.tryToDecodeLogOrNoop<ContractEventArgs>(
+ // At this moment we are sure that no error occured and log is defined.
+ logIfExists as LogEntryEvent,
+ );
const isLogDecoded = !_.isUndefined(((maybeDecodedLog as any) as LogWithDecodedArgs<ContractEventArgs>).event);
if (!isLogDecoded) {
return; // noop
}
const decodedLog = (maybeDecodedLog as any) as LogWithDecodedArgs<ContractEventArgs>;
- let makerToken: string;
- let makerAddress: string;
switch (decodedLog.event) {
- case TokenEvents.Approval: {
- // Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as TokenApprovalEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ case ERC20TokenEvents.Approval:
+ case ERC721TokenEvents.Approval: {
+ // ERC20 and ERC721 Transfer events have the same name so we need to distinguish them by args
+ if (!_.isUndefined(decodedLog.args._value)) {
+ // ERC20
+ // Invalidate cache
+ const args = decodedLog.args as ERC20TokenApprovalEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC20AssetData(decodedLog.address);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner);
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._owner,
+ tokenAssetData,
+ );
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ break;
+ } else {
+ // ERC721
+ // Invalidate cache
+ const args = decodedLog.args as ERC721TokenApprovalEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC721AssetData(decodedLog.address, args._tokenId);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner);
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._owner,
+ tokenAssetData,
+ );
await this._emitRevalidateOrdersAsync(orderHashes);
+ break;
}
- break;
}
- case TokenEvents.Transfer: {
- // Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as TokenTransferEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._from;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ case ERC20TokenEvents.Transfer:
+ case ERC721TokenEvents.Transfer: {
+ // ERC20 and ERC721 Transfer events have the same name so we need to distinguish them by args
+ if (!_.isUndefined(decodedLog.args._value)) {
+ // ERC20
+ // Invalidate cache
+ const args = decodedLog.args as ERC20TokenTransferEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC20AssetData(decodedLog.address);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to);
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._from,
+ tokenAssetData,
+ );
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ break;
+ } else {
+ // ERC721
+ // Invalidate cache
+ const args = decodedLog.args as ERC721TokenTransferEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC721AssetData(decodedLog.address, args._tokenId);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to);
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._from,
+ tokenAssetData,
+ );
await this._emitRevalidateOrdersAsync(orderHashes);
+ break;
}
+ }
+ case ERC721TokenEvents.ApprovalForAll: {
+ // Invalidate cache
+ const args = decodedLog.args as ERC721TokenApprovalForAllEventArgs;
+ const tokenAddress = decodedLog.address;
+ this._balanceAndProxyAllowanceLazyStore.deleteAllERC721ProxyAllowance(tokenAddress, args._owner);
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByERC721ByMaker(
+ args._owner,
+ tokenAddress,
+ );
+ await this._emitRevalidateOrdersAsync(orderHashes);
break;
}
- case EtherTokenEvents.Deposit: {
+ case WETH9Events.Deposit: {
// Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as EtherTokenDepositEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ const args = decodedLog.args as WETH9DepositEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC20AssetData(decodedLog.address);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner);
// Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._owner,
+ tokenAssetData,
+ );
+ await this._emitRevalidateOrdersAsync(orderHashes);
break;
}
- case EtherTokenEvents.Withdrawal: {
+ case WETH9Events.Withdrawal: {
// Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as EtherTokenWithdrawalEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ const args = decodedLog.args as WETH9WithdrawalEventArgs;
+ const tokenAssetData = assetProxyUtils.encodeERC20AssetData(decodedLog.address);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner);
// Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
+ args._owner,
+ tokenAssetData,
+ );
+ await this._emitRevalidateOrdersAsync(orderHashes);
break;
}
- case ExchangeEvents.LogFill: {
+ case ExchangeEvents.Fill: {
// Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as ExchangeLogFillEventArgs;
+ const args = decodedLog.args as ExchangeFillEventArgs;
this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
// Revalidate orders
const orderHash = args.orderHash;
@@ -332,11 +383,10 @@ export class OrderWatcher {
}
break;
}
- case ExchangeEvents.LogCancel: {
+ case ExchangeEvents.Cancel: {
// Invalidate cache
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const args = decodedLog.args as ExchangeLogCancelEventArgs;
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
+ const args = decodedLog.args as ExchangeCancelEventArgs;
+ this._orderFilledCancelledLazyStore.deleteIsCancelled(args.orderHash);
// Revalidate orders
const orderHash = args.orderHash;
const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
@@ -345,8 +395,16 @@ export class OrderWatcher {
}
break;
}
- case ExchangeEvents.LogError:
- return; // noop
+ case ExchangeEvents.CancelUpTo: {
+ // TODO(logvinov): Do it smarter and actually look at the salt and order epoch
+ // Invalidate cache
+ const args = decodedLog.args as ExchangeCancelUpToEventArgs;
+ this._orderFilledCancelledLazyStore.deleteAllIsCancelled();
+ // Revalidate orders
+ const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByMaker(args.makerAddress);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ break;
+ }
default:
throw errorUtils.spawnSwitchErr('decodedLog.event', decodedLog.event);
@@ -357,7 +415,7 @@ export class OrderWatcher {
const signedOrder = this._orderByOrderHash[orderHash];
// 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);
+ const orderState = await this._orderStateUtils.getOpenOrderStateAsync(signedOrder);
if (_.isUndefined(this._callbackIfExists)) {
break; // Unsubscribe was called
}
@@ -370,31 +428,4 @@ export class OrderWatcher {
this._callbackIfExists(null, orderState);
}
}
- private _addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string): void {
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker])) {
- this._dependentOrderHashes[signedOrder.maker] = {};
- }
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress].add(orderHash);
- const zrxTokenAddress = this._getZRXTokenAddress();
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress].add(orderHash);
- }
- private _removeFromDependentOrderHashes(makerAddress: string, tokenAddress: string, orderHash: string): void {
- this._dependentOrderHashes[makerAddress][tokenAddress].delete(orderHash);
- if (this._dependentOrderHashes[makerAddress][tokenAddress].size === 0) {
- delete this._dependentOrderHashes[makerAddress][tokenAddress];
- }
- if (_.isEmpty(this._dependentOrderHashes[makerAddress])) {
- delete this._dependentOrderHashes[makerAddress];
- }
- }
- private _getZRXTokenAddress(): string {
- const zrxTokenAddress = this._orderFilledCancelledLazyStore.getZRXTokenAddress();
- return zrxTokenAddress;
- }
}
diff --git a/packages/order-watcher/src/types.ts b/packages/order-watcher/src/types.ts
index 63e4e7848..7991df58c 100644
--- a/packages/order-watcher/src/types.ts
+++ b/packages/order-watcher/src/types.ts
@@ -1,4 +1,5 @@
-import { BlockParamLiteral, LogEntryEvent, OrderState } from '@0xproject/types';
+import { OrderState } from '@0xproject/types';
+import { BlockParamLiteral, LogEntryEvent } from 'ethereum-types';
export enum OrderWatcherError {
SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
@@ -8,20 +9,21 @@ export enum OrderWatcherError {
export type EventWatcherCallback = (err: null | Error, log?: LogEntryEvent) => void;
/**
+ * stateLayer: Optional blockchain state layer OrderWatcher will monitor for new events. Default=latest.
* orderExpirationCheckingIntervalMs: How often to check for expired orders. Default=50.
* eventPollingIntervalMs: How often to poll the Ethereum node for new events. Default=200.
* expirationMarginMs: Amount of time before order expiry that you'd like to be notified
* of an orders expiration. Default=0.
* cleanupJobIntervalMs: How often to run a cleanup job which revalidates all the orders. Default=1hr.
- * stateLayer: Optional blockchain state layer OrderWatcher will monitor for new events. Default=latest.
+ * isVerbose: Weather the order watcher should be verbose. Default=true.
*/
export interface OrderWatcherConfig {
stateLayer: BlockParamLiteral;
- orderExpirationCheckingIntervalMs?: number;
- eventPollingIntervalMs?: number;
- expirationMarginMs?: number;
- cleanupJobIntervalMs?: number;
- isVerbose?: boolean;
+ orderExpirationCheckingIntervalMs: number;
+ eventPollingIntervalMs: number;
+ expirationMarginMs: number;
+ cleanupJobIntervalMs: number;
+ isVerbose: boolean;
}
export type OnOrderStateChangeCallback = (err: Error | null, orderState?: OrderState) => void;
diff --git a/packages/order-watcher/src/utils/assert.ts b/packages/order-watcher/src/utils/assert.ts
index 9c992d9b4..fa22617c7 100644
--- a/packages/order-watcher/src/utils/assert.ts
+++ b/packages/order-watcher/src/utils/assert.ts
@@ -5,13 +5,19 @@ import { Schema } from '@0xproject/json-schemas';
import { ECSignature } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
// tslint:enable:no-unused-variable
+import { Provider } from 'ethereum-types';
-import { isValidSignature } from '@0xproject/order-utils';
+import { isValidSignatureAsync } from '@0xproject/order-utils';
export const assert = {
...sharedAssert,
- isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string): void {
- const isValid = isValidSignature(orderHash, ecSignature, signerAddress);
+ async isValidSignatureAsync(
+ provider: Provider,
+ orderHash: string,
+ signature: string,
+ signerAddress: string,
+ ): Promise<void> {
+ const isValid = await isValidSignatureAsync(provider, orderHash, signature, signerAddress);
this.assert(isValid, `Expected order with hash '${orderHash}' to have a valid signature`);
},
};
diff --git a/packages/order-watcher/test/expiration_watcher_test.ts b/packages/order-watcher/test/expiration_watcher_test.ts
index dfd3556bc..3c92ddb63 100644
--- a/packages/order-watcher/test/expiration_watcher_test.ts
+++ b/packages/order-watcher/test/expiration_watcher_test.ts
@@ -1,8 +1,9 @@
import { ContractWrappers } from '@0xproject/contract-wrappers';
+import { tokenUtils } from '@0xproject/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils';
import { FillScenarios } from '@0xproject/fill-scenarios';
-import { getOrderHashHex } from '@0xproject/order-utils';
-import { DoneCallback, Token } from '@0xproject/types';
+import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
+import { DoneCallback } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
@@ -14,7 +15,6 @@ import { utils } from '../src/utils/utils';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
-import { TokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
@@ -23,15 +23,16 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const MILISECONDS_IN_SECOND = 1000;
describe('ExpirationWatcher', () => {
- let contractWrappers: ContractWrappers;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const contractWrappers = new ContractWrappers(provider, config);
let userAddresses: string[];
let zrxTokenAddress: string;
let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
+ const exchangeContractAddress = contractWrappers.exchange.getContractAddress();
+ let makerAssetData: string;
+ let takerAssetData: string;
let coinbase: string;
let makerAddress: string;
let takerAddress: string;
@@ -41,21 +42,26 @@ describe('ExpirationWatcher', () => {
let timer: Sinon.SinonFakeTimers;
let expirationWatcher: ExpirationWatcher;
before(async () => {
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- contractWrappers = new ContractWrappers(provider, config);
- exchangeContractAddress = contractWrappers.exchange.getContractAddress();
+ await blockchainLifecycle.startAsync();
+ const erc20ProxyAddress = contractWrappers.erc20Proxy.getContractAddress();
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ zrxTokenAddress = tokenUtils.getProtocolTokenAddress();
+ fillScenarios = new FillScenarios(
+ provider,
+ userAddresses,
+ zrxTokenAddress,
+ exchangeContractAddress,
+ erc20ProxyAddress,
+ );
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
+ const [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
+ [makerAssetData, takerAssetData] = [
+ assetProxyUtils.encodeERC20AssetData(makerTokenAddress),
+ assetProxyUtils.encodeERC20AssetData(takerTokenAddress),
+ ];
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -75,15 +81,15 @@ describe('ExpirationWatcher', () => {
const orderLifetimeSec = 60;
const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
expirationUnixTimestampSec,
);
- const orderHash = getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND));
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
const callbackAsync = callbackErrorReporter.reportNoErrorCallbackErrors(done)((hash: string) => {
expect(hash).to.be.equal(orderHash);
expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec);
@@ -97,15 +103,15 @@ describe('ExpirationWatcher', () => {
const orderLifetimeSec = 60;
const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
expirationUnixTimestampSec,
);
- const orderHash = getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND));
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
const callbackAsync = callbackErrorReporter.reportNoErrorCallbackErrors(done)(async (_hash: string) => {
done(new Error('Emitted expiration went before the order actually expired'));
});
@@ -122,31 +128,25 @@ describe('ExpirationWatcher', () => {
const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
order1ExpirationUnixTimestampSec,
);
const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
order2ExpirationUnixTimestampSec,
);
- const orderHash1 = getOrderHashHex(signedOrder1);
- const orderHash2 = getOrderHashHex(signedOrder2);
- expirationWatcher.addOrder(
- orderHash2,
- signedOrder2.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND),
- );
- expirationWatcher.addOrder(
- orderHash1,
- signedOrder1.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND),
- );
+ const orderHash1 = orderHashUtils.getOrderHashHex(signedOrder1);
+ const orderHash2 = orderHashUtils.getOrderHashHex(signedOrder2);
+ expirationWatcher.addOrder(orderHash2, signedOrder2.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
+ expirationWatcher.addOrder(orderHash1, signedOrder1.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
const expirationOrder = [orderHash1, orderHash2];
const expectToBeCalledOnce = false;
const callbackAsync = callbackErrorReporter.reportNoErrorCallbackErrors(done, expectToBeCalledOnce)(
@@ -169,31 +169,25 @@ describe('ExpirationWatcher', () => {
const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
order1ExpirationUnixTimestampSec,
);
const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
+ makerAssetData,
+ takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
order2ExpirationUnixTimestampSec,
);
- const orderHash1 = getOrderHashHex(signedOrder1);
- const orderHash2 = getOrderHashHex(signedOrder2);
- expirationWatcher.addOrder(
- orderHash1,
- signedOrder1.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND),
- );
- expirationWatcher.addOrder(
- orderHash2,
- signedOrder2.expirationUnixTimestampSec.times(MILISECONDS_IN_SECOND),
- );
+ const orderHash1 = orderHashUtils.getOrderHashHex(signedOrder1);
+ const orderHash2 = orderHashUtils.getOrderHashHex(signedOrder2);
+ expirationWatcher.addOrder(orderHash1, signedOrder1.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
+ expirationWatcher.addOrder(orderHash2, signedOrder2.expirationTimeSeconds.times(MILISECONDS_IN_SECOND));
const expirationOrder = orderHash1 < orderHash2 ? [orderHash1, orderHash2] : [orderHash2, orderHash1];
const expectToBeCalledOnce = false;
const callbackAsync = callbackErrorReporter.reportNoErrorCallbackErrors(done, expectToBeCalledOnce)(
diff --git a/packages/order-watcher/test/global_hooks.ts b/packages/order-watcher/test/global_hooks.ts
index 30b0cd697..4552e01b0 100644
--- a/packages/order-watcher/test/global_hooks.ts
+++ b/packages/order-watcher/test/global_hooks.ts
@@ -1,5 +1,5 @@
import { devConstants } from '@0xproject/dev-utils';
-import { runV1MigrationsAsync } from '@0xproject/migrations';
+import { runV2MigrationsAsync } from '@0xproject/migrations';
import { provider } from './utils/web3_wrapper';
@@ -12,6 +12,6 @@ before('migrate contracts', async function(): Promise<void> {
gas: devConstants.GAS_LIMIT,
from: devConstants.TESTRPC_FIRST_ADDRESS,
};
- const artifactsDir = `../migrations/artifacts/1.0.0`;
- await runV1MigrationsAsync(provider, artifactsDir, txDefaults);
+ const artifactsDir = `../migrations/artifacts/2.0.0`;
+ await runV2MigrationsAsync(provider, artifactsDir, txDefaults);
});
diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts
index 2889051bc..6339505ce 100644
--- a/packages/order-watcher/test/order_watcher_test.ts
+++ b/packages/order-watcher/test/order_watcher_test.ts
@@ -1,8 +1,9 @@
// tslint:disable:no-unnecessary-type-assertion
-import { ContractWrappers } from '@0xproject/contract-wrappers';
+import { BlockParamLiteral, ContractWrappers } from '@0xproject/contract-wrappers';
+import { tokenUtils } from '@0xproject/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils';
import { FillScenarios } from '@0xproject/fill-scenarios';
-import { getOrderHashHex } from '@0xproject/order-utils';
+import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
import {
DoneCallback,
ExchangeContractErrs,
@@ -10,7 +11,6 @@ import {
OrderStateInvalid,
OrderStateValid,
SignedOrder,
- Token,
} from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
@@ -18,12 +18,15 @@ import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
+import {
+ DependentOrderHashesTracker,
+ OrderHashesByERC20ByMakerAddress,
+} from '../src/order_watcher/dependent_order_hashes_tracker';
import { OrderWatcher } from '../src/order_watcher/order_watcher';
import { OrderWatcherError } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
-import { TokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
const TIMEOUT_MS = 150;
@@ -33,37 +36,49 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('OrderWatcher', () => {
- let contractWrappers: ContractWrappers;
- let tokens: Token[];
- let tokenUtils: TokenUtils;
+ const networkId = constants.TESTRPC_NETWORK_ID;
+ const config = { networkId };
+ const contractWrappers = new ContractWrappers(provider, config);
let fillScenarios: FillScenarios;
let userAddresses: string[];
let zrxTokenAddress: string;
let exchangeContractAddress: string;
- let makerToken: Token;
- let takerToken: Token;
- let maker: string;
- let taker: string;
+ let makerAssetData: string;
+ let takerAssetData: string;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let coinbase: string;
+ let feeRecipient: string;
let signedOrder: SignedOrder;
let orderWatcher: OrderWatcher;
const decimals = constants.ZRX_DECIMALS;
const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals);
before(async () => {
- const networkId = await web3Wrapper.getNetworkIdAsync();
- const config = {
- networkId,
- };
- contractWrappers = new ContractWrappers(provider, config);
- orderWatcher = new OrderWatcher(provider, networkId);
- exchangeContractAddress = contractWrappers.exchange.getContractAddress();
+ await blockchainLifecycle.startAsync();
+ const erc20ProxyAddress = contractWrappers.erc20Proxy.getContractAddress();
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
- [, maker, taker] = userAddresses;
- tokens = await contractWrappers.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ zrxTokenAddress = tokenUtils.getProtocolTokenAddress();
+ exchangeContractAddress = contractWrappers.exchange.getContractAddress();
+ fillScenarios = new FillScenarios(
+ provider,
+ userAddresses,
+ zrxTokenAddress,
+ exchangeContractAddress,
+ erc20ProxyAddress,
+ );
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
+ [makerAssetData, takerAssetData] = [
+ assetProxyUtils.encodeERC20AssetData(makerTokenAddress),
+ assetProxyUtils.encodeERC20AssetData(takerTokenAddress),
+ ];
+ const orderWatcherConfig = { stateLayer: BlockParamLiteral.Latest };
+ orderWatcher = new OrderWatcher(provider, networkId, orderWatcherConfig);
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -74,35 +89,40 @@ describe('OrderWatcher', () => {
describe('#removeOrder', async () => {
it('should successfully remove existing order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
expect((orderWatcher as any)._orderByOrderHash).to.include({
[orderHash]: signedOrder,
});
- let dependentOrderHashes = (orderWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash);
+ const dependentOrderHashesTracker = (orderWatcher as any)
+ ._dependentOrderHashesTracker as DependentOrderHashesTracker;
+ let orderHashesByERC20ByMakerAddress: OrderHashesByERC20ByMakerAddress = (dependentOrderHashesTracker as any)
+ ._orderHashesByERC20ByMakerAddress;
+ expect(orderHashesByERC20ByMakerAddress[signedOrder.makerAddress][makerTokenAddress]).to.have.keys(
+ orderHash,
+ );
orderWatcher.removeOrder(orderHash);
expect((orderWatcher as any)._orderByOrderHash).to.not.include({
[orderHash]: signedOrder,
});
- dependentOrderHashes = (orderWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined();
+ orderHashesByERC20ByMakerAddress = (dependentOrderHashesTracker as any)._orderHashesByERC20ByMakerAddress;
+ expect(orderHashesByERC20ByMakerAddress[signedOrder.makerAddress]).to.be.undefined();
});
it('should no-op when removing a non-existing order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const nonExistentOrderHash = `0x${orderHash
.substr(2)
.split('')
@@ -123,19 +143,19 @@ describe('OrderWatcher', () => {
describe('tests with cleanup', async () => {
afterEach(async () => {
orderWatcher.unsubscribe();
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.removeOrder(orderHash);
});
- it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
+ it('should emit orderStateInvalid when makerAddress allowance set to 0 for watched order', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
expect(orderState.isValid).to.be.false();
@@ -144,16 +164,20 @@ describe('OrderWatcher', () => {
expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
});
orderWatcher.subscribe(callback);
- await contractWrappers.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ makerTokenAddress,
+ makerAddress,
+ new BigNumber(0),
+ );
})().catch(done);
});
it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
orderWatcher.addOrder(signedOrder);
@@ -162,10 +186,10 @@ describe('OrderWatcher', () => {
});
orderWatcher.subscribe(callback);
const notTheMaker = userAddresses[0];
- const anyRecipient = taker;
+ const anyRecipient = takerAddress;
const transferAmount = new BigNumber(2);
- await contractWrappers.token.transferAsync(
- makerToken.address,
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
notTheMaker,
anyRecipient,
transferAmount,
@@ -175,16 +199,16 @@ describe('OrderWatcher', () => {
}, TIMEOUT_MS);
})().catch(done);
});
- it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
+ it('should emit orderStateInvalid when makerAddress moves balance backing watched order', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
expect(orderState.isValid).to.be.false();
@@ -193,21 +217,26 @@ describe('OrderWatcher', () => {
expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
});
orderWatcher.subscribe(callback);
- const anyRecipient = taker;
- const makerBalance = await contractWrappers.token.getBalanceAsync(makerToken.address, maker);
- await contractWrappers.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
+ const anyRecipient = takerAddress;
+ const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
+ makerAddress,
+ anyRecipient,
+ makerBalance,
+ );
})().catch(done);
});
it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
@@ -218,28 +247,22 @@ describe('OrderWatcher', () => {
});
orderWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
+ await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress);
})().catch(done);
});
it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const makerBalance = await contractWrappers.token.getBalanceAsync(makerToken.address, maker);
+ const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
const fillAmountInBaseUnits = new BigNumber(2);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
@@ -249,22 +272,16 @@ describe('OrderWatcher', () => {
const orderRelevantState = validOrderState.orderRelevantState;
const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
remainingFillable,
);
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal(
remainingFillable,
);
expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
});
orderWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
+ await contractWrappers.exchange.fillOrderAsync(signedOrder, fillAmountInBaseUnits, takerAddress);
})().catch(done);
});
it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => {
@@ -272,19 +289,23 @@ describe('OrderWatcher', () => {
const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals);
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
+ makerAssetData,
+ takerAssetData,
makerFee,
takerFee,
- maker,
- taker,
+ makerAddress,
+ takerAddress,
fillableAmount,
- taker,
+ takerAddress,
);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)();
orderWatcher.addOrder(signedOrder);
orderWatcher.subscribe(callback);
- await contractWrappers.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0));
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ zrxTokenAddress,
+ makerAddress,
+ new BigNumber(0),
+ );
})().catch(done);
});
describe('remainingFillable(M|T)akerTokenAmount', () => {
@@ -293,45 +314,39 @@ describe('OrderWatcher', () => {
const takerFillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), decimals);
const makerFillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(20), decimals);
signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
makerFillableAmount,
takerFillableAmount,
);
const fillAmountInBaseUnits = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
expect(orderState.isValid).to.be.true();
const validOrderState = orderState as OrderStateValid;
expect(validOrderState.orderHash).to.be.equal(orderHash);
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
Web3Wrapper.toBaseUnitAmount(new BigNumber(16), decimals),
);
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal(
Web3Wrapper.toBaseUnitAmount(new BigNumber(8), decimals),
);
});
orderWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await contractWrappers.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
+ await contractWrappers.exchange.fillOrderAsync(signedOrder, fillAmountInBaseUnits, takerAddress);
})().catch(done);
});
it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
@@ -341,17 +356,17 @@ describe('OrderWatcher', () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
changedMakerApprovalAmount,
);
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal(
changedMakerApprovalAmount,
);
});
orderWatcher.subscribe(callback);
- await contractWrappers.token.setProxyAllowanceAsync(
- makerToken.address,
- maker,
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ makerTokenAddress,
+ makerAddress,
changedMakerApprovalAmount,
);
})().catch(done);
@@ -359,14 +374,17 @@ describe('OrderWatcher', () => {
it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const makerBalance = await contractWrappers.token.getBalanceAsync(makerToken.address, maker);
+ const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(
+ makerTokenAddress,
+ makerAddress,
+ );
const remainingAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals);
const transferAmount = makerBalance.sub(remainingAmount);
@@ -376,66 +394,33 @@ describe('OrderWatcher', () => {
expect(orderState.isValid).to.be.true();
const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
remainingAmount,
);
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal(
remainingAmount,
);
});
orderWatcher.subscribe(callback);
- await contractWrappers.token.transferAsync(
- makerToken.address,
- maker,
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
+ makerAddress,
constants.NULL_ADDRESS,
transferAmount,
);
})().catch(done);
});
- it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
- (async () => {
- const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
-
- const remainingTokenAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(4), decimals);
- const transferTokenAmount = makerFee.sub(remainingTokenAmount);
- orderWatcher.addOrder(signedOrder);
-
- const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingTokenAmount,
- );
- });
- orderWatcher.subscribe(callback);
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
- })().catch(done);
- });
it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
(async () => {
const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals);
const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
+ makerAssetData,
+ takerAssetData,
makerFee,
takerFee,
- maker,
- taker,
+ makerAddress,
+ takerAddress,
fillableAmount,
feeRecipient,
);
@@ -449,15 +434,19 @@ describe('OrderWatcher', () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
remainingFeeAmount,
);
});
orderWatcher.subscribe(callback);
- await contractWrappers.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
- await contractWrappers.token.transferAsync(
- makerToken.address,
- maker,
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ zrxTokenAddress,
+ makerAddress,
+ remainingFeeAmount,
+ );
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
+ makerAddress,
constants.NULL_ADDRESS,
transferTokenAmount,
);
@@ -467,14 +456,13 @@ describe('OrderWatcher', () => {
(async () => {
const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals);
const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
- const feeRecipient = taker;
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
+ makerAssetData,
+ takerAssetData,
makerFee,
takerFee,
- maker,
- taker,
+ makerAddress,
+ takerAddress,
fillableAmount,
feeRecipient,
);
@@ -484,14 +472,14 @@ describe('OrderWatcher', () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal(
fillableAmount,
);
});
orderWatcher.subscribe(callback);
- await contractWrappers.token.setProxyAllowanceAsync(
- makerToken.address,
- maker,
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ makerTokenAddress,
+ makerAddress,
Web3Wrapper.toBaseUnitAmount(new BigNumber(100), decimals),
);
})().catch(done);
@@ -500,37 +488,36 @@ describe('OrderWatcher', () => {
it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(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.OrderRemainingFillAmountZero);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
});
orderWatcher.subscribe(callback);
-
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ await contractWrappers.exchange.cancelOrderAsync(signedOrder);
})().catch(done);
});
it('should emit orderStateInvalid when within rounding error range', (done: DoneCallback) => {
(async () => {
const remainingFillableAmountInBaseUnits = new BigNumber(100);
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
+ makerAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
fillableAmount,
);
- const orderHash = getOrderHashHex(signedOrder);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
orderWatcher.addOrder(signedOrder);
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
@@ -540,36 +527,12 @@ describe('OrderWatcher', () => {
expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
});
orderWatcher.subscribe(callback);
- await contractWrappers.exchange.cancelOrderAsync(
+ await contractWrappers.exchange.fillOrderAsync(
signedOrder,
fillableAmount.minus(remainingFillableAmountInBaseUnits),
+ takerAddress,
);
})().catch(done);
});
- it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
-
- const cancelAmountInBaseUnits = new BigNumber(2);
- const orderHash = getOrderHashHex(signedOrder);
- orderWatcher.addOrder(signedOrder);
-
- const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
- });
- orderWatcher.subscribe(callback);
- await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
- })().catch(done);
- });
});
}); // tslint:disable:max-file-line-count
diff --git a/packages/order-watcher/test/utils/token_utils.ts b/packages/order-watcher/test/utils/token_utils.ts
deleted file mode 100644
index e1191b5bb..000000000
--- a/packages/order-watcher/test/utils/token_utils.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Token } from '@0xproject/types';
-import * as _ from 'lodash';
-
-import { InternalOrderWatcherError } from '../../src/types';
-
-const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
-const WETH_TOKEN_SYMBOL = 'WETH';
-
-export class TokenUtils {
- private _tokens: Token[];
- constructor(tokens: Token[]) {
- this._tokens = tokens;
- }
- public getProtocolTokenOrThrow(): Token {
- const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
- if (_.isUndefined(zrxToken)) {
- throw new Error(InternalOrderWatcherError.ZrxNotInTokenRegistry);
- }
- return zrxToken;
- }
- public getWethTokenOrThrow(): Token {
- const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
- if (_.isUndefined(wethToken)) {
- throw new Error(InternalOrderWatcherError.WethNotInTokenRegistry);
- }
- return wethToken;
- }
- public getDummyTokens(): Token[] {
- const dummyTokens = _.filter(this._tokens, token => {
- return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
- });
- return dummyTokens;
- }
-}
diff --git a/packages/order-watcher/test/utils/web3_wrapper.ts b/packages/order-watcher/test/utils/web3_wrapper.ts
index f7d11f138..ab801fa7f 100644
--- a/packages/order-watcher/test/utils/web3_wrapper.ts
+++ b/packages/order-watcher/test/utils/web3_wrapper.ts
@@ -1,6 +1,6 @@
import { web3Factory } from '@0xproject/dev-utils';
-import { Provider } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { Provider } from 'ethereum-types';
const provider: Provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
const web3Wrapper = new Web3Wrapper(provider);