From 7ae38906926dc09bc10670c361af0d2bf0050426 Mon Sep 17 00:00:00 2001 From: Hsuan Lee Date: Sat, 19 Jan 2019 18:42:04 +0800 Subject: Update dependency packages --- packages/order-watcher/.npmignore | 9 - packages/order-watcher/CHANGELOG.json | 385 --------- packages/order-watcher/CHANGELOG.md | 162 ---- packages/order-watcher/Dockerfile | 13 - packages/order-watcher/README.md | 167 ---- packages/order-watcher/coverage/.gitkeep | 0 packages/order-watcher/package.json | 89 --- packages/order-watcher/src/index.ts | 25 - .../collision_resistant_abi_decoder.ts | 54 -- .../dependent_order_hashes_tracker.ts | 243 ------ .../src/order_watcher/event_watcher.ts | 158 ---- .../src/order_watcher/expiration_watcher.ts | 89 --- .../src/order_watcher/order_watcher.ts | 490 ------------ .../order_watcher_web_socket_server.ts | 200 ----- .../schemas/order_watcher_partial_config_schema.ts | 13 - packages/order-watcher/src/server.ts | 44 - packages/order-watcher/src/types.ts | 97 --- packages/order-watcher/src/utils/assert.ts | 23 - packages/order-watcher/src/utils/utils.ts | 11 - .../order-watcher/test/expiration_watcher_test.ts | 199 ----- packages/order-watcher/test/global_hooks.ts | 6 - packages/order-watcher/test/order_watcher_test.ts | 887 --------------------- .../test/order_watcher_web_socket_server_test.ts | 312 -------- packages/order-watcher/test/utils/chai_setup.ts | 13 - packages/order-watcher/test/utils/constants.ts | 5 - packages/order-watcher/test/utils/migrate.ts | 18 - packages/order-watcher/test/utils/web3_wrapper.ts | 8 - packages/order-watcher/tsconfig.json | 8 - packages/order-watcher/tslint.json | 6 - packages/order-watcher/typedoc-tsconfig.json | 7 - 30 files changed, 3741 deletions(-) delete mode 100644 packages/order-watcher/.npmignore delete mode 100644 packages/order-watcher/CHANGELOG.json delete mode 100644 packages/order-watcher/CHANGELOG.md delete mode 100644 packages/order-watcher/Dockerfile delete mode 100644 packages/order-watcher/README.md delete mode 100644 packages/order-watcher/coverage/.gitkeep delete mode 100644 packages/order-watcher/package.json delete mode 100644 packages/order-watcher/src/index.ts delete mode 100644 packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts delete mode 100644 packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts delete mode 100644 packages/order-watcher/src/order_watcher/event_watcher.ts delete mode 100644 packages/order-watcher/src/order_watcher/expiration_watcher.ts delete mode 100644 packages/order-watcher/src/order_watcher/order_watcher.ts delete mode 100644 packages/order-watcher/src/order_watcher/order_watcher_web_socket_server.ts delete mode 100644 packages/order-watcher/src/schemas/order_watcher_partial_config_schema.ts delete mode 100644 packages/order-watcher/src/server.ts delete mode 100644 packages/order-watcher/src/types.ts delete mode 100644 packages/order-watcher/src/utils/assert.ts delete mode 100644 packages/order-watcher/src/utils/utils.ts delete mode 100644 packages/order-watcher/test/expiration_watcher_test.ts delete mode 100644 packages/order-watcher/test/global_hooks.ts delete mode 100644 packages/order-watcher/test/order_watcher_test.ts delete mode 100644 packages/order-watcher/test/order_watcher_web_socket_server_test.ts delete mode 100644 packages/order-watcher/test/utils/chai_setup.ts delete mode 100644 packages/order-watcher/test/utils/constants.ts delete mode 100644 packages/order-watcher/test/utils/migrate.ts delete mode 100644 packages/order-watcher/test/utils/web3_wrapper.ts delete mode 100644 packages/order-watcher/tsconfig.json delete mode 100644 packages/order-watcher/tslint.json delete mode 100644 packages/order-watcher/typedoc-tsconfig.json (limited to 'packages/order-watcher') diff --git a/packages/order-watcher/.npmignore b/packages/order-watcher/.npmignore deleted file mode 100644 index ac4ab11f2..000000000 --- a/packages/order-watcher/.npmignore +++ /dev/null @@ -1,9 +0,0 @@ -.* -tsconfig.json -webpack.config.js -yarn-error.log -test/ -/src/ -/_bundles/ -/generated_docs/ -/scripts/ diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json deleted file mode 100644 index 585bb48e6..000000000 --- a/packages/order-watcher/CHANGELOG.json +++ /dev/null @@ -1,385 +0,0 @@ -[ - { - "version": "3.0.0", - "changes": [ - { - "note": "Upgrade the bignumber.js to v8.0.2", - "pr": 1517 - } - ] - }, - { - "timestamp": 1547747677, - "version": "2.4.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1547561734, - "version": "2.4.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1547225310, - "version": "2.4.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "2.4.0", - "changes": [ - { - "note": "Add support for `MultiAssetProxy`", - "pr": 1363 - } - ], - "timestamp": 1547040760 - }, - { - "version": "2.3.0", - "changes": [ - { - "note": "Added a WebSocket interface to OrderWatcher so that it can be used by a client written in any language", - "pr": 1427 - } - ] - }, - { - "version": "2.2.8", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1544739608 - }, - { - "version": "2.2.7", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1544570656 - }, - { - "timestamp": 1543401373, - "version": "2.2.6", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1542821676, - "version": "2.2.5", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "2.2.4", - "changes": [ - { - "note": "Fix the bug when order watcher was throwing an error on order removal when maker token was ZRX", - "pr": 1259 - } - ], - "timestamp": 1542208198 - }, - { - "version": "2.2.3", - "changes": [ - { - "note": "Start jsonRpcRequestId at 1, not 0 as 0 breaks the web3.js websocket RPC provider", - "pr": 1227 - }, - { - "note": "Fix the bug when order watcher was trying to convert undefined to an object in case of CancelUpTo event" - } - ], - "timestamp": 1542134075 - }, - { - "timestamp": 1542028948, - "version": "2.2.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "2.2.1", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1541740904 - }, - { - "version": "2.2.0", - "changes": [ - { - "note": "Added getStats function and returns a Stats object", - "pr": 1118 - }, - { - "note": "Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers. Constructor has a new optional `contractAddresses` parameter.", - "pr": 1105 - } - ], - "timestamp": 1539871071 - }, - { - "version": "2.1.1", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1538693146 - }, - { - "version": "2.1.0", - "changes": [ - { - "note": "Export ExpirationWatcher", - "pr": 1097 - } - ], - "timestamp": 1538157789 - }, - { - "version": "2.0.0", - "changes": [ - { - "note": "Fixes dropped events issue by fetching logs by blockHash instead of blockNumber. Support for fetching by blockHash was added in Geth > v1.8.13 and Parity > v2.1.0. Infura works too.", - "pr": 1080 - }, - { - "note": "Fix misunderstanding about blockstream interface callbacks and pass the raw JSON RPC responses to it", - "pr": 1080 - }, - { - "note": "Add `transactionHash` to `OrderState` emitted by `OrderWatcher` subscriptions if the order's state change originated from a transaction.", - "pr": 1087 - } - ], - "timestamp": 1537907159 - }, - { - "version": "1.0.5", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1537875740 - }, - { - "version": "1.0.4", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1537541580 - }, - { - "version": "1.0.3", - "changes": [ - { - "note": "Drastically reduce the bundle size by removing unused parts of included contract artifacts." - } - ], - "timestamp": 1537369748 - }, - { - "version": "1.0.2", - "changes": [ - { - "note": "Add ZRX & WETH mainnet contract addresses into the included artifacts" - } - ], - "timestamp": 1537265493 - }, - { - "timestamp": 1536142250, - "version": "1.0.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "1.0.1-rc.5", - "changes": [ - { - "note": "Fix missing `BlockParamLiteral` type import issue" - } - ], - "timestamp": 1535377027 - }, - { - "version": "1.0.1-rc.4", - "changes": [ - { - "note": "Export types: `ExchangeContractErrs`, `OrderRelevantState`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload`", - "pr": 924 - }, - { - "note": "Remove exporting types: `BlockParamLiteral`, `BlockParam`, `Order`", - "pr": 924 - } - ], - "timestamp": 1535133899 - }, - { - "version": "1.0.1-rc.3", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1534210131 - }, - { - "version": "1.0.1-rc.2", - "changes": [ - { - "note": "Fixed bug caused by importing non-existent dep" - } - ], - "timestamp": 1532619515 - }, - { - "version": "1.0.1-rc.1", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1532605697 - }, - { - "timestamp": 1532357734, - "version": "1.0.0", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1532043000, - "version": "1.0.0-rc.1", - "changes": [ - { - "note": "Add support for ERC721 event watching and Exchange V2 events", - "pr": 887 - } - ] - }, - { - "timestamp": 1531919263, - "version": "0.0.8", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "0.0.7", - "changes": [ - { - "note": "Switch out simple getLogs polling with ethereumjs-blockstream", - "pr": 825 - }, - { - "note": "Do not stop subscription if error is encountered", - "pr": 825 - }, - { - "note": "Fixed a bug that caused the incorrect block to be fetched via JSON-RPC within Blockstream", - "pr": 875 - }, - { - "note": "Remove stateLayer config from OrderWatcher. It now always operates on the latest block", - "pr": 875 - } - ], - "timestamp": 1531149657 - }, - { - "timestamp": 1529397769, - "version": "0.0.6", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1527617805, - "version": "0.0.5", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1527617227, - "version": "0.0.4", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1527616612, - "version": "0.0.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1527008794, - "version": "0.0.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1527008794, - "version": "0.0.1", - "changes": [ - { - "note": "Moved OrderWatcher out of 0x.js package", - "pr": 579 - } - ] - } -] diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md deleted file mode 100644 index df065866c..000000000 --- a/packages/order-watcher/CHANGELOG.md +++ /dev/null @@ -1,162 +0,0 @@ - - -CHANGELOG - -## v2.4.3 - _January 17, 2019_ - - * Dependencies updated - -## v2.4.2 - _January 15, 2019_ - - * Dependencies updated - -## v2.4.1 - _January 11, 2019_ - - * Dependencies updated - -## v2.4.0 - _January 9, 2019_ - - * Add support for `MultiAssetProxy` (#1363) - -## v2.3.0 - _Invalid date_ - - * Added a WebSocket interface to OrderWatcher so that it can be used by a client written in any language (#1427) - -## v2.2.8 - _December 13, 2018_ - - * Dependencies updated - -## v2.2.7 - _December 11, 2018_ - - * Dependencies updated - -## v2.2.6 - _November 28, 2018_ - - * Dependencies updated - -## v2.2.5 - _November 21, 2018_ - - * Dependencies updated - -## v2.2.4 - _November 14, 2018_ - - * Fix the bug when order watcher was throwing an error on order removal when maker token was ZRX (#1259) - -## v2.2.3 - _November 13, 2018_ - - * Start jsonRpcRequestId at 1, not 0 as 0 breaks the web3.js websocket RPC provider (#1227) - * Fix the bug when order watcher was trying to convert undefined to an object in case of CancelUpTo event - -## v2.2.2 - _November 12, 2018_ - - * Dependencies updated - -## v2.2.1 - _November 9, 2018_ - - * Dependencies updated - -## v2.2.0 - _October 18, 2018_ - - * Added getStats function and returns a Stats object (#1118) - * Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers. Constructor has a new optional `contractAddresses` parameter. (#1105) - -## v2.1.1 - _October 4, 2018_ - - * Dependencies updated - -## v2.1.0 - _September 28, 2018_ - - * Export ExpirationWatcher (#1097) - -## v2.0.0 - _September 25, 2018_ - - * Fixes dropped events issue by fetching logs by blockHash instead of blockNumber. Support for fetching by blockHash was added in Geth > v1.8.13 and Parity > v2.1.0. Infura works too. (#1080) - * Fix misunderstanding about blockstream interface callbacks and pass the raw JSON RPC responses to it (#1080) - * Add `transactionHash` to `OrderState` emitted by `OrderWatcher` subscriptions if the order's state change originated from a transaction. (#1087) - -## v1.0.5 - _September 25, 2018_ - - * Dependencies updated - -## v1.0.4 - _September 21, 2018_ - - * Dependencies updated - -## v1.0.3 - _September 19, 2018_ - - * Drastically reduce the bundle size by removing unused parts of included contract artifacts. - -## v1.0.2 - _September 18, 2018_ - - * Add ZRX & WETH mainnet contract addresses into the included artifacts - -## v1.0.1 - _September 5, 2018_ - - * Dependencies updated - -## v1.0.1-rc.5 - _August 27, 2018_ - - * Fix missing `BlockParamLiteral` type import issue - -## v1.0.1-rc.4 - _August 24, 2018_ - - * Export types: `ExchangeContractErrs`, `OrderRelevantState`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload` (#924) - * Remove exporting types: `BlockParamLiteral`, `BlockParam`, `Order` (#924) - -## v1.0.1-rc.3 - _August 14, 2018_ - - * Dependencies updated - -## v1.0.1-rc.2 - _July 26, 2018_ - - * Fixed bug caused by importing non-existent dep - -## v1.0.1-rc.1 - _July 26, 2018_ - - * Dependencies updated - -## v1.0.0 - _July 23, 2018_ - - * Dependencies updated - -## v1.0.0-rc.1 - _July 19, 2018_ - - * Add support for ERC721 event watching and Exchange V2 events (#887) - -## v0.0.8 - _July 18, 2018_ - - * Dependencies updated - -## v0.0.7 - _July 9, 2018_ - - * Switch out simple getLogs polling with ethereumjs-blockstream (#825) - * Do not stop subscription if error is encountered (#825) - * Fixed a bug that caused the incorrect block to be fetched via JSON-RPC within Blockstream (#875) - * Remove stateLayer config from OrderWatcher. It now always operates on the latest block (#875) - -## v0.0.6 - _June 19, 2018_ - - * Dependencies updated - -## v0.0.5 - _May 29, 2018_ - - * Dependencies updated - -## v0.0.4 - _May 29, 2018_ - - * Dependencies updated - -## v0.0.3 - _May 29, 2018_ - - * Dependencies updated - -## v0.0.2 - _May 22, 2018_ - - * Dependencies updated - -## v0.0.1 - _May 22, 2018_ - - * Moved OrderWatcher out of 0x.js package (#579) diff --git a/packages/order-watcher/Dockerfile b/packages/order-watcher/Dockerfile deleted file mode 100644 index 3ffa1b72f..000000000 --- a/packages/order-watcher/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node - -WORKDIR /order-watcher - -COPY package.json . -RUN npm i -RUN npm install forever -g - -COPY . . - -EXPOSE 8080 - -CMD ["forever", "./lib/src/server.js"] diff --git a/packages/order-watcher/README.md b/packages/order-watcher/README.md deleted file mode 100644 index a841775b6..000000000 --- a/packages/order-watcher/README.md +++ /dev/null @@ -1,167 +0,0 @@ -## OrderWatcher - -An order watcher daemon that watches for order validity. - -#### Read the wiki [article](https://0xproject.com/wiki#0x-OrderWatcher). - -OrderWatcher also comes with a WebSocket server to provide language-agnostic access -to order watching functionality. We used the [WebSocket Client and Server Implementation for Node](https://www.npmjs.com/package/websocket). The server sends and receives messages that conform to the [JSON RPC specifications](https://www.jsonrpc.org/specification). - -## Installation - -**Install** - -```bash -npm install @0x/order-watcher --save -``` - -**Import** - -```javascript -import { OrderWatcher } from '@0x/order-watcher'; -``` - -If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: - -```json -"compilerOptions": { - "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], -} -``` - -## Using the WebSocket Server - -**Setup** - -**Environmental Variables** -Several environmental variables can be set to configure the server: - -- `ORDER_WATCHER_HTTP_PORT` specifies the port that the http server will listen on - and accept connections from. When this is not set, we default to 8080. - -**Requests** -The server accepts three types of requests: `ADD_ORDER`, `REMOVE_ORDER` and `GET_STATS`. These mirror what the underlying OrderWatcher does. You can read more in the [wiki](https://0xproject.com/wiki#0x-OrderWatcher). Unlike the OrderWatcher, it does not expose any `subscribe` or `unsubscribe` functionality because the WebSocket server keeps a single subscription open for all clients. - -The first step for making a request is establishing a connection with the server. In Javascript: - -``` -var W3CWebSocket = require('websocket').w3cwebsocket; -wsClient = new W3CWebSocket('ws://127.0.0.1:8080'); -``` - -In Python, you could use the [websocket-client library](http://pypi.python.org/pypi/websocket-client/) and run: - -``` -from websocket import create_connection -wsClient = create_connection("ws://127.0.0.1:8080") -``` - -With the connection established, you prepare the payload for your request. The payload is a json object with a format established by the [JSON RPC specification](https://www.jsonrpc.org/specification): - -- `id`: All requests require you to specify a numerical `id`. When the server responds to the request, the response will have the same `id` as the one supplied with your request. -- `jsonrpc`: This is always the string `'2.0'`. -- `method`: This specifies the OrderWatcher method you want to call. I.e., `'ADD_ORDER'`, `'REMOVE_ORDER'` or `'GET_STATS'`. -- `params`: These contain the parameters needed by OrderWatcher to execute the method you called. For `ADD_ORDER`, provide `{ signedOrder: }`. For `REMOVE_ORDER`, provide `{ orderHash: }`. For `GET_STATS`, no parameters are needed, so you may leave this empty. - -Next, convert the payload to a string and send it through the connection. -In Javascript: - -``` -const addOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: 'ADD_ORDER', - params: { signedOrder: }, -}; -wsClient.send(JSON.stringify(addOrderPayload)); -``` - -In Python: - -``` -import json -remove_order_payload = { - 'id': 1, - 'jsonrpc': '2.0', - 'method': 'REMOVE_ORDER', - 'params': {'orderHash': '0x6edc16bf37fde79f5012088c33784c730e2f103d9ab1caf73060c386ad107b7e'}, -} -wsClient.send(json.dumps(remove_order_payload)); -``` - -**Response** -The server responds to all requests in a similar format. In the data field, you'll find another object containing the following fields: - -- `id`: The id corresponding to the request that the server is responding to. `UPDATE` responses are not based on any requests so the `id` field is omitted`. -- `jsonrpc`: Always `'2.0'`. -- `method`: The method the server is responding to. Eg. `ADD_ORDER`. When order states change the server may also initiate a response. In this case, method will be listed as `UPDATE`. -- `result`: This field varies based on the method. `UPDATE` responses contain the new order state. `GET_STATS` responses contain the current order count. When there are errors, this field is omitted. -- `error`: When there is an error executing a request, the [JSON RPC](https://www.jsonrpc.org/specification) error object is listed here. When the server responds successfully, this field is omitted. - -In Javascript, the responses can be parsed using the `onmessage` callback: - -``` -wsClient.onmessage = (msg) => { - const responseData = JSON.parse(msg.data); - const method = responseData.method -}; -``` - -In Python, `recv` is a lightweight way to receive a response: - -``` -result = wsClient.recv() -method = result.method -``` - -## Contributing - -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. - -Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. - -### Install dependencies - -If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: - -```bash -yarn config set workspaces-experimental true -``` - -Then install dependencies - -```bash -yarn install -``` - -### Build - -To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: - -```bash -PKG=@0x/order-watcher yarn build -``` - -Or continuously rebuild on change: - -```bash -PKG=@0x/order-watcher yarn watch -``` - -### Clean - -```bash -yarn clean -``` - -### Lint - -```bash -yarn lint -``` - -### Run Tests - -```bash -yarn test -``` diff --git a/packages/order-watcher/coverage/.gitkeep b/packages/order-watcher/coverage/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json deleted file mode 100644 index 895c64813..000000000 --- a/packages/order-watcher/package.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "name": "@0x/order-watcher", - "version": "2.4.3", - "description": "An order watcher daemon that watches for order validity", - "keywords": [ - "0x", - "0xproject", - "ethereum", - "exchange", - "orderbook" - ], - "main": "lib/src/index.js", - "types": "lib/src/index.d.ts", - "scripts": { - "build": "yarn tsc -b", - "build:ci": "yarn build", - "lint": "tslint --format stylish --project .", - "test:circleci": "run-s test:coverage", - "test": "yarn run_mocha", - "rebuild_and_test": "run-s build test", - "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", - "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "clean": "shx rm -rf _bundles lib test_temp generated_docs", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 10000 --bail --exit", - "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" - }, - "config": { - "postpublish": { - "dockerHubRepo": "order-watcher" - } - }, - "repository": { - "type": "git", - "url": "https://github.com/0xProject/0x-monorepo" - }, - "license": "Apache-2.0", - "engines": { - "node": ">=6.0.0" - }, - "devDependencies": { - "@0x/dev-utils": "^1.0.24", - "@0x/migrations": "^2.4.0", - "@0x/subproviders": "^2.1.11", - "@0x/tslint-config": "^2.0.2", - "@types/bintrees": "^1.0.2", - "@types/lodash": "4.14.104", - "@types/mocha": "^2.2.42", - "@types/node": "*", - "@types/sinon": "^2.2.2", - "chai": "^4.0.1", - "chai-as-promised": "^7.1.0", - "chai-bignumber": "^3.0.0", - "dirty-chai": "^2.0.1", - "make-promises-safe": "^1.1.0", - "mocha": "^4.1.0", - "npm-run-all": "^4.1.2", - "nyc": "^11.0.1", - "opn-cli": "^3.1.0", - "shx": "^0.2.2", - "sinon": "^4.0.0", - "source-map-support": "^0.5.0", - "tslint": "5.11.0", - "typescript": "3.0.1" - }, - "dependencies": { - "@0x/abi-gen-wrappers": "^2.2.0", - "@0x/assert": "^1.0.23", - "@0x/base-contract": "^3.0.13", - "@0x/contract-addresses": "^2.2.0", - "@0x/contract-artifacts": "^1.3.0", - "@0x/contract-wrappers": "^5.0.1", - "@0x/fill-scenarios": "^1.1.2", - "@0x/json-schemas": "^2.1.7", - "@0x/order-utils": "^3.1.2", - "@0x/types": "^1.5.2", - "@0x/typescript-typings": "^3.0.8", - "@0x/utils": "^3.0.1", - "@0x/web3-wrapper": "^3.2.4", - "bintrees": "^1.0.2", - "ethereum-types": "^1.1.6", - "ethereumjs-blockstream": "6.0.0", - "ethers": "~4.0.4", - "lodash": "^4.17.5", - "websocket": "^1.0.25" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts deleted file mode 100644 index 1f4e5eff1..000000000 --- a/packages/order-watcher/src/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export { OrderWatcher } from './order_watcher/order_watcher'; -export { OrderWatcherWebSocketServer } from './order_watcher/order_watcher_web_socket_server'; -export { ExpirationWatcher } from './order_watcher/expiration_watcher'; - -export { - OrderStateValid, - OrderStateInvalid, - OrderState, - ExchangeContractErrs, - ObjectMap, - OrderRelevantState, - Stats, -} from '@0x/types'; - -export { OnOrderStateChangeCallback, OrderWatcherConfig } from './types'; - -export { ContractAddresses } from '@0x/contract-addresses'; -export { SignedOrder } from '@0x/types'; -export { - JSONRPCRequestPayload, - JSONRPCErrorCallback, - Provider, - JSONRPCResponsePayload, - JSONRPCResponseError, -} 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 deleted file mode 100644 index 2ea796947..000000000 --- a/packages/order-watcher/src/order_watcher/collision_resistant_abi_decoder.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { AbiDecoder } from '@0x/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(log: LogEntry): LogWithDecodedArgs | 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 deleted file mode 100644 index d1085014c..000000000 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; - -export interface OrderHashesByMakerAddress { - [makerAddress: string]: Set; -} - -export interface OrderHashesByERC20ByMakerAddress { - [makerAddress: string]: { - [erc20TokenAddress: string]: Set; - }; -} - -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; - }; - }; -} - -/** - */ -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 = assetDataUtils.decodeAssetDataOrThrow(assetData); - const dependentOrderHashes = - decodedAssetData.assetProxyId === AssetProxyId.ERC20 - ? this._getDependentOrderHashesByERC20AssetData(makerAddress, assetData) - : this._getDependentOrderHashesByERC721AssetData(makerAddress, assetData); - return dependentOrderHashes; - } - public addToDependentOrderHashes(signedOrder: SignedOrder): void { - this._addAssetDataToDependentOrderHashes(signedOrder, signedOrder.makerAssetData); - this._addToERC20DependentOrderHashes(signedOrder, this._zrxTokenAddress); - this._addToMakerDependentOrderHashes(signedOrder); - } - public removeFromDependentOrderHashes(signedOrder: SignedOrder): void { - this._removeAssetDataFromDependentOrderHashes(signedOrder, signedOrder.makerAssetData); - // If makerToken === ZRX then we already removed it and we don't need to remove it again. - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if ( - assetDataUtils.isERC20AssetData(decodedMakerAssetData) && - decodedMakerAssetData.tokenAddress !== this._zrxTokenAddress - ) { - this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); - } - this._removeFromMakerDependentOrderhashes(signedOrder); - } - private _getDependentOrderHashesByERC20AssetData(makerAddress: string, erc20AssetData: string): string[] { - const tokenAddress = assetDataUtils.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 = assetDataUtils.decodeERC721AssetData(erc721AssetData).tokenAddress; - const tokenId = assetDataUtils.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 _addAssetDataToDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (assetDataUtils.isERC20AssetData(decodedAssetData)) { - this._addToERC20DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress); - } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { - this._addToERC721DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress, decodedAssetData.tokenId); - } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { - _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => - this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement), - ); - } - } - 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]; - } - } - private _removeAssetDataFromDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (assetDataUtils.isERC20AssetData(decodedAssetData)) { - this._removeFromERC20DependentOrderhashes(signedOrder, decodedAssetData.tokenAddress); - } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { - this._removeFromERC721DependentOrderhashes( - signedOrder, - decodedAssetData.tokenAddress, - decodedAssetData.tokenId, - ); - } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { - _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => - this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement), - ); - } - } -} diff --git a/packages/order-watcher/src/order_watcher/event_watcher.ts b/packages/order-watcher/src/order_watcher/event_watcher.ts deleted file mode 100644 index 3149d858b..000000000 --- a/packages/order-watcher/src/order_watcher/event_watcher.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { intervalUtils, logUtils } from '@0x/utils'; -import { marshaller, Web3Wrapper } from '@0x/web3-wrapper'; -import { BlockParamLiteral, FilterObject, LogEntry, Provider, RawLogEntry } from 'ethereum-types'; -import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; -import * as _ from 'lodash'; - -import { EventWatcherCallback, OrderWatcherError } from '../types'; -import { assert } from '../utils/assert'; - -const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200; - -enum LogEventState { - Removed, - Added, -} - -/** - * The EventWatcher watches for blockchain events at the specified block confirmation - * depth. - */ -export class EventWatcher { - private readonly _web3Wrapper: Web3Wrapper; - private readonly _isVerbose: boolean; - private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined; - private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; - private _onLogAddedSubscriptionToken: string | undefined; - private _onLogRemovedSubscriptionToken: string | undefined; - private readonly _pollingIntervalMs: number; - constructor( - provider: Provider, - pollingIntervalIfExistsMs: undefined | number, - stateLayer: BlockParamLiteral, - isVerbose: boolean, - ) { - this._isVerbose = isVerbose; - this._web3Wrapper = new Web3Wrapper(provider); - this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs) - ? DEFAULT_EVENT_POLLING_INTERVAL_MS - : pollingIntervalIfExistsMs; - this._blockAndLogStreamerIfExists = undefined; - this._blockAndLogStreamIntervalIfExists = undefined; - this._onLogAddedSubscriptionToken = undefined; - this._onLogRemovedSubscriptionToken = undefined; - } - public subscribe(callback: EventWatcherCallback): void { - assert.isFunction('callback', callback); - if (!_.isUndefined(this._blockAndLogStreamIntervalIfExists)) { - throw new Error(OrderWatcherError.SubscriptionAlreadyPresent); - } - this._startBlockAndLogStream(callback); - } - public unsubscribe(): void { - if (_.isUndefined(this._blockAndLogStreamIntervalIfExists)) { - throw new Error(OrderWatcherError.SubscriptionNotFound); - } - this._stopBlockAndLogStream(); - } - private _startBlockAndLogStream(callback: EventWatcherCallback): void { - if (!_.isUndefined(this._blockAndLogStreamerIfExists)) { - throw new Error(OrderWatcherError.SubscriptionAlreadyPresent); - } - this._blockAndLogStreamerIfExists = new BlockAndLogStreamer( - this._blockstreamGetBlockOrNullAsync.bind(this), - this._blockstreamGetLogsAsync.bind(this), - this._onBlockAndLogStreamerError.bind(this), - ); - const catchAllLogFilter = {}; - this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); - this._blockAndLogStreamIntervalIfExists = intervalUtils.setAsyncExcludingInterval( - this._reconcileBlockAsync.bind(this), - this._pollingIntervalMs, - this._onBlockAndLogStreamerError.bind(this), - ); - let isRemoved = false; - this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded( - this._onLogStateChangedAsync.bind(this, callback, isRemoved), - ); - isRemoved = true; - this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved( - this._onLogStateChangedAsync.bind(this, callback, isRemoved), - ); - } - // This method only exists in order to comply with the expected interface of Blockstream's constructor - private async _blockstreamGetBlockOrNullAsync(hash: string): Promise { - const shouldIncludeTransactionData = false; - const blockOrNull = await this._web3Wrapper.sendRawPayloadAsync({ - method: 'eth_getBlockByHash', - params: [hash, shouldIncludeTransactionData], - }); - return blockOrNull; - } - // This method only exists in order to comply with the expected interface of Blockstream's constructor - private async _blockstreamGetLatestBlockOrNullAsync(): Promise { - const shouldIncludeTransactionData = false; - const blockOrNull = await this._web3Wrapper.sendRawPayloadAsync({ - method: 'eth_getBlockByNumber', - params: [BlockParamLiteral.Latest, shouldIncludeTransactionData], - }); - return blockOrNull; - } - // This method only exists in order to comply with the expected interface of Blockstream's constructor - private async _blockstreamGetLogsAsync(filterOptions: FilterObject): Promise { - const logs = await this._web3Wrapper.sendRawPayloadAsync({ - method: 'eth_getLogs', - params: [filterOptions], - }); - return logs as RawLogEntry[]; - } - private _stopBlockAndLogStream(): void { - if (_.isUndefined(this._blockAndLogStreamerIfExists)) { - throw new Error(OrderWatcherError.SubscriptionNotFound); - } - this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string); - this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string); - intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamIntervalIfExists as NodeJS.Timer); - delete this._blockAndLogStreamerIfExists; - delete this._blockAndLogStreamIntervalIfExists; - } - private async _onLogStateChangedAsync( - callback: EventWatcherCallback, - isRemoved: boolean, - rawLog: RawLogEntry, - ): Promise { - const log: LogEntry = marshaller.unmarshalLog(rawLog); - await this._emitDifferencesAsync(log, isRemoved ? LogEventState.Removed : LogEventState.Added, callback); - } - private async _reconcileBlockAsync(): Promise { - const latestBlockOrNull = await this._blockstreamGetLatestBlockOrNullAsync(); - if (_.isNull(latestBlockOrNull)) { - return; // noop - } - // 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(latestBlockOrNull); - } - } - private async _emitDifferencesAsync( - log: LogEntry, - logEventState: LogEventState, - callback: EventWatcherCallback, - ): Promise { - const logEvent = { - removed: logEventState === LogEventState.Removed, - ...log, - }; - if (!_.isUndefined(this._blockAndLogStreamIntervalIfExists)) { - callback(null, logEvent); - } - } - private _onBlockAndLogStreamerError(err: Error): void { - // Since Blockstream errors are all recoverable, we simply log them if the verbose - // config is passed in. - if (this._isVerbose) { - logUtils.warn(err); - } - } -} diff --git a/packages/order-watcher/src/order_watcher/expiration_watcher.ts b/packages/order-watcher/src/order_watcher/expiration_watcher.ts deleted file mode 100644 index 82590efde..000000000 --- a/packages/order-watcher/src/order_watcher/expiration_watcher.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { BigNumber, intervalUtils } from '@0x/utils'; -import { RBTree } from 'bintrees'; -import * as _ from 'lodash'; - -import { OrderWatcherError } from '../types'; -import { utils } from '../utils/utils'; - -const DEFAULT_EXPIRATION_MARGIN_MS = 0; -const DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS = 50; - -/** - * This class includes the functionality to detect expired orders. - * It stores them in a min heap by expiration time and checks for expired ones every `orderExpirationCheckingIntervalMs` - */ -export class ExpirationWatcher { - private readonly _orderHashByExpirationRBTree: RBTree; - 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 = - orderExpirationCheckingIntervalIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS; - this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS; - this._orderExpirationCheckingIntervalMs = - expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS; - const comparator = (lhsOrderHash: string, rhsOrderHash: string) => { - const lhsExpiration = this._expiration[lhsOrderHash].toNumber(); - const rhsExpiration = this._expiration[rhsOrderHash].toNumber(); - if (lhsExpiration !== rhsExpiration) { - return lhsExpiration - rhsExpiration; - } else { - // HACK: If two orders have identical expirations, the order in which they are emitted by the - // ExpirationWatcher does not matter, so we emit them in alphabetical order by orderHash. - return lhsOrderHash.localeCompare(rhsOrderHash); - } - }; - this._orderHashByExpirationRBTree = new RBTree(comparator); - } - public subscribe(callback: (orderHash: string) => void): void { - if (!_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) { - throw new Error(OrderWatcherError.SubscriptionAlreadyPresent); - } - this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setInterval( - this._pruneExpiredOrders.bind(this, callback), - this._orderExpirationCheckingIntervalMs, - _.noop.bind(_), // _pruneExpiredOrders never throws - ); - } - public unsubscribe(): void { - if (_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) { - throw new Error(OrderWatcherError.SubscriptionNotFound); - } - intervalUtils.clearInterval(this._orderExpirationCheckingIntervalIdIfExists); - delete this._orderExpirationCheckingIntervalIdIfExists; - } - public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void { - this._expiration[orderHash] = expirationUnixTimestampMs; - this._orderHashByExpirationRBTree.insert(orderHash); - } - public removeOrder(orderHash: string): void { - if (_.isUndefined(this._expiration[orderHash])) { - return; // noop since order already removed - } - this._orderHashByExpirationRBTree.remove(orderHash); - delete this._expiration[orderHash]; - } - private _pruneExpiredOrders(callback: (orderHash: string) => void): void { - const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs(); - while (true) { - const hasNoTrackedOrders = this._orderHashByExpirationRBTree.size === 0; - if (hasNoTrackedOrders) { - break; - } - const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min(); - const hasNoExpiredOrders = this._expiration[nextOrderHashToExpire].isGreaterThan( - currentUnixTimestampMs.plus(this._expirationMarginMs), - ); - const isSubscriptionActive = _.isUndefined(this._orderExpirationCheckingIntervalIdIfExists); - if (hasNoExpiredOrders || isSubscriptionActive) { - break; - } - const orderHash = this._orderHashByExpirationRBTree.min(); - this._orderHashByExpirationRBTree.remove(orderHash); - delete this._expiration[orderHash]; - callback(orderHash); - } - } -} diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts deleted file mode 100644 index a06fd0cfe..000000000 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ /dev/null @@ -1,490 +0,0 @@ -// tslint:disable:no-unnecessary-type-assertion -import { ContractAddresses } from '@0x/contract-addresses'; -import * as artifacts from '@0x/contract-artifacts'; -import { - AssetBalanceAndProxyAllowanceFetcher, - ContractWrappers, - ERC20TokenApprovalEventArgs, - ERC20TokenEventArgs, - ERC20TokenEvents, - ERC20TokenTransferEventArgs, - ERC721TokenApprovalEventArgs, - ERC721TokenApprovalForAllEventArgs, - ERC721TokenEventArgs, - ERC721TokenEvents, - ERC721TokenTransferEventArgs, - ExchangeCancelEventArgs, - ExchangeCancelUpToEventArgs, - ExchangeEventArgs, - ExchangeEvents, - ExchangeFillEventArgs, - OrderFilledCancelledFetcher, - WETH9DepositEventArgs, - WETH9EventArgs, - WETH9Events, - WETH9WithdrawalEventArgs, -} from '@0x/contract-wrappers'; -import { schemas } from '@0x/json-schemas'; -import { - assetDataUtils, - BalanceAndProxyAllowanceLazyStore, - OrderFilledCancelledLazyStore, - orderHashUtils, - OrderStateUtils, -} from '@0x/order-utils'; -import { AssetProxyId, ExchangeContractErrs, OrderState, SignedOrder, Stats } from '@0x/types'; -import { errorUtils, intervalUtils } from '@0x/utils'; -import { BlockParamLiteral, LogEntryEvent, LogWithDecodedArgs, Provider } from 'ethereum-types'; -import * as _ from 'lodash'; - -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'; - -const MILLISECONDS_IN_A_SECOND = 1000; - -type ContractEventArgs = WETH9EventArgs | ExchangeEventArgs | ERC20TokenEventArgs | ERC721TokenEventArgs; - -interface OrderByOrderHash { - [orderHash: string]: SignedOrder; -} - -interface OrderStateByOrderHash { - [orderHash: string]: OrderState; -} - -const DEFAULT_ORDER_WATCHER_CONFIG: OrderWatcherConfig = { - orderExpirationCheckingIntervalMs: 50, - eventPollingIntervalMs: 200, - expirationMarginMs: 0, - // tslint:disable-next-line:custom-no-magic-numbers - cleanupJobIntervalMs: 1000 * 60 * 60, // 1h - isVerbose: true, -}; -const STATE_LAYER = BlockParamLiteral.Latest; - -/** - * This class includes all the functionality related to watching a set of orders - * for potential changes in order validity/fillability. The orderWatcher notifies - * the subscriber of these changes so that a final decision can be made on whether - * the order should be deemed invalid. - */ -export class OrderWatcher { - 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; - private _callbackIfExists?: OnOrderStateChangeCallback; - /** - * Instantiate a new OrderWatcher - * @param provider Web3 provider to use for JSON RPC calls - * @param networkId NetworkId to watch orders on - * @param contractAddresses Optional contract addresses. Defaults to known - * addresses based on networkId. - * @param partialConfig Optional configurations - */ - constructor( - provider: Provider, - networkId: number, - contractAddresses?: ContractAddresses, - partialConfig: Partial = 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.compilerOutput.abi, - artifacts.ERC721Token.compilerOutput.abi, - [artifacts.WETH9.compilerOutput.abi, artifacts.Exchange.compilerOutput.abi], - ); - const contractWrappers = new ContractWrappers(provider, { - networkId, - // Note(albrow): We let the contract-wrappers package handle - // default values for contractAddresses. - contractAddresses, - }); - this._eventWatcher = new EventWatcher(provider, config.eventPollingIntervalMs, STATE_LAYER, config.isVerbose); - const balanceAndProxyAllowanceFetcher = new AssetBalanceAndProxyAllowanceFetcher( - contractWrappers.erc20Token, - contractWrappers.erc721Token, - STATE_LAYER, - ); - this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - balanceAndProxyAllowanceFetcher, - ); - const orderFilledCancelledFetcher = new OrderFilledCancelledFetcher(contractWrappers.exchange, STATE_LAYER); - this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(orderFilledCancelledFetcher); - this._orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); - const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs; - this._expirationWatcher = new ExpirationWatcher( - expirationMarginIfExistsMs, - config.orderExpirationCheckingIntervalMs, - ); - this._cleanupJobInterval = config.cleanupJobIntervalMs; - const zrxTokenAddress = assetDataUtils.decodeERC20AssetData(orderFilledCancelledFetcher.getZRXAssetData()) - .tokenAddress; - this._dependentOrderHashesTracker = new DependentOrderHashesTracker(zrxTokenAddress); - } - /** - * Add an order to the orderWatcher. Before the order is added, it's - * signature is verified. - * @param signedOrder The order you wish to start watching. - */ - public async addOrderAsync(signedOrder: SignedOrder): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await 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); - - const orderAssetDatas = [signedOrder.makerAssetData, signedOrder.takerAssetData]; - _.each(orderAssetDatas, assetData => this._addAssetDataToAbiDecoder(assetData)); - } - /** - * Removes an order from the orderWatcher - * @param orderHash The orderHash of the order you wish to stop watching. - */ - public removeOrder(orderHash: string): void { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); - const signedOrder = this._orderByOrderHash[orderHash]; - if (_.isUndefined(signedOrder)) { - return; // noop - } - this._dependentOrderHashesTracker.removeFromDependentOrderHashes(signedOrder); - delete this._orderByOrderHash[orderHash]; - this._expirationWatcher.removeOrder(orderHash); - delete this._orderStateByOrderHashCache[orderHash]; - } - /** - * Starts an orderWatcher subscription. The callback will be called every time a watched order's - * backing blockchain state has changed. This is a call-to-action for the caller to re-validate the order. - * @param callback Receives the orderHash of the order that should be re-validated, together - * with all the order-relevant blockchain state needed to re-validate the order. - */ - public subscribe(callback: OnOrderStateChangeCallback): void { - assert.isFunction('callback', callback); - if (!_.isUndefined(this._callbackIfExists)) { - throw new Error(OrderWatcherError.SubscriptionAlreadyPresent); - } - this._callbackIfExists = callback; - this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); - this._expirationWatcher.subscribe(this._onOrderExpired.bind(this)); - this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval( - this._cleanupAsync.bind(this), - this._cleanupJobInterval, - (err: Error) => { - this.unsubscribe(); - callback(err); - }, - ); - } - /** - * Ends an orderWatcher subscription. - */ - public unsubscribe(): void { - if (_.isUndefined(this._callbackIfExists) || _.isUndefined(this._cleanupJobIntervalIdIfExists)) { - throw new Error(OrderWatcherError.SubscriptionNotFound); - } - this._balanceAndProxyAllowanceLazyStore.deleteAll(); - this._orderFilledCancelledLazyStore.deleteAll(); - delete this._callbackIfExists; - this._eventWatcher.unsubscribe(); - this._expirationWatcher.unsubscribe(); - intervalUtils.clearAsyncExcludingInterval(this._cleanupJobIntervalIdIfExists); - } - /** - * Gets statistics of the OrderWatcher Instance. - */ - public getStats(): Stats { - return { - orderCount: _.size(this._orderByOrderHash), - }; - } - private async _cleanupAsync(): Promise { - for (const orderHash of _.keys(this._orderByOrderHash)) { - this._cleanupOrderRelatedState(orderHash); - await this._emitRevalidateOrdersAsync([orderHash]); - } - } - private _addAssetDataToAbiDecoder(assetData: string): void { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (assetDataUtils.isERC20AssetData(decodedAssetData)) { - this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress); - } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { - this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress); - } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { - _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => - this._addAssetDataToAbiDecoder(nestedAssetDataElement), - ); - } - } - private _deleteLazyStoreBalance(assetData: string, userAddress: string): void { - const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (assetProxyId) { - case AssetProxyId.ERC20: - case AssetProxyId.ERC721: - this._balanceAndProxyAllowanceLazyStore.deleteBalance(assetData, userAddress); - break; - case AssetProxyId.MultiAsset: - const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); - _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => - this._deleteLazyStoreBalance(nestedAssetDataElement, userAddress), - ); - break; - default: - break; - } - } - private _deleteLazyStoreProxyAllowance(assetData: string, userAddress: string): void { - const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (assetProxyId) { - case AssetProxyId.ERC20: - case AssetProxyId.ERC721: - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(assetData, userAddress); - break; - case AssetProxyId.MultiAsset: - const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); - _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => - this._deleteLazyStoreProxyAllowance(nestedAssetDataElement, userAddress), - ); - break; - default: - break; - } - } - private _cleanupOrderRelatedState(orderHash: string): void { - const signedOrder = this._orderByOrderHash[orderHash]; - - this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash); - this._orderFilledCancelledLazyStore.deleteIsCancelled(orderHash); - - this._deleteLazyStoreBalance(signedOrder.makerAssetData, signedOrder.makerAddress); - this._deleteLazyStoreProxyAllowance(signedOrder.makerAssetData, signedOrder.makerAddress); - this._deleteLazyStoreBalance(signedOrder.takerAssetData, signedOrder.takerAddress); - this._deleteLazyStoreProxyAllowance(signedOrder.takerAssetData, signedOrder.takerAddress); - - const zrxAssetData = this._orderFilledCancelledLazyStore.getZRXAssetData(); - if (!signedOrder.makerFee.isZero()) { - this._deleteLazyStoreBalance(zrxAssetData, signedOrder.makerAddress); - this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.makerAddress); - } - if (!signedOrder.takerFee.isZero()) { - this._deleteLazyStoreBalance(zrxAssetData, signedOrder.takerAddress); - this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.takerAddress); - } - } - private _onOrderExpired(orderHash: string): void { - const orderState: OrderState = { - isValid: false, - orderHash, - error: ExchangeContractErrs.OrderFillExpired, - }; - if (!_.isUndefined(this._orderByOrderHash[orderHash])) { - this.removeOrder(orderHash); - if (!_.isUndefined(this._callbackIfExists)) { - this._callbackIfExists(null, orderState); - } - } - } - private async _onEventWatcherCallbackAsync(err: Error | null, logIfExists?: LogEntryEvent): Promise { - if (!_.isNull(err)) { - if (!_.isUndefined(this._callbackIfExists)) { - this._callbackIfExists(err); - } - return; - } - const maybeDecodedLog = this._collisionResistantAbiDecoder.tryToDecodeLogOrNoop( - // 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).event); - if (!isLogDecoded) { - return; // noop - } - const decodedLog = (maybeDecodedLog as any) as LogWithDecodedArgs; - const transactionHash = decodedLog.transactionHash; - switch (decodedLog.event) { - 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 = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._owner, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - break; - } else { - // ERC721 - // Invalidate cache - const args = decodedLog.args as ERC721TokenApprovalEventArgs; - const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._owner, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - break; - } - } - 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 = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._deleteLazyStoreBalance(tokenAssetData, args._from); - this._deleteLazyStoreBalance(tokenAssetData, args._to); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._from, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - break; - } else { - // ERC721 - // Invalidate cache - const args = decodedLog.args as ERC721TokenTransferEventArgs; - const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._deleteLazyStoreBalance(tokenAssetData, args._from); - this._deleteLazyStoreBalance(tokenAssetData, args._to); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._from, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - 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, transactionHash); - break; - } - case WETH9Events.Deposit: { - // Invalidate cache - const args = decodedLog.args as WETH9DepositEventArgs; - const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._deleteLazyStoreBalance(tokenAssetData, args._owner); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._owner, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - break; - } - case WETH9Events.Withdrawal: { - // Invalidate cache - const args = decodedLog.args as WETH9WithdrawalEventArgs; - const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._deleteLazyStoreBalance(tokenAssetData, args._owner); - // Revalidate orders - const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( - args._owner, - tokenAssetData, - ); - await this._emitRevalidateOrdersAsync(orderHashes, transactionHash); - break; - } - case ExchangeEvents.Fill: { - // Invalidate cache - const args = decodedLog.args as ExchangeFillEventArgs; - this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); - // Revalidate orders - const orderHash = args.orderHash; - const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); - if (isOrderWatched) { - await this._emitRevalidateOrdersAsync([orderHash], transactionHash); - } - break; - } - case ExchangeEvents.Cancel: { - // Invalidate cache - const args = decodedLog.args as ExchangeCancelEventArgs; - this._orderFilledCancelledLazyStore.deleteIsCancelled(args.orderHash); - // Revalidate orders - const orderHash = args.orderHash; - const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]); - if (isOrderWatched) { - await this._emitRevalidateOrdersAsync([orderHash], transactionHash); - } - break; - } - 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, transactionHash); - break; - } - - default: - throw errorUtils.spawnSwitchErr('decodedLog.event', decodedLog.event); - } - } - private async _emitRevalidateOrdersAsync(orderHashes: string[], transactionHash?: string): Promise { - for (const orderHash of orderHashes) { - 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.getOpenOrderStateAsync(signedOrder, transactionHash); - if (_.isUndefined(this._callbackIfExists)) { - break; // Unsubscribe was called - } - if (_.isEqual(orderState, this._orderStateByOrderHashCache[orderHash])) { - // Actual order state didn't change - continue; - } else { - this._orderStateByOrderHashCache[orderHash] = orderState; - } - this._callbackIfExists(null, orderState); - } - } -} diff --git a/packages/order-watcher/src/order_watcher/order_watcher_web_socket_server.ts b/packages/order-watcher/src/order_watcher/order_watcher_web_socket_server.ts deleted file mode 100644 index b75b07603..000000000 --- a/packages/order-watcher/src/order_watcher/order_watcher_web_socket_server.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { ContractAddresses } from '@0x/contract-addresses'; -import { schemas } from '@0x/json-schemas'; -import { OrderStateInvalid, OrderStateValid, SignedOrder } from '@0x/types'; -import { BigNumber, logUtils } from '@0x/utils'; -import { Provider } from 'ethereum-types'; -import * as http from 'http'; -import * as WebSocket from 'websocket'; - -import { GetStatsResult, OrderWatcherConfig, OrderWatcherMethod, WebSocketRequest, WebSocketResponse } from '../types'; -import { assert } from '../utils/assert'; - -import { OrderWatcher } from './order_watcher'; - -const DEFAULT_HTTP_PORT = 8080; -const JSON_RPC_VERSION = '2.0'; - -// Wraps the OrderWatcher functionality in a WebSocket server. Motivations: -// 1) Users can watch orders via non-typescript programs. -// 2) Better encapsulation so that users can work -export class OrderWatcherWebSocketServer { - private readonly _orderWatcher: OrderWatcher; - private readonly _httpServer: http.Server; - private readonly _connectionStore: Set; - private readonly _wsServer: WebSocket.server; - private readonly _isVerbose: boolean; - /** - * Recover types lost when the payload is stringified. - */ - private static _parseSignedOrder(rawRequest: any): SignedOrder { - const bigNumberFields = [ - 'salt', - 'makerFee', - 'takerFee', - 'makerAssetAmount', - 'takerAssetAmount', - 'expirationTimeSeconds', - ]; - for (const field of bigNumberFields) { - rawRequest[field] = new BigNumber(rawRequest[field]); - } - return rawRequest; - } - - /** - * Instantiate a new WebSocket server which provides OrderWatcher functionality - * @param provider Web3 provider to use for JSON RPC calls. - * @param networkId NetworkId to watch orders on. - * @param contractAddresses Optional contract addresses. Defaults to known - * addresses based on networkId. - * @param orderWatcherConfig OrderWatcher configurations. isVerbose sets the verbosity for the WebSocket server aswell. - * @param isVerbose Whether to enable verbose logging. Defaults to true. - */ - constructor( - provider: Provider, - networkId: number, - contractAddresses?: ContractAddresses, - orderWatcherConfig?: Partial, - ) { - this._isVerbose = - orderWatcherConfig !== undefined && orderWatcherConfig.isVerbose !== undefined - ? orderWatcherConfig.isVerbose - : true; - this._orderWatcher = new OrderWatcher(provider, networkId, contractAddresses, orderWatcherConfig); - this._connectionStore = new Set(); - this._httpServer = http.createServer(); - this._wsServer = new WebSocket.server({ - httpServer: this._httpServer, - // Avoid setting autoAcceptConnections to true as it defeats all - // standard cross-origin protection facilities built into the protocol - // and the browser. - // Source: https://www.npmjs.com/package/websocket#server-example - // Also ensures that a request event is emitted by - // the server whenever a new WebSocket request is made. - autoAcceptConnections: false, - }); - - this._wsServer.on('request', async (request: any) => { - // Designed for usage pattern where client and server are run on the same - // machine by the same user. As such, no security checks are in place. - const connection: WebSocket.connection = request.accept(null, request.origin); - this._log(`${new Date()} [Server] Accepted connection from origin ${request.origin}.`); - connection.on('message', this._onMessageCallbackAsync.bind(this, connection)); - connection.on('close', this._onCloseCallback.bind(this, connection)); - this._connectionStore.add(connection); - }); - } - - /** - * Activates the WebSocket server by subscribing to the OrderWatcher and - * starting the WebSocket's HTTP server - */ - public start(): void { - // Have the WebSocket server subscribe to the OrderWatcher to receive updates. - // These updates are then broadcast to clients in the _connectionStore. - this._orderWatcher.subscribe(this._broadcastCallback.bind(this)); - - const port = process.env.ORDER_WATCHER_HTTP_PORT || DEFAULT_HTTP_PORT; - this._httpServer.listen(port, () => { - this._log(`${new Date()} [Server] Listening on port ${port}`); - }); - } - - /** - * Deactivates the WebSocket server by stopping the HTTP server from accepting - * new connections and unsubscribing from the OrderWatcher - */ - public stop(): void { - this._httpServer.close(); - this._orderWatcher.unsubscribe(); - } - - private _log(...args: any[]): void { - if (this._isVerbose) { - logUtils.log(...args); - } - } - - private async _onMessageCallbackAsync(connection: WebSocket.connection, message: any): Promise { - let response: WebSocketResponse; - let id: number | null = null; - try { - assert.doesConformToSchema('message', message, schemas.orderWatcherWebSocketUtf8MessageSchema); - const request: WebSocketRequest = JSON.parse(message.utf8Data); - id = request.id; - assert.doesConformToSchema('request', request, schemas.orderWatcherWebSocketRequestSchema); - assert.isString(request.jsonrpc, JSON_RPC_VERSION); - response = { - id, - jsonrpc: JSON_RPC_VERSION, - method: request.method, - result: await this._routeRequestAsync(request), - }; - } catch (err) { - response = { - id, - jsonrpc: JSON_RPC_VERSION, - method: null, - error: err.toString(), - }; - } - this._log(`${new Date()} [Server] OrderWatcher output: ${JSON.stringify(response)}`); - connection.sendUTF(JSON.stringify(response)); - } - - private _onCloseCallback(connection: WebSocket.connection): void { - this._connectionStore.delete(connection); - this._log(`${new Date()} [Server] Client ${connection.remoteAddress} disconnected.`); - } - - private async _routeRequestAsync(request: WebSocketRequest): Promise { - this._log(`${new Date()} [Server] Request received: ${request.method}`); - switch (request.method) { - case OrderWatcherMethod.AddOrder: { - const signedOrder: SignedOrder = OrderWatcherWebSocketServer._parseSignedOrder( - request.params.signedOrder, - ); - await this._orderWatcher.addOrderAsync(signedOrder); - break; - } - case OrderWatcherMethod.RemoveOrder: { - this._orderWatcher.removeOrder(request.params.orderHash || 'undefined'); - break; - } - case OrderWatcherMethod.GetStats: { - return this._orderWatcher.getStats(); - } - default: - // Should never reach here. Should be caught by JSON schema check. - throw new Error(`Unexpected default case hit for request.method`); - } - return undefined; - } - - /** - * Broadcasts OrderState changes to ALL connected clients. At the moment, - * we do not support clients subscribing to only a subset of orders. As such, - * Client B will be notified of changes to an order that Client A added. - */ - private _broadcastCallback(err: Error | null, orderState?: OrderStateValid | OrderStateInvalid | undefined): void { - const method = OrderWatcherMethod.Update; - const response = - err === null - ? { - jsonrpc: JSON_RPC_VERSION, - method, - result: orderState, - } - : { - jsonrpc: JSON_RPC_VERSION, - method, - error: { - code: -32000, - message: err.message, - }, - }; - this._connectionStore.forEach((connection: WebSocket.connection) => { - connection.sendUTF(JSON.stringify(response)); - }); - } -} diff --git a/packages/order-watcher/src/schemas/order_watcher_partial_config_schema.ts b/packages/order-watcher/src/schemas/order_watcher_partial_config_schema.ts deleted file mode 100644 index 8bfced063..000000000 --- a/packages/order-watcher/src/schemas/order_watcher_partial_config_schema.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const orderWatcherPartialConfigSchema = { - id: '/OrderWatcherPartialConfigSchema', - properties: { - stateLayer: { $ref: '/blockParamSchema' }, - orderExpirationCheckingIntervalMs: { type: 'number' }, - eventPollingIntervalMs: { type: 'number' }, - expirationMarginMs: { type: 'number' }, - cleanupJobIntervalMs: { type: 'number' }, - isVerbose: { type: 'boolean' }, - }, - type: 'object', - required: [], -}; diff --git a/packages/order-watcher/src/server.ts b/packages/order-watcher/src/server.ts deleted file mode 100644 index 1d31e87ab..000000000 --- a/packages/order-watcher/src/server.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses'; -import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders'; -import * as _ from 'lodash'; - -import { OrderWatcherWebSocketServer } from './order_watcher/order_watcher_web_socket_server'; - -const GANACHE_NETWORK_ID = 50; -const DEFAULT_RPC_URL = 'http://localhost:8545'; - -const provider = new Web3ProviderEngine(); -const jsonRpcUrl = process.env.JSON_RPC_URL || DEFAULT_RPC_URL; -const rpcSubprovider = new RPCSubprovider(jsonRpcUrl); -provider.addProvider(rpcSubprovider); -provider.start(); - -const networkId = process.env.NETWORK_ID !== undefined ? _.parseInt(process.env.NETWORK_ID) : GANACHE_NETWORK_ID; - -const contractAddressesString = process.env.contractAddresses; -const contractAddressesIfExists = - contractAddressesString === undefined - ? getContractAddressesForNetworkOrThrow(networkId) - : JSON.parse(contractAddressesString); - -const orderWatcherConfig: any = { - isVerbose: process.env.IS_VERBOSE === 'true', -}; -const orderExpirationCheckingIntervalMs = process.env.ORDER_EXPIRATION_CHECKING_INTERVAL_MS; -if (orderExpirationCheckingIntervalMs !== undefined) { - orderWatcherConfig.orderExpirationCheckingIntervalMs = _.parseInt(orderExpirationCheckingIntervalMs); -} -const eventPollingIntervalMs = process.env.EVENT_POLLING_INTERVAL_MS; -if (eventPollingIntervalMs !== undefined) { - orderWatcherConfig.eventPollingIntervalMs = _.parseInt(eventPollingIntervalMs); -} -const expirationMarginMs = process.env.EXPIRATION_MARGIN_MS; -if (expirationMarginMs !== undefined) { - orderWatcherConfig.expirationMarginMs = _.parseInt(expirationMarginMs); -} -const cleanupJobIntervalMs = process.env.CLEANUP_JOB_INTERVAL_MS; -if (cleanupJobIntervalMs !== undefined) { - orderWatcherConfig.cleanupJobIntervalMs = _.parseInt(cleanupJobIntervalMs); -} -const wsServer = new OrderWatcherWebSocketServer(provider, networkId, contractAddressesIfExists, orderWatcherConfig); -wsServer.start(); diff --git a/packages/order-watcher/src/types.ts b/packages/order-watcher/src/types.ts deleted file mode 100644 index 2b529a939..000000000 --- a/packages/order-watcher/src/types.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { OrderState, SignedOrder } from '@0x/types'; -import { LogEntryEvent } from 'ethereum-types'; - -export enum OrderWatcherError { - SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT', - SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', -} - -export type EventWatcherCallback = (err: null | Error, log?: LogEntryEvent) => void; - -/** - * 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. - * isVerbose: Weather the order watcher should be verbose. Default=true. - */ -export interface OrderWatcherConfig { - orderExpirationCheckingIntervalMs: number; - eventPollingIntervalMs: number; - expirationMarginMs: number; - cleanupJobIntervalMs: number; - isVerbose: boolean; -} - -export type OnOrderStateChangeCallback = (err: Error | null, orderState?: OrderState) => void; - -export enum InternalOrderWatcherError { - NoAbiDecoder = 'NO_ABI_DECODER', - ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY', - WethNotInTokenRegistry = 'WETH_NOT_IN_TOKEN_REGISTRY', -} - -export enum OrderWatcherMethod { - // Methods initiated by the user. - GetStats = 'GET_STATS', - AddOrder = 'ADD_ORDER', - RemoveOrder = 'REMOVE_ORDER', - // These are spontaneous; they are primarily orderstate changes. - Update = 'UPDATE', - // `subscribe` and `unsubscribe` are methods of OrderWatcher, but we don't - // need to expose them to the WebSocket server user because the user implicitly - // subscribes and unsubscribes by connecting and disconnecting from the server. -} - -// Users have to create a json object of this format and attach it to -// the data field of their WebSocket message to interact with the server. -export type WebSocketRequest = AddOrderRequest | RemoveOrderRequest | GetStatsRequest; - -export interface AddOrderRequest { - id: number; - jsonrpc: string; - method: OrderWatcherMethod.AddOrder; - params: { signedOrder: SignedOrder }; -} - -export interface RemoveOrderRequest { - id: number; - jsonrpc: string; - method: OrderWatcherMethod.RemoveOrder; - params: { orderHash: string }; -} - -export interface GetStatsRequest { - id: number; - jsonrpc: string; - method: OrderWatcherMethod.GetStats; -} - -// Users should expect a json object of this format in the data field -// of the WebSocket messages that the server sends out. -export type WebSocketResponse = SuccessfulWebSocketResponse | ErrorWebSocketResponse; - -export interface SuccessfulWebSocketResponse { - id: number; - jsonrpc: string; - method: OrderWatcherMethod; - result: OrderState | GetStatsResult | undefined; // result is undefined for ADD_ORDER and REMOVE_ORDER -} - -export interface ErrorWebSocketResponse { - id: number | null; - jsonrpc: string; - method: null; - error: JSONRPCError; -} - -export interface JSONRPCError { - code: number; - message: string; - data?: string | object; -} - -export interface GetStatsResult { - orderCount: number; -} diff --git a/packages/order-watcher/src/utils/assert.ts b/packages/order-watcher/src/utils/assert.ts deleted file mode 100644 index ccfc7325c..000000000 --- a/packages/order-watcher/src/utils/assert.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { assert as sharedAssert } from '@0x/assert'; -// HACK: We need those two unused imports because they're actually used by sharedAssert which gets injected here -// tslint:disable:no-unused-variable -import { Schema } from '@0x/json-schemas'; -import { ECSignature } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -// tslint:enable:no-unused-variable -import { Provider } from 'ethereum-types'; - -import { signatureUtils } from '@0x/order-utils'; - -export const assert = { - ...sharedAssert, - async isValidSignatureAsync( - provider: Provider, - orderHash: string, - signature: string, - signerAddress: string, - ): Promise { - const isValid = await signatureUtils.isValidSignatureAsync(provider, orderHash, signature, signerAddress); - assert.assert(isValid, `Expected order with hash '${orderHash}' to have a valid signature`); - }, -}; diff --git a/packages/order-watcher/src/utils/utils.ts b/packages/order-watcher/src/utils/utils.ts deleted file mode 100644 index 9c3849ff1..000000000 --- a/packages/order-watcher/src/utils/utils.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -export const utils = { - getCurrentUnixTimestampSec(): BigNumber { - const milisecondsInASecond = 1000; - return new BigNumber(Date.now() / milisecondsInASecond).integerValue(); - }, - getCurrentUnixTimestampMs(): BigNumber { - return new BigNumber(Date.now()); - }, -}; diff --git a/packages/order-watcher/test/expiration_watcher_test.ts b/packages/order-watcher/test/expiration_watcher_test.ts deleted file mode 100644 index fb5ea2a27..000000000 --- a/packages/order-watcher/test/expiration_watcher_test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; -import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils'; -import { FillScenarios } from '@0x/fill-scenarios'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { DoneCallback } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; -import * as Sinon from 'sinon'; - -import { ExpirationWatcher } from '../src/order_watcher/expiration_watcher'; -import { utils } from '../src/utils/utils'; - -import { chaiSetup } from './utils/chai_setup'; -import { migrateOnceAsync } from './utils/migrate'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const MILISECONDS_IN_SECOND = 1000; - -describe('ExpirationWatcher', () => { - let userAddresses: string[]; - let fillScenarios: FillScenarios; - let makerAssetData: string; - let takerAssetData: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const fillableAmount = new BigNumber(5); - let currentUnixTimestampSec: BigNumber; - let timer: Sinon.SinonFakeTimers; - let expirationWatcher: ExpirationWatcher; - before(async () => { - const contractAddresses = await migrateOnceAsync(); - await blockchainLifecycle.startAsync(); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - fillScenarios = new FillScenarios( - provider, - userAddresses, - contractAddresses.zrxToken, - contractAddresses.exchange, - contractAddresses.erc20Proxy, - contractAddresses.erc721Proxy, - ); - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - const [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - [makerAssetData, takerAssetData] = [ - assetDataUtils.encodeERC20AssetData(makerTokenAddress), - assetDataUtils.encodeERC20AssetData(takerTokenAddress), - ]; - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - const sinonTimerConfig = { shouldAdvanceTime: true } as any; - // This constructor has incorrect types - timer = Sinon.useFakeTimers(sinonTimerConfig); - currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - expirationWatcher = new ExpirationWatcher(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - timer.restore(); - expirationWatcher.unsubscribe(); - }); - it('correctly emits events when order expires', (done: DoneCallback) => { - (async () => { - const orderLifetimeSec = 60; - const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - expirationUnixTimestampSec, - ); - 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); - }); - expirationWatcher.subscribe(callbackAsync); - timer.tick(orderLifetimeSec * MILISECONDS_IN_SECOND); - })().catch(done); - }); - it("doesn't emit events before order expires", (done: DoneCallback) => { - (async () => { - const orderLifetimeSec = 60; - const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - expirationUnixTimestampSec, - ); - 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')); - }); - expirationWatcher.subscribe(callbackAsync); - const notEnoughTime = orderLifetimeSec - 1; - timer.tick(notEnoughTime * MILISECONDS_IN_SECOND); - done(); - })().catch(done); - }); - it('emits events in correct order', (done: DoneCallback) => { - (async () => { - const order1Lifetime = 60; - const order2Lifetime = 120; - const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime); - const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime); - const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - order1ExpirationUnixTimestampSec, - ); - const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - order2ExpirationUnixTimestampSec, - ); - 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)( - (hash: string) => { - const orderHash = expirationOrder.shift(); - expect(hash).to.be.equal(orderHash); - if (_.isEmpty(expirationOrder)) { - done(); - } - }, - ); - expirationWatcher.subscribe(callbackAsync); - timer.tick(order2Lifetime * MILISECONDS_IN_SECOND); - })().catch(done); - }); - it('emits events in correct order when expirations are equal', (done: DoneCallback) => { - (async () => { - const order1Lifetime = 60; - const order2Lifetime = 60; - const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime); - const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime); - const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - order1ExpirationUnixTimestampSec, - ); - const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - order2ExpirationUnixTimestampSec, - ); - 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)( - (hash: string) => { - const orderHash = expirationOrder.shift(); - expect(hash).to.be.equal(orderHash); - if (_.isEmpty(expirationOrder)) { - done(); - } - }, - ); - expirationWatcher.subscribe(callbackAsync); - timer.tick(order2Lifetime * MILISECONDS_IN_SECOND); - })().catch(done); - }); -}); diff --git a/packages/order-watcher/test/global_hooks.ts b/packages/order-watcher/test/global_hooks.ts deleted file mode 100644 index 26c37158f..000000000 --- a/packages/order-watcher/test/global_hooks.ts +++ /dev/null @@ -1,6 +0,0 @@ -before('set up mocha', async function(): Promise { - // HACK: Since the migrations take longer then our global mocha timeout limit - // we manually increase it for this before hook. - const mochaTestTimeoutMs = 25000; - this.timeout(mochaTestTimeoutMs); // tslint:disable-line:no-invalid-this -}); diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts deleted file mode 100644 index 28b564b32..000000000 --- a/packages/order-watcher/test/order_watcher_test.ts +++ /dev/null @@ -1,887 +0,0 @@ -// tslint:disable:no-unnecessary-type-assertion -import { ContractWrappers } from '@0x/contract-wrappers'; -import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; -import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils'; -import { FillScenarios } from '@0x/fill-scenarios'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { - DoneCallback, - ExchangeContractErrs, - OrderState, - OrderStateInvalid, - OrderStateValid, - SignedOrder, -} from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -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 { migrateOnceAsync } from './utils/migrate'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -const TIMEOUT_MS = 150; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('OrderWatcher', () => { - let contractWrappers: ContractWrappers; - let fillScenarios: FillScenarios; - let userAddresses: string[]; - let zrxTokenAddress: 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 contractAddresses = await migrateOnceAsync(); - await blockchainLifecycle.startAsync(); - const networkId = constants.TESTRPC_NETWORK_ID; - const config = { - networkId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - zrxTokenAddress = contractAddresses.zrxToken; - fillScenarios = new FillScenarios( - provider, - userAddresses, - zrxTokenAddress, - contractAddresses.exchange, - contractAddresses.erc20Proxy, - contractAddresses.erc721Proxy, - ); - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - [makerAssetData, takerAssetData] = [ - assetDataUtils.encodeERC20AssetData(makerTokenAddress), - assetDataUtils.encodeERC20AssetData(takerTokenAddress), - ]; - const orderWatcherConfig = {}; - orderWatcher = new OrderWatcher(provider, networkId, contractAddresses, orderWatcherConfig); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('#removeOrder', async () => { - it('should successfully remove existing order', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(signedOrder); - expect((orderWatcher as any)._orderByOrderHash).to.include({ - [orderHash]: signedOrder, - }); - 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, - }); - 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( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const nonExistentOrderHash = `0x${orderHash - .substr(2) - .split('') - .reverse() - .join('')}`; - orderWatcher.removeOrder(nonExistentOrderHash); - }); - }); - describe('#subscribe', async () => { - afterEach(async () => { - orderWatcher.unsubscribe(); - }); - it('should fail when trying to subscribe twice', async () => { - orderWatcher.subscribe(_.noop.bind(_)); - expect(() => orderWatcher.subscribe(_.noop.bind(_))).to.throw(OrderWatcherError.SubscriptionAlreadyPresent); - }); - }); - describe('#getStats', async () => { - it('orderCount should increment and decrement with order additions and removals', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - expect(orderWatcher.getStats().orderCount).to.be.eq(0); - await orderWatcher.addOrderAsync(signedOrder); - expect(orderWatcher.getStats().orderCount).to.be.eq(1); - orderWatcher.removeOrder(orderHash); - expect(orderWatcher.getStats().orderCount).to.be.eq(0); - }); - }); - describe('tests with cleanup', async () => { - afterEach(async () => { - orderWatcher.unsubscribe(); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - orderWatcher.removeOrder(orderHash); - }); - it('should emit orderStateInvalid when makerAddress allowance set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - 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( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - await orderWatcher.addOrderAsync(signedOrder); - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((_orderState: OrderState) => { - throw new Error('OrderState callback fired for irrelevant order'); - }); - orderWatcher.subscribe(callback); - const notTheMaker = userAddresses[0]; - const anyRecipient = takerAddress; - const transferAmount = new BigNumber(2); - await contractWrappers.erc20Token.transferAsync( - makerTokenAddress, - notTheMaker, - anyRecipient, - transferAmount, - ); - setTimeout(() => { - done(); - }, TIMEOUT_MS); - })().catch(done); - }); - it('should emit orderStateInvalid when makerAddress moves balance backing watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerBalance); - }); - orderWatcher.subscribe(callback); - 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( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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); - }); - orderWatcher.subscribe(callback); - - await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); - })().catch(done); - }); - it('should include transactionHash in emitted orderStateInvalid when watched order fully filled', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - await orderWatcher.addOrderAsync(signedOrder); - - let transactionHash: string; - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { - expect(orderState.isValid).to.be.false(); - const invalidOrderState = orderState as OrderStateInvalid; - expect(invalidOrderState.transactionHash).to.be.equal(transactionHash); - }); - orderWatcher.subscribe(callback); - - transactionHash = 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( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - - const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress); - const fillAmountInBaseUnits = new BigNumber(2); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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; - const remainingMakerBalance = makerBalance.minus(fillAmountInBaseUnits); - const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits); - expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - remainingFillable, - ); - expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal( - remainingFillable, - ); - expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.exchange.fillOrderAsync(signedOrder, fillAmountInBaseUnits, takerAddress); - })().catch(done); - }); - it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => { - (async () => { - const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); - const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals); - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - takerAddress, - ); - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(); - await orderWatcher.addOrderAsync(signedOrder); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.setProxyAllowanceAsync( - zrxTokenAddress, - makerAddress, - new BigNumber(0), - ); - })().catch(done); - }); - describe('remainingFillable(M|T)akerTokenAmount', () => { - it('should calculate correct remaining fillable', (done: DoneCallback) => { - (async () => { - const takerFillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), decimals); - const makerFillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(20), decimals); - signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - makerFillableAmount, - takerFillableAmount, - ); - const fillAmountInBaseUnits = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - Web3Wrapper.toBaseUnitAmount(new BigNumber(16), decimals), - ); - expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal( - Web3Wrapper.toBaseUnitAmount(new BigNumber(8), decimals), - ); - }); - orderWatcher.subscribe(callback); - 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( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - - const changedMakerApprovalAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), decimals); - await orderWatcher.addOrderAsync(signedOrder); - - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { - const validOrderState = orderState as OrderStateValid; - const orderRelevantState = validOrderState.orderRelevantState; - expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - changedMakerApprovalAmount, - ); - expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal( - changedMakerApprovalAmount, - ); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.setProxyAllowanceAsync( - makerTokenAddress, - makerAddress, - changedMakerApprovalAmount, - ); - })().catch(done); - }); - it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - - const makerBalance = await contractWrappers.erc20Token.getBalanceAsync( - makerTokenAddress, - makerAddress, - ); - - const remainingAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals); - const transferAmount = makerBalance.minus(remainingAmount); - await orderWatcher.addOrderAsync(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.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - remainingAmount, - ); - expect(orderRelevantState.remainingFillableTakerAssetAmount).to.be.bignumber.equal( - remainingAmount, - ); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.transferAsync( - makerTokenAddress, - makerAddress, - constants.NULL_ADDRESS, - transferAmount, - ); - })().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); - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - - const remainingFeeAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), decimals); - - const remainingTokenAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(4), decimals); - const transferTokenAmount = makerFee.minus(remainingTokenAmount); - await orderWatcher.addOrderAsync(signedOrder); - - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { - const validOrderState = orderState as OrderStateValid; - const orderRelevantState = validOrderState.orderRelevantState; - expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - remainingFeeAmount, - ); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.setProxyAllowanceAsync( - zrxTokenAddress, - makerAddress, - remainingFeeAmount, - ); - await contractWrappers.erc20Token.transferAsync( - makerTokenAddress, - makerAddress, - constants.NULL_ADDRESS, - transferTokenAmount, - ); - })().catch(done); - }); - it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => { - (async () => { - const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals); - const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - - await orderWatcher.addOrderAsync(signedOrder); - - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { - const validOrderState = orderState as OrderStateValid; - const orderRelevantState = validOrderState.orderRelevantState; - expect(orderRelevantState.remainingFillableMakerAssetAmount).to.be.bignumber.equal( - fillableAmount, - ); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.setProxyAllowanceAsync( - makerTokenAddress, - makerAddress, - Web3Wrapper.toBaseUnitAmount(new BigNumber(100), decimals), - ); - })().catch(done); - }); - }); - it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.OrderCancelled); - }); - orderWatcher.subscribe(callback); - await contractWrappers.exchange.cancelOrderAsync(signedOrder); - })().catch(done); - }); - it('should emit orderStateInvalid when within rounding error range after a partial fill', (done: DoneCallback) => { - (async () => { - const fillAmountInBaseUnits = new BigNumber(2); - const makerAssetAmount = new BigNumber(1001); - const takerAssetAmount = new BigNumber(3); - signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - makerAssetAmount, - takerAssetAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.OrderFillRoundingError); - }); - orderWatcher.subscribe(callback); - await contractWrappers.exchange.fillOrderAsync(signedOrder, fillAmountInBaseUnits, takerAddress); - })().catch(done); - }); - describe('erc721', () => { - let makerErc721AssetData: string; - let makerErc721TokenAddress: string; - const tokenId = new BigNumber(42); - [makerErc721TokenAddress] = tokenUtils.getDummyERC721TokenAddresses(); - makerErc721AssetData = assetDataUtils.encodeERC721AssetData(makerErc721TokenAddress, tokenId); - const fillableErc721Amount = new BigNumber(1); - it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerErc721AssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc721Token.setApprovalAsync( - makerErc721TokenAddress, - constants.NULL_ADDRESS, - tokenId, - ); - })().catch(done); - }); - it('should emit orderStateInvalid when maker allowance for all set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerErc721AssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - await contractWrappers.erc721Token.setApprovalAsync( - makerErc721TokenAddress, - constants.NULL_ADDRESS, - tokenId, - ); - let isApproved = true; - await contractWrappers.erc721Token.setProxyApprovalForAllAsync( - makerErc721TokenAddress, - makerAddress, - isApproved, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - isApproved = false; - await contractWrappers.erc721Token.setProxyApprovalForAllAsync( - makerErc721TokenAddress, - makerAddress, - isApproved, - ); - })().catch(done); - }); - it('should emit orderStateInvalid when maker moves NFT backing watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerErc721AssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerBalance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc721Token.transferFromAsync( - makerErc721TokenAddress, - coinbase, - makerAddress, - tokenId, - ); - })().catch(done); - }); - }); - describe('multiAsset', async () => { - const tokenId = new BigNumber(42); - const [makerErc721TokenAddress] = tokenUtils.getDummyERC721TokenAddresses(); - const makerErc721AssetData = assetDataUtils.encodeERC721AssetData(makerErc721TokenAddress, tokenId); - const fillableErc721Amount = new BigNumber(1); - const [makerErc20TokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - const makerErc20AssetData = assetDataUtils.encodeERC20AssetData(makerErc20TokenAddress); - const fillableErc20Amount = new BigNumber(2); - const multiAssetAmounts = [fillableErc721Amount, fillableErc20Amount]; - const nestedAssetData = [makerErc721AssetData, makerErc20AssetData]; - const makerMultiAssetData = assetDataUtils.encodeMultiAssetData(multiAssetAmounts, nestedAssetData); - it('should emit orderStateInvalid when maker allowance of ERC721 token set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc721Token.setApprovalAsync( - makerErc721TokenAddress, - constants.NULL_ADDRESS, - tokenId, - ); - })().catch(done); - }); - it('should emit orderStateInvalid when maker allowance for all of ERC721 token set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - await contractWrappers.erc721Token.setApprovalAsync( - makerErc721TokenAddress, - constants.NULL_ADDRESS, - tokenId, - ); - let isApproved = true; - await contractWrappers.erc721Token.setProxyApprovalForAllAsync( - makerErc721TokenAddress, - makerAddress, - isApproved, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - isApproved = false; - await contractWrappers.erc721Token.setProxyApprovalForAllAsync( - makerErc721TokenAddress, - makerAddress, - isApproved, - ); - })().catch(done); - }); - it('should emit orderStateInvalid when maker moves ERC721 backing watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerBalance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc721Token.transferFromAsync( - makerErc721TokenAddress, - coinbase, - makerAddress, - tokenId, - ); - })().catch(done); - }); - it('should emit orderStateInvalid when maker allowance of ERC20 token set to 0 for watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableErc721Amount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerAllowance); - }); - orderWatcher.subscribe(callback); - await contractWrappers.erc20Token.setProxyAllowanceAsync( - makerErc20TokenAddress, - makerAddress, - new BigNumber(0), - ); - })().catch(done); - }); - it('should not emit an orderState event when irrelevant ERC20 Transfer event received', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - await orderWatcher.addOrderAsync(signedOrder); - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((_orderState: OrderState) => { - throw new Error('OrderState callback fired for irrelevant order'); - }); - orderWatcher.subscribe(callback); - const notTheMaker = userAddresses[0]; - const anyRecipient = takerAddress; - const transferAmount = new BigNumber(2); - await contractWrappers.erc20Token.transferAsync( - makerTokenAddress, - notTheMaker, - anyRecipient, - transferAmount, - ); - setTimeout(() => { - done(); - }, TIMEOUT_MS); - })().catch(done); - }); - it('should emit orderStateInvalid when makerAddress moves ERC20 balance backing watched order', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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.InsufficientMakerBalance); - }); - orderWatcher.subscribe(callback); - const anyRecipient = takerAddress; - const makerBalance = await contractWrappers.erc20Token.getBalanceAsync( - makerTokenAddress, - makerAddress, - ); - await contractWrappers.erc20Token.transferAsync( - makerTokenAddress, - makerAddress, - anyRecipient, - makerBalance, - ); - })().catch(done); - }); - // TODO(abandeali1): The following test will fail until the MAP has been deployed and activated. - it.skip('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { - (async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerMultiAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - await orderWatcher.addOrderAsync(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); - }); - orderWatcher.subscribe(callback); - - await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); - })().catch(done); - }); - }); - }); -}); // tslint:disable:max-file-line-count diff --git a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts deleted file mode 100644 index 36135f65c..000000000 --- a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { ContractAddresses } from '@0x/contract-addresses'; -import { ContractWrappers } from '@0x/contract-wrappers'; -import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { FillScenarios } from '@0x/fill-scenarios'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { ExchangeContractErrs, OrderStateInvalid, SignedOrder } from '@0x/types'; -import { BigNumber, logUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import 'mocha'; -import * as WebSocket from 'websocket'; - -import { OrderWatcherWebSocketServer } from '../src/order_watcher/order_watcher_web_socket_server'; -import { AddOrderRequest, OrderWatcherMethod, RemoveOrderRequest } from '../src/types'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { migrateOnceAsync } from './utils/migrate'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -interface WsMessage { - data: string; -} - -describe('OrderWatcherWebSocketServer', async () => { - let contractWrappers: ContractWrappers; - let wsServer: OrderWatcherWebSocketServer; - let wsClient: WebSocket.w3cwebsocket; - let wsClientTwo: WebSocket.w3cwebsocket; - let fillScenarios: FillScenarios; - let userAddresses: string[]; - let makerAssetData: string; - let takerAssetData: string; - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAddress: string; - let takerAddress: string; - let zrxTokenAddress: string; - let signedOrder: SignedOrder; - let orderHash: string; - let addOrderPayload: AddOrderRequest; - let removeOrderPayload: RemoveOrderRequest; - let networkId: number; - let contractAddresses: ContractAddresses; - const decimals = constants.ZRX_DECIMALS; - const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals); - - before(async () => { - // Set up constants - contractAddresses = await migrateOnceAsync(); - await blockchainLifecycle.startAsync(); - networkId = constants.TESTRPC_NETWORK_ID; - const config = { - networkId, - contractAddresses, - }; - contractWrappers = new ContractWrappers(provider, config); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - zrxTokenAddress = contractAddresses.zrxToken; - [makerAddress, takerAddress] = userAddresses; - [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); - [makerAssetData, takerAssetData] = [ - assetDataUtils.encodeERC20AssetData(makerTokenAddress), - assetDataUtils.encodeERC20AssetData(takerTokenAddress), - ]; - fillScenarios = new FillScenarios( - provider, - userAddresses, - zrxTokenAddress, - contractAddresses.exchange, - contractAddresses.erc20Proxy, - contractAddresses.erc721Proxy, - ); - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - ); - orderHash = orderHashUtils.getOrderHashHex(signedOrder); - addOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: OrderWatcherMethod.AddOrder, - params: { signedOrder }, - }; - removeOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: OrderWatcherMethod.RemoveOrder, - params: { orderHash }, - }; - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - // Prepare OrderWatcher WebSocket server - const orderWatcherConfig = { - isVerbose: true, - }; - wsServer = new OrderWatcherWebSocketServer(provider, networkId, contractAddresses, orderWatcherConfig); - wsServer.start(); - await blockchainLifecycle.startAsync(); - wsClient = new WebSocket.w3cwebsocket('ws://127.0.0.1:8080/'); - logUtils.log(`${new Date()} [Client] Connected.`); - }); - afterEach(async () => { - wsClient.close(); - await blockchainLifecycle.revertAsync(); - wsServer.stop(); - logUtils.log(`${new Date()} [Client] Closed.`); - }); - - it('responds to getStats requests correctly', (done: any) => { - const payload = { - id: 1, - jsonrpc: '2.0', - method: 'GET_STATS', - }; - wsClient.onopen = () => wsClient.send(JSON.stringify(payload)); - wsClient.onmessage = (msg: any) => { - const responseData = JSON.parse(msg.data); - expect(responseData.id).to.be.eq(1); - expect(responseData.jsonrpc).to.be.eq('2.0'); - expect(responseData.method).to.be.eq('GET_STATS'); - expect(responseData.result.orderCount).to.be.eq(0); - done(); - }; - }); - - it('throws an error when an invalid method is attempted', async () => { - const invalidMethodPayload = { - id: 1, - jsonrpc: '2.0', - method: 'BAD_METHOD', - }; - wsClient.onopen = () => wsClient.send(JSON.stringify(invalidMethodPayload)); - const errorMsg = await onMessageAsync(wsClient, null); - const errorData = JSON.parse(errorMsg.data); - // tslint:disable-next-line:no-unused-expression - expect(errorData.id).to.be.null; - // tslint:disable-next-line:no-unused-expression - expect(errorData.method).to.be.null; - expect(errorData.jsonrpc).to.be.eq('2.0'); - expect(errorData.error).to.match(/^Error: Expected request to conform to schema/); - }); - - it('throws an error when jsonrpc field missing from request', async () => { - const noJsonRpcPayload = { - id: 1, - method: 'GET_STATS', - }; - wsClient.onopen = () => wsClient.send(JSON.stringify(noJsonRpcPayload)); - const errorMsg = await onMessageAsync(wsClient, null); - const errorData = JSON.parse(errorMsg.data); - // tslint:disable-next-line:no-unused-expression - expect(errorData.method).to.be.null; - expect(errorData.jsonrpc).to.be.eq('2.0'); - expect(errorData.error).to.match(/^Error: Expected request to conform to schema/); - }); - - it('throws an error when we try to add an order without a signedOrder', async () => { - const noSignedOrderAddOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: 'ADD_ORDER', - orderHash: '0x7337e2f2a9aa2ed6afe26edc2df7ad79c3ffa9cf9b81a964f707ea63f5272355', - }; - wsClient.onopen = () => wsClient.send(JSON.stringify(noSignedOrderAddOrderPayload)); - const errorMsg = await onMessageAsync(wsClient, null); - const errorData = JSON.parse(errorMsg.data); - // tslint:disable-next-line:no-unused-expression - expect(errorData.id).to.be.null; - // tslint:disable-next-line:no-unused-expression - expect(errorData.method).to.be.null; - expect(errorData.jsonrpc).to.be.eq('2.0'); - expect(errorData.error).to.match(/^Error: Expected request to conform to schema/); - }); - - it('throws an error when we try to add a bad signedOrder', async () => { - const invalidAddOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: 'ADD_ORDER', - signedOrder: { - makerAddress: '0x0', - }, - }; - wsClient.onopen = () => wsClient.send(JSON.stringify(invalidAddOrderPayload)); - const errorMsg = await onMessageAsync(wsClient, null); - const errorData = JSON.parse(errorMsg.data); - // tslint:disable-next-line:no-unused-expression - expect(errorData.id).to.be.null; - // tslint:disable-next-line:no-unused-expression - expect(errorData.method).to.be.null; - expect(errorData.error).to.match(/^Error: Expected request to conform to schema/); - }); - - it('executes addOrder and removeOrder requests correctly', async () => { - wsClient.onopen = () => wsClient.send(JSON.stringify(addOrderPayload)); - const addOrderMsg = await onMessageAsync(wsClient, OrderWatcherMethod.AddOrder); - const addOrderData = JSON.parse(addOrderMsg.data); - expect(addOrderData.method).to.be.eq('ADD_ORDER'); - expect((wsServer as any)._orderWatcher._orderByOrderHash).to.deep.include({ - [orderHash]: signedOrder, - }); - - const clientOnMessagePromise = onMessageAsync(wsClient, OrderWatcherMethod.RemoveOrder); - wsClient.send(JSON.stringify(removeOrderPayload)); - const removeOrderMsg = await clientOnMessagePromise; - const removeOrderData = JSON.parse(removeOrderMsg.data); - expect(removeOrderData.method).to.be.eq('REMOVE_ORDER'); - expect((wsServer as any)._orderWatcher._orderByOrderHash).to.not.deep.include({ - [orderHash]: signedOrder, - }); - }); - - it('broadcasts orderStateInvalid message when makerAddress allowance set to 0 for watched order', async () => { - // Add the regular order - wsClient.onopen = () => wsClient.send(JSON.stringify(addOrderPayload)); - - // We register the onMessage callback before calling `setProxyAllowanceAsync` which we - // expect will cause a message to be emitted. We do now "await" here, since we want to - // check for messages _after_ calling `setProxyAllowanceAsync` - const clientOnMessagePromise = onMessageAsync(wsClient, OrderWatcherMethod.Update); - - // Set the allowance to 0 - await contractWrappers.erc20Token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, new BigNumber(0)); - - // We now await the `onMessage` promise to check for the message - const orderWatcherUpdateMsg = await clientOnMessagePromise; - const orderWatcherUpdateData = JSON.parse(orderWatcherUpdateMsg.data); - expect(orderWatcherUpdateData.method).to.be.eq('UPDATE'); - const invalidOrderState = orderWatcherUpdateData.result as OrderStateInvalid; - expect(invalidOrderState.isValid).to.be.false(); - expect(invalidOrderState.orderHash).to.be.eq(orderHash); - expect(invalidOrderState.error).to.be.eq(ExchangeContractErrs.InsufficientMakerAllowance); - }); - - it('broadcasts to multiple clients when an order backing ZRX allowance changes', async () => { - // Prepare order - const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); - const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0), decimals); - const nonZeroMakerFeeSignedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - takerAddress, - ); - const nonZeroMakerFeeOrderPayload = { - id: 1, - jsonrpc: '2.0', - method: 'ADD_ORDER', - params: { - signedOrder: nonZeroMakerFeeSignedOrder, - }, - }; - - // Set up a second client and have it add the order - wsClientTwo = new WebSocket.w3cwebsocket('ws://127.0.0.1:8080/'); - logUtils.log(`${new Date()} [Client] Connected.`); - wsClientTwo.onopen = () => wsClientTwo.send(JSON.stringify(nonZeroMakerFeeOrderPayload)); - - // Setup the onMessage callbacks, but don't await them yet - const clientOneOnMessagePromise = onMessageAsync(wsClient, OrderWatcherMethod.Update); - const clientTwoOnMessagePromise = onMessageAsync(wsClientTwo, OrderWatcherMethod.Update); - - // Change the allowance - await contractWrappers.erc20Token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress, new BigNumber(0)); - - // Check that both clients receive the emitted event by awaiting the onMessageAsync promises - let updateMsg = await clientOneOnMessagePromise; - let updateData = JSON.parse(updateMsg.data); - let orderState = updateData.result as OrderStateInvalid; - expect(orderState.isValid).to.be.false(); - expect(orderState.error).to.be.eq('INSUFFICIENT_MAKER_FEE_ALLOWANCE'); - - updateMsg = await clientTwoOnMessagePromise; - updateData = JSON.parse(updateMsg.data); - orderState = updateData.result as OrderStateInvalid; - expect(orderState.isValid).to.be.false(); - expect(orderState.error).to.be.eq('INSUFFICIENT_MAKER_FEE_ALLOWANCE'); - - wsClientTwo.close(); - logUtils.log(`${new Date()} [Client] Closed.`); - }); -}); - -// HACK: createFillableSignedOrderAsync is Promise-based, which forces us -// to use Promises instead of the done() callbacks for tests. -// onmessage callback must thus be wrapped as a Promise. -async function onMessageAsync(client: WebSocket.w3cwebsocket, method: string | null): Promise { - return new Promise(resolve => { - client.onmessage = (msg: WsMessage) => { - const data = JSON.parse(msg.data); - if (data.method === method) { - resolve(msg); - } - }; - }); -} diff --git a/packages/order-watcher/test/utils/chai_setup.ts b/packages/order-watcher/test/utils/chai_setup.ts deleted file mode 100644 index 1a8733093..000000000 --- a/packages/order-watcher/test/utils/chai_setup.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as chai from 'chai'; -import chaiAsPromised = require('chai-as-promised'); -import ChaiBigNumber = require('chai-bignumber'); -import * as dirtyChai from 'dirty-chai'; - -export const chaiSetup = { - configure(): void { - chai.config.includeStack = true; - chai.use(ChaiBigNumber()); - chai.use(dirtyChai); - chai.use(chaiAsPromised); - }, -}; diff --git a/packages/order-watcher/test/utils/constants.ts b/packages/order-watcher/test/utils/constants.ts deleted file mode 100644 index 78037647c..000000000 --- a/packages/order-watcher/test/utils/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const constants = { - NULL_ADDRESS: '0x0000000000000000000000000000000000000000', - TESTRPC_NETWORK_ID: 50, - ZRX_DECIMALS: 18, -}; diff --git a/packages/order-watcher/test/utils/migrate.ts b/packages/order-watcher/test/utils/migrate.ts deleted file mode 100644 index 665ce0faa..000000000 --- a/packages/order-watcher/test/utils/migrate.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ContractAddresses } from '@0x/contract-addresses'; -import { devConstants } from '@0x/dev-utils'; -import { runMigrationsOnceAsync } from '@0x/migrations'; - -import { provider } from './web3_wrapper'; - -/** - * Configures and runs the migrations exactly once. Any subsequent times this is - * called, it returns the cached addresses. - * @returns The addresses of contracts that were deployed during the migrations. - */ -export async function migrateOnceAsync(): Promise { - const txDefaults = { - gas: devConstants.GAS_LIMIT, - from: devConstants.TESTRPC_FIRST_ADDRESS, - }; - return runMigrationsOnceAsync(provider, txDefaults); -} diff --git a/packages/order-watcher/test/utils/web3_wrapper.ts b/packages/order-watcher/test/utils/web3_wrapper.ts deleted file mode 100644 index accfcb7fe..000000000 --- a/packages/order-watcher/test/utils/web3_wrapper.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Provider } from 'ethereum-types'; - -const provider: Provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); -const web3Wrapper = new Web3Wrapper(provider); - -export { provider, web3Wrapper }; diff --git a/packages/order-watcher/tsconfig.json b/packages/order-watcher/tsconfig.json deleted file mode 100644 index 2ee711adc..000000000 --- a/packages/order-watcher/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig", - "compilerOptions": { - "outDir": "lib", - "rootDir": "." - }, - "include": ["./src/**/*", "./test/**/*"] -} diff --git a/packages/order-watcher/tslint.json b/packages/order-watcher/tslint.json deleted file mode 100644 index 4ade3b924..000000000 --- a/packages/order-watcher/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "rules": { - "prefer-readonly": true - }, - "extends": ["@0x/tslint-config"] -} diff --git a/packages/order-watcher/typedoc-tsconfig.json b/packages/order-watcher/typedoc-tsconfig.json deleted file mode 100644 index c9b0af1ae..000000000 --- a/packages/order-watcher/typedoc-tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../typedoc-tsconfig", - "compilerOptions": { - "outDir": "lib" - }, - "include": ["./src/**/*", "./test/**/*"] -} -- cgit v1.2.3