aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/0x.js/CHANGELOG.md221
-rw-r--r--packages/0x.js/package.json15
-rw-r--r--packages/0x.js/src/0x.ts2
-rw-r--r--packages/0x.js/src/contract.ts2
-rw-r--r--packages/0x.js/src/contract_wrappers/exchange_wrapper.ts6
-rw-r--r--packages/0x.js/src/contract_wrappers/token_wrapper.ts2
-rw-r--r--packages/0x.js/src/order_watcher/order_state_watcher.ts2
-rw-r--r--packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts2
-rw-r--r--packages/0x.js/src/types.ts2
-rw-r--r--packages/0x.js/src/utils/abi_decoder.ts2
-rw-r--r--packages/0x.js/src/utils/assert.ts84
-rw-r--r--packages/0x.js/src/utils/order_state_utils.ts6
-rw-r--r--packages/0x.js/test/exchange_wrapper_test.ts16
-rw-r--r--packages/0x.js/test/order_state_watcher_test.ts8
-rw-r--r--packages/0x.js/test/token_registry_wrapper_test.ts2
-rw-r--r--packages/0x.js/tslint.json2
-rw-r--r--packages/assert/README.md1
-rw-r--r--packages/assert/package.json46
-rw-r--r--packages/assert/src/globals.d.ts5
-rw-r--r--packages/assert/src/index.ts93
-rw-r--r--packages/assert/test/assert_test.ts338
-rw-r--r--packages/assert/tsconfig.json18
-rw-r--r--packages/assert/tslint.json5
-rw-r--r--packages/json-schemas/README.md15
-rw-r--r--packages/json-schemas/package.json46
-rw-r--r--packages/json-schemas/schemas/basic_type_schemas.ts11
-rw-r--r--packages/json-schemas/schemas/ec_signature_schema.ts20
-rw-r--r--packages/json-schemas/schemas/index_filter_values_schema.ts11
-rw-r--r--packages/json-schemas/schemas/order_cancel_schema.ts12
-rw-r--r--packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts12
-rw-r--r--packages/json-schemas/schemas/order_fill_requests_schema.ts12
-rw-r--r--packages/json-schemas/schemas/order_hash_schema.ts5
-rw-r--r--packages/json-schemas/schemas/order_schemas.ts35
-rw-r--r--packages/json-schemas/schemas/relayer_api_error_response_schema.ts21
-rw-r--r--packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts19
-rw-r--r--packages/json-schemas/schemas/relayer_api_fees_response_schema.ts10
-rw-r--r--packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts22
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts21
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts11
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts9
-rw-r--r--packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts24
-rw-r--r--packages/json-schemas/schemas/signed_orders_schema.ts5
-rw-r--r--packages/json-schemas/schemas/subscription_opts_schema.ts20
-rw-r--r--packages/json-schemas/schemas/token_schema.ts11
-rw-r--r--packages/json-schemas/schemas/tx_data_schema.ts42
-rw-r--r--packages/json-schemas/src/globals.d.ts7
-rw-r--r--packages/json-schemas/src/index.ts4
-rw-r--r--packages/json-schemas/src/schema_validator.ts28
-rw-r--r--packages/json-schemas/src/schemas.ts99
-rw-r--r--packages/json-schemas/test/schema_test.ts972
-rw-r--r--packages/json-schemas/tsconfig.json17
-rw-r--r--packages/json-schemas/tslint.json5
-rw-r--r--packages/tslint-config/README.md4
-rw-r--r--packages/tslint-config/package.json38
-rw-r--r--packages/tslint-config/tslint.json53
55 files changed, 2393 insertions, 108 deletions
diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md
new file mode 100644
index 000000000..b31e05923
--- /dev/null
+++ b/packages/0x.js/CHANGELOG.md
@@ -0,0 +1,221 @@
+# CHANGELOG
+
+v0.24.0 - _November 13, 2017_
+------------------------
+ * Standardise on Cancelled over Canceled
+ * Add missing `DecodedLogEvent` type to exported types
+
+v0.23.0 - _November 12, 2017_
+------------------------
+ * Fixed unhandled promise rejection error in subscribe methods (#209)
+ * Subscribe callbacks now receive an error object as their first argument
+
+v0.22.6 - _November 10, 2017_
+------------------------
+ * Add a timeout parameter to transaction awaiting (#206)
+
+v0.22.5 - _November 7, 2017_
+------------------------
+ * Re-publish v0.22.4 to fix publishing issue
+
+v0.22.4 - _October 25, 2017_
+------------------------
+ * Upgraded bignumber.js to a new version that ships with native typings
+
+v0.22.3 - _October 25, 2017_
+------------------------
+ * Fixed an issue with new version of testrpc and unlimited proxy allowance (#199)
+
+v0.22.2 - _October 24, 2017_
+------------------------
+ * Fixed rounding of maker fill amount and incorrect validation of partial fees (#197)
+
+v0.22.0 - _October 16, 2017_
+------------------------
+ * Started using `OrderFillRequest` interface instead of `OrderFillOrKillRequest` interface for `zeroEx.exchange.batchFillOrKill` (#187)
+ * Removed `OrderFillOrKillRequest` (#187)
+
+v0.21.4 - _October 13, 2017_
+------------------------
+ * Made 0x.js more type-safe by making `getLogsAsync` and `subscribe/subscribeAsync` generics parametrized with arg type (#194)
+
+v0.21.3 - _October 12, 2017_
+------------------------
+ * Fixed a bug causing order fills to throw `INSUFFICIENT_TAKER_ALLOWANCE` (#193)
+
+v0.21.2 - _October 11, 2017_
+------------------------
+ * Exported `ContractEventArg` as a public type (#190)
+
+v0.21.1 - _October 11, 2017_
+------------------------
+ * Fixed a bug in subscriptions (#189)
+
+v0.21.0 - _October 10, 2017_
+------------------------
+ * Complete rewrite of subscription logic (#182)
+ * Subscriptions no longer return historical logs. If you want them - use `getLogsAsync`
+ * Subscriptions now use [ethereumjs-blockstream](https://github.com/ethereumjs/ethereumjs-blockstream) under the hood
+ * Subscriptions correctly handle block re-orgs (forks)
+ * Subscriptions correctly backfill logs (connection problems)
+ * They no longer setup filters on the underlying nodes, so you can use them with infura without a filter Subprovider
+ * Removed `ContractEventEmitter` and added `LogEvent`
+ * Renamed `zeroEx.token.subscribeAsync` to `zeroEx.token.subscribe`
+ * Added `zeroEx.token.unsubscribe` and `zeroEx.exchange.unsubscribe`
+ * Renamed `zeroEx.exchange.stopWatchingAllEventsAsync` to `zeroEx.exhange.unsubscribeAll`
+ * Renamed `zeroEx.token.stopWatchingAllEventsAsync` to `zeroEx.token.unsubscribeAll`
+ * Fixed the batch fills validation by emulating all balance & proxy allowance changes (#185)
+
+v0.20.0 - _October 5, 2017_
+------------------------
+ * Add `zeroEx.token.getLogsAsync` (#178)
+ * Add `zeroEx.exchange.getLogsAsync` (#178)
+ * Fixed fees validation when one of the tokens transferred is ZRX (#181)
+
+v0.19.0 - _September 29, 2017_
+------------------------
+ * Made order validation optional (#172)
+ * Added Ropsten testnet support (#173)
+ * Fixed a bug causing awaitTransactionMinedAsync to DDos backend nodes (#175)
+
+v0.18.0 - _September 26, 2017_
+------------------------
+ * Added `zeroEx.exchange.validateOrderFillableOrThrowAsync` to simplify orderbook pruning (#170)
+
+v0.17.0 - _September 26, 2017_
+------------------------
+ * Made `zeroEx.exchange.getZRXTokenAddressAsync` public (#171)
+
+v0.16.0 - _September 20, 2017_
+------------------------
+ * Added the ability to specify custom contract addresses to be used with 0x.js (#165)
+ * ZeroExConfig.exchangeContractAddress
+ * ZeroExConfig.tokenRegistryContractAddress
+ * ZeroExConfig.etherTokenContractAddress
+ * Added `zeroEx.tokenRegistry.getContractAddressAsync` (#165)
+
+v0.15.0 - _September 8, 2017_
+------------------------
+ * Added the ability to specify a historical `blockNumber` at which to query the blockchain's state when calling a token or exchange method (#161)
+
+v0.14.2 - _September 7, 2017_
+------------------------
+ * Fixed an issue with bignumber.js types not found (#160)
+
+v0.14.1 - _September 7, 2017_
+------------------------
+ * Fixed an issue with Artifact type not found (#159)
+
+v0.14.0 - _September 6, 2017_
+------------------------
+ * Added `zeroEx.exchange.throwLogErrorsAsErrors` method to public interface (#157)
+ * Fixed an issue with overlapping async intervals in `zeroEx.awaitTransactionMinedAsync` (#157)
+ * Fixed an issue with log decoder returning `BigNumber`s as `strings` (#157)
+
+v0.13.0 - _September 6, 2017_
+------------------------
+ * Made all the functions submitting transactions to the network to immediately return transaction hash (#151)
+ * Added `zeroEx.awaitTransactionMinedAsync` (#151)
+ * Added `TransactionReceiptWithDecodedLogs`, `LogWithDecodedArgs`, `DecodedLogArgs` to public types (#151)
+ * Added signature validation to `validateFillOrderThrowIfInvalidAsync` (#152)
+
+v0.12.1 - _September 2, 2017_
+------------------------
+ * Added the support for web3@1.x.x provider (#142)
+ * Added the optional `zeroExConfig` parameter to the constructor of `ZeroEx` (#139)
+ * Added the ability to specify `gasPrice` when instantiating `ZeroEx` (#139)
+
+v0.11.0 - _August 24, 2017_
+------------------------
+ * Added `zeroEx.token.setUnlimitedProxyAllowanceAsync` (#137)
+ * Added `zeroEx.token.setUnlimitedAllowanceAsync` (#137)
+ * Added `zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS` (#137)
+
+v0.10.4 - _Aug 24, 2017_
+------------------------
+ * Fixed a bug where checksummed addresses were being pulled from artifacts and not lower-cased. (#135)
+
+v0.10.1 - _Aug 24, 2017_
+------------------------
+ * Added `zeroEx.exchange.validateFillOrderThrowIfInvalidAsync` (#128)
+ * Added `zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync` (#128)
+ * Added `zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync` (#128)
+ * Added `zeroEx.exchange.isRoundingErrorAsync` (#128)
+ * Added `zeroEx.proxy.getContractAddressAsync` (#130)
+ * Added `zeroEx.tokenRegistry.getTokenAddressesAsync` (#132)
+ * Added `zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync` (#132)
+ * Added `zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync` (#132)
+ * Added `zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync` (#132)
+ * Added `zeroEx.tokenRegistry.getTokenByNameIfExistsAsync` (#132)
+ * Added clear error message when checksummed address is passed to a public method (#124)
+ * Fixes the description of `shouldThrowOnInsufficientBalanceOrAllowance` in docs (#127)
+
+v0.9.3 - _Aug 22, 2017_
+------------------------
+ * Update contract artifacts to include latest Kovan and Mainnet deploys (#118)
+
+v0.9.2 - _Aug 21, 2017_
+------------------------
+ * *This version was unpublished because of a publishing issue.*
+ * Update contract artifacts to include latest Kovan and Mainnet deploys (#118)
+
+v0.9.1 - _Aug. 16, 2017_
+------------------------
+ * Fixed the bug causing `zeroEx.token.getBalanceAsync()` to fail if no addresses available (#120)
+
+v0.9.0 - _Jul. 26, 2017_
+------------------------
+ * Migrated to the new version of smart contracts (#101)
+ * Removed the ability to call methods on multiple authorized Exchange smart contracts (#106)
+ * Made `zeroEx.getOrderHashHex` a static method (#107)
+ * Cached `net_version` requests and invalidate the cache on calls to `setProvider` (#95)
+ * Renamed `zeroEx.exchange.batchCancelOrderAsync` to `zeroEx.exchange.batchCancelOrdersAsync`
+ * Renamed `zeroEx.exchange.batchFillOrderAsync` to `zeroEx.exchange.batchFillOrdersAsync`
+ * Updated to typescript v2.4 (#104)
+ * Fixed an issue with incorrect balance/allowance validation when ZRX is one of the tokens traded (#109)
+
+v0.8.0 - _Jul. 4, 2017_
+------------------------
+ * Added the ability to call methods on different authorized versions of the Exchange smart contract (#82)
+ * Updated contract artifacts to reflect latest changes to the smart contracts (0xproject/contracts#59)
+ * Added `zeroEx.proxy.isAuthorizedAsync` and `zeroEx.proxy.getAuthorizedAddressesAsync` (#89)
+ * Added `zeroEx.token.subscribeAsync` (#90)
+ * Made contract invalidation functions private (#90)
+ * `zeroEx.token.invalidateContractInstancesAsync`
+ * `zeroEx.exchange.invalidateContractInstancesAsync`
+ * `zeroEx.proxy.invalidateContractInstance`
+ * `zeroEx.tokenRegistry.invalidateContractInstance`
+ * Fixed the bug where `zeroEx.setProviderAsync` didn't invalidate etherToken contract's instance
+
+v0.7.1 - _Jun. 26, 2017_
+------------------------
+ * Added the ability to convert Ether to wrapped Ether tokens and back via `zeroEx.etherToken.depostAsync` and `zeroEx.etherToken.withdrawAsync` (#81)
+
+v0.7.0 - _Jun. 22, 2017_
+------------------------
+ * Added Kovan smart contract artifacts (#78)
+ * Started returning fillAmount from `fillOrderAsync` and `fillUpToAsync` (#72)
+ * Started returning cancelledAmount from `cancelOrderAsync` (#72)
+ * Renamed type `LogCancelArgs` to `LogCancelContractEventArgs` and `LogFillArgs` to `LogFillContractEventArgs`
+
+v0.6.2 - _Jun. 21, 2017_
+------------------------
+ * Reduced bundle size
+ * Improved documentation
+
+v0.6.1 - _Jun. 19, 2017_
+------------------------
+ * Improved documentation
+
+v0.6.0 - _Jun. 19, 2017_
+------------------------
+ * Made `ZeroEx` class accept `Web3Provider` instance instead of `Web3` instance
+ * Added types for contract event arguments
+
+v0.5.2 - _Jun. 15, 2017_
+------------------------
+ * Fixed the bug in `postpublish` script that caused that only unminified UMD bundle was uploaded to release page
+
+v0.5.1 - _Jun. 15, 2017_
+------------------------
+ * Added `postpublish` script to publish to Github Releases with assets.
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index 6e30df612..0204d723f 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -1,6 +1,6 @@
{
"name": "0x.js",
- "version": "0.23.0",
+ "version": "0.25.1",
"description": "A javascript library for interacting with the 0x protocol",
"keywords": [
"0x.js",
@@ -13,12 +13,13 @@
"types": "lib/src/index.d.ts",
"scripts": {
"prebuild": "npm run clean",
- "build": "run-p build:umd:prod build:commonjs",
+ "build": "run-p build:umd:prod build:commonjs; exit 0;",
"prepublishOnly": "run-p build",
"postpublish": "run-s release docs:json upload_docs_json",
"release": "publish-release --assets _bundles/index.js,_bundles/index.min.js --tag $(git describe --tags) --owner 0xProject --repo 0x.js",
"upload_docs_json": "aws s3 cp docs/index.json s3://0xjs-docs-jsons/$(git describe --tags).json --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type aplication/json",
"lint": "tslint src/**/*.ts test/**/*.ts",
+ "test:circleci": "run-s test:coverage report_test_coverage; if [ $CIRCLE_BRANCH = \"development\" ]; then yarn test:umd; fi",
"test": "run-s clean test:commonjs",
"test:umd": "./scripts/test_umd.sh",
"test:coverage": "nyc npm run test --all",
@@ -48,6 +49,7 @@
"node": ">=6.0.0"
},
"devDependencies": {
+ "@0xproject/tslint-config": "^0.1.0",
"@types/jsonschema": "^1.1.1",
"@types/lodash": "^4.14.64",
"@types/mocha": "^2.2.41",
@@ -75,8 +77,7 @@
"sinon": "^4.0.0",
"source-map-support": "^0.5.0",
"truffle-hdwallet-provider": "^0.0.3",
- "tslint": "~5.5.0",
- "tslint-config-0xproject": "^0.0.2",
+ "tslint": "5.8.0",
"typedoc": "~0.8.0",
"types-bn": "^0.0.1",
"types-ethereumjs-util": "0xProject/types-ethereumjs-util",
@@ -86,8 +87,10 @@
"webpack": "^3.1.0"
},
"dependencies": {
- "0x-json-schemas": "^0.6.1",
- "bignumber.js": "^4.1.0",
+ "@0xproject/assert": "^0.0.4",
+ "@0xproject/json-schemas": "^0.6.7",
+ "bignumber.js": "~4.1.0",
+ "bn.js": "4.11.8",
"compare-versions": "^3.0.1",
"es6-promisify": "^5.0.0",
"ethereumjs-abi": "^0.6.4",
diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts
index fe765bbbe..85c2b7724 100644
--- a/packages/0x.js/src/0x.ts
+++ b/packages/0x.js/src/0x.ts
@@ -1,6 +1,6 @@
import * as _ from 'lodash';
import BigNumber from 'bignumber.js';
-import {SchemaValidator, schemas} from '0x-json-schemas';
+import {SchemaValidator, schemas} from '@0xproject/json-schemas';
import {bigNumberConfigs} from './bignumber_config';
import * as ethUtil from 'ethereumjs-util';
import {Web3Wrapper} from './web3_wrapper';
diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts
index 1aacc65dc..7ccd336d6 100644
--- a/packages/0x.js/src/contract.ts
+++ b/packages/0x.js/src/contract.ts
@@ -1,7 +1,7 @@
import * as Web3 from 'web3';
import * as _ from 'lodash';
import promisify = require('es6-promisify');
-import {SchemaValidator, schemas} from '0x-json-schemas';
+import {SchemaValidator, schemas} from '@0xproject/json-schemas';
import {AbiType} from './types';
export class Contract implements Web3.ContractInstance {
diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
index 654637a38..3e631b73e 100644
--- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
import * as Web3 from 'web3';
import BigNumber from 'bignumber.js';
-import {schemas} from '0x-json-schemas';
+import {schemas} from '@0xproject/json-schemas';
import {Web3Wrapper} from '../web3_wrapper';
import {
ECSignature,
@@ -95,7 +95,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @param orderHash The hex encoded orderHash for which you would like to retrieve the
* unavailable takerAmount.
* @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has either been filled or canceled.
+ * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
*/
public async getUnavailableTakerAmountAsync(orderHash: string,
methodOpts?: MethodOpts): Promise<BigNumber> {
@@ -133,7 +133,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @param methodOpts Optional arguments this method accepts.
* @return The amount of the order (in taker tokens) that has been cancelled.
*/
- public async getCanceledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const exchangeContract = await this._getExchangeContractAsync();
diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
index 614ac19d4..4b89d3cfc 100644
--- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
@@ -1,6 +1,6 @@
import * as _ from 'lodash';
import BigNumber from 'bignumber.js';
-import {schemas} from '0x-json-schemas';
+import {schemas} from '@0xproject/json-schemas';
import {Web3Wrapper} from '../web3_wrapper';
import {assert} from '../utils/assert';
import {constants} from '../utils/constants';
diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts
index 2b9d7997e..bafd7a994 100644
--- a/packages/0x.js/src/order_watcher/order_state_watcher.ts
+++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts
@@ -1,5 +1,5 @@
import * as _ from 'lodash';
-import {schemas} from '0x-json-schemas';
+import {schemas} from '@0xproject/json-schemas';
import {ZeroEx} from '../0x';
import {EventWatcher} from './event_watcher';
import {assert} from '../utils/assert';
diff --git a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
index 9d74da096..666d8363c 100644
--- a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
+++ b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
@@ -42,7 +42,7 @@ export class OrderFilledCancelledLazyStore {
const methodOpts = {
defaultBlock: BlockParamLiteral.Pending,
};
- const cancelledTakerAmount = await this.exchange.getCanceledTakerAmountAsync(orderHash, methodOpts);
+ const cancelledTakerAmount = await this.exchange.getCancelledTakerAmountAsync(orderHash, methodOpts);
this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
}
const cachedCancelled = this.cancelledTakerAmount[orderHash];
diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts
index da1971387..71089f9a1 100644
--- a/packages/0x.js/src/types.ts
+++ b/packages/0x.js/src/types.ts
@@ -488,7 +488,7 @@ export interface OrderRelevantState {
makerFeeBalance: BigNumber;
makerFeeProxyAllowance: BigNumber;
filledTakerTokenAmount: BigNumber;
- canceledTakerTokenAmount: BigNumber;
+ cancelledTakerTokenAmount: BigNumber;
remainingFillableMakerTokenAmount: BigNumber;
remainingFillableTakerTokenAmount: BigNumber;
}
diff --git a/packages/0x.js/src/utils/abi_decoder.ts b/packages/0x.js/src/utils/abi_decoder.ts
index 840ad9be0..df0fb2d6f 100644
--- a/packages/0x.js/src/utils/abi_decoder.ts
+++ b/packages/0x.js/src/utils/abi_decoder.ts
@@ -34,7 +34,7 @@ export class AbiDecoder {
value = this.padZeros(new BigNumber(value).toString(16));
} else if (param.type === SolidityTypes.Uint256 ||
param.type === SolidityTypes.Uint8 ||
- param.type === SolidityTypes.Uint ) {
+ param.type === SolidityTypes.Uint) {
value = new BigNumber(value);
}
decodedParams[param.name] = value;
diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts
index e5c9439f3..63d975c03 100644
--- a/packages/0x.js/src/utils/assert.ts
+++ b/packages/0x.js/src/utils/assert.ts
@@ -1,66 +1,24 @@
import * as _ from 'lodash';
import * as Web3 from 'web3';
import BigNumber from 'bignumber.js';
-import {SchemaValidator, Schema} from '0x-json-schemas';
+import {SchemaValidator, Schema} from '@0xproject/json-schemas';
+import {assert as sharedAssert} from '@0xproject/assert';
import {Web3Wrapper} from '../web3_wrapper';
import {signatureUtils} from '../utils/signature_utils';
import {ECSignature} from '../types';
const HEX_REGEX = /^0x[0-9A-F]*$/i;
-export const assert = {
- isBigNumber(variableName: string, value: BigNumber): void {
- const isBigNumber = _.isObject(value) && (value as any).isBigNumber;
- this.assert(isBigNumber, this.typeAssertionMessage(variableName, 'BigNumber', value));
- },
- isValidBaseUnitAmount(variableName: string, value: BigNumber) {
- assert.isBigNumber(variableName, value);
- const hasDecimals = value.decimalPlaces() !== 0;
- this.assert(
- !hasDecimals, `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`,
- );
- },
+export const assert = _.extend({}, sharedAssert, {
isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
},
- isUndefined(value: any, variableName?: string): void {
- this.assert(_.isUndefined(value), this.typeAssertionMessage(variableName, 'undefined', value));
- },
- isString(variableName: string, value: string): void {
- this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value));
- },
- isFunction(variableName: string, value: any): void {
- this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value));
- },
- isHexString(variableName: string, value: string): void {
- this.assert(_.isString(value) && HEX_REGEX.test(value),
- this.typeAssertionMessage(variableName, 'HexString', value));
- },
- isETHAddressHex(variableName: string, value: string): void {
- const web3 = new Web3();
- this.assert(web3.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
- this.assert(
- web3.isAddress(value) && value.toLowerCase() === value,
- `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
- );
- },
- doesBelongToStringEnum(variableName: string, value: string,
- stringEnum: any /* There is no base type for every string enum */): void {
- const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
- const enumValues = _.keys(stringEnum);
- const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
- const enumValuesAsString = enumValuesAsStrings.join(', ');
- assert.assert(
- doesBelongToStringEnum,
- `Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`,
- );
- },
async isSenderAddressAsync(variableName: string, senderAddressHex: string,
web3Wrapper: Web3Wrapper): Promise<void> {
- assert.isETHAddressHex(variableName, senderAddressHex);
+ sharedAssert.isETHAddressHex(variableName, senderAddressHex);
const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
- assert.assert(isSenderAddressAvailable,
+ sharedAssert.assert(isSenderAddressAvailable,
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
);
},
@@ -68,34 +26,4 @@ export const assert = {
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
},
- hasAtMostOneUniqueValue(value: any[], errMsg: string): void {
- this.assert(_.uniq(value).length <= 1, errMsg);
- },
- isNumber(variableName: string, value: number): void {
- this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
- },
- isBoolean(variableName: string, value: boolean): void {
- this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
- },
- isWeb3Provider(variableName: string, value: Web3.Provider): void {
- const isWeb3Provider = _.isFunction((value as any).send) || _.isFunction((value as any).sendAsync);
- this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
- },
- doesConformToSchema(variableName: string, value: any, schema: Schema): void {
- const schemaValidator = new SchemaValidator();
- const validationResult = schemaValidator.validate(value, schema);
- const hasValidationErrors = validationResult.errors.length > 0;
- const msg = `Expected ${variableName} to conform to schema ${schema.id}
-Encountered: ${JSON.stringify(value, null, '\t')}
-Validation errors: ${validationResult.errors.join(', ')}`;
- this.assert(!hasValidationErrors, msg);
- },
- assert(condition: boolean, message: string): void {
- if (!condition) {
- throw new Error(message);
- }
- },
- typeAssertionMessage(variableName: string, type: string, value: any): string {
- return `Expected ${variableName} to be of type ${type}, encountered: ${value}`;
- },
-};
+});
diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts
index af6392c81..123584f90 100644
--- a/packages/0x.js/src/utils/order_state_utils.ts
+++ b/packages/0x.js/src/utils/order_state_utils.ts
@@ -69,7 +69,7 @@ export class OrderStateUtils {
zrxTokenAddress, signedOrder.maker,
);
const filledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
- const canceledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
+ const cancelledTakerTokenAmount = await this.orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
orderHash,
);
const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
@@ -90,14 +90,14 @@ export class OrderStateUtils {
makerFeeBalance,
makerFeeProxyAllowance,
filledTakerTokenAmount,
- canceledTakerTokenAmount,
+ cancelledTakerTokenAmount,
remainingFillableMakerTokenAmount,
remainingFillableTakerTokenAmount,
};
return orderRelevantState;
}
private validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
- const unavailableTakerTokenAmount = orderRelevantState.canceledTakerTokenAmount.add(
+ const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
orderRelevantState.filledTakerTokenAmount,
);
const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts
index 26b8c1e0e..add89a3b2 100644
--- a/packages/0x.js/test/exchange_wrapper_test.ts
+++ b/packages/0x.js/test/exchange_wrapper_test.ts
@@ -443,7 +443,7 @@ describe('ExchangeWrapper', () => {
it('should cancel an order', async () => {
const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
await zeroEx.awaitTransactionMinedAsync(txHash);
- const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
});
});
@@ -502,8 +502,8 @@ describe('ExchangeWrapper', () => {
describe('successful batch cancels', () => {
it('should cancel a batch of orders', async () => {
await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
- const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
- const anotherCancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
+ const anotherCancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(
anotherOrderHashHex,
);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
@@ -592,23 +592,23 @@ describe('ExchangeWrapper', () => {
expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
});
});
- describe('#getCanceledTakerAmountAsync', () => {
+ describe('#getCancelledTakerAmountAsync', () => {
it('should throw if passed an invalid orderHash', async () => {
const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getCanceledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ return expect(zeroEx.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
});
it('should return zero if passed a valid but non-existent orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
expect(cancelledValueT).to.be.bignumber.equal(0);
});
it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHash);
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
expect(cancelledValueT).to.be.bignumber.equal(0);
});
it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
const cancelAmount = fillableAmount.minus(partialFillAmount);
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHash);
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
});
});
diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts
index 03b1f36a4..a112bac1d 100644
--- a/packages/0x.js/test/order_state_watcher_test.ts
+++ b/packages/0x.js/test/order_state_watcher_test.ts
@@ -61,6 +61,12 @@ describe('OrderStateWatcher', () => {
[makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
web3Wrapper = (zeroEx as any)._web3Wrapper;
});
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
describe('#removeOrder', async () => {
it('should successfully remove existing order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
@@ -377,7 +383,7 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid;
expect(validOrderState.orderHash).to.be.equal(orderHash);
const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.canceledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
+ expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
done();
});
zeroEx.orderStateWatcher.subscribe(callback);
diff --git a/packages/0x.js/test/token_registry_wrapper_test.ts b/packages/0x.js/test/token_registry_wrapper_test.ts
index 6b5dd517e..d3497451b 100644
--- a/packages/0x.js/test/token_registry_wrapper_test.ts
+++ b/packages/0x.js/test/token_registry_wrapper_test.ts
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
import 'mocha';
import * as chai from 'chai';
-import {SchemaValidator, schemas} from '0x-json-schemas';
+import {SchemaValidator, schemas} from '@0xproject/json-schemas';
import {chaiSetup} from './utils/chai_setup';
import {web3Factory} from './utils/web3_factory';
import {ZeroEx, Token} from '../src';
diff --git a/packages/0x.js/tslint.json b/packages/0x.js/tslint.json
index 5842a872a..a07795151 100644
--- a/packages/0x.js/tslint.json
+++ b/packages/0x.js/tslint.json
@@ -1,5 +1,5 @@
{
"extends": [
- "tslint-config-0xproject"
+ "@0xproject/tslint-config"
]
}
diff --git a/packages/assert/README.md b/packages/assert/README.md
new file mode 100644
index 000000000..0c60671ea
--- /dev/null
+++ b/packages/assert/README.md
@@ -0,0 +1 @@
+Standard type and schema assertions to be used across all 0x projects and packages
diff --git a/packages/assert/package.json b/packages/assert/package.json
new file mode 100644
index 000000000..ed1d2a98b
--- /dev/null
+++ b/packages/assert/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@0xproject/assert",
+ "version": "0.0.4",
+ "description": "Provides a standard way of performing type and schema validation across 0x projects",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "lint": "tslint src/**/*.ts test/**/*.ts",
+ "run_mocha": "mocha lib/test/**/*_test.js",
+ "prepublishOnly": "run-p build",
+ "test": "run-s clean build run_mocha",
+ "test:circleci": "yarn test"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.1.0",
+ "@types/lodash": "^4.14.78",
+ "@types/mocha": "^2.2.42",
+ "@types/valid-url": "^1.0.2",
+ "chai": "^4.0.1",
+ "chai-typescript-typings": "^0.0.1",
+ "dirty-chai": "^2.0.1",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.1",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "^2.4.2"
+ },
+ "dependencies": {
+ "@0xproject/json-schemas": "^0.6.7",
+ "bignumber.js": "~4.1.0",
+ "ethereum-address": "^0.0.4",
+ "lodash": "^4.17.4",
+ "valid-url": "^1.0.9"
+ }
+}
diff --git a/packages/assert/src/globals.d.ts b/packages/assert/src/globals.d.ts
new file mode 100644
index 000000000..cc47f3113
--- /dev/null
+++ b/packages/assert/src/globals.d.ts
@@ -0,0 +1,5 @@
+declare module 'dirty-chai';
+
+declare module 'ethereum-address' {
+ const isAddress: (arg: any) => boolean;
+}
diff --git a/packages/assert/src/index.ts b/packages/assert/src/index.ts
new file mode 100644
index 000000000..eb224223f
--- /dev/null
+++ b/packages/assert/src/index.ts
@@ -0,0 +1,93 @@
+import BigNumber from 'bignumber.js';
+import * as ethereum_address from 'ethereum-address';
+import * as _ from 'lodash';
+import * as validUrl from 'valid-url';
+import {
+ SchemaValidator,
+ Schema,
+} from '@0xproject/json-schemas';
+
+const HEX_REGEX = /^0x[0-9A-F]*$/i;
+
+export const assert = {
+ isBigNumber(variableName: string, value: BigNumber): void {
+ const isBigNumber = _.isObject(value) && (value as any).isBigNumber;
+ this.assert(isBigNumber, this.typeAssertionMessage(variableName, 'BigNumber', value));
+ },
+ isValidBaseUnitAmount(variableName: string, value: BigNumber) {
+ assert.isBigNumber(variableName, value);
+ const hasDecimals = value.decimalPlaces() !== 0;
+ this.assert(
+ !hasDecimals, `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`,
+ );
+ },
+ isUndefined(value: any, variableName?: string): void {
+ this.assert(_.isUndefined(value), this.typeAssertionMessage(variableName, 'undefined', value));
+ },
+ isString(variableName: string, value: string): void {
+ this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value));
+ },
+ isFunction(variableName: string, value: any): void {
+ this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value));
+ },
+ isHexString(variableName: string, value: string): void {
+ this.assert(_.isString(value) && HEX_REGEX.test(value),
+ this.typeAssertionMessage(variableName, 'HexString', value));
+ },
+ isETHAddressHex(variableName: string, value: string): void {
+ this.assert(ethereum_address.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
+ this.assert(
+ ethereum_address.isAddress(value) && value.toLowerCase() === value,
+ `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
+ );
+ },
+ doesBelongToStringEnum(variableName: string, value: string,
+ stringEnum: any /* There is no base type for every string enum */): void {
+ const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
+ const enumValues = _.keys(stringEnum);
+ const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
+ const enumValuesAsString = enumValuesAsStrings.join(', ');
+ assert.assert(
+ doesBelongToStringEnum,
+ `Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`,
+ );
+ },
+ hasAtMostOneUniqueValue(value: any[], errMsg: string): void {
+ this.assert(_.uniq(value).length <= 1, errMsg);
+ },
+ isNumber(variableName: string, value: number): void {
+ this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
+ },
+ isBoolean(variableName: string, value: boolean): void {
+ this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
+ },
+ isWeb3Provider(variableName: string, value: any): void {
+ const isWeb3Provider = _.isFunction((value as any).send) || _.isFunction((value as any).sendAsync);
+ this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
+ },
+ doesConformToSchema(variableName: string, value: any, schema: Schema): void {
+ const schemaValidator = new SchemaValidator();
+ const validationResult = schemaValidator.validate(value, schema);
+ const hasValidationErrors = validationResult.errors.length > 0;
+ const msg = `Expected ${variableName} to conform to schema ${schema.id}
+Encountered: ${JSON.stringify(value, null, '\t')}
+Validation errors: ${validationResult.errors.join(', ')}`;
+ this.assert(!hasValidationErrors, msg);
+ },
+ isHttpUrl(variableName: string, value: any): void {
+ const isValidUrl = validUrl.isWebUri(value);
+ this.assert(isValidUrl, this.typeAssertionMessage(variableName, 'http url', value));
+ },
+ isUri(variableName: string, value: any): void {
+ const isValidUri = validUrl.isUri(value);
+ this.assert(isValidUri, this.typeAssertionMessage(variableName, 'uri', value));
+ },
+ assert(condition: boolean, message: string): void {
+ if (!condition) {
+ throw new Error(message);
+ }
+ },
+ typeAssertionMessage(variableName: string, type: string, value: any): string {
+ return `Expected ${variableName} to be of type ${type}, encountered: ${value}`;
+ },
+};
diff --git a/packages/assert/test/assert_test.ts b/packages/assert/test/assert_test.ts
new file mode 100644
index 000000000..66fa4eb54
--- /dev/null
+++ b/packages/assert/test/assert_test.ts
@@ -0,0 +1,338 @@
+import 'mocha';
+import * as dirtyChai from 'dirty-chai';
+import * as chai from 'chai';
+import {BigNumber} from 'bignumber.js';
+import {schemas} from '@0xproject/json-schemas';
+import {assert} from '../src/index';
+
+chai.config.includeStack = true;
+chai.use(dirtyChai);
+const expect = chai.expect;
+
+describe('Assertions', () => {
+ const variableName = 'variable';
+ describe('#isBigNumber', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ new BigNumber(23),
+ new BigNumber('45'),
+ ];
+ validInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 'test',
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ ];
+ invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isUndefined', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ undefined,
+ ];
+ validInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 'test',
+ 42,
+ false,
+ { random: 'test' },
+ ];
+ invalidInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.throw());
+ });
+ });
+ describe('#isString', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 'hello',
+ 'goodbye',
+ ];
+ validInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isFunction', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ BigNumber,
+ assert.isString.bind(this),
+ ];
+ validInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isHexString', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ '0x61a3ed31B43c8780e905a260a35faYfEc527be7516aa11c0256729b5b351bc33',
+ ];
+ invalidInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isETHAddressHex', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x0000000000000000000000000000000000000000',
+ '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x12459c951127e0c374ff9105dda097662a027093',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ '0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x6FFFd0ae3f7d88c9b4925323f54c6e4',
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
+ describe('#doesBelongToStringEnum', () => {
+ enum TestEnums {
+ Test1 = 'Test1',
+ Test2 = 'Test2',
+ }
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ TestEnums.Test1,
+ TestEnums.Test2,
+ ];
+ validInputs.forEach(input =>
+ expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.throw(),
+ );
+ });
+ });
+ describe('#hasAtMostOneUniqueValue', () => {
+ const errorMsg = 'more than one unique value';
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ ['hello'],
+ ['goodbye', 'goodbye', 'goodbye'],
+ ];
+ validInputs.forEach(input =>
+ expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ ['hello', 'goodbye'],
+ ['goodbye', 42, false, false],
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.throw(),
+ );
+ });
+ });
+ describe('#isNumber', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 42,
+ 0.00,
+ 21e+42,
+ ];
+ validInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isBoolean', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ true,
+ false,
+ ];
+ validInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isWeb3Provider', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ { send: () => 45 },
+ { sendAsync: () => 45 },
+ ];
+ validInputs.forEach(input =>
+ expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
+ describe('#doesConformToSchema', () => {
+ const schema = schemas.addressSchema;
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x12459c951127e0c374ff9105dda097662a027093',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.throw(),
+ );
+ });
+ });
+ describe('#isHttpUrl', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 'http://www.google.com',
+ 'https://api.example-relayer.net',
+ 'https://api.radarrelay.com/0x/v0/',
+ 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.isHttpUrl.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ 'ws://www.api.example-relayer.net',
+ 'www.google.com',
+ 'api.example-relayer.net',
+ 'user:password@api.example-relayer.net',
+ '//api.example-relayer.net',
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.isHttpUrl.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
+ describe('#isUri', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 'http://www.google.com',
+ 'https://api.example-relayer.net',
+ 'https://api.radarrelay.com/0x/v0/',
+ 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
+ 'ws://www.api.example-relayer.net',
+ 'wss://www.api.example-relayer.net',
+ 'user:password@api.example-relayer.net',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.isUri.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ 'www.google.com',
+ 'api.example-relayer.net',
+ '//api.example-relayer.net',
+ ];
+ invalidInputs.forEach(input =>
+ expect(assert.isUri.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
+ describe('#assert', () => {
+ const assertMessage = 'assert not satisfied';
+ it('should not throw for valid input', () => {
+ expect(assert.assert.bind(assert, true, assertMessage)).to.not.throw();
+ });
+ it('should throw for invalid input', () => {
+ expect(assert.assert.bind(assert, false, assertMessage)).to.throw();
+ });
+ });
+ describe('#typeAssertionMessage', () => {
+ it('should render correct message', () => {
+ expect(assert.typeAssertionMessage('variable', 'string', 'number'))
+ .to.equal(`Expected variable to be of type string, encountered: number`);
+ });
+ });
+});
diff --git a/packages/assert/tsconfig.json b/packages/assert/tsconfig.json
new file mode 100644
index 000000000..709e20154
--- /dev/null
+++ b/packages/assert/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "lib": [ "es2017", "dom"],
+ "outDir": "lib",
+ "sourceMap": true,
+ "declaration": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts"
+ ]
+}
diff --git a/packages/assert/tslint.json b/packages/assert/tslint.json
new file mode 100644
index 000000000..a07795151
--- /dev/null
+++ b/packages/assert/tslint.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "@0xproject/tslint-config"
+ ]
+}
diff --git a/packages/json-schemas/README.md b/packages/json-schemas/README.md
new file mode 100644
index 000000000..e9f80e106
--- /dev/null
+++ b/packages/json-schemas/README.md
@@ -0,0 +1,15 @@
+Contains 0x-related json schemas
+
+## Usage:
+```
+import {SchemaValidator, ValidatorResult, schemas} from '@0xproject/json-schemas';
+
+const {orderSchema} = schemas;
+const validator = new SchemaValidator();
+
+const order = {
+ ...
+};
+const validatorResult: ValidatorResult = validator.validate(order, orderSchema); // Contains all errors
+const isValid: boolean = validator.isValid(order, orderSchema); // Only returns boolean
+```
diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json
new file mode 100644
index 000000000..07ed20551
--- /dev/null
+++ b/packages/json-schemas/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@0xproject/json-schemas",
+ "version": "0.6.7",
+ "description": "0x-related json schemas",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "lint": "tslint src/*.ts test/*.ts",
+ "test": "run-s clean build run_mocha",
+ "test:circleci": "yarn test",
+ "run_mocha": "mocha lib/test/**/*_test.js",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "build": "tsc"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "author": "",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/json-schemas/README.md",
+ "dependencies": {
+ "es6-promisify": "^5.0.0",
+ "jsonschema": "^1.2.0",
+ "lodash.values": "^4.3.0"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.1.0",
+ "@types/lodash.foreach": "^4.5.3",
+ "@types/lodash.values": "^4.3.3",
+ "@types/mocha": "^2.2.42",
+ "bignumber.js": "^4.0.2",
+ "chai": "^4.1.1",
+ "chai-typescript-typings": "^0.0.1",
+ "dirty-chai": "^2.0.1",
+ "lodash.foreach": "^4.5.0",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.1",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ }
+}
diff --git a/packages/json-schemas/schemas/basic_type_schemas.ts b/packages/json-schemas/schemas/basic_type_schemas.ts
new file mode 100644
index 000000000..9d81ff333
--- /dev/null
+++ b/packages/json-schemas/schemas/basic_type_schemas.ts
@@ -0,0 +1,11 @@
+export const addressSchema = {
+ id: '/Address',
+ type: 'string',
+ pattern: '^0x[0-9a-f]{40}$',
+};
+
+export const numberSchema = {
+ id: '/Number',
+ type: 'string',
+ pattern: '^\\d+(\\.\\d+)?$',
+};
diff --git a/packages/json-schemas/schemas/ec_signature_schema.ts b/packages/json-schemas/schemas/ec_signature_schema.ts
new file mode 100644
index 000000000..2b769f3b6
--- /dev/null
+++ b/packages/json-schemas/schemas/ec_signature_schema.ts
@@ -0,0 +1,20 @@
+export const ecSignatureParameterSchema = {
+ id: '/ECSignatureParameter',
+ type: 'string',
+ pattern: '^0[xX][0-9A-Fa-f]{64}$',
+};
+
+export const ecSignatureSchema = {
+ id: '/ECSignature',
+ properties: {
+ v: {
+ type: 'number',
+ minimum: 27,
+ maximum: 28,
+ },
+ r: {$ref: '/ECSignatureParameter'},
+ s: {$ref: '/ECSignatureParameter'},
+ },
+ required: ['v', 'r', 's'],
+ type: 'object',
+};
diff --git a/packages/json-schemas/schemas/index_filter_values_schema.ts b/packages/json-schemas/schemas/index_filter_values_schema.ts
new file mode 100644
index 000000000..f7e323e45
--- /dev/null
+++ b/packages/json-schemas/schemas/index_filter_values_schema.ts
@@ -0,0 +1,11 @@
+export const indexFilterValuesSchema = {
+ id: '/IndexFilterValues',
+ additionalProperties: {
+ oneOf: [
+ {$ref: '/Number'},
+ {$ref: '/Address'},
+ {$ref: '/OrderHashSchema'},
+ ],
+ },
+ type: 'object',
+};
diff --git a/packages/json-schemas/schemas/order_cancel_schema.ts b/packages/json-schemas/schemas/order_cancel_schema.ts
new file mode 100644
index 000000000..ac7d2ee20
--- /dev/null
+++ b/packages/json-schemas/schemas/order_cancel_schema.ts
@@ -0,0 +1,12 @@
+export const orderCancellationRequestsSchema = {
+ id: '/OrderCancellationRequests',
+ type: 'array',
+ items: {
+ properties: {
+ order: {$ref: '/Order'},
+ takerTokenCancelAmount: {$ref: '/Number'},
+ },
+ required: ['order', 'takerTokenCancelAmount'],
+ type: 'object',
+ },
+};
diff --git a/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts b/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts
new file mode 100644
index 000000000..4ef7b069a
--- /dev/null
+++ b/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts
@@ -0,0 +1,12 @@
+export const orderFillOrKillRequestsSchema = {
+ id: '/OrderFillOrKillRequests',
+ type: 'array',
+ items: {
+ properties: {
+ signedOrder: {$ref: '/SignedOrder'},
+ fillTakerAmount: {$ref: '/Number'},
+ },
+ required: ['signedOrder', 'fillTakerAmount'],
+ type: 'object',
+ },
+};
diff --git a/packages/json-schemas/schemas/order_fill_requests_schema.ts b/packages/json-schemas/schemas/order_fill_requests_schema.ts
new file mode 100644
index 000000000..ec19dd9f8
--- /dev/null
+++ b/packages/json-schemas/schemas/order_fill_requests_schema.ts
@@ -0,0 +1,12 @@
+export const orderFillRequestsSchema = {
+ id: '/OrderFillRequests',
+ type: 'array',
+ items: {
+ properties: {
+ signedOrder: {$ref: '/SignedOrder'},
+ takerTokenFillAmount: {$ref: '/Number'},
+ },
+ required: ['signedOrder', 'takerTokenFillAmount'],
+ type: 'object',
+ },
+};
diff --git a/packages/json-schemas/schemas/order_hash_schema.ts b/packages/json-schemas/schemas/order_hash_schema.ts
new file mode 100644
index 000000000..6af06927f
--- /dev/null
+++ b/packages/json-schemas/schemas/order_hash_schema.ts
@@ -0,0 +1,5 @@
+export const orderHashSchema = {
+ id: '/OrderHashSchema',
+ type: 'string',
+ pattern: '^0x[0-9a-fA-F]{64}$',
+};
diff --git a/packages/json-schemas/schemas/order_schemas.ts b/packages/json-schemas/schemas/order_schemas.ts
new file mode 100644
index 000000000..3cce49351
--- /dev/null
+++ b/packages/json-schemas/schemas/order_schemas.ts
@@ -0,0 +1,35 @@
+export const orderSchema = {
+ id: '/Order',
+ properties: {
+ maker: {$ref: '/Address'},
+ taker: {$ref: '/Address'},
+ makerFee: {$ref: '/Number'},
+ takerFee: {$ref: '/Number'},
+ makerTokenAmount: {$ref: '/Number'},
+ takerTokenAmount: {$ref: '/Number'},
+ makerTokenAddress: {$ref: '/Address'},
+ takerTokenAddress: {$ref: '/Address'},
+ salt: {$ref: '/Number'},
+ feeRecipient: {$ref: '/Address'},
+ expirationUnixTimestampSec: {$ref: '/Number'},
+ exchangeContractAddress: {$ref: '/Address'},
+ },
+ required: [
+ 'maker', 'taker', 'makerFee', 'takerFee', 'makerTokenAmount', 'takerTokenAmount',
+ 'salt', 'feeRecipient', 'expirationUnixTimestampSec', 'exchangeContractAddress',
+ ],
+ type: 'object',
+};
+
+export const signedOrderSchema = {
+ id: '/SignedOrder',
+ allOf: [
+ { $ref: '/Order' },
+ {
+ properties: {
+ ecSignature: {$ref: '/ECSignature'},
+ },
+ required: ['ecSignature'],
+ },
+ ],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_error_response_schema.ts b/packages/json-schemas/schemas/relayer_api_error_response_schema.ts
new file mode 100644
index 000000000..eacbb2bce
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_error_response_schema.ts
@@ -0,0 +1,21 @@
+export const relayerApiErrorResponseSchema = {
+ id: '/RelayerApiErrorResponse',
+ type: 'object',
+ properties: {
+ code: {type: 'number'},
+ reason: {type: 'string'},
+ validationErrors: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ field: {type: 'string'},
+ code: {type: 'number'},
+ reason: {type: 'string'},
+ },
+ required: ['field', 'code', 'reason'],
+ },
+ },
+ },
+ required: ['code', 'reason'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts b/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts
new file mode 100644
index 000000000..645660844
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts
@@ -0,0 +1,19 @@
+export const relayerApiFeesPayloadSchema = {
+ id: '/RelayerApiFeesPayload',
+ type: 'object',
+ properties: {
+ exchangeContractAddress: {$ref: '/Address'},
+ maker: {$ref: '/Address'},
+ taker: {$ref: '/Address'},
+ makerTokenAddress: {$ref: '/Address'},
+ takerTokenAddress: {$ref: '/Address'},
+ makerTokenAmount: {$ref: '/Number'},
+ takerTokenAmount: {$ref: '/Number'},
+ expirationUnixTimestampSec: {$ref: '/Number'},
+ salt: {$ref: '/Number'},
+ },
+ required: [
+ 'exchangeContractAddress', 'maker', 'taker', 'makerTokenAddress', 'takerTokenAddress',
+ 'expirationUnixTimestampSec', 'salt',
+ ],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts b/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts
new file mode 100644
index 000000000..86e51feb0
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts
@@ -0,0 +1,10 @@
+export const relayerApiFeesResponseSchema = {
+ id: '/RelayerApiFeesResponse',
+ type: 'object',
+ properties: {
+ makerFee: {$ref: '/Number'},
+ takerFee: {$ref: '/Number'},
+ feeRecipient: {$ref: '/Address'},
+ },
+ required: ['makerFee', 'takerFee', 'feeRecipient'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts
new file mode 100644
index 000000000..8ded9adb0
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts
@@ -0,0 +1,22 @@
+export const relayerApiOrderbookChannelSubscribeSchema = {
+ id: '/RelayerApiOrderbookChannelSubscribe',
+ type: 'object',
+ properties: {
+ type: {enum: ['subscribe']},
+ channel: {enum: ['orderbook']},
+ payload: {$ref: '/RelayerApiOrderbookChannelSubscribePayload'},
+ },
+ required: ['type', 'channel', 'payload'],
+};
+
+export const relayerApiOrderbookChannelSubscribePayload = {
+ id: '/RelayerApiOrderbookChannelSubscribePayload',
+ type: 'object',
+ properties: {
+ baseTokenAddress: {$ref: '/Address'},
+ quoteTokenAddress: {$ref: '/Address'},
+ snapshot: {type: 'boolean'},
+ limit: {type: 'number'},
+ },
+ required: ['baseTokenAddress', 'quoteTokenAddress'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts
new file mode 100644
index 000000000..cfc0ddc8f
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts
@@ -0,0 +1,21 @@
+export const relayerApiOrderbookChannelSnapshotSchema = {
+ id: '/RelayerApiOrderbookChannelSnapshot',
+ type: 'object',
+ properties: {
+ type: {enum: ['snapshot']},
+ channel: {enum: ['orderbook']},
+ channelId: {type: 'number'},
+ payload: {$ref: '/RelayerApiOrderbookChannelSnapshotPayload'},
+ },
+ required: ['type', 'channel', 'channelId', 'payload'],
+};
+
+export const relayerApiOrderbookChannelSnapshotPayload = {
+ id: '/RelayerApiOrderbookChannelSnapshotPayload',
+ type: 'object',
+ properties: {
+ bids: {$ref: '/signedOrdersSchema'},
+ asks: {$ref: '/signedOrdersSchema'},
+ },
+ required: ['bids', 'asks'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts
new file mode 100644
index 000000000..51308ed49
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts
@@ -0,0 +1,11 @@
+export const relayerApiOrderbookChannelUpdateSchema = {
+ id: '/RelayerApiOrderbookChannelUpdate',
+ type: 'object',
+ properties: {
+ type: {enum: ['update']},
+ channel: {enum: ['orderbook']},
+ channelId: {type: 'number'},
+ payload: {$ref: '/SignedOrder'},
+ },
+ required: ['type', 'channel', 'channelId', 'payload'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts
new file mode 100644
index 000000000..b592d4f8e
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts
@@ -0,0 +1,9 @@
+export const relayerApiOrderBookResponseSchema = {
+ id: '/RelayerApiOrderBookResponse',
+ type: 'object',
+ properties: {
+ bids: {$ref: '/signedOrdersSchema'},
+ asks: {$ref: '/signedOrdersSchema'},
+ },
+ required: ['bids', 'asks'],
+};
diff --git a/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts b/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts
new file mode 100644
index 000000000..8ecab1424
--- /dev/null
+++ b/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts
@@ -0,0 +1,24 @@
+export const relayerApiTokenPairsResponseSchema = {
+ id: '/RelayerApiTokenPairsResponse',
+ type: 'array',
+ items: {
+ properties: {
+ tokenA: {$ref: '/RelayerApiTokenTradeInfo'},
+ tokenB: {$ref: '/RelayerApiTokenTradeInfo'},
+ },
+ required: ['tokenA', 'tokenB'],
+ type: 'object',
+ },
+};
+
+export const relayerApiTokenTradeInfoSchema = {
+ id: '/RelayerApiTokenTradeInfo',
+ type: 'object',
+ properties: {
+ address: {$ref: '/Address'},
+ minAmount: {$ref: '/Number'},
+ maxAmount: {$ref: '/Number'},
+ precision: {type: 'number'},
+ },
+ required: ['address'],
+};
diff --git a/packages/json-schemas/schemas/signed_orders_schema.ts b/packages/json-schemas/schemas/signed_orders_schema.ts
new file mode 100644
index 000000000..c4c4a68ac
--- /dev/null
+++ b/packages/json-schemas/schemas/signed_orders_schema.ts
@@ -0,0 +1,5 @@
+export const signedOrdersSchema = {
+ id: '/signedOrdersSchema',
+ type: 'array',
+ items: {$ref: '/SignedOrder'},
+};
diff --git a/packages/json-schemas/schemas/subscription_opts_schema.ts b/packages/json-schemas/schemas/subscription_opts_schema.ts
new file mode 100644
index 000000000..a476e6963
--- /dev/null
+++ b/packages/json-schemas/schemas/subscription_opts_schema.ts
@@ -0,0 +1,20 @@
+export const blockParamSchema = {
+ id: '/BlockParam',
+ oneOf: [
+ {
+ type: 'number',
+ },
+ {
+ enum: ['latest', 'earliest', 'pending'],
+ },
+ ],
+};
+
+export const subscriptionOptsSchema = {
+ id: '/SubscriptionOpts',
+ properties: {
+ fromBlock: {$ref: '/BlockParam'},
+ toBlock: {$ref: '/BlockParam'},
+ },
+ type: 'object',
+};
diff --git a/packages/json-schemas/schemas/token_schema.ts b/packages/json-schemas/schemas/token_schema.ts
new file mode 100644
index 000000000..aca4d4ad2
--- /dev/null
+++ b/packages/json-schemas/schemas/token_schema.ts
@@ -0,0 +1,11 @@
+export const tokenSchema = {
+ id: '/Token',
+ properties: {
+ name: {type: 'string'},
+ symbol: {type: 'string'},
+ decimals: {type: 'number'},
+ address: {$ref: '/Address'},
+ },
+ required: ['name', 'symbol', 'decimals', 'address'],
+ type: 'object',
+};
diff --git a/packages/json-schemas/schemas/tx_data_schema.ts b/packages/json-schemas/schemas/tx_data_schema.ts
new file mode 100644
index 000000000..41eaadd3c
--- /dev/null
+++ b/packages/json-schemas/schemas/tx_data_schema.ts
@@ -0,0 +1,42 @@
+export const jsNumber = {
+ id: '/JsNumber',
+ type: 'number',
+ minimum: 0,
+};
+
+export const txDataSchema = {
+ id: '/TxData',
+ properties: {
+ from: {$ref: '/Address'},
+ to: {$ref: '/Address'},
+ value: {
+ oneOf: [
+ {$ref: '/Number'},
+ {$ref: '/JsNumber'},
+ ],
+ },
+ gas: {
+ oneOf: [
+ {$ref: '/Number'},
+ {$ref: '/JsNumber'},
+ ],
+ },
+ gasPrice: {
+ oneOf: [
+ {$ref: '/Number'},
+ {$ref: '/JsNumber'},
+ ],
+ },
+ data: {
+ type: 'string',
+ pattern: '^0x[0-9a-f]*$',
+ },
+ nonce: {
+ type: 'number',
+ minimum: 0,
+ },
+ },
+ required: ['from'],
+ type: 'object',
+ additionalProperties: false,
+};
diff --git a/packages/json-schemas/src/globals.d.ts b/packages/json-schemas/src/globals.d.ts
new file mode 100644
index 000000000..157705f57
--- /dev/null
+++ b/packages/json-schemas/src/globals.d.ts
@@ -0,0 +1,7 @@
+declare module 'dirty-chai';
+
+// es6-promisify declarations
+declare function promisify(original: any, settings?: any): ((...arg: any[]) => Promise<any>);
+declare module 'es6-promisify' {
+ export = promisify;
+}
diff --git a/packages/json-schemas/src/index.ts b/packages/json-schemas/src/index.ts
new file mode 100644
index 000000000..b7cae277e
--- /dev/null
+++ b/packages/json-schemas/src/index.ts
@@ -0,0 +1,4 @@
+export {ValidatorResult, Schema} from 'jsonschema';
+
+export {SchemaValidator} from './schema_validator';
+export {schemas} from './schemas';
diff --git a/packages/json-schemas/src/schema_validator.ts b/packages/json-schemas/src/schema_validator.ts
new file mode 100644
index 000000000..0bc88cc45
--- /dev/null
+++ b/packages/json-schemas/src/schema_validator.ts
@@ -0,0 +1,28 @@
+import values = require('lodash.values');
+import {Validator, ValidatorResult, Schema} from 'jsonschema';
+import {schemas} from './schemas';
+
+export class SchemaValidator {
+ private validator: Validator;
+ constructor() {
+ this.validator = new Validator();
+ for (const schema of values(schemas)) {
+ this.validator.addSchema(schema, schema.id);
+ }
+ }
+ public addSchema(schema: Schema) {
+ this.validator.addSchema(schema, schema.id);
+ }
+ // In order to validate a complex JS object using jsonschema, we must replace any complex
+ // sub-types (e.g BigNumber) with a simpler string representation. Since BigNumber and other
+ // complex types implement the `toString` method, we can stringify the object and
+ // then parse it. The resultant object can then be checked using jsonschema.
+ public validate(instance: any, schema: Schema): ValidatorResult {
+ const jsonSchemaCompatibleObject = JSON.parse(JSON.stringify(instance));
+ return this.validator.validate(jsonSchemaCompatibleObject, schema);
+ }
+ public isValid(instance: any, schema: Schema): boolean {
+ const isValid = this.validate(instance, schema).errors.length === 0;
+ return isValid;
+ }
+}
diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts
new file mode 100644
index 000000000..a8e5ecbcb
--- /dev/null
+++ b/packages/json-schemas/src/schemas.ts
@@ -0,0 +1,99 @@
+import {
+ numberSchema,
+ addressSchema,
+} from '../schemas/basic_type_schemas';
+import {
+ ecSignatureSchema,
+ ecSignatureParameterSchema,
+} from '../schemas/ec_signature_schema';
+import {
+ indexFilterValuesSchema,
+} from '../schemas/index_filter_values_schema';
+import {
+ orderCancellationRequestsSchema,
+} from '../schemas/order_cancel_schema';
+import {
+ orderFillOrKillRequestsSchema,
+} from '../schemas/order_fill_or_kill_requests_schema';
+import {
+ orderFillRequestsSchema,
+} from '../schemas/order_fill_requests_schema';
+import {
+ orderHashSchema,
+} from '../schemas/order_hash_schema';
+import {
+ orderSchema,
+ signedOrderSchema,
+} from '../schemas/order_schemas';
+import {
+ blockParamSchema,
+ subscriptionOptsSchema,
+} from '../schemas/subscription_opts_schema';
+import {
+ tokenSchema,
+} from '../schemas/token_schema';
+import {
+ signedOrdersSchema,
+} from '../schemas/signed_orders_schema';
+import {
+ relayerApiErrorResponseSchema,
+} from '../schemas/relayer_api_error_response_schema';
+import {
+ relayerApiFeesResponseSchema,
+} from '../schemas/relayer_api_fees_response_schema';
+import {
+ relayerApiFeesPayloadSchema,
+} from '../schemas/relayer_api_fees_payload_schema';
+import {
+ relayerApiOrderBookResponseSchema,
+} from '../schemas/relayer_api_orderbook_response_schema';
+import {
+ relayerApiTokenPairsResponseSchema,
+ relayerApiTokenTradeInfoSchema,
+} from '../schemas/relayer_api_token_pairs_response_schema';
+import {
+ jsNumber,
+ txDataSchema,
+} from '../schemas/tx_data_schema';
+import {
+ relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelSubscribePayload,
+} from '../schemas/relayer_api_orberbook_channel_subscribe_schema';
+import {
+ relayerApiOrderbookChannelUpdateSchema,
+} from '../schemas/relayer_api_orderbook_channel_update_response_schema';
+import {
+ relayerApiOrderbookChannelSnapshotSchema,
+ relayerApiOrderbookChannelSnapshotPayload,
+} from '../schemas/relayer_api_orderbook_channel_snapshot_schema';
+
+export const schemas = {
+ numberSchema,
+ addressSchema,
+ ecSignatureSchema,
+ ecSignatureParameterSchema,
+ indexFilterValuesSchema,
+ orderCancellationRequestsSchema,
+ orderFillOrKillRequestsSchema,
+ orderFillRequestsSchema,
+ orderHashSchema,
+ orderSchema,
+ signedOrderSchema,
+ signedOrdersSchema,
+ blockParamSchema,
+ subscriptionOptsSchema,
+ tokenSchema,
+ jsNumber,
+ txDataSchema,
+ relayerApiErrorResponseSchema,
+ relayerApiFeesPayloadSchema,
+ relayerApiFeesResponseSchema,
+ relayerApiOrderBookResponseSchema,
+ relayerApiTokenPairsResponseSchema,
+ relayerApiTokenTradeInfoSchema,
+ relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelSubscribePayload,
+ relayerApiOrderbookChannelUpdateSchema,
+ relayerApiOrderbookChannelSnapshotSchema,
+ relayerApiOrderbookChannelSnapshotPayload,
+};
diff --git a/packages/json-schemas/test/schema_test.ts b/packages/json-schemas/test/schema_test.ts
new file mode 100644
index 000000000..0ff456dec
--- /dev/null
+++ b/packages/json-schemas/test/schema_test.ts
@@ -0,0 +1,972 @@
+import 'mocha';
+import forEach = require('lodash.foreach');
+import * as dirtyChai from 'dirty-chai';
+import * as chai from 'chai';
+import BigNumber from 'bignumber.js';
+import promisify = require('es6-promisify');
+import {SchemaValidator, schemas} from '../src/index';
+
+chai.config.includeStack = true;
+chai.use(dirtyChai);
+const expect = chai.expect;
+const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
+const {
+ numberSchema,
+ addressSchema,
+ ecSignatureSchema,
+ ecSignatureParameterSchema,
+ indexFilterValuesSchema,
+ orderCancellationRequestsSchema,
+ orderFillOrKillRequestsSchema,
+ orderFillRequestsSchema,
+ orderHashSchema,
+ orderSchema,
+ signedOrderSchema,
+ signedOrdersSchema,
+ blockParamSchema,
+ subscriptionOptsSchema,
+ tokenSchema,
+ jsNumber,
+ txDataSchema,
+ relayerApiErrorResponseSchema,
+ relayerApiOrderBookResponseSchema,
+ relayerApiTokenPairsResponseSchema,
+ relayerApiFeesPayloadSchema,
+ relayerApiFeesResponseSchema,
+ relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelUpdateSchema,
+ relayerApiOrderbookChannelSnapshotSchema,
+} = schemas;
+
+describe('Schema', () => {
+ const validator = new SchemaValidator();
+ const validateAgainstSchema = (testCases: any[], schema: any, shouldFail = false) => {
+ forEach(testCases, (testCase: any) => {
+ const validationResult = validator.validate(testCase, schema);
+ const hasErrors = validationResult.errors.length !== 0;
+ if (shouldFail) {
+ if (!hasErrors) {
+ throw new Error(
+ `Expected testCase: ${JSON.stringify(testCase, null, '\t')} to fail and it didn't.`,
+ );
+ }
+ } else {
+ if (hasErrors) {
+ throw new Error(JSON.stringify(validationResult.errors, null, '\t'));
+ }
+ }
+ });
+ };
+ describe('#numberSchema', () => {
+ it('should validate valid numbers', () => {
+ const testCases = ['42', '0', '1.3', '0.2', '00.00'];
+ validateAgainstSchema(testCases, numberSchema);
+ });
+ it('should fail for invalid numbers', () => {
+ const testCases = ['.3', '1.', 'abacaba', 'и', '1..0'];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, numberSchema, shouldFail);
+ });
+ });
+ describe('#addressSchema', () => {
+ it('should validate valid addresses', () => {
+ const testCases = ['0x8b0292b11a196601ed2ce54b665cafeca0347d42', NULL_ADDRESS];
+ validateAgainstSchema(testCases, addressSchema);
+ });
+ it('should fail for invalid addresses', () => {
+ const testCases = [
+ '0x',
+ '0',
+ '0x00',
+ '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42',
+ '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, addressSchema, shouldFail);
+ });
+ });
+ describe('#ecSignatureParameterSchema', () => {
+ it('should validate valid parameters', () => {
+ const testCases = [
+ '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ '0X40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validateAgainstSchema(testCases, ecSignatureParameterSchema);
+ });
+ it('should fail for invalid parameters', () => {
+ const testCases = [
+ '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3', // shorter
+ '0xzzzz9190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // invalid characters
+ '40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // no 0x
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, ecSignatureParameterSchema, shouldFail);
+ });
+ });
+ describe('#ecSignatureSchema', () => {
+ it('should validate valid signature', () => {
+ const signature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const testCases = [
+ signature,
+ {
+ ...signature,
+ v: 28,
+ },
+ ];
+ validateAgainstSchema(testCases, ecSignatureSchema);
+ });
+ it('should fail for invalid signature', () => {
+ const v = 27;
+ const r = '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33';
+ const s = '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254';
+ const testCases = [
+ {},
+ {v},
+ {r, s, v: 31},
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, ecSignatureSchema, shouldFail);
+ });
+ });
+ describe('#orderHashSchema', () => {
+ it('should validate valid order hash', () => {
+ const testCases = [
+ '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validateAgainstSchema(testCases, orderHashSchema);
+ });
+ it('should fail for invalid order hash', () => {
+ const testCases = [
+ {},
+ '0x',
+ '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
+ '61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderHashSchema, shouldFail);
+ });
+ });
+ describe('#blockParamSchema', () => {
+ it('should validate valid block param', () => {
+ const testCases = [
+ 42,
+ 'latest',
+ 'pending',
+ 'earliest',
+ ];
+ validateAgainstSchema(testCases, blockParamSchema);
+ });
+ it('should fail for invalid block param', () => {
+ const testCases = [
+ {},
+ '42',
+ 'pemding',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, blockParamSchema, shouldFail);
+ });
+ });
+ describe('#subscriptionOptsSchema', () => {
+ it('should validate valid subscription opts', () => {
+ const testCases = [
+ {fromBlock: 42, toBlock: 'latest'},
+ {fromBlock: 42},
+ {},
+ ];
+ validateAgainstSchema(testCases, subscriptionOptsSchema);
+ });
+ it('should fail for invalid subscription opts', () => {
+ const testCases = [
+ {fromBlock: '42'},
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, subscriptionOptsSchema, shouldFail);
+ });
+ });
+ describe('#tokenSchema', () => {
+ const token = {
+ name: 'Zero Ex',
+ symbol: 'ZRX',
+ decimals: 100500,
+ address: '0x8b0292b11a196601ed2ce54b665cafeca0347d42',
+ url: 'https://0xproject.com',
+ };
+ it('should validate valid token', () => {
+ const testCases = [
+ token,
+ ];
+ validateAgainstSchema(testCases, tokenSchema);
+ });
+ it('should fail for invalid token', () => {
+ const testCases = [
+ {
+ ...token,
+ address: null,
+ },
+ {
+ ...token,
+ decimals: undefined,
+ },
+ [],
+ 4,
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, tokenSchema, shouldFail);
+ });
+ });
+ describe('order including schemas', () => {
+ const order = {
+ maker: NULL_ADDRESS,
+ taker: NULL_ADDRESS,
+ makerFee: '1',
+ takerFee: '2',
+ makerTokenAmount: '1',
+ takerTokenAmount: '2',
+ makerTokenAddress: NULL_ADDRESS,
+ takerTokenAddress: NULL_ADDRESS,
+ salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
+ feeRecipient: NULL_ADDRESS,
+ exchangeContractAddress: NULL_ADDRESS,
+ expirationUnixTimestampSec: '42',
+ };
+ describe('#orderSchema', () => {
+ it('should validate valid order', () => {
+ const testCases = [
+ order,
+ ];
+ validateAgainstSchema(testCases, orderSchema);
+ });
+ it('should fail for invalid order', () => {
+ const testCases = [
+ {
+ ...order,
+ salt: undefined,
+ },
+ {
+ ...order,
+ salt: 'salt',
+ },
+ 'order',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderSchema, shouldFail);
+ });
+ });
+ describe('signed order including schemas', () => {
+ const signedOrder = {
+ ...order,
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
+ };
+ describe('#signedOrdersSchema', () => {
+ it('should validate valid signed orders', () => {
+ const testCases = [
+ [signedOrder],
+ [],
+ ];
+ validateAgainstSchema(testCases, signedOrdersSchema);
+ });
+ it('should fail for invalid signed orders', () => {
+ const testCases = [
+ [
+ signedOrder,
+ 1,
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, signedOrdersSchema, shouldFail);
+ });
+ });
+ describe('#signedOrderSchema', () => {
+ it('should validate valid signed order', () => {
+ const testCases = [
+ signedOrder,
+ ];
+ validateAgainstSchema(testCases, signedOrderSchema);
+ });
+ it('should fail for invalid signed order', () => {
+ const testCases = [
+ {
+ ...signedOrder,
+ ecSignature: undefined,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, signedOrderSchema, shouldFail);
+ });
+ });
+ describe('#orderFillOrKillRequestsSchema', () => {
+ const orderFillOrKillRequests = [
+ {
+ signedOrder,
+ fillTakerAmount: '5',
+ },
+ ];
+ it('should validate valid order fill or kill requests', () => {
+ const testCases = [
+ orderFillOrKillRequests,
+ ];
+ validateAgainstSchema(testCases, orderFillOrKillRequestsSchema);
+ });
+ it('should fail for invalid order fill or kill requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderFillOrKillRequests[0],
+ fillTakerAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderFillOrKillRequestsSchema, shouldFail);
+ });
+ });
+ describe('#orderCancellationRequestsSchema', () => {
+ const orderCancellationRequests = [
+ {
+ order,
+ takerTokenCancelAmount: '5',
+ },
+ ];
+ it('should validate valid order cancellation requests', () => {
+ const testCases = [
+ orderCancellationRequests,
+ ];
+ validateAgainstSchema(testCases, orderCancellationRequestsSchema);
+ });
+ it('should fail for invalid order cancellation requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderCancellationRequests[0],
+ takerTokenCancelAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderCancellationRequestsSchema, shouldFail);
+ });
+ });
+ describe('#orderFillRequestsSchema', () => {
+ const orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: '5',
+ },
+ ];
+ it('should validate valid order fill requests', () => {
+ const testCases = [
+ orderFillRequests,
+ ];
+ validateAgainstSchema(testCases, orderFillRequestsSchema);
+ });
+ it('should fail for invalid order fill requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderFillRequests[0],
+ takerTokenFillAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderFillRequestsSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderBookResponseSchema', () => {
+ it('should validate valid order book responses', () => {
+ const testCases = [
+ {
+ bids: [],
+ asks: [],
+ },
+ {
+ bids: [signedOrder, signedOrder],
+ asks: [],
+ },
+ {
+ bids: [],
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: [signedOrder],
+ asks: [signedOrder, signedOrder],
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema);
+ });
+ it('should fail for invalid order fill requests', () => {
+ const testCases = [
+ {},
+ {
+ bids: [signedOrder, signedOrder],
+ },
+ {
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: signedOrder,
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: [signedOrder],
+ asks: signedOrder,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelSubscribeSchema', () => {
+ it('should validate valid orderbook channel websocket subscribe message', () => {
+ const testCases = [
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: true,
+ limit: 100,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema);
+ });
+ it('should fail for invalid orderbook channel websocket subscribe message', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'bar',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: checksummedAddress,
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: checksummedAddress,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: 'true',
+ limit: 100,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: true,
+ limit: '100',
+ },
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelSnapshotSchema', () => {
+ it('should validate valid orderbook channel websocket snapshot message', () => {
+ const testCases = [
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [],
+ asks: [],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema);
+ });
+ it('should fail for invalid orderbook channel websocket snapshot message', () => {
+ const testCases = [
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'bar',
+ channelId: 2,
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: '2',
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [
+ signedOrder,
+ ],
+ asks: [
+ {},
+ ],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: {
+ bids: [
+ {},
+ ],
+ asks: [
+ signedOrder,
+ ],
+ },
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelUpdateSchema', () => {
+ it('should validate valid orderbook channel websocket update message', () => {
+ const testCases = [
+ {
+ type: 'update',
+ channel: 'orderbook',
+ channelId: 2,
+ payload: signedOrder,
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema);
+ });
+ it('should fail for invalid orderbook channel websocket update message', () => {
+ const testCases = [
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ payload: signedOrder,
+ },
+ {
+ type: 'update',
+ channel: 'bar',
+ payload: signedOrder,
+ },
+ {
+ type: 'update',
+ channel: 'orderbook',
+ payload: {},
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema, shouldFail);
+ });
+ });
+ });
+ });
+ describe('BigNumber serialization', () => {
+ it('should correctly serialize BigNumbers', () => {
+ const testCases = {
+ '42': '42',
+ '0': '0',
+ '1.3': '1.3',
+ '0.2': '0.2',
+ '00.00': '0',
+ '.3': '0.3',
+ };
+ forEach(testCases, (serialized: string, input: string) => {
+ expect(JSON.parse(JSON.stringify(new BigNumber(input)))).to.be.equal(serialized);
+ });
+ });
+ });
+ describe('#relayerApiErrorResponseSchema', () => {
+ it('should validate valid errorResponse', () => {
+ const testCases = [
+ {
+ code: 102,
+ reason: 'Order submission disabled',
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ code: 1002,
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiErrorResponseSchema);
+ });
+ it('should fail for invalid error responses', () => {
+ const testCases = [
+ {},
+ {
+ code: 102,
+ },
+ {
+ code: '102',
+ reason: 'Order submission disabled',
+ },
+ {
+ reason: 'Order submission disabled',
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ code: '1002',
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiErrorResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiFeesPayloadSchema', () => {
+ it('should validate valid fees payloads', () => {
+ const testCases = [
+ {
+ exchangeContractAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ maker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ taker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ expirationUnixTimestampSec: '42',
+ salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiFeesPayloadSchema);
+ });
+ it('should fail for invalid fees payloads', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {},
+ {
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ },
+ {
+ taker: checksummedAddress,
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ },
+ {
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: 10000000000000000000,
+ takerTokenAmount: 30000000000000000000,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiFeesPayloadSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiFeesResponseSchema', () => {
+ it('should validate valid fees responses', () => {
+ const testCases = [
+ {
+ makerFee: '10000000000000000',
+ takerFee: '30000000000000000',
+ feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiFeesResponseSchema);
+ });
+ it('should fail for invalid fees responses', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {},
+ {
+ makerFee: 10000000000000000,
+ takerFee: 30000000000000000,
+ },
+ {
+ feeRecipient: checksummedAddress,
+ takerToSpecify: checksummedAddress,
+ makerFee: '10000000000000000',
+ takerFee: '30000000000000000',
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiFeesResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiTokenPairsResponseSchema', () => {
+ it('should validate valid tokenPairs response', () => {
+ const testCases = [
+ [],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ minAmount: '0',
+ maxAmount: '10000000000000000000',
+ precision: 5,
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ minAmount: '0',
+ maxAmount: '50000000000000000000',
+ precision: 5,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ },
+ },
+ ],
+ ];
+ validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema);
+ });
+ it('should fail for invalid tokenPairs responses', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ [
+ {
+ tokenA: {
+ address: checksummedAddress,
+ },
+ tokenB: {
+ address: checksummedAddress,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ minAmount: 0,
+ maxAmount: 10000000000000000000,
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ minAmount: 0,
+ maxAmount: 50000000000000000000,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ precision: '5',
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ precision: '5',
+ },
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema, shouldFail);
+ });
+ });
+ describe('#jsNumberSchema', () => {
+ it('should validate valid js number', () => {
+ const testCases = [
+ 1,
+ 42,
+ ];
+ validateAgainstSchema(testCases, jsNumber);
+ });
+ it('should fail for invalid js number', () => {
+ const testCases = [
+ NaN,
+ -1,
+ new BigNumber(1),
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, jsNumber, shouldFail);
+ });
+ });
+ describe('#txDataSchema', () => {
+ it('should validate valid txData', () => {
+ const testCases = [
+ {
+ from: NULL_ADDRESS,
+ },
+ {
+ from: NULL_ADDRESS,
+ gas: new BigNumber(42),
+ },
+ {
+ from: NULL_ADDRESS,
+ gas: 42,
+ },
+ ];
+ validateAgainstSchema(testCases, txDataSchema);
+ });
+ it('should fail for invalid txData', () => {
+ const testCases = [
+ {
+ gas: new BigNumber(42),
+ },
+ {
+ from: NULL_ADDRESS,
+ unknownProp: 'here',
+ },
+ {},
+ [],
+ new BigNumber(1),
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, txDataSchema, shouldFail);
+ });
+ });
+});
diff --git a/packages/json-schemas/tsconfig.json b/packages/json-schemas/tsconfig.json
new file mode 100644
index 000000000..40c2f0c8c
--- /dev/null
+++ b/packages/json-schemas/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "lib": [ "es2017", "dom"],
+ "outDir": "lib",
+ "sourceMap": true,
+ "declaration": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/chai-typescript-typings/index.d.ts"
+ ]
+}
diff --git a/packages/json-schemas/tslint.json b/packages/json-schemas/tslint.json
new file mode 100644
index 000000000..a07795151
--- /dev/null
+++ b/packages/json-schemas/tslint.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "@0xproject/tslint-config"
+ ]
+}
diff --git a/packages/tslint-config/README.md b/packages/tslint-config/README.md
new file mode 100644
index 000000000..d8bb0a51e
--- /dev/null
+++ b/packages/tslint-config/README.md
@@ -0,0 +1,4 @@
+tslint-config
+-----------------------
+
+Lint rules related to 0xProject for TSLint.
diff --git a/packages/tslint-config/package.json b/packages/tslint-config/package.json
new file mode 100644
index 000000000..ca46d63fc
--- /dev/null
+++ b/packages/tslint-config/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@0xproject/tslint-config",
+ "version": "0.1.0",
+ "description": "Lint rules related to 0xProject for TSLint",
+ "main": "tslint.json",
+ "files": [
+ "tslint.js",
+ "README.md",
+ "LICENSE"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/0xProject/0x.js.git"
+ },
+ "keywords": [
+ "tslint",
+ "config",
+ "0xProject",
+ "typescript",
+ "ts"
+ ],
+ "author": {
+ "name": "Fabio Berger",
+ "email": "fabio@0xproject.com"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/tslint-config/README.md",
+ "devDependencies": {
+ "tslint": "5.8.0",
+ "typescript": "2.6.1"
+ },
+ "dependencies": {
+ "tslint-react": "^3.2.0"
+ }
+}
diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json
new file mode 100644
index 000000000..8b839f25a
--- /dev/null
+++ b/packages/tslint-config/tslint.json
@@ -0,0 +1,53 @@
+{
+ "extends": [
+ "tslint:latest",
+ "tslint-react"
+ ],
+ "rules": {
+ "arrow-parens": [true, "ban-single-arg-parens"],
+ "ordered-imports": false,
+ "quotemark": [true, "single", "avoid-escape", "jsx-double"],
+ "callable-types": true,
+ "interface-name": false,
+ "interface-over-type-literal": true,
+ "object-literal-sort-keys": false,
+ "max-classes-per-file": false,
+ "max-line-length": [true, 120],
+ "member-ordering": [true,
+ "public-before-private",
+ "static-before-instance",
+ "variables-before-functions"
+ ],
+ "no-angle-bracket-type-assertion": true,
+ "no-default-export": true,
+ "no-empty-interface": false,
+ "no-string-throw": true,
+ "no-submodule-imports": false,
+ "no-implicit-dependencies": [true, "dev"],
+ "prefer-const": true,
+ "variable-name": [true,
+ "ban-keywords",
+ "allow-pascal-case"
+ ],
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-rest-spread",
+ "check-type",
+ "check-typecast",
+ "check-preblock"
+ ],
+ "jsx-alignment": true,
+ "jsx-boolean-value": true,
+ "jsx-curly-spacing": [true, "never"],
+ "jsx-no-lambda": true,
+ "jsx-no-multiline-js": false,
+ "jsx-no-string-ref": true,
+ "jsx-self-close": true,
+ "jsx-wrap-multiline": false,
+ "jsx-no-bind": false
+ }
+}