diff options
21 files changed, 191 insertions, 91 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 85b055571..d95940898 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,24 +6,117 @@ jobs: - image: circleci/node:6.12 environment: CONTRACTS_COMMIT_HASH: '9ed05f5' + working_directory: ~/repo steps: - checkout - run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} - run: name: yarn - command: yarn + command: yarn --frozen-lockfile - save_cache: key: dependency-cache-{{ checksum "package.json" }} paths: - - ~/.cache/yarn + - ./node_modules - run: wget https://s3.amazonaws.com/testrpc-shapshots/${CONTRACTS_COMMIT_HASH}.zip - run: unzip ${CONTRACTS_COMMIT_HASH}.zip -d testrpc_snapshot - run: node ./node_modules/lerna/bin/lerna.js bootstrap - run: yarn lerna:run build + - save_cache: + key: repo-{{ .Environment.CIRCLE_SHA1 }} + paths: + - ~/repo + test-0xjs: + docker: + - image: circleci/node:6.12 + working_directory: ~/repo + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} - run: name: testrpc command: npm run testrpc -- --db testrpc_snapshot background: true - - run: yarn lerna:run test:circleci + - run: yarn lerna:run --scope 0x.js test:circleci + test-contracts: + docker: + - image: circleci/node:6.12 + working_directory: ~/repo + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: testrpc + command: npm run testrpc -- --db testrpc_snapshot + background: true + - run: yarn lerna:run --scope contracts test:circleci:contracts + test-deployer: + docker: + - image: circleci/node:6.12 + working_directory: ~/repo + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: testrpc + command: npm run testrpc -- --db testrpc_snapshot + background: true + - run: yarn lerna:run --scope contracts test:circleci:deployer + test-rest: + docker: + - image: circleci/node:6.12 + working_directory: ~/repo + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: testrpc + command: npm run testrpc -- --db testrpc_snapshot + background: true + - run: yarn lerna:run --ignore contracts --ignore 0x.js --ignore subproviders test:circleci + lint: + working_directory: ~/repo + docker: + - image: circleci/node:6.12 + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} - run: yarn lerna:run lint + prettier: + working_directory: ~/repo + docker: + - image: circleci/node:6.12 + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} - run: yarn prettier:ci +workflows: + version: 2 + main: + jobs: + - build + - test-0xjs: + requires: + - build + - test-contracts: + requires: + - build + - test-deployer: + requires: + - build + - test-rest: + requires: + - build + - prettier: + requires: + - build + - lint: + requires: + - build diff --git a/package.json b/package.json index 189a68937..dcb48c10d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@0xproject/utils": "^0.2.0", "async-child-process": "^1.1.1", - "ethereumjs-testrpc": "6.0.3", + "ethereumjs-testrpc": "^6.0.3", "lerna": "^2.5.1", "prettier": "1.9.2", "publish-release": "0xproject/publish-release", diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 99da45ffb..1405e1bf7 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -14,21 +14,16 @@ "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated", "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'", - "test:circleci": - "run-s test:coverage report_test_coverage && if [ $CIRCLE_BRANCH = \"development\" ]; then yarn test:umd; fi", + "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", "report_test_coverage": "nyc report --reporter=text-lcov | coveralls", "update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;", "clean": "shx rm -rf _bundles lib test_temp", - "build:umd:dev": "webpack", "build:umd:prod": "NODE_ENV=production webpack", "build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;", "test:commonjs": "run-s build:commonjs run_mocha", - "pretest:umd": "run-s clean build:umd:dev build:commonjs", - "substitute_umd_bundle": "shx mv _bundles/* lib/src", "run_mocha": "mocha lib/test/**/*_test.js --timeout 10000 --bail --exit" }, "config": { @@ -88,11 +83,9 @@ "@0xproject/web3-wrapper": "^0.1.5", "bintrees": "^1.0.2", "bn.js": "^4.11.8", - "compare-versions": "^3.0.1", "ethereumjs-abi": "^0.6.4", "ethereumjs-blockstream": "^2.0.6", "ethereumjs-util": "^5.1.1", - "find-versions": "^2.0.0", "js-sha3": "^0.6.1", "lodash": "^4.17.4", "uuid": "^3.1.0", diff --git a/packages/0x.js/scripts/test_umd.sh b/packages/0x.js/scripts/test_umd.sh deleted file mode 100755 index e3eba088a..000000000 --- a/packages/0x.js/scripts/test_umd.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -# This script runs umd tests and cleans up after them while preserving the `return_code` for CI -# UMD tests should only be run after building the commonjs because they reuse some of the commonjs build artifacts -run-s substitute_umd_bundle run_mocha -return_code=$? -exit $return_code diff --git a/packages/0x.js/src/globals.d.ts b/packages/0x.js/src/globals.d.ts index ff8e00b69..4f4932b6e 100644 --- a/packages/0x.js/src/globals.d.ts +++ b/packages/0x.js/src/globals.d.ts @@ -25,18 +25,6 @@ declare module '*.json' { /* tslint:enable */ } -// find-version declarations -declare function findVersions(version: string): string[]; -declare module 'find-versions' { - export = findVersions; -} - -// compare-version declarations -declare function compareVersions(firstVersion: string, secondVersion: string): number; -declare module 'compare-versions' { - export = compareVersions; -} - declare module 'ethereumjs-abi' { const soliditySHA3: (argTypes: string[], args: any[]) => Buffer; } diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts index 43a60957b..5d05bfb60 100644 --- a/packages/0x.js/src/order_watcher/event_watcher.ts +++ b/packages/0x.js/src/order_watcher/event_watcher.ts @@ -51,6 +51,10 @@ export class EventWatcher { } private async _pollForBlockchainEventsAsync(callback: EventWatcherCallback): Promise<void> { const pendingEvents = await this._getEventsAsync(); + if (_.isUndefined(pendingEvents)) { + // HACK: This should never happen, but happens frequently on CI due to a ganache bug + return; + } if (pendingEvents.length === 0) { // HACK: Sometimes when node rebuilds the pending block we get back the empty result. // We don't want to emit a lot of removal events and bring them back after a couple of miliseconds, diff --git a/packages/0x.js/test/utils/constants.ts b/packages/0x.js/test/utils/constants.ts index 7b6b2ee7b..a9e665c25 100644 --- a/packages/0x.js/test/utils/constants.ts +++ b/packages/0x.js/test/utils/constants.ts @@ -1,6 +1,6 @@ export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', - RPC_URL: 'http://localhost:8545/', + RPC_URL: 'http://localhost:8545', ROPSTEN_NETWORK_ID: 3, KOVAN_NETWORK_ID: 42, TESTRPC_NETWORK_ID: 50, diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 2991201c3..1cf716f92 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -16,7 +16,8 @@ "migrate:truffle": "npm run build; truffle migrate", "migrate": "npm run build; node lib/deploy/cli.js migrate", "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'", - "test:circleci": "yarn test; yarn test:deployer", + "test:circleci:contracts": "yarn test", + "test:circleci:deployer": "yarn test:deployer", "test:deployer": "npm run build; mocha lib/deploy/test/*_test.js" }, "repository": { diff --git a/packages/dev-utils/src/rpc.ts b/packages/dev-utils/src/rpc.ts index cf6678f81..36f8b1ef9 100644 --- a/packages/dev-utils/src/rpc.ts +++ b/packages/dev-utils/src/rpc.ts @@ -41,7 +41,7 @@ export class RPC { method, params, }); - this._url += 1; + this._id += 1; return payload; } private async _sendAsync(payload: string): Promise<any> { diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json index 486780de6..971588b08 100644 --- a/packages/tslint-config/tslint.json +++ b/packages/tslint-config/tslint.json @@ -60,6 +60,7 @@ "prefer-function-over-method": true, "promise-function-async": true, "quotemark": [true, "single", "avoid-escape", "jsx-double"], + "restrict-plus-operands": true, "semicolon": [true, "always"], "space-before-function-paren": [ true, diff --git a/packages/website/public/images/social/discourse.png b/packages/website/public/images/social/discourse.png Binary files differnew file mode 100644 index 000000000..4bca3de0d --- /dev/null +++ b/packages/website/public/images/social/discourse.png diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 1320fbeb9..5530701c0 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -368,14 +368,22 @@ export class Blockchain { const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval(async () => { - const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - if (!balance.eq(currBalance)) { - this._dispatcher.replaceTokenBalanceByAddress(token.address, balance); - clearInterval(this._zrxPollIntervalId); + this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval( + async () => { + const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); + if (!balance.eq(currBalance)) { + this._dispatcher.replaceTokenBalanceByAddress(token.address, balance); + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); + delete this._zrxPollIntervalId; + } + }, + 5000, + (err: Error) => { + utils.consoleLog(`Polling tokenBalance failed: ${err}`); + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); delete this._zrxPollIntervalId; - } - }, 5000); + }, + ); } public async signOrderHashAsync(orderHash: string): Promise<SignatureData> { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); @@ -471,7 +479,7 @@ export class Blockchain { this._web3Wrapper.updatePrevUserAddress(newUserAddress); } public destroy() { - clearInterval(this._zrxPollIntervalId); + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); this._web3Wrapper.destroy(); this._stopWatchingExchangeLogFillEvents(); } diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index f2c505207..300e71f1f 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -105,7 +105,7 @@ export class EthWethConversionButton extends React.Component< } this.props.onConversionSuccessful(); } catch (err) { - const errMsg = '' + err; + const errMsg = `${err}`; if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); } else if (!_.includes(errMsg, 'User denied transaction')) { diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index 3346f2545..a0f1a0c96 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -13,7 +13,6 @@ interface FooterMenuItem { title: string; path?: string; isExternal?: boolean; - fileName?: string; } enum Sections { @@ -56,25 +55,26 @@ const menuItemsBySection: MenuItemsBySection = { title: 'Rocket.chat', isExternal: true, path: constants.URL_ZEROEX_CHAT, - fileName: 'rocketchat.png', }, { title: 'Blog', isExternal: true, path: constants.URL_BLOG, - fileName: 'medium.png', }, { title: 'Twitter', isExternal: true, path: constants.URL_TWITTER, - fileName: 'twitter.png', }, { title: 'Reddit', isExternal: true, path: constants.URL_REDDIT, - fileName: 'reddit.png', + }, + { + title: 'Forum', + isExternal: true, + path: constants.URL_DISCOURSE_FORUM, }, ], Organization: [ @@ -105,6 +105,7 @@ const titleToIcon: { [title: string]: string } = { Blog: 'medium.png', Twitter: 'twitter.png', Reddit: 'reddit.png', + Forum: 'discourse.png', }; export interface FooterProps {} diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx index 3144fc3bd..3ae0d48a7 100644 --- a/packages/website/ts/components/generate_order/generate_order_form.tsx +++ b/packages/website/ts/components/generate_order/generate_order_form.tsx @@ -329,7 +329,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G ${validationResult.errors}`); } } catch (err) { - const errMsg = '' + err; + const errMsg = `${err}`; if (utils.didUserDenyWeb3Request(errMsg)) { globalErrMsg = 'User denied sign request'; } else { diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx index 1be441e17..da46db4f4 100644 --- a/packages/website/ts/components/inputs/allowance_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_toggle.tsx @@ -77,7 +77,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow this.setState({ isSpinnerVisible: false, }); - const errMsg = '' + err; + const errMsg = `${err}`; if (_.includes(errMsg, 'User denied transaction')) { return; } diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 112b3fe60..2cef413c7 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -169,7 +169,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \ gas costs. It might take a bit of time for the test ether to show up.' : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \ - You can convert between Ether and Ether Tokens by clicking the "convert" button below.'} + You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'} </div> <Table selectable={false} style={styles.bgColor}> <TableHeader displaySelectAll={false} adjustForCheckbox={false}> @@ -514,7 +514,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); return true; } catch (err) { - const errMsg = '' + err; + const errMsg = `${err}`; if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); return false; diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx index 601471222..0859755fe 100644 --- a/packages/website/ts/components/top_bar.tsx +++ b/packages/website/ts/components/top_bar.tsx @@ -168,14 +168,14 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> )} {this.props.blockchainIsLoaded && - !_.isEmpty(this.props.userAddress) && <div className="col col-5">{this._renderUser()}</div>} - {!this._isViewingPortal() && ( - <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}> - <div style={menuIconStyle}> - <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} /> - </div> + _.isEmpty(this.props.userAddress) && ( + <div className="col col-5 sm-hide xs-hide">{this._renderUser()}</div> + )} + <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}> + <div style={menuIconStyle}> + <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} /> </div> - )} + </div> </div> {this._renderDrawer()} </div> diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 3fc1e6397..dded82114 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -65,6 +65,7 @@ export const constants = { URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js', URL_BITLY_API: 'https://api-ssl.bitly.com', URL_BLOG: 'https://blog.0xproject.com/latest', + URL_DISCOURSE_FORUM: 'https://forum.0xproject.com', URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/', URL_ETHER_FAUCET: 'https://faucet.0xproject.com', URL_GITHUB_ORG: 'https://github.com/0xProject', diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 6f260dc48..415df6e8b 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -1,6 +1,7 @@ import { BigNumber, intervalUtils, promisify } from '@0xproject/utils'; import * as _ from 'lodash'; import { Dispatcher } from 'ts/redux/dispatcher'; +import { utils } from 'ts/utils/utils'; import * as Web3 from 'web3'; export class Web3Wrapper { @@ -101,41 +102,48 @@ export class Web3Wrapper { let prevNodeVersion: string; this._prevUserEtherBalanceInEth = new BigNumber(0); this._dispatcher.updateNetworkId(this._prevNetworkId); - this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(async () => { - // Check for network state changes - const currentNetworkId = await this.getNetworkIdIfExists(); - if (currentNetworkId !== this._prevNetworkId) { - this._prevNetworkId = currentNetworkId; - this._dispatcher.updateNetworkId(currentNetworkId); - } - - // Check for node version changes - const currentNodeVersion = await this.getNodeVersionAsync(); - if (currentNodeVersion !== prevNodeVersion) { - prevNodeVersion = currentNodeVersion; - this._dispatcher.updateNodeVersion(currentNodeVersion); - } - - if (this._shouldPollUserAddress) { - const userAddressIfExists = await this.getFirstAccountIfExistsAsync(); - // Update makerAddress on network change - if (this._prevUserAddress !== userAddressIfExists) { - this._prevUserAddress = userAddressIfExists; - this._dispatcher.updateUserAddress(userAddressIfExists); + this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval( + async () => { + // Check for network state changes + const currentNetworkId = await this.getNetworkIdIfExists(); + if (currentNetworkId !== this._prevNetworkId) { + this._prevNetworkId = currentNetworkId; + this._dispatcher.updateNetworkId(currentNetworkId); } - // Check for user ether balance changes - if (userAddressIfExists !== '') { - await this._updateUserEtherBalanceAsync(userAddressIfExists); + // Check for node version changes + const currentNodeVersion = await this.getNodeVersionAsync(); + if (currentNodeVersion !== prevNodeVersion) { + prevNodeVersion = currentNodeVersion; + this._dispatcher.updateNodeVersion(currentNodeVersion); } - } else { - // This logic is primarily for the Ledger, since we don't regularly poll for the address - // we simply update the balance for the last fetched address. - if (!_.isEmpty(this._prevUserAddress)) { - await this._updateUserEtherBalanceAsync(this._prevUserAddress); + + if (this._shouldPollUserAddress) { + const userAddressIfExists = await this.getFirstAccountIfExistsAsync(); + // Update makerAddress on network change + if (this._prevUserAddress !== userAddressIfExists) { + this._prevUserAddress = userAddressIfExists; + this._dispatcher.updateUserAddress(userAddressIfExists); + } + + // Check for user ether balance changes + if (userAddressIfExists !== '') { + await this._updateUserEtherBalanceAsync(userAddressIfExists); + } + } else { + // This logic is primarily for the Ledger, since we don't regularly poll for the address + // we simply update the balance for the last fetched address. + if (!_.isEmpty(this._prevUserAddress)) { + await this._updateUserEtherBalanceAsync(this._prevUserAddress); + } } - } - }, 5000); + }, + 5000, + (err: Error) => { + utils.consoleLog(`Watching network and balances failed: ${err}`); + this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); + }, + ); } private async _updateUserEtherBalanceAsync(userAddress: string) { const balance = await this.getBalanceInEthAsync(userAddress); @@ -145,6 +153,6 @@ export class Web3Wrapper { } } private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() { - clearInterval(this._watchNetworkAndBalanceIntervalId); + intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); } } @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@0xproject/utils@^0.1.0": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.1.3.tgz#58a9c7e19ab7710e0af17a0c2f1c7fc1b3140e85" + dependencies: + bignumber.js "~4.1.0" + js-sha3 "^0.7.0" + lodash "^4.17.4" + "@types/accounting@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@types/accounting/-/accounting-0.4.1.tgz#865d9f5694fd7c438fba34eb4bc82eec6f34cdd5" @@ -3184,7 +3192,7 @@ ethereumjs-blockstream@^2.0.6: source-map-support "0.4.14" uuid "3.0.1" -ethereumjs-testrpc@6.0.3: +ethereumjs-testrpc@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz#7a0b87bf3670f92f607f98fa6a78801d9741b124" dependencies: |