aboutsummaryrefslogtreecommitdiffstats
path: root/packages/json-schemas
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2017-11-14 08:32:01 +0800
committerBrandon Millman <brandon.millman@gmail.com>2017-11-14 22:42:50 +0800
commit56b5619d24b44d23f770b58b0c9e1d4a63b89aca (patch)
tree52c047aa9ce6dbd5af93f495dbb21a8c3a662a6f /packages/json-schemas
parent08963f269b543d0f6f125ff77efdda23339865ed (diff)
downloaddexon-0x-contracts-56b5619d24b44d23f770b58b0c9e1d4a63b89aca.tar.gz
dexon-0x-contracts-56b5619d24b44d23f770b58b0c9e1d4a63b89aca.tar.zst
dexon-0x-contracts-56b5619d24b44d23f770b58b0c9e1d4a63b89aca.zip
Add json-schemas package to mono repo
Diffstat (limited to 'packages/json-schemas')
-rw-r--r--packages/json-schemas/README.md15
-rw-r--r--packages/json-schemas/package.json45
-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
29 files changed, 1525 insertions, 0 deletions
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..e9dcf0379
--- /dev/null
+++ b/packages/json-schemas/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "@0xproject/json-schemas",
+ "version": "0.6.6",
+ "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": {
+ "jsonschema": "^1.2.0",
+ "lodash.values": "^4.3.0"
+ },
+ "devDependencies": {
+ "@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.5.0",
+ "tslint-config-0xproject": "^0.0.2",
+ "typescript": "^2.4.2"
+ }
+}
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..5842a872a
--- /dev/null
+++ b/packages/json-schemas/tslint.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "tslint-config-0xproject"
+ ]
+}