From 1147cb56ba4415cd0217cbbad5ca03bb5a8e523a Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Nov 2017 16:25:21 -0500 Subject: Add assert package to the monorepo --- packages/0x.js/package.json | 2 +- packages/assert/README.md | 5 + packages/assert/circle.yml | 14 ++ packages/assert/package.json | 45 +++++ packages/assert/src/globals.d.ts | 5 + packages/assert/src/index.ts | 84 +++++++++ packages/assert/test/assert_test.ts | 338 ++++++++++++++++++++++++++++++++++++ packages/assert/tsconfig.json | 18 ++ packages/assert/tslint.json | 5 + yarn.lock | 30 +++- 10 files changed, 537 insertions(+), 9 deletions(-) create mode 100644 packages/assert/README.md create mode 100644 packages/assert/circle.yml create mode 100644 packages/assert/package.json create mode 100644 packages/assert/src/globals.d.ts create mode 100644 packages/assert/src/index.ts create mode 100644 packages/assert/test/assert_test.ts create mode 100644 packages/assert/tsconfig.json create mode 100644 packages/assert/tslint.json diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 6e30df612..02ae87cd5 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -87,7 +87,7 @@ }, "dependencies": { "0x-json-schemas": "^0.6.1", - "bignumber.js": "^4.1.0", + "bignumber.js": "~4.1.0", "compare-versions": "^3.0.1", "es6-promisify": "^5.0.0", "ethereumjs-abi": "^0.6.4", diff --git a/packages/assert/README.md b/packages/assert/README.md new file mode 100644 index 000000000..4e72d5bcb --- /dev/null +++ b/packages/assert/README.md @@ -0,0 +1,5 @@ + + +--- + +Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/assert/circle.yml b/packages/assert/circle.yml new file mode 100644 index 000000000..32b9e7315 --- /dev/null +++ b/packages/assert/circle.yml @@ -0,0 +1,14 @@ +machine: + node: + version: 6.5.0 + +dependencies: + override: + - yarn + cache_directories: + - ~/.cache/yarn + +test: + override: + - yarn test + - yarn lint diff --git a/packages/assert/package.json b/packages/assert/package.json new file mode 100644 index 000000000..43ecf5371 --- /dev/null +++ b/packages/assert/package.json @@ -0,0 +1,45 @@ +{ + "name": "0x-assert", + "version": "0.0.3", + "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" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/assert.git" + }, + "bugs": { + "url": "https://github.com/0xProject/assert/issues" + }, + "homepage": "https://github.com/0xProject/assert#readme", + "devDependencies": { + "@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.5.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "^2.4.2" + }, + "dependencies": { + "0x-json-schemas": "^0.6.5", + "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..b391a8bbb --- /dev/null +++ b/packages/assert/src/index.ts @@ -0,0 +1,84 @@ +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 '0x-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)); + }, + 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..0e35f7f50 --- /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 '0x-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..44f7b7a96 --- /dev/null +++ b/packages/assert/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "lib": [ "es2017"], + "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..5842a872a --- /dev/null +++ b/packages/assert/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "tslint-config-0xproject" + ] +} diff --git a/yarn.lock b/yarn.lock index 16493a1dd..7eb98bc97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"0x-json-schemas@^0.6.1": +"0x-json-schemas@^0.6.1", "0x-json-schemas@^0.6.5": version "0.6.6" resolved "https://registry.yarnpkg.com/0x-json-schemas/-/0x-json-schemas-0.6.6.tgz#3852e639245474a14daa2f8c454ba83ca5df8a9c" dependencies: @@ -36,7 +36,7 @@ dependencies: jsonschema "*" -"@types/lodash@^4.14.37", "@types/lodash@^4.14.64": +"@types/lodash@^4.14.37", "@types/lodash@^4.14.64", "@types/lodash@^4.14.78": version "4.14.85" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.85.tgz#a16fbf942422f6eca5622b6910492c496c35069b" @@ -48,7 +48,7 @@ version "2.0.29" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" -"@types/mocha@^2.2.41": +"@types/mocha@^2.2.41", "@types/mocha@^2.2.42": version "2.2.44" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" @@ -73,6 +73,10 @@ dependencies: "@types/node" "*" +"@types/valid-url@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45" + JSONStream@^1.0.4: version "1.3.1" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a" @@ -802,7 +806,7 @@ big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" -bignumber.js@^4.0.2, bignumber.js@^4.1.0: +bignumber.js@^4.0.2, bignumber.js@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" @@ -1599,7 +1603,7 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@^3.1.4: +crypto-js@^3.1.4, crypto-js@^3.1.6: version "3.1.8" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.8.tgz#715f070bf6014f2ae992a98b3929258b713f08d5" @@ -2011,6 +2015,12 @@ eth-sig-util@^1.3.0: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +ethereum-address@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/ethereum-address/-/ethereum-address-0.0.4.tgz#91729b2bc8a0044bbee2c05ccf6d0417953e5f95" + dependencies: + crypto-js "^3.1.6" + ethereum-common@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" @@ -3827,7 +3837,7 @@ mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: dependencies: minimist "0.0.8" -mocha@^4.0.0: +mocha@^4.0.0, mocha@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.0.1.tgz#0aee5a95cf69a4618820f5e51fa31717117daf1b" dependencies: @@ -3982,7 +3992,7 @@ normalize-path@^2.0.0, normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" -npm-run-all@^4.0.2: +npm-run-all@^4.0.2, npm-run-all@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.2.tgz#90d62d078792d20669139e718621186656cea056" dependencies: @@ -5701,7 +5711,7 @@ typescript@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" -typescript@^2.4.1: +typescript@^2.4.2, typescript@~2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.1.tgz#ef39cdea27abac0b500242d6726ab90e0c846631" @@ -5813,6 +5823,10 @@ uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +valid-url@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" -- cgit From b0f13c17e2c62f9f1942d6ec2a6ef3fa6873558d Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Nov 2017 16:25:21 -0500 Subject: Add assert package to the monorepo --- packages/0x.js/package.json | 2 +- packages/assert/README.md | 5 + packages/assert/circle.yml | 14 ++ packages/assert/package.json | 45 +++++ packages/assert/src/globals.d.ts | 5 + packages/assert/src/index.ts | 84 +++++++++ packages/assert/test/assert_test.ts | 338 ++++++++++++++++++++++++++++++++++++ packages/assert/tsconfig.json | 18 ++ packages/assert/tslint.json | 5 + yarn.lock | 30 +++- 10 files changed, 537 insertions(+), 9 deletions(-) create mode 100644 packages/assert/README.md create mode 100644 packages/assert/circle.yml create mode 100644 packages/assert/package.json create mode 100644 packages/assert/src/globals.d.ts create mode 100644 packages/assert/src/index.ts create mode 100644 packages/assert/test/assert_test.ts create mode 100644 packages/assert/tsconfig.json create mode 100644 packages/assert/tslint.json diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 6e30df612..02ae87cd5 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -87,7 +87,7 @@ }, "dependencies": { "0x-json-schemas": "^0.6.1", - "bignumber.js": "^4.1.0", + "bignumber.js": "~4.1.0", "compare-versions": "^3.0.1", "es6-promisify": "^5.0.0", "ethereumjs-abi": "^0.6.4", diff --git a/packages/assert/README.md b/packages/assert/README.md new file mode 100644 index 000000000..4e72d5bcb --- /dev/null +++ b/packages/assert/README.md @@ -0,0 +1,5 @@ + + +--- + +Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/assert/circle.yml b/packages/assert/circle.yml new file mode 100644 index 000000000..32b9e7315 --- /dev/null +++ b/packages/assert/circle.yml @@ -0,0 +1,14 @@ +machine: + node: + version: 6.5.0 + +dependencies: + override: + - yarn + cache_directories: + - ~/.cache/yarn + +test: + override: + - yarn test + - yarn lint diff --git a/packages/assert/package.json b/packages/assert/package.json new file mode 100644 index 000000000..43ecf5371 --- /dev/null +++ b/packages/assert/package.json @@ -0,0 +1,45 @@ +{ + "name": "0x-assert", + "version": "0.0.3", + "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" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/assert.git" + }, + "bugs": { + "url": "https://github.com/0xProject/assert/issues" + }, + "homepage": "https://github.com/0xProject/assert#readme", + "devDependencies": { + "@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.5.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "^2.4.2" + }, + "dependencies": { + "0x-json-schemas": "^0.6.5", + "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..b391a8bbb --- /dev/null +++ b/packages/assert/src/index.ts @@ -0,0 +1,84 @@ +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 '0x-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)); + }, + 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..0e35f7f50 --- /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 '0x-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..bdb4e43a8 --- /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..5842a872a --- /dev/null +++ b/packages/assert/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "tslint-config-0xproject" + ] +} diff --git a/yarn.lock b/yarn.lock index 16493a1dd..7eb98bc97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"0x-json-schemas@^0.6.1": +"0x-json-schemas@^0.6.1", "0x-json-schemas@^0.6.5": version "0.6.6" resolved "https://registry.yarnpkg.com/0x-json-schemas/-/0x-json-schemas-0.6.6.tgz#3852e639245474a14daa2f8c454ba83ca5df8a9c" dependencies: @@ -36,7 +36,7 @@ dependencies: jsonschema "*" -"@types/lodash@^4.14.37", "@types/lodash@^4.14.64": +"@types/lodash@^4.14.37", "@types/lodash@^4.14.64", "@types/lodash@^4.14.78": version "4.14.85" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.85.tgz#a16fbf942422f6eca5622b6910492c496c35069b" @@ -48,7 +48,7 @@ version "2.0.29" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" -"@types/mocha@^2.2.41": +"@types/mocha@^2.2.41", "@types/mocha@^2.2.42": version "2.2.44" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" @@ -73,6 +73,10 @@ dependencies: "@types/node" "*" +"@types/valid-url@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45" + JSONStream@^1.0.4: version "1.3.1" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a" @@ -802,7 +806,7 @@ big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" -bignumber.js@^4.0.2, bignumber.js@^4.1.0: +bignumber.js@^4.0.2, bignumber.js@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" @@ -1599,7 +1603,7 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@^3.1.4: +crypto-js@^3.1.4, crypto-js@^3.1.6: version "3.1.8" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.8.tgz#715f070bf6014f2ae992a98b3929258b713f08d5" @@ -2011,6 +2015,12 @@ eth-sig-util@^1.3.0: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +ethereum-address@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/ethereum-address/-/ethereum-address-0.0.4.tgz#91729b2bc8a0044bbee2c05ccf6d0417953e5f95" + dependencies: + crypto-js "^3.1.6" + ethereum-common@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" @@ -3827,7 +3837,7 @@ mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: dependencies: minimist "0.0.8" -mocha@^4.0.0: +mocha@^4.0.0, mocha@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.0.1.tgz#0aee5a95cf69a4618820f5e51fa31717117daf1b" dependencies: @@ -3982,7 +3992,7 @@ normalize-path@^2.0.0, normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" -npm-run-all@^4.0.2: +npm-run-all@^4.0.2, npm-run-all@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.2.tgz#90d62d078792d20669139e718621186656cea056" dependencies: @@ -5701,7 +5711,7 @@ typescript@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" -typescript@^2.4.1: +typescript@^2.4.2, typescript@~2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.1.tgz#ef39cdea27abac0b500242d6726ab90e0c846631" @@ -5813,6 +5823,10 @@ uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +valid-url@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" -- cgit From 3e8e3478a3d770b16a5e6fec1fbd43a3d7967450 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 17:42:42 -0500 Subject: Rename assert sub-package and removed duplicate methods from 0x.js's assert module --- packages/0x-assert/README.md | 5 + packages/0x-assert/circle.yml | 14 ++ packages/0x-assert/package.json | 47 +++++ packages/0x-assert/src/globals.d.ts | 5 + packages/0x-assert/src/index.ts | 91 +++++++++ packages/0x-assert/test/assert_test.ts | 338 +++++++++++++++++++++++++++++++++ packages/0x-assert/tsconfig.json | 18 ++ packages/0x-assert/tslint.json | 5 + packages/0x.js/src/utils/assert.ts | 82 +------- packages/assert/README.md | 5 - packages/assert/circle.yml | 14 -- packages/assert/package.json | 45 ----- packages/assert/src/globals.d.ts | 5 - packages/assert/src/index.ts | 84 -------- packages/assert/test/assert_test.ts | 338 --------------------------------- packages/assert/tsconfig.json | 18 -- packages/assert/tslint.json | 5 - 17 files changed, 528 insertions(+), 591 deletions(-) create mode 100644 packages/0x-assert/README.md create mode 100644 packages/0x-assert/circle.yml create mode 100644 packages/0x-assert/package.json create mode 100644 packages/0x-assert/src/globals.d.ts create mode 100644 packages/0x-assert/src/index.ts create mode 100644 packages/0x-assert/test/assert_test.ts create mode 100644 packages/0x-assert/tsconfig.json create mode 100644 packages/0x-assert/tslint.json delete mode 100644 packages/assert/README.md delete mode 100644 packages/assert/circle.yml delete mode 100644 packages/assert/package.json delete mode 100644 packages/assert/src/globals.d.ts delete mode 100644 packages/assert/src/index.ts delete mode 100644 packages/assert/test/assert_test.ts delete mode 100644 packages/assert/tsconfig.json delete mode 100644 packages/assert/tslint.json diff --git a/packages/0x-assert/README.md b/packages/0x-assert/README.md new file mode 100644 index 000000000..4e72d5bcb --- /dev/null +++ b/packages/0x-assert/README.md @@ -0,0 +1,5 @@ + + +--- + +Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/0x-assert/circle.yml b/packages/0x-assert/circle.yml new file mode 100644 index 000000000..32b9e7315 --- /dev/null +++ b/packages/0x-assert/circle.yml @@ -0,0 +1,14 @@ +machine: + node: + version: 6.5.0 + +dependencies: + override: + - yarn + cache_directories: + - ~/.cache/yarn + +test: + override: + - yarn test + - yarn lint diff --git a/packages/0x-assert/package.json b/packages/0x-assert/package.json new file mode 100644 index 000000000..c560ca5dc --- /dev/null +++ b/packages/0x-assert/package.json @@ -0,0 +1,47 @@ +{ + "private": true, + "name": "0x-assert", + "version": "0.0.3", + "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" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/assert.git" + }, + "bugs": { + "url": "https://github.com/0xProject/assert/issues" + }, + "homepage": "https://github.com/0xProject/assert#readme", + "devDependencies": { + "@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.5.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "^2.4.2" + }, + "dependencies": { + "0x-json-schemas": "^0.6.5", + "bignumber.js": "~4.1.0", + "ethereum-address": "^0.0.4", + "lodash": "^4.17.4", + "valid-url": "^1.0.9", + "0x-assert": "0.0.3" + } +} diff --git a/packages/0x-assert/src/globals.d.ts b/packages/0x-assert/src/globals.d.ts new file mode 100644 index 000000000..cc47f3113 --- /dev/null +++ b/packages/0x-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/0x-assert/src/index.ts b/packages/0x-assert/src/index.ts new file mode 100644 index 000000000..bfbb56b23 --- /dev/null +++ b/packages/0x-assert/src/index.ts @@ -0,0 +1,91 @@ +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 '0x-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/0x-assert/test/assert_test.ts b/packages/0x-assert/test/assert_test.ts new file mode 100644 index 000000000..0e35f7f50 --- /dev/null +++ b/packages/0x-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 '0x-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/0x-assert/tsconfig.json b/packages/0x-assert/tsconfig.json new file mode 100644 index 000000000..709e20154 --- /dev/null +++ b/packages/0x-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/0x-assert/tslint.json b/packages/0x-assert/tslint.json new file mode 100644 index 000000000..5842a872a --- /dev/null +++ b/packages/0x-assert/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "tslint-config-0xproject" + ] +} diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index e5c9439f3..b6f6c5c95 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -5,62 +5,20 @@ import {SchemaValidator, Schema} from '0x-json-schemas'; import {Web3Wrapper} from '../web3_wrapper'; import {signatureUtils} from '../utils/signature_utils'; import {ECSignature} from '../types'; +import {assert as sharedAssert} from '0x-assert'; 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 { - 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/assert/README.md b/packages/assert/README.md deleted file mode 100644 index 4e72d5bcb..000000000 --- a/packages/assert/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - ---- - -Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/assert/circle.yml b/packages/assert/circle.yml deleted file mode 100644 index 32b9e7315..000000000 --- a/packages/assert/circle.yml +++ /dev/null @@ -1,14 +0,0 @@ -machine: - node: - version: 6.5.0 - -dependencies: - override: - - yarn - cache_directories: - - ~/.cache/yarn - -test: - override: - - yarn test - - yarn lint diff --git a/packages/assert/package.json b/packages/assert/package.json deleted file mode 100644 index 43ecf5371..000000000 --- a/packages/assert/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "0x-assert", - "version": "0.0.3", - "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" - }, - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/0xProject/assert.git" - }, - "bugs": { - "url": "https://github.com/0xProject/assert/issues" - }, - "homepage": "https://github.com/0xProject/assert#readme", - "devDependencies": { - "@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.5.0", - "tslint-config-0xproject": "^0.0.2", - "typescript": "^2.4.2" - }, - "dependencies": { - "0x-json-schemas": "^0.6.5", - "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 deleted file mode 100644 index cc47f3113..000000000 --- a/packages/assert/src/globals.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index b391a8bbb..000000000 --- a/packages/assert/src/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -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 '0x-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)); - }, - 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 deleted file mode 100644 index 0e35f7f50..000000000 --- a/packages/assert/test/assert_test.ts +++ /dev/null @@ -1,338 +0,0 @@ -import 'mocha'; -import * as dirtyChai from 'dirty-chai'; -import * as chai from 'chai'; -import {BigNumber} from 'bignumber.js'; -import {schemas} from '0x-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 deleted file mode 100644 index 709e20154..000000000 --- a/packages/assert/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "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 deleted file mode 100644 index 5842a872a..000000000 --- a/packages/assert/tslint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "tslint-config-0xproject" - ] -} -- cgit From 4354d3f121a6d21844445a28cf7ffe86efc97591 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 17:57:02 -0500 Subject: Rename back --- packages/0x-assert/README.md | 5 - packages/0x-assert/circle.yml | 14 -- packages/0x-assert/package.json | 47 ----- packages/0x-assert/src/globals.d.ts | 5 - packages/0x-assert/src/index.ts | 91 --------- packages/0x-assert/test/assert_test.ts | 338 --------------------------------- packages/0x-assert/tsconfig.json | 18 -- packages/0x-assert/tslint.json | 5 - packages/0x.js/package.json | 3 +- packages/0x.js/src/utils/assert.ts | 2 +- packages/assert/README.md | 5 + packages/assert/circle.yml | 14 ++ packages/assert/package.json | 46 +++++ packages/assert/src/globals.d.ts | 5 + packages/assert/src/index.ts | 91 +++++++++ packages/assert/test/assert_test.ts | 338 +++++++++++++++++++++++++++++++++ packages/assert/tsconfig.json | 18 ++ packages/assert/tslint.json | 5 + 18 files changed, 525 insertions(+), 525 deletions(-) delete mode 100644 packages/0x-assert/README.md delete mode 100644 packages/0x-assert/circle.yml delete mode 100644 packages/0x-assert/package.json delete mode 100644 packages/0x-assert/src/globals.d.ts delete mode 100644 packages/0x-assert/src/index.ts delete mode 100644 packages/0x-assert/test/assert_test.ts delete mode 100644 packages/0x-assert/tsconfig.json delete mode 100644 packages/0x-assert/tslint.json create mode 100644 packages/assert/README.md create mode 100644 packages/assert/circle.yml create mode 100644 packages/assert/package.json create mode 100644 packages/assert/src/globals.d.ts create mode 100644 packages/assert/src/index.ts create mode 100644 packages/assert/test/assert_test.ts create mode 100644 packages/assert/tsconfig.json create mode 100644 packages/assert/tslint.json diff --git a/packages/0x-assert/README.md b/packages/0x-assert/README.md deleted file mode 100644 index 4e72d5bcb..000000000 --- a/packages/0x-assert/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - ---- - -Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/0x-assert/circle.yml b/packages/0x-assert/circle.yml deleted file mode 100644 index 32b9e7315..000000000 --- a/packages/0x-assert/circle.yml +++ /dev/null @@ -1,14 +0,0 @@ -machine: - node: - version: 6.5.0 - -dependencies: - override: - - yarn - cache_directories: - - ~/.cache/yarn - -test: - override: - - yarn test - - yarn lint diff --git a/packages/0x-assert/package.json b/packages/0x-assert/package.json deleted file mode 100644 index c560ca5dc..000000000 --- a/packages/0x-assert/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "private": true, - "name": "0x-assert", - "version": "0.0.3", - "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" - }, - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/0xProject/assert.git" - }, - "bugs": { - "url": "https://github.com/0xProject/assert/issues" - }, - "homepage": "https://github.com/0xProject/assert#readme", - "devDependencies": { - "@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.5.0", - "tslint-config-0xproject": "^0.0.2", - "typescript": "^2.4.2" - }, - "dependencies": { - "0x-json-schemas": "^0.6.5", - "bignumber.js": "~4.1.0", - "ethereum-address": "^0.0.4", - "lodash": "^4.17.4", - "valid-url": "^1.0.9", - "0x-assert": "0.0.3" - } -} diff --git a/packages/0x-assert/src/globals.d.ts b/packages/0x-assert/src/globals.d.ts deleted file mode 100644 index cc47f3113..000000000 --- a/packages/0x-assert/src/globals.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'dirty-chai'; - -declare module 'ethereum-address' { - const isAddress: (arg: any) => boolean; -} diff --git a/packages/0x-assert/src/index.ts b/packages/0x-assert/src/index.ts deleted file mode 100644 index bfbb56b23..000000000 --- a/packages/0x-assert/src/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -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 '0x-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/0x-assert/test/assert_test.ts b/packages/0x-assert/test/assert_test.ts deleted file mode 100644 index 0e35f7f50..000000000 --- a/packages/0x-assert/test/assert_test.ts +++ /dev/null @@ -1,338 +0,0 @@ -import 'mocha'; -import * as dirtyChai from 'dirty-chai'; -import * as chai from 'chai'; -import {BigNumber} from 'bignumber.js'; -import {schemas} from '0x-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/0x-assert/tsconfig.json b/packages/0x-assert/tsconfig.json deleted file mode 100644 index 709e20154..000000000 --- a/packages/0x-assert/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "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/0x-assert/tslint.json b/packages/0x-assert/tslint.json deleted file mode 100644 index 5842a872a..000000000 --- a/packages/0x-assert/tslint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "tslint-config-0xproject" - ] -} diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 02ae87cd5..2866f1a9e 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -98,6 +98,7 @@ "lodash": "^4.17.4", "publish-release": "^1.3.3", "uuid": "^3.1.0", - "web3": "^0.20.0" + "web3": "^0.20.0", + "@0xproject/assert": "0.0.3" } } diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index b6f6c5c95..775a7a609 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -5,7 +5,7 @@ import {SchemaValidator, Schema} from '0x-json-schemas'; import {Web3Wrapper} from '../web3_wrapper'; import {signatureUtils} from '../utils/signature_utils'; import {ECSignature} from '../types'; -import {assert as sharedAssert} from '0x-assert'; +import {assert as sharedAssert} from '@0xproject/assert'; const HEX_REGEX = /^0x[0-9A-F]*$/i; diff --git a/packages/assert/README.md b/packages/assert/README.md new file mode 100644 index 000000000..4e72d5bcb --- /dev/null +++ b/packages/assert/README.md @@ -0,0 +1,5 @@ + + +--- + +Standard type and schema assertions to be used across all 0x projects and packages diff --git a/packages/assert/circle.yml b/packages/assert/circle.yml new file mode 100644 index 000000000..32b9e7315 --- /dev/null +++ b/packages/assert/circle.yml @@ -0,0 +1,14 @@ +machine: + node: + version: 6.5.0 + +dependencies: + override: + - yarn + cache_directories: + - ~/.cache/yarn + +test: + override: + - yarn test + - yarn lint diff --git a/packages/assert/package.json b/packages/assert/package.json new file mode 100644 index 000000000..ab640b361 --- /dev/null +++ b/packages/assert/package.json @@ -0,0 +1,46 @@ +{ + "private": true, + "name": "@0xproject/assert", + "version": "0.0.3", + "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" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/assert.git" + }, + "bugs": { + "url": "https://github.com/0xProject/assert/issues" + }, + "homepage": "https://github.com/0xProject/assert#readme", + "devDependencies": { + "@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.5.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "^2.4.2" + }, + "dependencies": { + "0x-json-schemas": "^0.6.5", + "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..bfbb56b23 --- /dev/null +++ b/packages/assert/src/index.ts @@ -0,0 +1,91 @@ +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 '0x-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..0e35f7f50 --- /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 '0x-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..5842a872a --- /dev/null +++ b/packages/assert/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "tslint-config-0xproject" + ] +} -- cgit From 087ea1f06824491663df5dc7bc4a44a4b5352d24 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:11:31 -0500 Subject: Standardize around a `test:circleci` command that any sub-package can implement in order to have their tests run on CircleCi --- .circleci/config.yml | 3 +-- packages/0x.js/package.json | 1 + packages/assert/package.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 330420db4..d6d02a4f9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,6 @@ jobs: name: testrpc command: npm run testrpc -- --db testrpc_snapshot background: true - - run: yarn lerna:run test:coverage - - run: yarn lerna:run report_test_coverage + - run: yarn lerna:run test:circleci - run: if [ $CIRCLE_BRANCH = "development" ]; then yarn lerna:run test:umd; fi - run: yarn lerna:run lint diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 2866f1a9e..b98188cc4 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -19,6 +19,7 @@ "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", "test": "run-s clean test:commonjs", "test:umd": "./scripts/test_umd.sh", "test:coverage": "nyc npm run test --all", diff --git a/packages/assert/package.json b/packages/assert/package.json index ab640b361..289bc5431 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -11,7 +11,8 @@ "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": "run-s clean build run_mocha", + "test:circleci": "yarn test" }, "license": "Apache-2.0", "repository": { -- cgit From abb2ad45ce0911f7ab28d91a24d2dde6c9f80314 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:12:01 -0500 Subject: fix orderings --- packages/0x.js/package.json | 4 ++-- packages/0x.js/src/utils/assert.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index b98188cc4..5cbbc2f59 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -87,6 +87,7 @@ "webpack": "^3.1.0" }, "dependencies": { + "@0xproject/assert": "0.0.3", "0x-json-schemas": "^0.6.1", "bignumber.js": "~4.1.0", "compare-versions": "^3.0.1", @@ -99,7 +100,6 @@ "lodash": "^4.17.4", "publish-release": "^1.3.3", "uuid": "^3.1.0", - "web3": "^0.20.0", - "@0xproject/assert": "0.0.3" + "web3": "^0.20.0" } } diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index 775a7a609..4aa83ef17 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -2,10 +2,10 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import BigNumber from 'bignumber.js'; import {SchemaValidator, Schema} from '0x-json-schemas'; +import {assert as sharedAssert} from '@0xproject/assert'; import {Web3Wrapper} from '../web3_wrapper'; import {signatureUtils} from '../utils/signature_utils'; import {ECSignature} from '../types'; -import {assert as sharedAssert} from '@0xproject/assert'; const HEX_REGEX = /^0x[0-9A-F]*$/i; -- cgit From 5b70383ce6a195a093f0dab93d0704d2963bacc2 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:20:04 -0500 Subject: remove image from sub-readme --- packages/assert/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/assert/README.md b/packages/assert/README.md index 4e72d5bcb..0c60671ea 100644 --- a/packages/assert/README.md +++ b/packages/assert/README.md @@ -1,5 +1 @@ - - ---- - Standard type and schema assertions to be used across all 0x projects and packages -- cgit From ee1a44ebebeb3ce1d4693c7162aea75de851e6d5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:20:20 -0500 Subject: Add preliminary instructions to top-level README --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index d63b0e070..da7a24823 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,26 @@ This repository contains all the 0x developer tools written in TypeScript. Our h [![Join the chat at https://gitter.im/0xProject/Lobby](https://badges.gitter.im/0xProject/Lobby.svg)](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Greenkeeper badge](https://badges.greenkeeper.io/0xProject/0x.js.svg?token=7c22e5c72acf39d3ead8d29c5d9bb38f9096df3e643024dcedd53ab732847be1&ts=1496426342666)](https://greenkeeper.io/) + +Instructions +------------ + +Make sure you have `yarn@1.x` installed locally. + +### Creating a new sub-package + +1. Make sure the `name` field in the sub-package's `package.json` starts with `@0xproject/` and has a unique name (e.g `0xproject/assert`). + +2. Run `yarn install` to install all it's dependencies. + +### How to add a sub-package as a dependency to another sub-package: + +1. Add the sub-packages name (declared in it's `package.json`) to your sub-packages `package.json` under `dependencies` or `devDependencies`. + +2. Run `yarn install` from anywhere in the mono repo. + +3. Import the sub-package as: + +``` +import {myPkg} from '0xproject/myPkg'; +``` -- cgit From c7e57a412416daa953ed62150a67a61b0924597e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:42:02 -0500 Subject: Move conditional running of umd test to 0x.js's package.json --- .circleci/config.yml | 1 - packages/0x.js/package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d6d02a4f9..9a39dd33c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,5 +25,4 @@ jobs: command: npm run testrpc -- --db testrpc_snapshot background: true - run: yarn lerna:run test:circleci - - run: if [ $CIRCLE_BRANCH = "development" ]; then yarn lerna:run test:umd; fi - run: yarn lerna:run lint diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 5cbbc2f59..6839e6513 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -19,7 +19,7 @@ "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", + "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", -- cgit From 9b083eebd72c18a450b20028809b9387811adbf4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:42:14 -0500 Subject: Remove unused circle.yml --- packages/assert/circle.yml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 packages/assert/circle.yml diff --git a/packages/assert/circle.yml b/packages/assert/circle.yml deleted file mode 100644 index 32b9e7315..000000000 --- a/packages/assert/circle.yml +++ /dev/null @@ -1,14 +0,0 @@ -machine: - node: - version: 6.5.0 - -dependencies: - override: - - yarn - cache_directories: - - ~/.cache/yarn - -test: - override: - - yarn test - - yarn lint -- cgit From 3d623126572301012f35353d95471e120fb502b9 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:45:30 -0500 Subject: Missing @ --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da7a24823..ed126187e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Make sure you have `yarn@1.x` installed locally. ### Creating a new sub-package -1. Make sure the `name` field in the sub-package's `package.json` starts with `@0xproject/` and has a unique name (e.g `0xproject/assert`). +1. Make sure the `name` field in the sub-package's `package.json` starts with `@0xproject/` and has a unique name (e.g `@0xproject/assert`). 2. Run `yarn install` to install all it's dependencies. @@ -34,5 +34,5 @@ Make sure you have `yarn@1.x` installed locally. 3. Import the sub-package as: ``` -import {myPkg} from '0xproject/myPkg'; +import {myPkg} from '@0xproject/myPkg'; ``` -- cgit From a15f78652fd3c2dffbb7e76575a1ada3179fcf48 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:50:00 -0500 Subject: Update urls --- packages/assert/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/assert/package.json b/packages/assert/package.json index 289bc5431..3ce67c97b 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -17,12 +17,12 @@ "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/0xProject/assert.git" + "url": "https://github.com/0xProject/0x.js.git" }, "bugs": { - "url": "https://github.com/0xProject/assert/issues" + "url": "https://github.com/0xProject/0x.js/issues" }, - "homepage": "https://github.com/0xProject/assert#readme", + "homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md", "devDependencies": { "@types/lodash": "^4.14.78", "@types/mocha": "^2.2.42", -- cgit From 2d0fd14d3c92c61cb64f7fe300637240c6d272ad Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 13 Nov 2017 18:50:09 -0500 Subject: remove new line --- packages/assert/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/assert/src/index.ts b/packages/assert/src/index.ts index bfbb56b23..5a9a7cc43 100644 --- a/packages/assert/src/index.ts +++ b/packages/assert/src/index.ts @@ -2,7 +2,6 @@ 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 '0x-json-schemas'; const HEX_REGEX = /^0x[0-9A-F]*$/i; -- cgit